500 幅动画图解、12 种编程语言代码、2000 条社区问答,助你快速入门数据结构与算法
+diff --git a/assets/hero/astronaut.png b/assets/hero/astronaut.png new file mode 100644 index 000000000..2d63a9f7a Binary files /dev/null and b/assets/hero/astronaut.png differ diff --git a/assets/hero/chapter_array_and_linkedlist.png b/assets/hero/chapter_array_and_linkedlist.png new file mode 100644 index 000000000..376c1da62 Binary files /dev/null and b/assets/hero/chapter_array_and_linkedlist.png differ diff --git a/assets/hero/chapter_backtracking.png b/assets/hero/chapter_backtracking.png new file mode 100644 index 000000000..af259be6f Binary files /dev/null and b/assets/hero/chapter_backtracking.png differ diff --git a/assets/hero/chapter_computational_complexity.png b/assets/hero/chapter_computational_complexity.png new file mode 100644 index 000000000..7e5bf9eaf Binary files /dev/null and b/assets/hero/chapter_computational_complexity.png differ diff --git a/assets/hero/chapter_divide_and_conquer.png b/assets/hero/chapter_divide_and_conquer.png new file mode 100644 index 000000000..63794b27c Binary files /dev/null and b/assets/hero/chapter_divide_and_conquer.png differ diff --git a/assets/hero/chapter_dynamic_programming.png b/assets/hero/chapter_dynamic_programming.png new file mode 100644 index 000000000..77b05dc49 Binary files /dev/null and b/assets/hero/chapter_dynamic_programming.png differ diff --git a/assets/hero/chapter_graph.png b/assets/hero/chapter_graph.png new file mode 100644 index 000000000..667ecb822 Binary files /dev/null and b/assets/hero/chapter_graph.png differ diff --git a/assets/hero/chapter_greedy.png b/assets/hero/chapter_greedy.png new file mode 100644 index 000000000..2ffc33448 Binary files /dev/null and b/assets/hero/chapter_greedy.png differ diff --git a/assets/hero/chapter_hashing.png b/assets/hero/chapter_hashing.png new file mode 100644 index 000000000..cf375aa7c Binary files /dev/null and b/assets/hero/chapter_hashing.png differ diff --git a/assets/hero/chapter_heap.png b/assets/hero/chapter_heap.png new file mode 100644 index 000000000..97e3bef91 Binary files /dev/null and b/assets/hero/chapter_heap.png differ diff --git a/assets/hero/chapter_searching.png b/assets/hero/chapter_searching.png new file mode 100644 index 000000000..c694bd145 Binary files /dev/null and b/assets/hero/chapter_searching.png differ diff --git a/assets/hero/chapter_sorting.png b/assets/hero/chapter_sorting.png new file mode 100644 index 000000000..1863d864d Binary files /dev/null and b/assets/hero/chapter_sorting.png differ diff --git a/assets/hero/chapter_stack_and_queue.png b/assets/hero/chapter_stack_and_queue.png new file mode 100644 index 000000000..c8581279a Binary files /dev/null and b/assets/hero/chapter_stack_and_queue.png differ diff --git a/assets/hero/chapter_tree.png b/assets/hero/chapter_tree.png new file mode 100644 index 000000000..117fc603a Binary files /dev/null and b/assets/hero/chapter_tree.png differ diff --git a/assets/hero/ground.png b/assets/hero/ground.png new file mode 100644 index 000000000..eece5b0bd Binary files /dev/null and b/assets/hero/ground.png differ diff --git a/assets/hero/links.png b/assets/hero/links.png new file mode 100644 index 000000000..a0189f949 Binary files /dev/null and b/assets/hero/links.png differ diff --git a/assets/hero/pdf_ipad.png b/assets/hero/pdf_ipad.png new file mode 100644 index 000000000..7ae7db57c Binary files /dev/null and b/assets/hero/pdf_ipad.png differ diff --git a/assets/hero/universe_bg.png b/assets/hero/universe_bg.png new file mode 100644 index 000000000..9da001555 Binary files /dev/null and b/assets/hero/universe_bg.png differ diff --git a/assets/hero/web_iphone.png b/assets/hero/web_iphone.png new file mode 100644 index 000000000..074764af7 Binary files /dev/null and b/assets/hero/web_iphone.png differ diff --git a/assets/hero/web_mac.png b/assets/hero/web_mac.png new file mode 100644 index 000000000..95f54ecc2 Binary files /dev/null and b/assets/hero/web_mac.png differ diff --git a/assets/images/logo.png b/assets/images/logo.png index 6fc6dab37..08b5970a9 100644 Binary files a/assets/images/logo.png and b/assets/images/logo.png differ diff --git a/chapter_computational_complexity/iteration_and_recursion/index.html b/chapter_computational_complexity/iteration_and_recursion/index.html index 7e7ac2422..b30388bd0 100644 --- a/chapter_computational_complexity/iteration_and_recursion/index.html +++ b/chapter_computational_complexity/iteration_and_recursion/index.html @@ -4062,7 +4062,7 @@ int WhileLoopII(int n) { int res = 0; int i = 1; // 初始化条件变量 - // 循环求和 1, 2, 4, 5... + // 循环求和 1, 4, 10, ... while (i <= n) { res += i; // 更新条件变量 diff --git a/en/assets/images/logo.png b/en/assets/images/logo.png index 6fc6dab37..08b5970a9 100644 Binary files a/en/assets/images/logo.png and b/en/assets/images/logo.png differ diff --git a/en/chapter_computational_complexity/iteration_and_recursion/index.html b/en/chapter_computational_complexity/iteration_and_recursion/index.html index ae83a0cee..0b92bc453 100644 --- a/en/chapter_computational_complexity/iteration_and_recursion/index.html +++ b/en/chapter_computational_complexity/iteration_and_recursion/index.html @@ -1929,7 +1929,7 @@ int WhileLoopII(int n) { int res = 0; int i = 1; // 初始化条件变量 - // 循环求和 1, 2, 4, 5... + // 循环求和 1, 4, 10, ... while (i <= n) { res += i; // 更新条件变量 diff --git a/en/index.html b/en/index.html index 52442cf6a..7bfa50081 100644 --- a/en/index.html +++ b/en/index.html @@ -1367,8 +1367,7 @@
"Chase the wind and moon, never stopping"
-"Beyond the plains, there are spring mountains"
+"Knowledge increases by sharing."
Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code
Dive In Clone Repo Get PDF
The English edition is brewing...
Feel free to engage in Chinese-to-English translation and pull request review! For guidelines, please see #914.
Quote
\"An easy-to-understand book on data structures and algorithms, which guides readers to learn by minds-on and hands-on. Strongly recommended for algorithm beginners!\"
\u2014\u2014 Junhui Deng, Professor of Computer Science, Tsinghua University
Quote
\"If I had 'Hello Algo' when I was learning data structures and algorithms, it would have been 10 times easier!\"
\u2014\u2014 Mu Li, Senior Principal Scientist, Amazon
Animated illustrationsEasy to understandSmooth learning curve
\"A picture is worth a thousand words.\"
Off-the-Shelf CodeMulti programming languagesRun with one click
\"Talk is cheap. Show me the code.\"
Learning TogetherDiscussion and questions welcomeReaders progress together
\"Chase the wind and moon, never stopping\"
\"Beyond the plains, there are spring mountains\"
PrefaceTwo years ago, I shared the \"Sword Offer\" series of problem solutions on LeetCode, which received much love and support from many students. During my interactions with readers, the most common question I encountered was \"How to get started with algorithms.\" Gradually, I developed a deep interest in this question.
Blindly solving problems seems to be the most popular method, being simple, direct, and effective. However, problem-solving is like playing a \"Minesweeper\" game, where students with strong self-learning abilities can successfully clear the mines one by one, but those with insufficient foundations may end up bruised from explosions, retreating step by step in frustration. Thoroughly reading textbooks is also common, but for students aiming for job applications, the energy consumed by graduation, resume submissions, and preparing for written tests and interviews makes tackling thick books a daunting challenge.
If you are facing similar troubles, then you are lucky to have found this book. This book is my answer to this question, not necessarily the best solution, but at least an active attempt. Although this book won't directly land you an Offer, it will guide you through the \"knowledge map\" of data structures and algorithms, help you understand the shape, size, and distribution of different \"mines,\" and equip you with various \"demining methods.\" With these skills, I believe you can more comfortably solve problems and read literature, gradually building a complete knowledge system.
I deeply agree with Professor Feynman's saying: \"Knowledge isn't free. You have to pay attention.\" In this sense, this book is not entirely \"free.\" To not disappoint the precious \"attention\" you pay to this book, I will do my utmost, investing the greatest \"attention\" to complete the creation of this book.
AuthorYudong Jin(Krahets), Senior Algorithm Engineer in a top tech company, Master's degree from Shanghai Jiao Tong University. The highest-read blogger across the entire LeetCode, his published \"Illustration of Algorithm Data Structures\" has been subscribed to by over 300k.
ContributionThis book is continuously improved with the joint efforts of many contributors from the open-source community. Thanks to each writer who invested their time and energy, listed in the order generated by GitHub:
The code review work for this book was completed by codingonion, Gonglja, gvenusleo, hpstory, justin\u2010tse, krahets, night-cruise, nuomi1, and Reanon (listed in alphabetical order). Thanks to them for their time and effort, ensuring the standardization and uniformity of the code in various languages.
codingonionRust, Zig GongljaC, C++ gvenusleoDart hpstoryC# justin-tseJS, TS krahetsJava, Python night-cruiseRust nuomi1Swift ReanonGo, C"},{"location":"chapter_array_and_linkedlist/","title":"Chapter 4. \u00a0 Arrays and Linked Lists","text":"Abstract
The world of data structures resembles a sturdy brick wall.
In arrays, envision bricks snugly aligned, each resting seamlessly beside the next, creating a unified formation. Meanwhile, in linked lists, these bricks disperse freely, embraced by vines gracefully knitting connections between them.
"},{"location":"chapter_array_and_linkedlist/#chapter-contents","title":"Chapter Contents","text":"An \"array\" is a linear data structure that operates as a lineup of similar items, stored together in a computer's memory in contiguous spaces. It's like a sequence that maintains organized storage. Each item in this lineup has its unique 'spot' known as an \"index\". Please refer to the Figure 4-1 to observe how arrays work and grasp these key terms.
Figure 4-1 \u00a0 Array Definition and Storage Method
"},{"location":"chapter_array_and_linkedlist/array/#411-common-operations-on-arrays","title":"4.1.1 \u00a0 Common Operations on Arrays","text":""},{"location":"chapter_array_and_linkedlist/array/#1-initializing-arrays","title":"1. \u00a0 Initializing Arrays","text":"Arrays can be initialized in two ways depending on the needs: either without initial values or with specified initial values. When initial values are not specified, most programming languages will set the array elements to \\(0\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.py# Initialize array\narr: list[int] = [0] * 5 # [ 0, 0, 0, 0, 0 ]\nnums: list[int] = [1, 3, 2, 5, 4]\n
array.cpp/* Initialize array */\n// Stored on stack\nint arr[5];\nint nums[5] = { 1, 3, 2, 5, 4 };\n// Stored on heap (manual memory release needed)\nint* arr1 = new int[5];\nint* nums1 = new int[5] { 1, 3, 2, 5, 4 };\n
array.java/* Initialize array */\nint[] arr = new int[5]; // { 0, 0, 0, 0, 0 }\nint[] nums = { 1, 3, 2, 5, 4 };\n
array.cs/* Initialize array */\nint[] arr = new int[5]; // [ 0, 0, 0, 0, 0 ]\nint[] nums = [1, 3, 2, 5, 4];\n
array.go/* Initialize array */\nvar arr [5]int\n// In Go, specifying the length ([5]int) denotes an array, while not specifying it ([]int) denotes a slice.\n// Since Go's arrays are designed to have compile-time fixed length, only constants can be used to specify the length.\n// For convenience in implementing the extend() method, the Slice will be considered as an Array here.\nnums := []int{1, 3, 2, 5, 4}\n
array.swift/* Initialize array */\nlet arr = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0]\nlet nums = [1, 3, 2, 5, 4]\n
array.js/* Initialize array */\nvar arr = new Array(5).fill(0);\nvar nums = [1, 3, 2, 5, 4];\n
array.ts/* Initialize array */\nlet arr: number[] = new Array(5).fill(0);\nlet nums: number[] = [1, 3, 2, 5, 4];\n
array.dart/* Initialize array */\nList<int> arr = List.filled(5, 0); // [0, 0, 0, 0, 0]\nList<int> nums = [1, 3, 2, 5, 4];\n
array.rs/* Initialize array */\nlet arr: Vec<i32> = vec![0; 5]; // [0, 0, 0, 0, 0]\nlet nums: Vec<i32> = vec![1, 3, 2, 5, 4];\n
array.c/* Initialize array */\nint arr[5] = { 0 }; // { 0, 0, 0, 0, 0 }\nint nums[5] = { 1, 3, 2, 5, 4 };\n
array.zig// Initialize array\nvar arr = [_]i32{0} ** 5; // { 0, 0, 0, 0, 0 }\nvar nums = [_]i32{ 1, 3, 2, 5, 4 };\n
"},{"location":"chapter_array_and_linkedlist/array/#2-accessing-elements","title":"2. \u00a0 Accessing Elements","text":"Elements in an array are stored in contiguous memory spaces, making it simpler to compute each element's memory address. The formula shown in the Figure below aids in determining an element's memory address, utilizing the array's memory address (specifically, the first element's address) and the element's index. This computation streamlines direct access to the desired element.
Figure 4-2 \u00a0 Memory Address Calculation for Array Elements
As observed in the above illustration, array indexing conventionally begins at \\(0\\). While this might appear counterintuitive, considering counting usually starts at \\(1\\), within the address calculation formula, an index is essentially an offset from the memory address. For the first element's address, this offset is \\(0\\), validating its index as \\(0\\).
Accessing elements in an array is highly efficient, allowing us to randomly access any element in \\(O(1)\\) time.
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef random_access(nums: list[int]) -> int:\n \"\"\"\u968f\u673a\u8bbf\u95ee\u5143\u7d20\"\"\"\n # \u5728\u533a\u95f4 [0, len(nums)-1] \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n random_index = random.randint(0, len(nums) - 1)\n # \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n random_num = nums[random_index]\n return random_num\n
array.cpp/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nint randomAccess(int *nums, int size) {\n // \u5728\u533a\u95f4 [0, size) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n int randomIndex = rand() % size;\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n int randomNum = nums[randomIndex];\n return randomNum;\n}\n
array.java/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nint randomAccess(int[] nums) {\n // \u5728\u533a\u95f4 [0, nums.length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n int randomIndex = ThreadLocalRandom.current().nextInt(0, nums.length);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n int randomNum = nums[randomIndex];\n return randomNum;\n}\n
array.cs/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nint RandomAccess(int[] nums) {\n Random random = new();\n // \u5728\u533a\u95f4 [0, nums.Length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n int randomIndex = random.Next(nums.Length);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n int randomNum = nums[randomIndex];\n return randomNum;\n}\n
array.go/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nfunc randomAccess(nums []int) (randomNum int) {\n // \u5728\u533a\u95f4 [0, nums.length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n randomIndex := rand.Intn(len(nums))\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n randomNum = nums[randomIndex]\n return\n}\n
array.swift/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nfunc randomAccess(nums: [Int]) -> Int {\n // \u5728\u533a\u95f4 [0, nums.count) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n let randomIndex = nums.indices.randomElement()!\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n let randomNum = nums[randomIndex]\n return randomNum\n}\n
array.js/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nfunction randomAccess(nums) {\n // \u5728\u533a\u95f4 [0, nums.length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n const random_index = Math.floor(Math.random() * nums.length);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n const random_num = nums[random_index];\n return random_num;\n}\n
array.ts/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nfunction randomAccess(nums: number[]): number {\n // \u5728\u533a\u95f4 [0, nums.length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n const random_index = Math.floor(Math.random() * nums.length);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n const random_num = nums[random_index];\n return random_num;\n}\n
array.dart/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nint randomAccess(List<int> nums) {\n // \u5728\u533a\u95f4 [0, nums.length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n int randomIndex = Random().nextInt(nums.length);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n int randomNum = nums[randomIndex];\n return randomNum;\n}\n
array.rs/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nfn random_access(nums: &[i32]) -> i32 {\n // \u5728\u533a\u95f4 [0, nums.len()) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n let random_index = rand::thread_rng().gen_range(0..nums.len());\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n let random_num = nums[random_index];\n random_num\n}\n
array.c/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nint randomAccess(int *nums, int size) {\n // \u5728\u533a\u95f4 [0, size) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n int randomIndex = rand() % size;\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n int randomNum = nums[randomIndex];\n return randomNum;\n}\n
array.zig// \u968f\u673a\u8bbf\u95ee\u5143\u7d20\nfn randomAccess(nums: []i32) i32 {\n // \u5728\u533a\u95f4 [0, nums.len) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6574\u6570\n var randomIndex = std.crypto.random.intRangeLessThan(usize, 0, nums.len);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n var randomNum = nums[randomIndex];\n return randomNum;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/array/#3-inserting-elements","title":"3. \u00a0 Inserting Elements","text":"Array elements are tightly packed in memory, with no space available to accommodate additional data between them. Illustrated in Figure below, inserting an element in the middle of an array requires shifting all subsequent elements back by one position to create room for the new element.
Figure 4-3 \u00a0 Array Element Insertion Example
It's important to note that due to the fixed length of an array, inserting an element will unavoidably result in the loss of the last element in the array. Solutions to address this issue will be explored in the \"List\" chapter.
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef insert(nums: list[int], num: int, index: int):\n \"\"\"\u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num\"\"\"\n # \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for i in range(len(nums) - 1, index, -1):\n nums[i] = nums[i - 1]\n # \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num\n
array.cpp/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nvoid insert(int *nums, int size, int num, int index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int i = size - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.java/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nvoid insert(int[] nums, int num, int index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int i = nums.length - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.cs/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nvoid Insert(int[] nums, int num, int index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int i = nums.Length - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.go/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nfunc insert(nums []int, num int, index int) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for i := len(nums) - 1; i > index; i-- {\n nums[i] = nums[i-1]\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num\n}\n
array.swift/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nfunc insert(nums: inout [Int], num: Int, index: Int) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for i in nums.indices.dropFirst(index).reversed() {\n nums[i] = nums[i - 1]\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num\n}\n
array.js/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nfunction insert(nums, num, index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (let i = nums.length - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.ts/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nfunction insert(nums: number[], num: number, index: number): void {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (let i = nums.length - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.dart/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 _num */\nvoid insert(List<int> nums, int _num, int index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (var i = nums.length - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 _num \u8d4b\u7ed9 index \u5904\u5143\u7d20\n nums[index] = _num;\n}\n
array.rs/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nfn insert(nums: &mut Vec<i32>, num: i32, index: usize) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for i in (index + 1..nums.len()).rev() {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.c/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nvoid insert(int *nums, int size, int num, int index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int i = size - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.zig// \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num\nfn insert(nums: []i32, num: i32, index: usize) void {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n var i = nums.len - 1;\n while (i > index) : (i -= 1) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/array/#4-deleting-elements","title":"4. \u00a0 Deleting Elements","text":"Similarly, as depicted in the Figure 4-4 , to delete an element at index \\(i\\), all elements following index \\(i\\) must be moved forward by one position.
Figure 4-4 \u00a0 Array Element Deletion Example
Please note that after deletion, the former last element becomes \"meaningless,\" hence requiring no specific modification.
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef remove(nums: list[int], index: int):\n \"\"\"\u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20\"\"\"\n # \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for i in range(index, len(nums) - 1):\n nums[i] = nums[i + 1]\n
array.cpp/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nvoid remove(int *nums, int size, int index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int i = index; i < size - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.java/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nvoid remove(int[] nums, int index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int i = index; i < nums.length - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.cs/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nvoid Remove(int[] nums, int index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int i = index; i < nums.Length - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.go/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nfunc remove(nums []int, index int) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for i := index; i < len(nums)-1; i++ {\n nums[i] = nums[i+1]\n }\n}\n
array.swift/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nfunc remove(nums: inout [Int], index: Int) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for i in nums.indices.dropFirst(index).dropLast() {\n nums[i] = nums[i + 1]\n }\n}\n
array.js/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nfunction remove(nums, index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (let i = index; i < nums.length - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.ts/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nfunction remove(nums: number[], index: number): void {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (let i = index; i < nums.length - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.dart/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nvoid remove(List<int> nums, int index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (var i = index; i < nums.length - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.rs/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nfn remove(nums: &mut Vec<i32>, index: usize) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for i in index..nums.len() - 1 {\n nums[i] = nums[i + 1];\n }\n}\n
array.c/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\n// \u6ce8\u610f\uff1astdio.h \u5360\u7528\u4e86 remove \u5173\u952e\u8bcd\nvoid removeItem(int *nums, int size, int index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int i = index; i < size - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.zig// \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20\nfn remove(nums: []i32, index: usize) void {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n var i = index;\n while (i < nums.len - 1) : (i += 1) {\n nums[i] = nums[i + 1];\n }\n}\n
Visualizing Code Full Screen >
In summary, the insertion and deletion operations in arrays present the following disadvantages:
In most programming languages, we can traverse an array either by using indices or by directly iterating over each element:
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef traverse(nums: list[int]):\n \"\"\"\u904d\u5386\u6570\u7ec4\"\"\"\n count = 0\n # \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for i in range(len(nums)):\n count += nums[i]\n # \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for num in nums:\n count += num\n # \u540c\u65f6\u904d\u5386\u6570\u636e\u7d22\u5f15\u548c\u5143\u7d20\n for i, num in enumerate(nums):\n count += nums[i]\n count += num\n
array.cpp/* \u904d\u5386\u6570\u7ec4 */\nvoid traverse(int *nums, int size) {\n int count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (int i = 0; i < size; i++) {\n count += nums[i];\n }\n}\n
array.java/* \u904d\u5386\u6570\u7ec4 */\nvoid traverse(int[] nums) {\n int count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (int i = 0; i < nums.length; i++) {\n count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for (int num : nums) {\n count += num;\n }\n}\n
array.cs/* \u904d\u5386\u6570\u7ec4 */\nvoid Traverse(int[] nums) {\n int count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (int i = 0; i < nums.Length; i++) {\n count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n foreach (int num in nums) {\n count += num;\n }\n}\n
array.go/* \u904d\u5386\u6570\u7ec4 */\nfunc traverse(nums []int) {\n count := 0\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for i := 0; i < len(nums); i++ {\n count += nums[i]\n }\n count = 0\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for _, num := range nums {\n count += num\n }\n // \u540c\u65f6\u904d\u5386\u6570\u636e\u7d22\u5f15\u548c\u5143\u7d20\n for i, num := range nums {\n count += nums[i]\n count += num\n }\n}\n
array.swift/* \u904d\u5386\u6570\u7ec4 */\nfunc traverse(nums: [Int]) {\n var count = 0\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for i in nums.indices {\n count += nums[i]\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for num in nums {\n count += num\n }\n}\n
array.js/* \u904d\u5386\u6570\u7ec4 */\nfunction traverse(nums) {\n let count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (let i = 0; i < nums.length; i++) {\n count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for (const num of nums) {\n count += num;\n }\n}\n
array.ts/* \u904d\u5386\u6570\u7ec4 */\nfunction traverse(nums: number[]): void {\n let count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (let i = 0; i < nums.length; i++) {\n count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for (const num of nums) {\n count += num;\n }\n}\n
array.dart/* \u904d\u5386\u6570\u7ec4\u5143\u7d20 */\nvoid traverse(List<int> nums) {\n int count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (var i = 0; i < nums.length; i++) {\n count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for (int _num in nums) {\n count += _num;\n }\n // \u901a\u8fc7 forEach \u65b9\u6cd5\u904d\u5386\u6570\u7ec4\n nums.forEach((_num) {\n count += _num;\n });\n}\n
array.rs/* \u904d\u5386\u6570\u7ec4 */\nfn traverse(nums: &[i32]) {\n let mut _count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for i in 0..nums.len() {\n _count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for num in nums {\n _count += num;\n }\n}\n
array.c/* \u904d\u5386\u6570\u7ec4 */\nvoid traverse(int *nums, int size) {\n int count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (int i = 0; i < size; i++) {\n count += nums[i];\n }\n}\n
array.zig// \u904d\u5386\u6570\u7ec4\nfn traverse(nums: []i32) void {\n var count: i32 = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n var i: i32 = 0;\n while (i < nums.len) : (i += 1) {\n count += nums[i];\n }\n count = 0;\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for (nums) |num| {\n count += num;\n }\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/array/#6-finding-elements","title":"6. \u00a0 Finding Elements","text":"Locating a specific element within an array involves iterating through the array, checking each element to determine if it matches the desired value.
Because arrays are linear data structures, this operation is commonly referred to as \"linear search.\"
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef find(nums: list[int], target: int) -> int:\n \"\"\"\u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20\"\"\"\n for i in range(len(nums)):\n if nums[i] == target:\n return i\n return -1\n
array.cpp/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nint find(int *nums, int size, int target) {\n for (int i = 0; i < size; i++) {\n if (nums[i] == target)\n return i;\n }\n return -1;\n}\n
array.java/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nint find(int[] nums, int target) {\n for (int i = 0; i < nums.length; i++) {\n if (nums[i] == target)\n return i;\n }\n return -1;\n}\n
array.cs/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nint Find(int[] nums, int target) {\n for (int i = 0; i < nums.Length; i++) {\n if (nums[i] == target)\n return i;\n }\n return -1;\n}\n
array.go/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nfunc find(nums []int, target int) (index int) {\n index = -1\n for i := 0; i < len(nums); i++ {\n if nums[i] == target {\n index = i\n break\n }\n }\n return\n}\n
array.swift/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nfunc find(nums: [Int], target: Int) -> Int {\n for i in nums.indices {\n if nums[i] == target {\n return i\n }\n }\n return -1\n}\n
array.js/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nfunction find(nums, target) {\n for (let i = 0; i < nums.length; i++) {\n if (nums[i] === target) return i;\n }\n return -1;\n}\n
array.ts/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nfunction find(nums: number[], target: number): number {\n for (let i = 0; i < nums.length; i++) {\n if (nums[i] === target) {\n return i;\n }\n }\n return -1;\n}\n
array.dart/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nint find(List<int> nums, int target) {\n for (var i = 0; i < nums.length; i++) {\n if (nums[i] == target) return i;\n }\n return -1;\n}\n
array.rs/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nfn find(nums: &[i32], target: i32) -> Option<usize> {\n for i in 0..nums.len() {\n if nums[i] == target {\n return Some(i);\n }\n }\n None\n}\n
array.c/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nint find(int *nums, int size, int target) {\n for (int i = 0; i < size; i++) {\n if (nums[i] == target)\n return i;\n }\n return -1;\n}\n
array.zig// \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20\nfn find(nums: []i32, target: i32) i32 {\n for (nums, 0..) |num, i| {\n if (num == target) return @intCast(i);\n }\n return -1;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/array/#7-expanding-arrays","title":"7. \u00a0 Expanding Arrays","text":"In complex system environments, ensuring the availability of memory space after an array for safe capacity extension becomes challenging. Consequently, in most programming languages, the length of an array is immutable.
To expand an array, it's necessary to create a larger array and then copy the elements from the original array. This operation has a time complexity of \\(O(n)\\) and can be time-consuming for large arrays. The code are as follows:
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef extend(nums: list[int], enlarge: int) -> list[int]:\n \"\"\"\u6269\u5c55\u6570\u7ec4\u957f\u5ea6\"\"\"\n # \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n res = [0] * (len(nums) + enlarge)\n # \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for i in range(len(nums)):\n res[i] = nums[i]\n # \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res\n
array.cpp/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nint *extend(int *nums, int size, int enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n int *res = new int[size + enlarge];\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (int i = 0; i < size; i++) {\n res[i] = nums[i];\n }\n // \u91ca\u653e\u5185\u5b58\n delete[] nums;\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.java/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nint[] extend(int[] nums, int enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n int[] res = new int[nums.length + enlarge];\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (int i = 0; i < nums.length; i++) {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.cs/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nint[] Extend(int[] nums, int enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n int[] res = new int[nums.Length + enlarge];\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (int i = 0; i < nums.Length; i++) {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.go/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nfunc extend(nums []int, enlarge int) []int {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n res := make([]int, len(nums)+enlarge)\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for i, num := range nums {\n res[i] = num\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res\n}\n
array.swift/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nfunc extend(nums: [Int], enlarge: Int) -> [Int] {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n var res = Array(repeating: 0, count: nums.count + enlarge)\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for i in nums.indices {\n res[i] = nums[i]\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res\n}\n
array.js/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\n// \u8bf7\u6ce8\u610f\uff0cJavaScript \u7684 Array \u662f\u52a8\u6001\u6570\u7ec4\uff0c\u53ef\u4ee5\u76f4\u63a5\u6269\u5c55\n// \u4e3a\u4e86\u65b9\u4fbf\u5b66\u4e60\uff0c\u672c\u51fd\u6570\u5c06 Array \u770b\u4f5c\u957f\u5ea6\u4e0d\u53ef\u53d8\u7684\u6570\u7ec4\nfunction extend(nums, enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n const res = new Array(nums.length + enlarge).fill(0);\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (let i = 0; i < nums.length; i++) {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.ts/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\n// \u8bf7\u6ce8\u610f\uff0cTypeScript \u7684 Array \u662f\u52a8\u6001\u6570\u7ec4\uff0c\u53ef\u4ee5\u76f4\u63a5\u6269\u5c55\n// \u4e3a\u4e86\u65b9\u4fbf\u5b66\u4e60\uff0c\u672c\u51fd\u6570\u5c06 Array \u770b\u4f5c\u957f\u5ea6\u4e0d\u53ef\u53d8\u7684\u6570\u7ec4\nfunction extend(nums: number[], enlarge: number): number[] {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n const res = new Array(nums.length + enlarge).fill(0);\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (let i = 0; i < nums.length; i++) {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.dart/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nList<int> extend(List<int> nums, int enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n List<int> res = List.filled(nums.length + enlarge, 0);\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (var i = 0; i < nums.length; i++) {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.rs/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nfn extend(nums: Vec<i32>, enlarge: usize) -> Vec<i32> {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n let mut res: Vec<i32> = vec![0; nums.len() + enlarge];\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\n for i in 0..nums.len() {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n res\n}\n
array.c/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nint *extend(int *nums, int size, int enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n int *res = (int *)malloc(sizeof(int) * (size + enlarge));\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (int i = 0; i < size; i++) {\n res[i] = nums[i];\n }\n // \u521d\u59cb\u5316\u6269\u5c55\u540e\u7684\u7a7a\u95f4\n for (int i = size; i < size + enlarge; i++) {\n res[i] = 0;\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.zig// \u6269\u5c55\u6570\u7ec4\u957f\u5ea6\nfn extend(mem_allocator: std.mem.Allocator, nums: []i32, enlarge: usize) ![]i32 {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n var res = try mem_allocator.alloc(i32, nums.len + enlarge);\n @memset(res, 0);\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n std.mem.copy(i32, res, nums);\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/array/#412-advantages-and-limitations-of-arrays","title":"4.1.2 \u00a0 Advantages and Limitations of Arrays","text":"Arrays are stored in contiguous memory spaces and consist of elements of the same type. This approach provides substantial prior information that systems can leverage to optimize the efficiency of data structure operations.
However, continuous space storage is a double-edged sword, with the following limitations:
Arrays are fundamental and widely used data structures. They find frequent application in various algorithms and serve in the implementation of complex data structures.
Memory space is a common resource for all programs. In a complex system environment, free memory space can be scattered throughout memory. We know that the memory space for storing an array must be contiguous, and when the array is very large, it may not be possible to provide such a large contiguous space. This is where the flexibility advantage of linked lists becomes apparent.
A \"linked list\" is a linear data structure where each element is a node object, and the nodes are connected via \"references\". A reference records the memory address of the next node, allowing access to the next node from the current one.
The design of a linked list allows its nodes to be scattered throughout memory, with no need for contiguous memory addresses.
Figure 4-5 \u00a0 Linked List Definition and Storage Method
Observing the image above, the fundamental unit of a linked list is the \"node\" object. Each node contains two pieces of data: the \"value\" of the node and the \"reference\" to the next node.
null
in Java, nullptr
in C++, and None
in Python.As shown in the following code, a linked list node ListNode
, apart from containing a value, also needs to store a reference (pointer). Therefore, a linked list consumes more memory space than an array for the same amount of data.
class ListNode:\n \"\"\"Linked List Node Class\"\"\"\n def __init__(self, val: int):\n self.val: int = val # Node value\n self.next: ListNode | None = None # Reference to the next node\n
/* Linked List Node Structure */\nstruct ListNode {\n int val; // Node value\n ListNode *next; // Pointer to the next node\n ListNode(int x) : val(x), next(nullptr) {} // Constructor\n};\n
/* Linked List Node Class */\nclass ListNode {\n int val; // Node value\n ListNode next; // Reference to the next node\n ListNode(int x) { val = x; } // Constructor\n}\n
/* Linked List Node Class */\nclass ListNode(int x) { // Constructor\n int val = x; // Node value\n ListNode? next; // Reference to the next node\n}\n
/* Linked List Node Structure */\ntype ListNode struct {\n Val int // Node value\n Next *ListNode // Pointer to the next node\n}\n\n// NewListNode Constructor, creates a new linked list\nfunc NewListNode(val int) *ListNode {\n return &ListNode{\n Val: val,\n Next: nil,\n }\n}\n
/* Linked List Node Class */\nclass ListNode {\n var val: Int // Node value\n var next: ListNode? // Reference to the next node\n\n init(x: Int) { // Constructor\n val = x\n }\n}\n
/* Linked List Node Class */\nclass ListNode {\n constructor(val, next) {\n this.val = (val === undefined ? 0 : val); // Node value\n this.next = (next === undefined ? null : next); // Reference to the next node\n }\n}\n
/* Linked List Node Class */\nclass ListNode {\n val: number;\n next: ListNode | null;\n constructor(val?: number, next?: ListNode | null) {\n this.val = val === undefined ? 0 : val; // Node value\n this.next = next === undefined ? null : next; // Reference to the next node\n }\n}\n
/* \u94fe\u8868\u8282\u70b9\u7c7b */\nclass ListNode {\n int val; // Node value\n ListNode? next; // Reference to the next node\n ListNode(this.val, [this.next]); // Constructor\n}\n
use std::rc::Rc;\nuse std::cell::RefCell;\n/* Linked List Node Class */\n#[derive(Debug)]\nstruct ListNode {\n val: i32, // Node value\n next: Option<Rc<RefCell<ListNode>>>, // Pointer to the next node\n}\n
/* Linked List Node Structure */\ntypedef struct ListNode {\n int val; // Node value\n struct ListNode *next; // Pointer to the next node\n} ListNode;\n\n/* Constructor */\nListNode *newListNode(int val) {\n ListNode *node;\n node = (ListNode *) malloc(sizeof(ListNode));\n node->val = val;\n node->next = NULL;\n return node;\n}\n
// Linked List Node Class\npub fn ListNode(comptime T: type) type {\n return struct {\n const Self = @This();\n\n val: T = 0, // Node value\n next: ?*Self = null, // Pointer to the next node\n\n // Constructor\n pub fn init(self: *Self, x: i32) void {\n self.val = x;\n self.next = null;\n }\n };\n}\n
"},{"location":"chapter_array_and_linkedlist/linked_list/#421-common-operations-on-linked-lists","title":"4.2.1 \u00a0 Common Operations on Linked Lists","text":""},{"location":"chapter_array_and_linkedlist/linked_list/#1-initializing-a-linked-list","title":"1. \u00a0 Initializing a Linked List","text":"Building a linked list involves two steps: initializing each node object and then establishing the references between nodes. Once initialized, we can access all nodes sequentially from the head node via the next
reference.
# Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4\n# Initialize each node\nn0 = ListNode(1)\nn1 = ListNode(3)\nn2 = ListNode(2)\nn3 = ListNode(5)\nn4 = ListNode(4)\n# Build references between nodes\nn0.next = n1\nn1.next = n2\nn2.next = n3\nn3.next = n4\n
linked_list.cpp/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nListNode* n0 = new ListNode(1);\nListNode* n1 = new ListNode(3);\nListNode* n2 = new ListNode(2);\nListNode* n3 = new ListNode(5);\nListNode* n4 = new ListNode(4);\n// Build references between nodes\nn0->next = n1;\nn1->next = n2;\nn2->next = n3;\nn3->next = n4;\n
linked_list.java/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nListNode n0 = new ListNode(1);\nListNode n1 = new ListNode(3);\nListNode n2 = new ListNode(2);\nListNode n3 = new ListNode(5);\nListNode n4 = new ListNode(4);\n// Build references between nodes\nn0.next = n1;\nn1.next = n2;\nn2.next = n3;\nn3.next = n4;\n
linked_list.cs/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nListNode n0 = new(1);\nListNode n1 = new(3);\nListNode n2 = new(2);\nListNode n3 = new(5);\nListNode n4 = new(4);\n// Build references between nodes\nn0.next = n1;\nn1.next = n2;\nn2.next = n3;\nn3.next = n4;\n
linked_list.go/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nn0 := NewListNode(1)\nn1 := NewListNode(3)\nn2 := NewListNode(2)\nn3 := NewListNode(5)\nn4 := NewListNode(4)\n// Build references between nodes\nn0.Next = n1\nn1.Next = n2\nn2.Next = n3\nn3.Next = n4\n
linked_list.swift/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nlet n0 = ListNode(x: 1)\nlet n1 = ListNode(x: 3)\nlet n2 = ListNode(x: 2)\nlet n3 = ListNode(x: 5)\nlet n4 = ListNode(x: 4)\n// Build references between nodes\nn0.next = n1\nn1.next = n2\nn2.next = n3\nn3.next = n4\n
linked_list.js/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nconst n0 = new ListNode(1);\nconst n1 = new ListNode(3);\nconst n2 = new ListNode(2);\nconst n3 = new ListNode(5);\nconst n4 = new ListNode(4);\n// Build references between nodes\nn0.next = n1;\nn1.next = n2;\nn2.next = n3;\nn3.next = n4;\n
linked_list.ts/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nconst n0 = new ListNode(1);\nconst n1 = new ListNode(3);\nconst n2 = new ListNode(2);\nconst n3 = new ListNode(5);\nconst n4 = new ListNode(4);\n// Build references between nodes\nn0.next = n1;\nn1.next = n2;\nn2.next = n3;\nn3.next = n4;\n
linked_list.dart/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nListNode n0 = ListNode(1);\nListNode n1 = ListNode(3);\nListNode n2 = ListNode(2);\nListNode n3 = ListNode(5);\nListNode n4 = ListNode(4);\n// Build references between nodes\nn0.next = n1;\nn1.next = n2;\nn2.next = n3;\nn3.next = n4;\n
linked_list.rs/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nlet n0 = Rc::new(RefCell::new(ListNode { val: 1, next: None }));\nlet n1 = Rc::new(RefCell::new(ListNode { val: 3, next: None }));\nlet n2 = Rc::new(RefCell::new(ListNode { val: 2, next: None }));\nlet n3 = Rc::new(RefCell::new(ListNode { val: 5, next: None }));\nlet n4 = Rc::new(RefCell::new(ListNode { val: 4, next: None }));\n\n// Build references between nodes\nn0.borrow_mut().next = Some(n1.clone());\nn1.borrow_mut().next = Some(n2.clone());\nn2.borrow_mut().next = Some(n3.clone());\nn3.borrow_mut().next = Some(n4.clone());\n
linked_list.c/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nListNode* n0 = newListNode(1);\nListNode* n1 = newListNode(3);\nListNode* n2 = newListNode(2);\nListNode* n3 = newListNode(5);\nListNode* n4 = newListNode(4);\n// Build references between nodes\nn0->next = n1;\nn1->next = n2;\nn2->next = n3;\nn3->next = n4;\n
linked_list.zig// Initialize linked list\n// Initialize each node\nvar n0 = inc.ListNode(i32){.val = 1};\nvar n1 = inc.ListNode(i32){.val = 3};\nvar n2 = inc.ListNode(i32){.val = 2};\nvar n3 = inc.ListNode(i32){.val = 5};\nvar n4 = inc.ListNode(i32){.val = 4};\n// Build references between nodes\nn0.next = &n1;\nn1.next = &n2;\nn2.next = &n3;\nn3.next = &n4;\n
An array is a single variable, such as the array nums
containing elements nums[0]
, nums[1]
, etc., while a linked list is composed of multiple independent node objects. We usually refer to the linked list by its head node, as in the linked list n0
in the above code.
Inserting a node in a linked list is very easy. As shown in the image below, suppose we want to insert a new node P
between two adjacent nodes n0
and n1
. This requires changing only two node references (pointers), with a time complexity of \\(O(1)\\).
In contrast, the time complexity of inserting an element in an array is \\(O(n)\\), which is less efficient with large data volumes.
Figure 4-6 \u00a0 Linked List Node Insertion Example
PythonC++JavaC#GoSwiftJSTSDartRustCZig linked_list.pydef insert(n0: ListNode, P: ListNode):\n \"\"\"\u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P\"\"\"\n n1 = n0.next\n P.next = n1\n n0.next = P\n
linked_list.cpp/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nvoid insert(ListNode *n0, ListNode *P) {\n ListNode *n1 = n0->next;\n P->next = n1;\n n0->next = P;\n}\n
linked_list.java/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nvoid insert(ListNode n0, ListNode P) {\n ListNode n1 = n0.next;\n P.next = n1;\n n0.next = P;\n}\n
linked_list.cs/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nvoid Insert(ListNode n0, ListNode P) {\n ListNode? n1 = n0.next;\n P.next = n1;\n n0.next = P;\n}\n
linked_list.go/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nfunc insertNode(n0 *ListNode, P *ListNode) {\n n1 := n0.Next\n P.Next = n1\n n0.Next = P\n}\n
linked_list.swift/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nfunc insert(n0: ListNode, P: ListNode) {\n let n1 = n0.next\n P.next = n1\n n0.next = P\n}\n
linked_list.js/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nfunction insert(n0, P) {\n const n1 = n0.next;\n P.next = n1;\n n0.next = P;\n}\n
linked_list.ts/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nfunction insert(n0: ListNode, P: ListNode): void {\n const n1 = n0.next;\n P.next = n1;\n n0.next = P;\n}\n
linked_list.dart/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nvoid insert(ListNode n0, ListNode P) {\n ListNode? n1 = n0.next;\n P.next = n1;\n n0.next = P;\n}\n
linked_list.rs/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\n#[allow(non_snake_case)]\npub fn insert<T>(n0: &Rc<RefCell<ListNode<T>>>, P: Rc<RefCell<ListNode<T>>>) {\n let n1 = n0.borrow_mut().next.take();\n P.borrow_mut().next = n1;\n n0.borrow_mut().next = Some(P);\n}\n
linked_list.c/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nvoid insert(ListNode *n0, ListNode *P) {\n ListNode *n1 = n0->next;\n P->next = n1;\n n0->next = P;\n}\n
linked_list.zig// \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P\nfn insert(n0: ?*inc.ListNode(i32), P: ?*inc.ListNode(i32)) void {\n var n1 = n0.?.next;\n P.?.next = n1;\n n0.?.next = P;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/linked_list/#3-deleting-a-node","title":"3. \u00a0 Deleting a Node","text":"As shown below, deleting a node in a linked list is also very convenient, requiring only the change of one node's reference (pointer).
Note that although node P
still points to n1
after the deletion operation is completed, it is no longer accessible when traversing the list, meaning P
is no longer part of the list.
Figure 4-7 \u00a0 Linked List Node Deletion
PythonC++JavaC#GoSwiftJSTSDartRustCZig linked_list.pydef remove(n0: ListNode):\n \"\"\"\u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9\"\"\"\n if not n0.next:\n return\n # n0 -> P -> n1\n P = n0.next\n n1 = P.next\n n0.next = n1\n
linked_list.cpp/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nvoid remove(ListNode *n0) {\n if (n0->next == nullptr)\n return;\n // n0 -> P -> n1\n ListNode *P = n0->next;\n ListNode *n1 = P->next;\n n0->next = n1;\n // \u91ca\u653e\u5185\u5b58\n delete P;\n}\n
linked_list.java/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nvoid remove(ListNode n0) {\n if (n0.next == null)\n return;\n // n0 -> P -> n1\n ListNode P = n0.next;\n ListNode n1 = P.next;\n n0.next = n1;\n}\n
linked_list.cs/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nvoid Remove(ListNode n0) {\n if (n0.next == null)\n return;\n // n0 -> P -> n1\n ListNode P = n0.next;\n ListNode? n1 = P.next;\n n0.next = n1;\n}\n
linked_list.go/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nfunc removeItem(n0 *ListNode) {\n if n0.Next == nil {\n return\n }\n // n0 -> P -> n1\n P := n0.Next\n n1 := P.Next\n n0.Next = n1\n}\n
linked_list.swift/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nfunc remove(n0: ListNode) {\n if n0.next == nil {\n return\n }\n // n0 -> P -> n1\n let P = n0.next\n let n1 = P?.next\n n0.next = n1\n P?.next = nil\n}\n
linked_list.js/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nfunction remove(n0) {\n if (!n0.next) return;\n // n0 -> P -> n1\n const P = n0.next;\n const n1 = P.next;\n n0.next = n1;\n}\n
linked_list.ts/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nfunction remove(n0: ListNode): void {\n if (!n0.next) {\n return;\n }\n // n0 -> P -> n1\n const P = n0.next;\n const n1 = P.next;\n n0.next = n1;\n}\n
linked_list.dart/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nvoid remove(ListNode n0) {\n if (n0.next == null) return;\n // n0 -> P -> n1\n ListNode P = n0.next!;\n ListNode? n1 = P.next;\n n0.next = n1;\n}\n
linked_list.rs/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\n#[allow(non_snake_case)]\npub fn remove<T>(n0: &Rc<RefCell<ListNode<T>>>) {\n if n0.borrow().next.is_none() {return};\n // n0 -> P -> n1\n let P = n0.borrow_mut().next.take();\n if let Some(node) = P {\n let n1 = node.borrow_mut().next.take();\n n0.borrow_mut().next = n1;\n }\n}\n
linked_list.c/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\n// \u6ce8\u610f\uff1astdio.h \u5360\u7528\u4e86 remove \u5173\u952e\u8bcd\nvoid removeItem(ListNode *n0) {\n if (!n0->next)\n return;\n // n0 -> P -> n1\n ListNode *P = n0->next;\n ListNode *n1 = P->next;\n n0->next = n1;\n // \u91ca\u653e\u5185\u5b58\n free(P);\n}\n
linked_list.zig// \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9\nfn remove(n0: ?*inc.ListNode(i32)) void {\n if (n0.?.next == null) return;\n // n0 -> P -> n1\n var P = n0.?.next;\n var n1 = P.?.next;\n n0.?.next = n1;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/linked_list/#4-accessing-nodes","title":"4. \u00a0 Accessing Nodes","text":"Accessing nodes in a linked list is less efficient. As mentioned earlier, any element in an array can be accessed in \\(O(1)\\) time. However, in a linked list, the program needs to start from the head node and traverse each node sequentially until it finds the target node. That is, accessing the \\(i\\)-th node of a linked list requires \\(i - 1\\) iterations, with a time complexity of \\(O(n)\\).
PythonC++JavaC#GoSwiftJSTSDartRustCZig linked_list.pydef access(head: ListNode, index: int) -> ListNode | None:\n \"\"\"\u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9\"\"\"\n for _ in range(index):\n if not head:\n return None\n head = head.next\n return head\n
linked_list.cpp/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nListNode *access(ListNode *head, int index) {\n for (int i = 0; i < index; i++) {\n if (head == nullptr)\n return nullptr;\n head = head->next;\n }\n return head;\n}\n
linked_list.java/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nListNode access(ListNode head, int index) {\n for (int i = 0; i < index; i++) {\n if (head == null)\n return null;\n head = head.next;\n }\n return head;\n}\n
linked_list.cs/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nListNode? Access(ListNode? head, int index) {\n for (int i = 0; i < index; i++) {\n if (head == null)\n return null;\n head = head.next;\n }\n return head;\n}\n
linked_list.go/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nfunc access(head *ListNode, index int) *ListNode {\n for i := 0; i < index; i++ {\n if head == nil {\n return nil\n }\n head = head.Next\n }\n return head\n}\n
linked_list.swift/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nfunc access(head: ListNode, index: Int) -> ListNode? {\n var head: ListNode? = head\n for _ in 0 ..< index {\n if head == nil {\n return nil\n }\n head = head?.next\n }\n return head\n}\n
linked_list.js/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nfunction access(head, index) {\n for (let i = 0; i < index; i++) {\n if (!head) {\n return null;\n }\n head = head.next;\n }\n return head;\n}\n
linked_list.ts/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nfunction access(head: ListNode | null, index: number): ListNode | null {\n for (let i = 0; i < index; i++) {\n if (!head) {\n return null;\n }\n head = head.next;\n }\n return head;\n}\n
linked_list.dart/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nListNode? access(ListNode? head, int index) {\n for (var i = 0; i < index; i++) {\n if (head == null) return null;\n head = head.next;\n }\n return head;\n}\n
linked_list.rs/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\npub fn access<T>(head: Rc<RefCell<ListNode<T>>>, index: i32) -> Rc<RefCell<ListNode<T>>> {\n if index <= 0 {return head};\n if let Some(node) = &head.borrow_mut().next {\n return access(node.clone(), index - 1);\n }\n return head;\n}\n
linked_list.c/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nListNode *access(ListNode *head, int index) {\n for (int i = 0; i < index; i++) {\n if (head == NULL)\n return NULL;\n head = head->next;\n }\n return head;\n}\n
linked_list.zig// \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9\nfn access(node: ?*inc.ListNode(i32), index: i32) ?*inc.ListNode(i32) {\n var head = node;\n var i: i32 = 0;\n while (i < index) : (i += 1) {\n head = head.?.next;\n if (head == null) return null;\n }\n return head;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/linked_list/#5-finding-nodes","title":"5. \u00a0 Finding Nodes","text":"Traverse the linked list to find a node with a value equal to target
, and output the index of that node in the linked list. This process also falls under linear search. The code is as follows:
def find(head: ListNode, target: int) -> int:\n \"\"\"\u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9\"\"\"\n index = 0\n while head:\n if head.val == target:\n return index\n head = head.next\n index += 1\n return -1\n
linked_list.cpp/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nint find(ListNode *head, int target) {\n int index = 0;\n while (head != nullptr) {\n if (head->val == target)\n return index;\n head = head->next;\n index++;\n }\n return -1;\n}\n
linked_list.java/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nint find(ListNode head, int target) {\n int index = 0;\n while (head != null) {\n if (head.val == target)\n return index;\n head = head.next;\n index++;\n }\n return -1;\n}\n
linked_list.cs/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nint Find(ListNode? head, int target) {\n int index = 0;\n while (head != null) {\n if (head.val == target)\n return index;\n head = head.next;\n index++;\n }\n return -1;\n}\n
linked_list.go/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nfunc findNode(head *ListNode, target int) int {\n index := 0\n for head != nil {\n if head.Val == target {\n return index\n }\n head = head.Next\n index++\n }\n return -1\n}\n
linked_list.swift/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nfunc find(head: ListNode, target: Int) -> Int {\n var head: ListNode? = head\n var index = 0\n while head != nil {\n if head?.val == target {\n return index\n }\n head = head?.next\n index += 1\n }\n return -1\n}\n
linked_list.js/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nfunction find(head, target) {\n let index = 0;\n while (head !== null) {\n if (head.val === target) {\n return index;\n }\n head = head.next;\n index += 1;\n }\n return -1;\n}\n
linked_list.ts/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nfunction find(head: ListNode | null, target: number): number {\n let index = 0;\n while (head !== null) {\n if (head.val === target) {\n return index;\n }\n head = head.next;\n index += 1;\n }\n return -1;\n}\n
linked_list.dart/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nint find(ListNode? head, int target) {\n int index = 0;\n while (head != null) {\n if (head.val == target) {\n return index;\n }\n head = head.next;\n index++;\n }\n return -1;\n}\n
linked_list.rs/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\npub fn find<T: PartialEq>(head: Rc<RefCell<ListNode<T>>>, target: T, index: i32) -> i32 {\n if head.borrow().val == target {return index};\n if let Some(node) = &head.borrow_mut().next {\n return find(node.clone(), target, index + 1);\n }\n return -1;\n}\n
linked_list.c/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nint find(ListNode *head, int target) {\n int index = 0;\n while (head) {\n if (head->val == target)\n return index;\n head = head->next;\n index++;\n }\n return -1;\n}\n
linked_list.zig// \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9\nfn find(node: ?*inc.ListNode(i32), target: i32) i32 {\n var head = node;\n var index: i32 = 0;\n while (head != null) {\n if (head.?.val == target) return index;\n head = head.?.next;\n index += 1;\n }\n return -1;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/linked_list/#422-arrays-vs-linked-lists","title":"4.2.2 \u00a0 Arrays vs. Linked Lists","text":"The following table summarizes the characteristics of arrays and linked lists and compares their operational efficiencies. Since they employ two opposite storage strategies, their properties and operational efficiencies also show contrasting features.
Table 4-1 \u00a0 Efficiency Comparison of Arrays and Linked Lists
Arrays Linked Lists Storage Contiguous Memory Space Dispersed Memory Space Capacity Expansion Fixed Length Flexible Expansion Memory Efficiency Less Memory per Element, Potential Space Wastage More Memory per Element Accessing Elements \\(O(1)\\) \\(O(n)\\) Adding Elements \\(O(n)\\) \\(O(1)\\) Deleting Elements \\(O(n)\\) \\(O(1)\\)"},{"location":"chapter_array_and_linkedlist/linked_list/#423-common-types-of-linked-lists","title":"4.2.3 \u00a0 Common Types of Linked Lists","text":"As shown in the following image, there are three common types of linked lists.
None
), is the tail node.class ListNode:\n \"\"\"Bidirectional linked list node class\"\"\"\"\n def __init__(self, val: int):\n self.val: int = val # Node value\n self.next: ListNode | None = None # Reference to the successor node\n self.prev: ListNode | None = None # Reference to a predecessor node\n
/* Bidirectional linked list node structure */\nstruct ListNode {\n int val; // Node value\n ListNode *next; // Pointer to the successor node\n ListNode *prev; // Pointer to the predecessor node\n ListNode(int x) : val(x), next(nullptr), prev(nullptr) {} // Constructor\n};\n
/* Bidirectional linked list node class */\nclass ListNode {\n int val; // Node value\n ListNode next; // Reference to the next node\n ListNode prev; // Reference to the predecessor node\n ListNode(int x) { val = x; } // Constructor\n}\n
/* Bidirectional linked list node class */\nclass ListNode(int x) { // Constructor\n int val = x; // Node value\n ListNode next; // Reference to the next node\n ListNode prev; // Reference to the predecessor node\n}\n
/* Bidirectional linked list node structure */\ntype DoublyListNode struct {\n Val int // Node value\n Next *DoublyListNode // Pointer to the successor node\n Prev *DoublyListNode // Pointer to the predecessor node\n}\n\n// NewDoublyListNode initialization\nfunc NewDoublyListNode(val int) *DoublyListNode {\n return &DoublyListNode{\n Val: val,\n Next: nil,\n Prev: nil,\n }\n}\n
/* Bidirectional linked list node class */\nclass ListNode {\n var val: Int // Node value\n var next: ListNode? // Reference to the next node\n var prev: ListNode? // Reference to the predecessor node\n\n init(x: Int) { // Constructor\n val = x\n }\n}\n
/* Bidirectional linked list node class */\nclass ListNode {\n constructor(val, next, prev) {\n this.val = val === undefined ? 0 : val; // Node value\n this.next = next === undefined ? null : next; // Reference to the successor node\n this.prev = prev === undefined ? null : prev; // Reference to the predecessor node\n }\n}\n
/* Bidirectional linked list node class */\nclass ListNode {\n val: number;\n next: ListNode | null;\n prev: ListNode | null;\n constructor(val?: number, next?: ListNode | null, prev?: ListNode | null) {\n this.val = val === undefined ? 0 : val; // Node value\n this.next = next === undefined ? null : next; // Reference to the successor node\n this.prev = prev === undefined ? null : prev; // Reference to the predecessor node\n }\n}\n
/* Bidirectional linked list node class */\nclass ListNode {\n int val; // Node value\n ListNode next; // Reference to the next node\n ListNode prev; // Reference to the predecessor node\n ListNode(this.val, [this.next, this.prev]); // Constructor\n}\n
use std::rc::Rc;\nuse std::cell::RefCell;\n\n/* Bidirectional linked list node type */\n#[derive(Debug)]\nstruct ListNode {\n val: i32, // Node value\n next: Option<Rc<RefCell<ListNode>>>, // Pointer to successor node\n prev: Option<Rc<RefCell<ListNode>>>, // Pointer to predecessor node\n}\n\n/* Constructors */\nimpl ListNode {\n fn new(val: i32) -> Self {\n ListNode {\n val,\n next: None,\n prev: None,\n }\n }\n}\n
/* Bidirectional linked list node structure */\ntypedef struct ListNode {\n int val; // Node value\n struct ListNode *next; // Pointer to the successor node\n struct ListNode *prev; // Pointer to the predecessor node\n} ListNode;\n\n/* Constructors */\nListNode *newListNode(int val) {\n ListNode *node, *next;\n node = (ListNode *) malloc(sizeof(ListNode));\n node->val = val;\n node->next = NULL;\n node->prev = NULL;\n return node;\n}\n
// Bidirectional linked list node class\npub fn ListNode(comptime T: type) type {\n return struct {\n const Self = @This();\n\n val: T = 0, // Node value\n next: ?*Self = null, // Pointer to the successor node\n prev: ?*Self = null, // Pointer to the predecessor node\n\n // Constructor\n pub fn init(self: *Self, x: i32) void {\n self.val = x;\n self.next = null;\n self.prev = null;\n }\n };\n}\n
Figure 4-8 \u00a0 Common Types of Linked Lists
"},{"location":"chapter_array_and_linkedlist/linked_list/#424-typical-applications-of-linked-lists","title":"4.2.4 \u00a0 Typical Applications of Linked Lists","text":"Singly linked lists are commonly used to implement stacks, queues, hash tables, and graphs.
Doubly linked lists are commonly used in scenarios that require quick access to the previous and next elements.
Circular linked lists are commonly used in scenarios requiring periodic operations, such as resource scheduling in operating systems.
A \"list\" is an abstract data structure concept, representing an ordered collection of elements. It supports operations like element access, modification, addition, deletion, and traversal, without requiring users to consider capacity limitations. Lists can be implemented based on linked lists or arrays.
When using arrays to implement lists, the fixed length property reduces the practicality of the list. This is because we often cannot determine in advance how much data needs to be stored, making it difficult to choose an appropriate list length. If the length is too small, it may not meet the requirements; if too large, it may waste memory space.
To solve this problem, we can use a \"dynamic array\" to implement lists. It inherits the advantages of arrays and can dynamically expand during program execution.
In fact, many programming languages' standard libraries implement lists using dynamic arrays, such as Python's list
, Java's ArrayList
, C++'s vector
, and C#'s List
. In the following discussion, we will consider \"list\" and \"dynamic array\" as synonymous concepts.
We typically use two methods of initialization: \"without initial values\" and \"with initial values\".
PythonC++JavaC#GoSwiftJSTSDartRustCZig list.py# Initialize list\n# Without initial values\nnums1: list[int] = []\n# With initial values\nnums: list[int] = [1, 3, 2, 5, 4]\n
list.cpp/* Initialize list */\n// Note, in C++ the vector is the equivalent of nums described here\n// Without initial values\nvector<int> nums1;\n// With initial values\nvector<int> nums = { 1, 3, 2, 5, 4 };\n
list.java/* Initialize list */\n// Without initial values\nList<Integer> nums1 = new ArrayList<>();\n// With initial values (note the element type should be the wrapper class Integer[] for int[])\nInteger[] numbers = new Integer[] { 1, 3, 2, 5, 4 };\nList<Integer> nums = new ArrayList<>(Arrays.asList(numbers));\n
list.cs/* Initialize list */\n// Without initial values\nList<int> nums1 = [];\n// With initial values\nint[] numbers = [1, 3, 2, 5, 4];\nList<int> nums = [.. numbers];\n
list_test.go/* Initialize list */\n// Without initial values\nnums1 := []int{}\n// With initial values\nnums := []int{1, 3, 2, 5, 4}\n
list.swift/* Initialize list */\n// Without initial values\nlet nums1: [Int] = []\n// With initial values\nvar nums = [1, 3, 2, 5, 4]\n
list.js/* Initialize list */\n// Without initial values\nconst nums1 = [];\n// With initial values\nconst nums = [1, 3, 2, 5, 4];\n
list.ts/* Initialize list */\n// Without initial values\nconst nums1: number[] = [];\n// With initial values\nconst nums: number[] = [1, 3, 2, 5, 4];\n
list.dart/* Initialize list */\n// Without initial values\nList<int> nums1 = [];\n// With initial values\nList<int> nums = [1, 3, 2, 5, 4];\n
list.rs/* Initialize list */\n// Without initial values\nlet nums1: Vec<i32> = Vec::new();\n// With initial values\nlet nums: Vec<i32> = vec![1, 3, 2, 5, 4];\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Initialize list\nvar nums = std.ArrayList(i32).init(std.heap.page_allocator);\ndefer nums.deinit();\ntry nums.appendSlice(&[_]i32{ 1, 3, 2, 5, 4 });\n
"},{"location":"chapter_array_and_linkedlist/list/#2-accessing-elements","title":"2. \u00a0 Accessing Elements","text":"Lists are essentially arrays, so accessing and updating elements can be done in \\(O(1)\\) time, which is very efficient.
PythonC++JavaC#GoSwiftJSTSDartRustCZig list.py# Access elements\nnum: int = nums[1] # Access the element at index 1\n\n# Update elements\nnums[1] = 0 # Update the element at index 1 to 0\n
list.cpp/* Access elements */\nint num = nums[1]; // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list.java/* Access elements */\nint num = nums.get(1); // Access the element at index 1\n\n/* Update elements */\nnums.set(1, 0); // Update the element at index 1 to 0\n
list.cs/* Access elements */\nint num = nums[1]; // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list_test.go/* Access elements */\nnum := nums[1] // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0 // Update the element at index 1 to 0\n
list.swift/* Access elements */\nlet num = nums[1] // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0 // Update the element at index 1 to 0\n
list.js/* Access elements */\nconst num = nums[1]; // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list.ts/* Access elements */\nconst num: number = nums[1]; // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list.dart/* Access elements */\nint num = nums[1]; // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list.rs/* Access elements */\nlet num: i32 = nums[1]; // Access the element at index 1\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Access elements\nvar num = nums.items[1]; // Access the element at index 1\n\n// Update elements\nnums.items[1] = 0; // Update the element at index 1 to 0 \n
"},{"location":"chapter_array_and_linkedlist/list/#3-inserting-and-deleting-elements","title":"3. \u00a0 Inserting and Deleting Elements","text":"Compared to arrays, lists can freely add and remove elements. Adding elements at the end of the list has a time complexity of \\(O(1)\\), but the efficiency of inserting and deleting elements is still the same as in arrays, with a time complexity of \\(O(n)\\).
PythonC++JavaC#GoSwiftJSTSDartRustCZig list.py# Clear list\nnums.clear()\n\n# Append elements at the end\nnums.append(1)\nnums.append(3)\nnums.append(2)\nnums.append(5)\nnums.append(4)\n\n# Insert element in the middle\nnums.insert(3, 6) # Insert number 6 at index 3\n\n# Remove elements\nnums.pop(3) # Remove the element at index 3\n
list.cpp/* Clear list */\nnums.clear();\n\n/* Append elements at the end */\nnums.push_back(1);\nnums.push_back(3);\nnums.push_back(2);\nnums.push_back(5);\nnums.push_back(4);\n\n/* Insert element in the middle */\nnums.insert(nums.begin() + 3, 6); // Insert number 6 at index 3\n\n/* Remove elements */\nnums.erase(nums.begin() + 3); // Remove the element at index 3\n
list.java/* Clear list */\nnums.clear();\n\n/* Append elements at the end */\nnums.add(1);\nnums.add(3);\nnums.add(2);\nnums.add(5);\nnums.add(4);\n\n/* Insert element in the middle */\nnums.add(3, 6); // Insert number 6 at index 3\n\n/* Remove elements */\nnums.remove(3); // Remove the element at index 3\n
list.cs/* Clear list */\nnums.Clear();\n\n/* Append elements at the end */\nnums.Add(1);\nnums.Add(3);\nnums.Add(2);\nnums.Add(5);\nnums.Add(4);\n\n/* Insert element in the middle */\nnums.Insert(3, 6);\n\n/* Remove elements */\nnums.RemoveAt(3);\n
list_test.go/* Clear list */\nnums = nil\n\n/* Append elements at the end */\nnums = append(nums, 1)\nnums = append(nums, 3)\nnums = append(nums, 2)\nnums = append(nums, 5)\nnums = append(nums, 4)\n\n/* Insert element in the middle */\nnums = append(nums[:3], append([]int{6}, nums[3:]...)...) // Insert number 6 at index 3\n\n/* Remove elements */\nnums = append(nums[:3], nums[4:]...) // Remove the element at index 3\n
list.swift/* Clear list */\nnums.removeAll()\n\n/* Append elements at the end */\nnums.append(1)\nnums.append(3)\nnums.append(2)\nnums.append(5)\nnums.append(4)\n\n/* Insert element in the middle */\nnums.insert(6, at: 3) // Insert number 6 at index 3\n\n/* Remove elements */\nnums.remove(at: 3) // Remove the element at index 3\n
list.js/* Clear list */\nnums.length = 0;\n\n/* Append elements at the end */\nnums.push(1);\nnums.push(3);\nnums.push(2);\nnums.push(5);\nnums.push(4);\n\n/* Insert element in the middle */\nnums.splice(3, 0, 6);\n\n/* Remove elements */\nnums.splice(3, 1);\n
list.ts/* Clear list */\nnums.length = 0;\n\n/* Append elements at the end */\nnums.push(1);\nnums.push(3);\nnums.push(2);\nnums.push(5);\nnums.push(4);\n\n/* Insert element in the middle */\nnums.splice(3, 0, 6);\n\n/* Remove elements */\nnums.splice(3, 1);\n
list.dart/* Clear list */\nnums.clear();\n\n/* Append elements at the end */\nnums.add(1);\nnums.add(3);\nnums.add(2);\nnums.add(5);\nnums.add(4);\n\n/* Insert element in the middle */\nnums.insert(3, 6); // Insert number 6 at index 3\n\n/* Remove elements */\nnums.removeAt(3); // Remove the element at index 3\n
list.rs/* Clear list */\nnums.clear();\n\n/* Append elements at the end */\nnums.push(1);\nnums.push(3);\nnums.push(2);\nnums.push(5);\nnums.push(4);\n\n/* Insert element in the middle */\nnums.insert(3, 6); // Insert number 6 at index 3\n\n/* Remove elements */\nnums.remove(3); // Remove the element at index 3\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Clear list\nnums.clearRetainingCapacity();\n\n// Append elements at the end\ntry nums.append(1);\ntry nums.append(3);\ntry nums.append(2);\ntry nums.append(5);\ntry nums.append(4);\n\n// Insert element in the middle\ntry nums.insert(3, 6); // Insert number 6 at index 3\n\n// Remove elements\n_ = nums.orderedRemove(3); // Remove the element at index 3\n
"},{"location":"chapter_array_and_linkedlist/list/#4-traversing-the-list","title":"4. \u00a0 Traversing the List","text":"Like arrays, lists can be traversed based on index, or by directly iterating over each element.
PythonC++JavaC#GoSwiftJSTSDartRustCZig list.py# Iterate through the list by index\ncount = 0\nfor i in range(len(nums)):\n count += nums[i]\n\n# Iterate directly through list elements\nfor num in nums:\n count += num\n
list.cpp/* Iterate through the list by index */\nint count = 0;\nfor (int i = 0; i < nums.size(); i++) {\n count += nums[i];\n}\n\n/* Iterate directly through list elements */\ncount = 0;\nfor (int num : nums) {\n count += num;\n}\n
list.java/* Iterate through the list by index */\nint count = 0;\nfor (int i = 0; i < nums.size(); i++) {\n count += nums.get(i);\n}\n\n/* Iterate directly through list elements */\nfor (int num : nums) {\n count += num;\n}\n
list.cs/* Iterate through the list by index */\nint count = 0;\nfor (int i = 0; i < nums.Count; i++) {\n count += nums[i];\n}\n\n/* Iterate directly through list elements */\ncount = 0;\nforeach (int num in nums) {\n count += num;\n}\n
list_test.go/* Iterate through the list by index */\ncount := 0\nfor i := 0; i < len(nums); i++ {\n count += nums[i]\n}\n\n/* Iterate directly through list elements */\ncount = 0\nfor _, num := range nums {\n count += num\n}\n
list.swift/* Iterate through the list by index */\nvar count = 0\nfor i in nums.indices {\n count += nums[i]\n}\n\n/* Iterate directly through list elements */\ncount = 0\nfor num in nums {\n count += num\n}\n
list.js/* Iterate through the list by index */\nlet count = 0;\nfor (let i = 0; i < nums.length; i++) {\n count += nums[i];\n}\n\n/* Iterate directly through list elements */\ncount = 0;\nfor (const num of nums) {\n count += num;\n}\n
list.ts/* Iterate through the list by index */\nlet count = 0;\nfor (let i = 0; i < nums.length; i++) {\n count += nums[i];\n}\n\n/* Iterate directly through list elements */\ncount = 0;\nfor (const num of nums) {\n count += num;\n}\n
list.dart/* Iterate through the list by index */\nint count = 0;\nfor (var i = 0; i < nums.length; i++) {\n count += nums[i];\n}\n\n/* Iterate directly through list elements */\ncount = 0;\nfor (var num in nums) {\n count += num;\n}\n
list.rs// Iterate through the list by index\nlet mut _count = 0;\nfor i in 0..nums.len() {\n _count += nums[i];\n}\n\n// Iterate directly through list elements\n_count = 0;\nfor num in &nums {\n _count += num;\n}\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Iterate through the list by index\nvar count: i32 = 0;\nvar i: i32 = 0;\nwhile (i < nums.items.len) : (i += 1) {\n count += nums[i];\n}\n\n// Iterate directly through list elements\ncount = 0;\nfor (nums.items) |num| {\n count += num;\n}\n
"},{"location":"chapter_array_and_linkedlist/list/#5-concatenating-lists","title":"5. \u00a0 Concatenating Lists","text":"Given a new list nums1
, we can append it to the end of the original list.
# Concatenate two lists\nnums1: list[int] = [6, 8, 7, 10, 9]\nnums += nums1 # Concatenate nums1 to the end of nums\n
list.cpp/* Concatenate two lists */\nvector<int> nums1 = { 6, 8, 7, 10, 9 };\n// Concatenate nums1 to the end of nums\nnums.insert(nums.end(), nums1.begin(), nums1.end());\n
list.java/* Concatenate two lists */\nList<Integer> nums1 = new ArrayList<>(Arrays.asList(new Integer[] { 6, 8, 7, 10, 9 }));\nnums.addAll(nums1); // Concatenate nums1 to the end of nums\n
list.cs/* Concatenate two lists */\nList<int> nums1 = [6, 8, 7, 10, 9];\nnums.AddRange(nums1); // Concatenate nums1 to the end of nums\n
list_test.go/* Concatenate two lists */\nnums1 := []int{6, 8, 7, 10, 9}\nnums = append(nums, nums1...) // Concatenate nums1 to the end of nums\n
list.swift/* Concatenate two lists */\nlet nums1 = [6, 8, 7, 10, 9]\nnums.append(contentsOf: nums1) // Concatenate nums1 to the end of nums\n
list.js/* Concatenate two lists */\nconst nums1 = [6, 8, 7, 10, 9];\nnums.push(...nums1); // Concatenate nums1 to the end of nums\n
list.ts/* Concatenate two lists */\nconst nums1: number[] = [6, 8, 7, 10, 9];\nnums.push(...nums1); // Concatenate nums1 to the end of nums\n
list.dart/* Concatenate two lists */\nList<int> nums1 = [6, 8, 7, 10, 9];\nnums.addAll(nums1); // Concatenate nums1 to the end of nums\n
list.rs/* Concatenate two lists */\nlet nums1: Vec<i32> = vec![6, 8, 7, 10, 9];\nnums.extend(nums1);\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Concatenate two lists\nvar nums1 = std.ArrayList(i32).init(std.heap.page_allocator);\ndefer nums1.deinit();\ntry nums1.appendSlice(&[_]i32{ 6, 8, 7, 10, 9 });\ntry nums.insertSlice(nums.items.len, nums1.items); // Concatenate nums1 to the end of nums\n
"},{"location":"chapter_array_and_linkedlist/list/#6-sorting-the-list","title":"6. \u00a0 Sorting the List","text":"After sorting the list, we can use algorithms often tested in array-related algorithm problems, such as \"binary search\" and \"two-pointer\" algorithms.
PythonC++JavaC#GoSwiftJSTSDartRustCZig list.py# Sort the list\nnums.sort() # After sorting, the list elements are in ascending order\n
list.cpp/* Sort the list */\nsort(nums.begin(), nums.end()); // After sorting, the list elements are in ascending order\n
list.java/* Sort the list */\nCollections.sort(nums); // After sorting, the list elements are in ascending order\n
list.cs/* Sort the list */\nnums.Sort(); // After sorting, the list elements are in ascending order\n
list_test.go/* Sort the list */\nsort.Ints(nums) // After sorting, the list elements are in ascending order\n
list.swift/* Sort the list */\nnums.sort() // After sorting, the list elements are in ascending order\n
list.js/* Sort the list */ \nnums.sort((a, b) => a - b); // After sorting, the list elements are in ascending order\n
list.ts/* Sort the list */\nnums.sort((a, b) => a - b); // After sorting, the list elements are in ascending order\n
list.dart/* Sort the list */\nnums.sort(); // After sorting, the list elements are in ascending order\n
list.rs/* Sort the list */\nnums.sort(); // After sorting, the list elements are in ascending order\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Sort the list\nstd.sort.sort(i32, nums.items, {}, comptime std.sort.asc(i32));\n
"},{"location":"chapter_array_and_linkedlist/list/#432-list-implementation","title":"4.3.2 \u00a0 List Implementation","text":"Many programming languages have built-in lists, such as Java, C++, Python, etc. Their implementations are quite complex, with very meticulous settings for parameters such as initial capacity and expansion multiplier. Interested readers can refer to the source code for learning.
To deepen the understanding of how lists work, let's try implementing a simple version of a list, focusing on three key designs.
size
to record the current number of elements in the list, updating in real-time with element insertion and deletion. With this variable, we can locate the end of the list and determine whether expansion is needed.class MyList:\n \"\"\"\u5217\u8868\u7c7b\"\"\"\n\n def __init__(self):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._capacity: int = 10 # \u5217\u8868\u5bb9\u91cf\n self._arr: list[int] = [0] * self._capacity # \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n self._size: int = 0 # \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n self._extend_ratio: int = 2 # \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\"\"\"\n return self._size\n\n def capacity(self) -> int:\n \"\"\"\u83b7\u53d6\u5217\u8868\u5bb9\u91cf\"\"\"\n return self._capacity\n\n def get(self, index: int) -> int:\n \"\"\"\u8bbf\u95ee\u5143\u7d20\"\"\"\n # \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if index < 0 or index >= self._size:\n raise IndexError(\"\u7d22\u5f15\u8d8a\u754c\")\n return self._arr[index]\n\n def set(self, num: int, index: int):\n \"\"\"\u66f4\u65b0\u5143\u7d20\"\"\"\n if index < 0 or index >= self._size:\n raise IndexError(\"\u7d22\u5f15\u8d8a\u754c\")\n self._arr[index] = num\n\n def add(self, num: int):\n \"\"\"\u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20\"\"\"\n # \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if self.size() == self.capacity():\n self.extend_capacity()\n self._arr[self._size] = num\n self._size += 1\n\n def insert(self, num: int, index: int):\n \"\"\"\u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20\"\"\"\n if index < 0 or index >= self._size:\n raise IndexError(\"\u7d22\u5f15\u8d8a\u754c\")\n # \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if self._size == self.capacity():\n self.extend_capacity()\n # \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for j in range(self._size - 1, index - 1, -1):\n self._arr[j + 1] = self._arr[j]\n self._arr[index] = num\n # \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self._size += 1\n\n def remove(self, index: int) -> int:\n \"\"\"\u5220\u9664\u5143\u7d20\"\"\"\n if index < 0 or index >= self._size:\n raise IndexError(\"\u7d22\u5f15\u8d8a\u754c\")\n num = self._arr[index]\n # \u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for j in range(index, self._size - 1):\n self._arr[j] = self._arr[j + 1]\n # \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self._size -= 1\n # \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num\n\n def extend_capacity(self):\n \"\"\"\u5217\u8868\u6269\u5bb9\"\"\"\n # \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 _extend_ratio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1)\n # \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n self._capacity = len(self._arr)\n\n def to_array(self) -> list[int]:\n \"\"\"\u8fd4\u56de\u6709\u6548\u957f\u5ea6\u7684\u5217\u8868\"\"\"\n return self._arr[: self._size]\n
my_list.cpp/* \u5217\u8868\u7c7b */\nclass MyList {\n private:\n int *arr; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n int arrCapacity = 10; // \u5217\u8868\u5bb9\u91cf\n int arrSize = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n int extendRatio = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n public:\n /* \u6784\u9020\u65b9\u6cd5 */\n MyList() {\n arr = new int[arrCapacity];\n }\n\n /* \u6790\u6784\u65b9\u6cd5 */\n ~MyList() {\n delete[] arr;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n int size() {\n return arrSize;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n int capacity() {\n return arrCapacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n int get(int index) {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 || index >= size())\n throw out_of_range(\"\u7d22\u5f15\u8d8a\u754c\");\n return arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n void set(int index, int num) {\n if (index < 0 || index >= size())\n throw out_of_range(\"\u7d22\u5f15\u8d8a\u754c\");\n arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n void add(int num) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (size() == capacity())\n extendCapacity();\n arr[size()] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n void insert(int index, int num) {\n if (index < 0 || index >= size())\n throw out_of_range(\"\u7d22\u5f15\u8d8a\u754c\");\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (size() == capacity())\n extendCapacity();\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int j = size() - 1; j >= index; j--) {\n arr[j + 1] = arr[j];\n }\n arr[index] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n int remove(int index) {\n if (index < 0 || index >= size())\n throw out_of_range(\"\u7d22\u5f15\u8d8a\u754c\");\n int num = arr[index];\n // \u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int j = index; j < size() - 1; j++) {\n arr[j] = arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n void extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extendRatio \u500d\u7684\u65b0\u6570\u7ec4\n int newCapacity = capacity() * extendRatio;\n int *tmp = arr;\n arr = new int[newCapacity];\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (int i = 0; i < size(); i++) {\n arr[i] = tmp[i];\n }\n // \u91ca\u653e\u5185\u5b58\n delete[] tmp;\n arrCapacity = newCapacity;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a Vector \u7528\u4e8e\u6253\u5370 */\n vector<int> toVector() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n vector<int> vec(size());\n for (int i = 0; i < size(); i++) {\n vec[i] = arr[i];\n }\n return vec;\n }\n};\n
my_list.java/* \u5217\u8868\u7c7b */\nclass MyList {\n private int[] arr; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n private int capacity = 10; // \u5217\u8868\u5bb9\u91cf\n private int size = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n private int extendRatio = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n public MyList() {\n arr = new int[capacity];\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09 */\n public int size() {\n return size;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n public int capacity() {\n return capacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n public int get(int index) {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 || index >= size)\n throw new IndexOutOfBoundsException(\"\u7d22\u5f15\u8d8a\u754c\");\n return arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n public void set(int index, int num) {\n if (index < 0 || index >= size)\n throw new IndexOutOfBoundsException(\"\u7d22\u5f15\u8d8a\u754c\");\n arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n public void add(int num) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (size == capacity())\n extendCapacity();\n arr[size] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n size++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n public void insert(int index, int num) {\n if (index < 0 || index >= size)\n throw new IndexOutOfBoundsException(\"\u7d22\u5f15\u8d8a\u754c\");\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (size == capacity())\n extendCapacity();\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int j = size - 1; j >= index; j--) {\n arr[j + 1] = arr[j];\n }\n arr[index] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n size++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n public int remove(int index) {\n if (index < 0 || index >= size)\n throw new IndexOutOfBoundsException(\"\u7d22\u5f15\u8d8a\u754c\");\n int num = arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int j = index; j < size - 1; j++) {\n arr[j] = arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n size--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n public void extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extendRatio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n arr = Arrays.copyOf(arr, capacity() * extendRatio);\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n capacity = arr.length;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n public int[] toArray() {\n int size = size();\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n int[] arr = new int[size];\n for (int i = 0; i < size; i++) {\n arr[i] = get(i);\n }\n return arr;\n }\n}\n
my_list.cs/* \u5217\u8868\u7c7b */\nclass MyList {\n private int[] arr; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n private int arrCapacity = 10; // \u5217\u8868\u5bb9\u91cf\n private int arrSize = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n private readonly int extendRatio = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n public MyList() {\n arr = new int[arrCapacity];\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n public int Size() {\n return arrSize;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n public int Capacity() {\n return arrCapacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n public int Get(int index) {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 || index >= arrSize)\n throw new IndexOutOfRangeException(\"\u7d22\u5f15\u8d8a\u754c\");\n return arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n public void Set(int index, int num) {\n if (index < 0 || index >= arrSize)\n throw new IndexOutOfRangeException(\"\u7d22\u5f15\u8d8a\u754c\");\n arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n public void Add(int num) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (arrSize == arrCapacity)\n ExtendCapacity();\n arr[arrSize] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n public void Insert(int index, int num) {\n if (index < 0 || index >= arrSize)\n throw new IndexOutOfRangeException(\"\u7d22\u5f15\u8d8a\u754c\");\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (arrSize == arrCapacity)\n ExtendCapacity();\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int j = arrSize - 1; j >= index; j--) {\n arr[j + 1] = arr[j];\n }\n arr[index] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n public int Remove(int index) {\n if (index < 0 || index >= arrSize)\n throw new IndexOutOfRangeException(\"\u7d22\u5f15\u8d8a\u754c\");\n int num = arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int j = index; j < arrSize - 1; j++) {\n arr[j] = arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n public void ExtendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a arrCapacity * extendRatio \u7684\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n Array.Resize(ref arr, arrCapacity * extendRatio);\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n arrCapacity = arr.Length;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n public int[] ToArray() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n int[] arr = new int[arrSize];\n for (int i = 0; i < arrSize; i++) {\n arr[i] = Get(i);\n }\n return arr;\n }\n}\n
my_list.go/* \u5217\u8868\u7c7b */\ntype myList struct {\n arrCapacity int\n arr []int\n arrSize int\n extendRatio int\n}\n\n/* \u6784\u9020\u51fd\u6570 */\nfunc newMyList() *myList {\n return &myList{\n arrCapacity: 10, // \u5217\u8868\u5bb9\u91cf\n arr: make([]int, 10), // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n arrSize: 0, // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n extendRatio: 2, // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n }\n}\n\n/* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09 */\nfunc (l *myList) size() int {\n return l.arrSize\n}\n\n/* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\nfunc (l *myList) capacity() int {\n return l.arrCapacity\n}\n\n/* \u8bbf\u95ee\u5143\u7d20 */\nfunc (l *myList) get(index int) int {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if index < 0 || index >= l.arrSize {\n panic(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n return l.arr[index]\n}\n\n/* \u66f4\u65b0\u5143\u7d20 */\nfunc (l *myList) set(num, index int) {\n if index < 0 || index >= l.arrSize {\n panic(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n l.arr[index] = num\n}\n\n/* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\nfunc (l *myList) add(num int) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if l.arrSize == l.arrCapacity {\n l.extendCapacity()\n }\n l.arr[l.arrSize] = num\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n l.arrSize++\n}\n\n/* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\nfunc (l *myList) insert(num, index int) {\n if index < 0 || index >= l.arrSize {\n panic(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if l.arrSize == l.arrCapacity {\n l.extendCapacity()\n }\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for j := l.arrSize - 1; j >= index; j-- {\n l.arr[j+1] = l.arr[j]\n }\n l.arr[index] = num\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n l.arrSize++\n}\n\n/* \u5220\u9664\u5143\u7d20 */\nfunc (l *myList) remove(index int) int {\n if index < 0 || index >= l.arrSize {\n panic(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n num := l.arr[index]\n // \u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for j := index; j < l.arrSize-1; j++ {\n l.arr[j] = l.arr[j+1]\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n l.arrSize--\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num\n}\n\n/* \u5217\u8868\u6269\u5bb9 */\nfunc (l *myList) extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extendRatio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n l.arr = append(l.arr, make([]int, l.arrCapacity*(l.extendRatio-1))...)\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n l.arrCapacity = len(l.arr)\n}\n\n/* \u8fd4\u56de\u6709\u6548\u957f\u5ea6\u7684\u5217\u8868 */\nfunc (l *myList) toArray() []int {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n return l.arr[:l.arrSize]\n}\n
my_list.swift/* \u5217\u8868\u7c7b */\nclass MyList {\n private var arr: [Int] // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n private var _capacity = 10 // \u5217\u8868\u5bb9\u91cf\n private var _size = 0 // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n private let extendRatio = 2 // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n init() {\n arr = Array(repeating: 0, count: _capacity)\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n func size() -> Int {\n _size\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n func capacity() -> Int {\n _capacity\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n func get(index: Int) -> Int {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\u5219\u629b\u51fa\u9519\u8bef\uff0c\u4e0b\u540c\n if index < 0 || index >= _size {\n fatalError(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n return arr[index]\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n func set(index: Int, num: Int) {\n if index < 0 || index >= _size {\n fatalError(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n arr[index] = num\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n func add(num: Int) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if _size == _capacity {\n extendCapacity()\n }\n arr[_size] = num\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size += 1\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n func insert(index: Int, num: Int) {\n if index < 0 || index >= _size {\n fatalError(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if _size == _capacity {\n extendCapacity()\n }\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for j in sequence(first: _size - 1, next: { $0 >= index + 1 ? $0 - 1 : nil }) {\n arr[j + 1] = arr[j]\n }\n arr[index] = num\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size += 1\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n @discardableResult\n func remove(index: Int) -> Int {\n if index < 0 || index >= _size {\n fatalError(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n let num = arr[index]\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for j in index ..< (_size - 1) {\n arr[j] = arr[j + 1]\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size -= 1\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n func extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extendRatio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n arr = arr + Array(repeating: 0, count: _capacity * (extendRatio - 1))\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n _capacity = arr.count\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n func toArray() -> [Int] {\n var arr = Array(repeating: 0, count: _size)\n for i in 0 ..< _size {\n arr[i] = get(index: i)\n }\n return arr\n }\n}\n
my_list.js/* \u5217\u8868\u7c7b */\nclass MyList {\n #arr = new Array(); // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n #capacity = 10; // \u5217\u8868\u5bb9\u91cf\n #size = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n #extendRatio = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n constructor() {\n this.#arr = new Array(this.#capacity);\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n size() {\n return this.#size;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n capacity() {\n return this.#capacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n get(index) {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 || index >= this.#size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n return this.#arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n set(index, num) {\n if (index < 0 || index >= this.#size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n this.#arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n add(num) {\n // \u5982\u679c\u957f\u5ea6\u7b49\u4e8e\u5bb9\u91cf\uff0c\u5219\u9700\u8981\u6269\u5bb9\n if (this.#size === this.#capacity) {\n this.extendCapacity();\n }\n // \u5c06\u65b0\u5143\u7d20\u6dfb\u52a0\u5230\u5217\u8868\u5c3e\u90e8\n this.#arr[this.#size] = num;\n this.#size++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n insert(index, num) {\n if (index < 0 || index >= this.#size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (this.#size === this.#capacity) {\n this.extendCapacity();\n }\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (let j = this.#size - 1; j >= index; j--) {\n this.#arr[j + 1] = this.#arr[j];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n this.#arr[index] = num;\n this.#size++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n remove(index) {\n if (index < 0 || index >= this.#size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n let num = this.#arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (let j = index; j < this.#size - 1; j++) {\n this.#arr[j] = this.#arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n this.#size--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extendRatio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n this.#arr = this.#arr.concat(\n new Array(this.capacity() * (this.#extendRatio - 1))\n );\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n this.#capacity = this.#arr.length;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n toArray() {\n let size = this.size();\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n const arr = new Array(size);\n for (let i = 0; i < size; i++) {\n arr[i] = this.get(i);\n }\n return arr;\n }\n}\n
my_list.ts/* \u5217\u8868\u7c7b */\nclass MyList {\n private arr: Array<number>; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n private _capacity: number = 10; // \u5217\u8868\u5bb9\u91cf\n private _size: number = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n private extendRatio: number = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n constructor() {\n this.arr = new Array(this._capacity);\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n public size(): number {\n return this._size;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n public capacity(): number {\n return this._capacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n public get(index: number): number {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 || index >= this._size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n return this.arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n public set(index: number, num: number): void {\n if (index < 0 || index >= this._size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n this.arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n public add(num: number): void {\n // \u5982\u679c\u957f\u5ea6\u7b49\u4e8e\u5bb9\u91cf\uff0c\u5219\u9700\u8981\u6269\u5bb9\n if (this._size === this._capacity) this.extendCapacity();\n // \u5c06\u65b0\u5143\u7d20\u6dfb\u52a0\u5230\u5217\u8868\u5c3e\u90e8\n this.arr[this._size] = num;\n this._size++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n public insert(index: number, num: number): void {\n if (index < 0 || index >= this._size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (this._size === this._capacity) {\n this.extendCapacity();\n }\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (let j = this._size - 1; j >= index; j--) {\n this.arr[j + 1] = this.arr[j];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n this.arr[index] = num;\n this._size++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n public remove(index: number): number {\n if (index < 0 || index >= this._size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n let num = this.arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (let j = index; j < this._size - 1; j++) {\n this.arr[j] = this.arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n this._size--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n public extendCapacity(): void {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a size \u7684\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n this.arr = this.arr.concat(\n new Array(this.capacity() * (this.extendRatio - 1))\n );\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n this._capacity = this.arr.length;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n public toArray(): number[] {\n let size = this.size();\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n const arr = new Array(size);\n for (let i = 0; i < size; i++) {\n arr[i] = this.get(i);\n }\n return arr;\n }\n}\n
my_list.dart/* \u5217\u8868\u7c7b */\nclass MyList {\n late List<int> _arr; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n int _capacity = 10; // \u5217\u8868\u5bb9\u91cf\n int _size = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n int _extendRatio = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n MyList() {\n _arr = List.filled(_capacity, 0);\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n int size() => _size;\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n int capacity() => _capacity;\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n int get(int index) {\n if (index >= _size) throw RangeError('\u7d22\u5f15\u8d8a\u754c');\n return _arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n void set(int index, int _num) {\n if (index >= _size) throw RangeError('\u7d22\u5f15\u8d8a\u754c');\n _arr[index] = _num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n void add(int _num) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (_size == _capacity) extendCapacity();\n _arr[_size] = _num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n void insert(int index, int _num) {\n if (index >= _size) throw RangeError('\u7d22\u5f15\u8d8a\u754c');\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (_size == _capacity) extendCapacity();\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (var j = _size - 1; j >= index; j--) {\n _arr[j + 1] = _arr[j];\n }\n _arr[index] = _num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n int remove(int index) {\n if (index >= _size) throw RangeError('\u7d22\u5f15\u8d8a\u754c');\n int _num = _arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (var j = index; j < _size - 1; j++) {\n _arr[j] = _arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return _num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n void extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 _extendRatio \u500d\u7684\u65b0\u6570\u7ec4\n final _newNums = List.filled(_capacity * _extendRatio, 0);\n // \u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n List.copyRange(_newNums, 0, _arr);\n // \u66f4\u65b0 _arr \u7684\u5f15\u7528\n _arr = _newNums;\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n _capacity = _arr.length;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n List<int> toArray() {\n List<int> arr = [];\n for (var i = 0; i < _size; i++) {\n arr.add(get(i));\n }\n return arr;\n }\n}\n
my_list.rs/* \u5217\u8868\u7c7b */\n#[allow(dead_code)]\nstruct MyList {\n arr: Vec<i32>, // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n capacity: usize, // \u5217\u8868\u5bb9\u91cf\n size: usize, // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n extend_ratio: usize, // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n}\n\n#[allow(unused,unused_comparisons)]\nimpl MyList {\n /* \u6784\u9020\u65b9\u6cd5 */\n pub fn new(capacity: usize) -> Self {\n let mut vec = Vec::new(); \n vec.resize(capacity, 0);\n Self {\n arr: vec,\n capacity,\n size: 0,\n extend_ratio: 2,\n }\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n pub fn size(&self) -> usize {\n return self.size;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n pub fn capacity(&self) -> usize {\n return self.capacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n pub fn get(&self, index: usize) -> i32 {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if index >= self.size {panic!(\"\u7d22\u5f15\u8d8a\u754c\")};\n return self.arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n pub fn set(&mut self, index: usize, num: i32) {\n if index >= self.size {panic!(\"\u7d22\u5f15\u8d8a\u754c\")};\n self.arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n pub fn add(&mut self, num: i32) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if self.size == self.capacity() {\n self.extend_capacity();\n }\n self.arr[self.size] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.size += 1;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n pub fn insert(&mut self, index: usize, num: i32) {\n if index >= self.size() {panic!(\"\u7d22\u5f15\u8d8a\u754c\")};\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if self.size == self.capacity() {\n self.extend_capacity();\n }\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for j in (index..self.size).rev() {\n self.arr[j + 1] = self.arr[j];\n }\n self.arr[index] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.size += 1;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n pub fn remove(&mut self, index: usize) -> i32 {\n if index >= self.size() {panic!(\"\u7d22\u5f15\u8d8a\u754c\")};\n let num = self.arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for j in (index..self.size - 1) {\n self.arr[j] = self.arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.size -= 1;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n pub fn extend_capacity(&mut self) {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extend_ratio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n let new_capacity = self.capacity * self.extend_ratio;\n self.arr.resize(new_capacity, 0);\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n self.capacity = new_capacity;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n pub fn to_array(&mut self) -> Vec<i32> {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n let mut arr = Vec::new();\n for i in 0..self.size {\n arr.push(self.get(i));\n }\n arr\n }\n}\n
my_list.c/* \u5217\u8868\u7c7b */\ntypedef struct {\n int *arr; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n int capacity; // \u5217\u8868\u5bb9\u91cf\n int size; // \u5217\u8868\u5927\u5c0f\n int extendRatio; // \u5217\u8868\u6bcf\u6b21\u6269\u5bb9\u7684\u500d\u6570\n} MyList;\n\n/* \u6784\u9020\u51fd\u6570 */\nMyList *newMyList() {\n MyList *nums = malloc(sizeof(MyList));\n nums->capacity = 10;\n nums->arr = malloc(sizeof(int) * nums->capacity);\n nums->size = 0;\n nums->extendRatio = 2;\n return nums;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delMyList(MyList *nums) {\n free(nums->arr);\n free(nums);\n}\n\n/* \u83b7\u53d6\u5217\u8868\u957f\u5ea6 */\nint size(MyList *nums) {\n return nums->size;\n}\n\n/* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\nint capacity(MyList *nums) {\n return nums->capacity;\n}\n\n/* \u8bbf\u95ee\u5143\u7d20 */\nint get(MyList *nums, int index) {\n assert(index >= 0 && index < nums->size);\n return nums->arr[index];\n}\n\n/* \u66f4\u65b0\u5143\u7d20 */\nvoid set(MyList *nums, int index, int num) {\n assert(index >= 0 && index < nums->size);\n nums->arr[index] = num;\n}\n\n/* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\nvoid add(MyList *nums, int num) {\n if (size(nums) == capacity(nums)) {\n extendCapacity(nums); // \u6269\u5bb9\n }\n nums->arr[size(nums)] = num;\n nums->size++;\n}\n\n/* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\nvoid insert(MyList *nums, int index, int num) {\n assert(index >= 0 && index < size(nums));\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (size(nums) == capacity(nums)) {\n extendCapacity(nums); // \u6269\u5bb9\n }\n for (int i = size(nums); i > index; --i) {\n nums->arr[i] = nums->arr[i - 1];\n }\n nums->arr[index] = num;\n nums->size++;\n}\n\n/* \u5220\u9664\u5143\u7d20 */\n// \u6ce8\u610f\uff1astdio.h \u5360\u7528\u4e86 remove \u5173\u952e\u8bcd\nint removeItem(MyList *nums, int index) {\n assert(index >= 0 && index < size(nums));\n int num = nums->arr[index];\n for (int i = index; i < size(nums) - 1; i++) {\n nums->arr[i] = nums->arr[i + 1];\n }\n nums->size--;\n return num;\n}\n\n/* \u5217\u8868\u6269\u5bb9 */\nvoid extendCapacity(MyList *nums) {\n // \u5148\u5206\u914d\u7a7a\u95f4\n int newCapacity = capacity(nums) * nums->extendRatio;\n int *extend = (int *)malloc(sizeof(int) * newCapacity);\n int *temp = nums->arr;\n\n // \u62f7\u8d1d\u65e7\u6570\u636e\u5230\u65b0\u6570\u636e\n for (int i = 0; i < size(nums); i++)\n extend[i] = nums->arr[i];\n\n // \u91ca\u653e\u65e7\u6570\u636e\n free(temp);\n\n // \u66f4\u65b0\u65b0\u6570\u636e\n nums->arr = extend;\n nums->capacity = newCapacity;\n}\n\n/* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a Array \u7528\u4e8e\u6253\u5370 */\nint *toArray(MyList *nums) {\n return nums->arr;\n}\n
my_list.zig// \u5217\u8868\u7c7b\nfn MyList(comptime T: type) type {\n return struct {\n const Self = @This();\n\n arr: []T = undefined, // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n arrCapacity: usize = 10, // \u5217\u8868\u5bb9\u91cf\n numSize: usize = 0, // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n extendRatio: usize = 2, // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n mem_arena: ?std.heap.ArenaAllocator = null,\n mem_allocator: std.mem.Allocator = undefined, // \u5185\u5b58\u5206\u914d\u5668\n\n // \u6784\u9020\u51fd\u6570\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u5217\u8868\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator) !void {\n if (self.mem_arena == null) {\n self.mem_arena = std.heap.ArenaAllocator.init(allocator);\n self.mem_allocator = self.mem_arena.?.allocator();\n }\n self.arr = try self.mem_allocator.alloc(T, self.arrCapacity);\n @memset(self.arr, @as(T, 0));\n }\n\n // \u6790\u6784\u51fd\u6570\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.mem_arena == null) return;\n self.mem_arena.?.deinit();\n }\n\n // \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n pub fn size(self: *Self) usize {\n return self.numSize;\n }\n\n // \u83b7\u53d6\u5217\u8868\u5bb9\u91cf\n pub fn capacity(self: *Self) usize {\n return self.arrCapacity;\n }\n\n // \u8bbf\u95ee\u5143\u7d20\n pub fn get(self: *Self, index: usize) T {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 or index >= self.size()) @panic(\"\u7d22\u5f15\u8d8a\u754c\");\n return self.arr[index];\n } \n\n // \u66f4\u65b0\u5143\u7d20\n pub fn set(self: *Self, index: usize, num: T) void {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 or index >= self.size()) @panic(\"\u7d22\u5f15\u8d8a\u754c\");\n self.arr[index] = num;\n } \n\n // \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20\n pub fn add(self: *Self, num: T) !void {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (self.size() == self.capacity()) try self.extendCapacity();\n self.arr[self.size()] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.numSize += 1;\n } \n\n // \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20\n pub fn insert(self: *Self, index: usize, num: T) !void {\n if (index < 0 or index >= self.size()) @panic(\"\u7d22\u5f15\u8d8a\u754c\");\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (self.size() == self.capacity()) try self.extendCapacity();\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n var j = self.size() - 1;\n while (j >= index) : (j -= 1) {\n self.arr[j + 1] = self.arr[j];\n }\n self.arr[index] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.numSize += 1;\n }\n\n // \u5220\u9664\u5143\u7d20\n pub fn remove(self: *Self, index: usize) T {\n if (index < 0 or index >= self.size()) @panic(\"\u7d22\u5f15\u8d8a\u754c\");\n var num = self.arr[index];\n // \u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n var j = index;\n while (j < self.size() - 1) : (j += 1) {\n self.arr[j] = self.arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.numSize -= 1;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n // \u5217\u8868\u6269\u5bb9\n pub fn extendCapacity(self: *Self) !void {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a size * extendRatio \u7684\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n var newCapacity = self.capacity() * self.extendRatio;\n var extend = try self.mem_allocator.alloc(T, newCapacity);\n @memset(extend, @as(T, 0));\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n std.mem.copy(T, extend, self.arr);\n self.arr = extend;\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n self.arrCapacity = newCapacity;\n }\n\n // \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4\n pub fn toArray(self: *Self) ![]T {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n var arr = try self.mem_allocator.alloc(T, self.size());\n @memset(arr, @as(T, 0));\n for (arr, 0..) |*num, i| {\n num.* = self.get(i);\n }\n return arr;\n }\n };\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/ram_and_cache/","title":"4.4 \u00a0 Memory and Cache *","text":"In the first two sections of this chapter, we explored arrays and linked lists, two fundamental and important data structures, representing \"continuous storage\" and \"dispersed storage\" respectively.
In fact, the physical structure largely determines the efficiency of a program's use of memory and cache, which in turn affects the overall performance of the algorithm.
"},{"location":"chapter_array_and_linkedlist/ram_and_cache/#441-computer-storage-devices","title":"4.4.1 \u00a0 Computer Storage Devices","text":"There are three types of storage devices in computers: \"hard disk,\" \"random-access memory (RAM),\" and \"cache memory.\" The following table shows their different roles and performance characteristics in computer systems.
Table 4-2 \u00a0 Computer Storage Devices
Hard Disk Memory Cache Usage Long-term storage of data, including OS, programs, files, etc. Temporary storage of currently running programs and data being processed Stores frequently accessed data and instructions, reducing the number of CPU accesses to memory Volatility Data is not lost after power off Data is lost after power off Data is lost after power off Capacity Larger, TB level Smaller, GB level Very small, MB level Speed Slower, several hundred to thousands MB/s Faster, several tens of GB/s Very fast, several tens to hundreds of GB/s Price Cheaper, several cents to yuan / GB More expensive, tens to hundreds of yuan / GB Very expensive, priced with CPUWe can imagine the computer storage system as a pyramid structure shown in the Figure 4-9 . The storage devices closer to the top of the pyramid are faster, have smaller capacity, and are more costly. This multi-level design is not accidental, but the result of careful consideration by computer scientists and engineers.
Figure 4-9 \u00a0 Computer Storage System
Note
The storage hierarchy of computers reflects a delicate balance between speed, capacity, and cost. In fact, this kind of trade-off is common in all industrial fields, requiring us to find the best balance between different advantages and limitations.
Overall, hard disks are used for long-term storage of large amounts of data, memory is used for temporary storage of data being processed during program execution, and cache is used to store frequently accessed data and instructions to improve program execution efficiency. Together, they ensure the efficient operation of computer systems.
As shown in the Figure 4-10 , during program execution, data is read from the hard disk into memory for CPU computation. The cache can be considered a part of the CPU, smartly loading data from memory to provide fast data access to the CPU, significantly enhancing program execution efficiency and reducing reliance on slower memory.
Figure 4-10 \u00a0 Data Flow Between Hard Disk, Memory, and Cache
"},{"location":"chapter_array_and_linkedlist/ram_and_cache/#442-memory-efficiency-of-data-structures","title":"4.4.2 \u00a0 Memory Efficiency of Data Structures","text":"In terms of memory space utilization, arrays and linked lists have their advantages and limitations.
On one hand, memory is limited and cannot be shared by multiple programs, so we hope that data structures can use space as efficiently as possible. The elements of an array are tightly packed without extra space for storing references (pointers) between linked list nodes, making them more space-efficient. However, arrays require allocating sufficient continuous memory space at once, which may lead to memory waste, and array expansion also requires additional time and space costs. In contrast, linked lists allocate and reclaim memory dynamically on a per-node basis, providing greater flexibility.
On the other hand, during program execution, as memory is repeatedly allocated and released, the degree of fragmentation of free memory becomes higher, leading to reduced memory utilization efficiency. Arrays, due to their continuous storage method, are relatively less likely to cause memory fragmentation. In contrast, the elements of a linked list are dispersedly stored, and frequent insertion and deletion operations make memory fragmentation more likely.
"},{"location":"chapter_array_and_linkedlist/ram_and_cache/#443-cache-efficiency-of-data-structures","title":"4.4.3 \u00a0 Cache Efficiency of Data Structures","text":"Although caches are much smaller in space capacity than memory, they are much faster and play a crucial role in program execution speed. Since the cache's capacity is limited and can only store a small part of frequently accessed data, when the CPU tries to access data not in the cache, a \"cache miss\" occurs, forcing the CPU to load the needed data from slower memory.
Clearly, the fewer the cache misses, the higher the CPU's data read-write efficiency, and the better the program performance. The proportion of successful data retrieval from the cache by the CPU is called the \"cache hit rate,\" a metric often used to measure cache efficiency.
To achieve higher efficiency, caches adopt the following data loading mechanisms.
In fact, arrays and linked lists have different cache utilization efficiencies, mainly reflected in the following aspects.
Overall, arrays have a higher cache hit rate and are generally more efficient in operation than linked lists. This makes data structures based on arrays more popular in solving algorithmic problems.
It should be noted that high cache efficiency does not mean that arrays are always better than linked lists. Which data structure to choose in actual applications should be based on specific requirements. For example, both arrays and linked lists can implement the \"stack\" data structure (which will be detailed in the next chapter), but they are suitable for different scenarios.
Q: Does storing arrays on the stack versus the heap affect time and space efficiency?
Arrays stored on both the stack and heap are stored in contiguous memory spaces, and data operation efficiency is essentially the same. However, stacks and heaps have their own characteristics, leading to the following differences.
Q: Why do arrays require elements of the same type, while linked lists do not emphasize same-type elements?
Linked lists consist of nodes connected by references (pointers), and each node can store data of different types, such as int, double, string, object, etc.
In contrast, array elements must be of the same type, allowing the calculation of offsets to access the corresponding element positions. For example, an array containing both int and long types, with single elements occupying 4 bytes and 8 bytes respectively, cannot use the following formula to calculate offsets, as the array contains elements of two different lengths.
# Element memory address = Array memory address + Element length * Element index\n
Q: After deleting a node, is it necessary to set P.next
to None
?
Not modifying P.next
is also acceptable. From the perspective of the linked list, traversing from the head node to the tail node will no longer encounter P
. This means that node P
has been effectively removed from the list, and where P
points no longer affects the list.
From a garbage collection perspective, for languages with automatic garbage collection mechanisms like Java, Python, and Go, whether node P
is collected depends on whether there are still references pointing to it, not on the value of P.next
. In languages like C and C++, we need to manually free the node's memory.
Q: In linked lists, the time complexity for insertion and deletion operations is O(1)
. But searching for the element before insertion or deletion takes O(n)
time, so why isn't the time complexity O(n)
?
If an element is searched first and then deleted, the time complexity is indeed O(n)
. However, the O(1)
advantage of linked lists in insertion and deletion can be realized in other applications. For example, in the implementation of double-ended queues using linked lists, we maintain pointers always pointing to the head and tail nodes, making each insertion and deletion operation O(1)
.
Q: In the image \"Linked List Definition and Storage Method\", do the light blue storage nodes occupy a single memory address, or do they share half with the node value?
The diagram is just a qualitative representation; quantitative analysis depends on specific situations.
Q: Is adding elements to the end of a list always O(1)
?
If adding an element exceeds the list length, the list needs to be expanded first. The system will request a new memory block and move all elements of the original list over, in which case the time complexity becomes O(n)
.
Q: The statement \"The emergence of lists greatly improves the practicality of arrays, but may lead to some memory space wastage\" - does this refer to the memory occupied by additional variables like capacity, length, and expansion multiplier?
The space wastage here mainly refers to two aspects: on the one hand, lists are set with an initial length, which we may not always need; on the other hand, to prevent frequent expansion, expansion usually multiplies by a coefficient, such as \\(\\times 1.5\\). This results in many empty slots, which we typically cannot fully fill.
Q: In Python, after initializing n = [1, 2, 3]
, the addresses of these 3 elements are contiguous, but initializing m = [2, 1, 3]
shows that each element's id
is not consecutive but identical to those in n
. If the addresses of these elements are not contiguous, is m
still an array?
If we replace list elements with linked list nodes n = [n1, n2, n3, n4, n5]
, these 5 node objects are also typically dispersed throughout memory. However, given a list index, we can still access the node's memory address in O(1)
time, thereby accessing the corresponding node. This is because the array stores references to the nodes, not the nodes themselves.
Unlike many languages, in Python, numbers are also wrapped as objects, and lists store references to these numbers, not the numbers themselves. Therefore, we find that the same number in two arrays has the same id
, and these numbers' memory addresses need not be contiguous.
Q: The std::list
in C++ STL has already implemented a doubly linked list, but it seems that some algorithm books don't directly use it. Is there any limitation?
On the one hand, we often prefer to use arrays to implement algorithms, only using linked lists when necessary, mainly for two reasons.
std::list
usually occupies more space than std::vector
.std::list
has a lower cache utilization rate. Generally, std::vector
performs better.On the other hand, linked lists are primarily necessary for binary trees and graphs. Stacks and queues are often implemented using the programming language's stack
and queue
classes, rather than linked lists.
Q: Does initializing a list res = [0] * self.size()
result in each element of res
referencing the same address?
No. However, this issue arises with two-dimensional arrays, for example, initializing a two-dimensional list res = [[0] * self.size()]
would reference the same list [0]
multiple times.
Q: In deleting a node, is it necessary to break the reference to its successor node?
From the perspective of data structures and algorithms (problem-solving), it's okay not to break the link, as long as the program's logic is correct. From the perspective of standard libraries, breaking the link is safer and more logically clear. If the link is not broken, and the deleted node is not properly recycled, it could affect the recycling of the successor node's memory.
"},{"location":"chapter_computational_complexity/","title":"Chapter 2. \u00a0 Complexity Analysis","text":"Abstract
Complexity analysis is like a space-time navigator in the vast universe of algorithms.
It guides us in exploring deeper within the the dimensions of time and space, seeking more elegant solutions.
"},{"location":"chapter_computational_complexity/#chapter-contents","title":"Chapter Contents","text":"In algorithms, repeatedly performing a task is common and closely related to complexity analysis. Therefore, before introducing time complexity and space complexity, let's first understand how to implement task repetition in programs, focusing on two basic programming control structures: iteration and recursion.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#221-iteration","title":"2.2.1 \u00a0 Iteration","text":"\"Iteration\" is a control structure for repeatedly performing a task. In iteration, a program repeats a block of code as long as a certain condition is met, until this condition is no longer satisfied.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#1-for-loop","title":"1. \u00a0 for Loop","text":"The for
loop is one of the most common forms of iteration, suitable for use when the number of iterations is known in advance.
The following function implements the sum \\(1 + 2 + \\dots + n\\) using a for
loop, with the sum result recorded in the variable res
. Note that in Python, range(a, b)
corresponds to a \"left-closed, right-open\" interval, covering \\(a, a + 1, \\dots, b-1\\):
def for_loop(n: int) -> int:\n \"\"\"for \u5faa\u73af\"\"\"\n res = 0\n # \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for i in range(1, n + 1):\n res += i\n return res\n
iteration.cpp/* for \u5faa\u73af */\nint forLoop(int n) {\n int res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (int i = 1; i <= n; ++i) {\n res += i;\n }\n return res;\n}\n
iteration.java/* for \u5faa\u73af */\nint forLoop(int n) {\n int res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.cs/* for \u5faa\u73af */\nint ForLoop(int n) {\n int res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.go/* for \u5faa\u73af */\nfunc forLoop(n int) int {\n res := 0\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for i := 1; i <= n; i++ {\n res += i\n }\n return res\n}\n
iteration.swift/* for \u5faa\u73af */\nfunc forLoop(n: Int) -> Int {\n var res = 0\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for i in 1 ... n {\n res += i\n }\n return res\n}\n
iteration.js/* for \u5faa\u73af */\nfunction forLoop(n) {\n let res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (let i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.ts/* for \u5faa\u73af */\nfunction forLoop(n: number): number {\n let res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (let i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.dart/* for \u5faa\u73af */\nint forLoop(int n) {\n int res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.rs/* for \u5faa\u73af */\nfn for_loop(n: i32) -> i32 {\n let mut res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for i in 1..=n {\n res += i;\n }\n res\n} \n
iteration.c/* for \u5faa\u73af */\nint forLoop(int n) {\n int res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.zig// for \u5faa\u73af\nfn forLoop(n: usize) i32 {\n var res: i32 = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (1..n+1) |i| {\n res = res + @as(i32, @intCast(i));\n }\n return res;\n} \n
Visualizing Code Full Screen >
The flowchart below represents this sum function.
Figure 2-1 \u00a0 Flowchart of the Sum Function
The number of operations in this sum function is proportional to the input data size \\(n\\), or in other words, it has a \"linear relationship\". This is actually what time complexity describes. This topic will be detailed in the next section.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#2-while-loop","title":"2. \u00a0 while Loop","text":"Similar to the for
loop, the while
loop is another method to implement iteration. In a while
loop, the program checks the condition in each round; if the condition is true, it continues, otherwise, the loop ends.
Below we use a while
loop to implement the sum \\(1 + 2 + \\dots + n\\):
def while_loop(n: int) -> int:\n \"\"\"while \u5faa\u73af\"\"\"\n res = 0\n i = 1 # \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n # \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while i <= n:\n res += i\n i += 1 # \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n return res\n
iteration.cpp/* while \u5faa\u73af */\nint whileLoop(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.java/* while \u5faa\u73af */\nint whileLoop(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.cs/* while \u5faa\u73af */\nint WhileLoop(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i += 1; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.go/* while \u5faa\u73af */\nfunc whileLoop(n int) int {\n res := 0\n // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n i := 1\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for i <= n {\n res += i\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++\n }\n return res\n}\n
iteration.swift/* while \u5faa\u73af */\nfunc whileLoop(n: Int) -> Int {\n var res = 0\n var i = 1 // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while i <= n {\n res += i\n i += 1 // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res\n}\n
iteration.js/* while \u5faa\u73af */\nfunction whileLoop(n) {\n let res = 0;\n let i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.ts/* while \u5faa\u73af */\nfunction whileLoop(n: number): number {\n let res = 0;\n let i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.dart/* while \u5faa\u73af */\nint whileLoop(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.rs/* while \u5faa\u73af */\nfn while_loop(n: i32) -> i32 {\n let mut res = 0;\n let mut i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while i <= n {\n res += i;\n i += 1; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n res\n}\n
iteration.c/* while \u5faa\u73af */\nint whileLoop(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.zig// while \u5faa\u73af\nfn whileLoop(n: i32) i32 {\n var res: i32 = 0;\n var i: i32 = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += @intCast(i);\n i += 1;\n }\n return res;\n}\n
Visualizing Code Full Screen >
The while
loop is more flexible than the for
loop. In a while
loop, we can freely design the initialization and update steps of the condition variable.
For example, in the following code, the condition variable \\(i\\) is updated twice in each round, which would be inconvenient to implement with a for
loop:
def while_loop_ii(n: int) -> int:\n \"\"\"while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09\"\"\"\n res = 0\n i = 1 # \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n # \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while i <= n:\n res += i\n # \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i += 1\n i *= 2\n return res\n
iteration.cpp/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nint whileLoopII(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.java/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nint whileLoopII(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.cs/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nint WhileLoopII(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, 4, 5...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i += 1; \n i *= 2;\n }\n return res;\n}\n
iteration.go/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nfunc whileLoopII(n int) int {\n res := 0\n // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n i := 1\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n for i <= n {\n res += i\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++\n i *= 2\n }\n return res\n}\n
iteration.swift/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nfunc whileLoopII(n: Int) -> Int {\n var res = 0\n var i = 1 // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while i <= n {\n res += i\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i += 1\n i *= 2\n }\n return res\n}\n
iteration.js/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nfunction whileLoopII(n) {\n let res = 0;\n let i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.ts/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nfunction whileLoopII(n: number): number {\n let res = 0;\n let i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.dart/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nint whileLoopII(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.rs/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nfn while_loop_ii(n: i32) -> i32 {\n let mut res = 0;\n let mut i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while i <= n {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i += 1;\n i *= 2;\n }\n res\n}\n
iteration.c/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nint whileLoopII(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.zig// while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09\nfn whileLoopII(n: i32) i32 {\n var res: i32 = 0;\n var i: i32 = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += @intCast(i);\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i += 1;\n i *= 2;\n }\n return res;\n}\n
Visualizing Code Full Screen >
Overall, for
loops are more concise, while while
loops are more flexible. Both can implement iterative structures. Which one to use should be determined based on the specific requirements of the problem.
We can nest one loop structure within another. Below is an example using for
loops:
def nested_for_loop(n: int) -> str:\n \"\"\"\u53cc\u5c42 for \u5faa\u73af\"\"\"\n res = \"\"\n # \u5faa\u73af i = 1, 2, ..., n-1, n\n for i in range(1, n + 1):\n # \u5faa\u73af j = 1, 2, ..., n-1, n\n for j in range(1, n + 1):\n res += f\"({i}, {j}), \"\n return res\n
iteration.cpp/* \u53cc\u5c42 for \u5faa\u73af */\nstring nestedForLoop(int n) {\n ostringstream res;\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (int i = 1; i <= n; ++i) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (int j = 1; j <= n; ++j) {\n res << \"(\" << i << \", \" << j << \"), \";\n }\n }\n return res.str();\n}\n
iteration.java/* \u53cc\u5c42 for \u5faa\u73af */\nString nestedForLoop(int n) {\n StringBuilder res = new StringBuilder();\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (int j = 1; j <= n; j++) {\n res.append(\"(\" + i + \", \" + j + \"), \");\n }\n }\n return res.toString();\n}\n
iteration.cs/* \u53cc\u5c42 for \u5faa\u73af */\nstring NestedForLoop(int n) {\n StringBuilder res = new();\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (int j = 1; j <= n; j++) {\n res.Append($\"({i}, {j}), \");\n }\n }\n return res.ToString();\n}\n
iteration.go/* \u53cc\u5c42 for \u5faa\u73af */\nfunc nestedForLoop(n int) string {\n res := \"\"\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for i := 1; i <= n; i++ {\n for j := 1; j <= n; j++ {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n res += fmt.Sprintf(\"(%d, %d), \", i, j)\n }\n }\n return res\n}\n
iteration.swift/* \u53cc\u5c42 for \u5faa\u73af */\nfunc nestedForLoop(n: Int) -> String {\n var res = \"\"\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for i in 1 ... n {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for j in 1 ... n {\n res.append(\"(\\(i), \\(j)), \")\n }\n }\n return res\n}\n
iteration.js/* \u53cc\u5c42 for \u5faa\u73af */\nfunction nestedForLoop(n) {\n let res = '';\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (let i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (let j = 1; j <= n; j++) {\n res += `(${i}, ${j}), `;\n }\n }\n return res;\n}\n
iteration.ts/* \u53cc\u5c42 for \u5faa\u73af */\nfunction nestedForLoop(n: number): string {\n let res = '';\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (let i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (let j = 1; j <= n; j++) {\n res += `(${i}, ${j}), `;\n }\n }\n return res;\n}\n
iteration.dart/* \u53cc\u5c42 for \u5faa\u73af */\nString nestedForLoop(int n) {\n String res = \"\";\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (int j = 1; j <= n; j++) {\n res += \"($i, $j), \";\n }\n }\n return res;\n}\n
iteration.rs/* \u53cc\u5c42 for \u5faa\u73af */\nfn nested_for_loop(n: i32) -> String {\n let mut res = vec![];\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for i in 1..=n {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for j in 1..=n {\n res.push(format!(\"({}, {}), \", i, j));\n }\n }\n res.join(\"\")\n}\n
iteration.c/* \u53cc\u5c42 for \u5faa\u73af */\nchar *nestedForLoop(int n) {\n // n * n \u4e3a\u5bf9\u5e94\u70b9\u6570\u91cf\uff0c\"(i, j), \" \u5bf9\u5e94\u5b57\u7b26\u4e32\u957f\u6700\u5927\u4e3a 6+10*2\uff0c\u52a0\u4e0a\u6700\u540e\u4e00\u4e2a\u7a7a\u5b57\u7b26 \\0 \u7684\u989d\u5916\u7a7a\u95f4\n int size = n * n * 26 + 1;\n char *res = malloc(size * sizeof(char));\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (int j = 1; j <= n; j++) {\n char tmp[26];\n snprintf(tmp, sizeof(tmp), \"(%d, %d), \", i, j);\n strncat(res, tmp, size - strlen(res) - 1);\n }\n }\n return res;\n}\n
iteration.zig// \u53cc\u5c42 for \u5faa\u73af\nfn nestedForLoop(allocator: Allocator, n: usize) ![]const u8 {\n var res = std.ArrayList(u8).init(allocator);\n defer res.deinit();\n var buffer: [20]u8 = undefined;\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (1..n+1) |i| {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (1..n+1) |j| {\n var _str = try std.fmt.bufPrint(&buffer, \"({d}, {d}), \", .{i, j});\n try res.appendSlice(_str);\n }\n }\n return res.toOwnedSlice();\n}\n
Visualizing Code Full Screen >
The flowchart below represents this nested loop.
Figure 2-2 \u00a0 Flowchart of the Nested Loop
In this case, the number of operations in the function is proportional to \\(n^2\\), or the algorithm's running time and the input data size \\(n\\) have a \"quadratic relationship\".
We can continue adding nested loops, each nesting is a \"dimensional escalation,\" which will increase the time complexity to \"cubic,\" \"quartic,\" and so on.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#222-recursion","title":"2.2.2 \u00a0 Recursion","text":"\"Recursion\" is an algorithmic strategy that solves problems by having a function call itself. It mainly consists of two phases.
From an implementation perspective, recursive code mainly includes three elements.
Observe the following code, where calling the function recur(n)
completes the computation of \\(1 + 2 + \\dots + n\\):
def recur(n: int) -> int:\n \"\"\"\u9012\u5f52\"\"\"\n # \u7ec8\u6b62\u6761\u4ef6\n if n == 1:\n return 1\n # \u9012\uff1a\u9012\u5f52\u8c03\u7528\n res = recur(n - 1)\n # \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res\n
recursion.cpp/* \u9012\u5f52 */\nint recur(int n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1)\n return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n int res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.java/* \u9012\u5f52 */\nint recur(int n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1)\n return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n int res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.cs/* \u9012\u5f52 */\nint Recur(int n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1)\n return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n int res = Recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.go/* \u9012\u5f52 */\nfunc recur(n int) int {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 1 {\n return 1\n }\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n res := recur(n - 1)\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res\n}\n
recursion.swift/* \u9012\u5f52 */\nfunc recur(n: Int) -> Int {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 1 {\n return 1\n }\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n let res = recur(n: n - 1)\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res\n}\n
recursion.js/* \u9012\u5f52 */\nfunction recur(n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n === 1) return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n const res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.ts/* \u9012\u5f52 */\nfunction recur(n: number): number {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n === 1) return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n const res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.dart/* \u9012\u5f52 */\nint recur(int n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1) return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n int res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.rs/* \u9012\u5f52 */\nfn recur(n: i32) -> i32 {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 1 {\n return 1;\n }\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n let res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n n + res\n}\n
recursion.c/* \u9012\u5f52 */\nint recur(int n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1)\n return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n int res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.zig// \u9012\u5f52\u51fd\u6570\nfn recur(n: i32) i32 {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1) {\n return 1;\n }\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n var res: i32 = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
Visualizing Code Full Screen >
The Figure 2-3 shows the recursive process of this function.
Figure 2-3 \u00a0 Recursive Process of the Sum Function
Although iteration and recursion can achieve the same results from a computational standpoint, they represent two entirely different paradigms of thinking and solving problems.
Taking the sum function as an example, let's define the problem as \\(f(n) = 1 + 2 + \\dots + n\\).
Each time a recursive function calls itself, the system allocates memory for the newly initiated function to store local variables, call addresses, and other information. This leads to two main consequences.
As shown in the Figure 2-4 , there are \\(n\\) unreturned recursive functions before triggering the termination condition, indicating a recursion depth of \\(n\\).
Figure 2-4 \u00a0 Recursion Call Depth
In practice, the depth of recursion allowed by programming languages is usually limited, and excessively deep recursion can lead to stack overflow errors.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#2-tail-recursion","title":"2. \u00a0 Tail Recursion","text":"Interestingly, if a function makes its recursive call as the last step before returning, it can be optimized by compilers or interpreters to be as space-efficient as iteration. This scenario is known as \"tail recursion\".
For example, in calculating \\(1 + 2 + \\dots + n\\), we can make the result variable res
a parameter of the function, thereby achieving tail recursion:
def tail_recur(n, res):\n \"\"\"\u5c3e\u9012\u5f52\"\"\"\n # \u7ec8\u6b62\u6761\u4ef6\n if n == 0:\n return res\n # \u5c3e\u9012\u5f52\u8c03\u7528\n return tail_recur(n - 1, res + n)\n
recursion.cpp/* \u5c3e\u9012\u5f52 */\nint tailRecur(int n, int res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0)\n return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.java/* \u5c3e\u9012\u5f52 */\nint tailRecur(int n, int res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0)\n return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.cs/* \u5c3e\u9012\u5f52 */\nint TailRecur(int n, int res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0)\n return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return TailRecur(n - 1, res + n);\n}\n
recursion.go/* \u5c3e\u9012\u5f52 */\nfunc tailRecur(n int, res int) int {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 0 {\n return res\n }\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n-1, res+n)\n}\n
recursion.swift/* \u5c3e\u9012\u5f52 */\nfunc tailRecur(n: Int, res: Int) -> Int {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 0 {\n return res\n }\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n: n - 1, res: res + n)\n}\n
recursion.js/* \u5c3e\u9012\u5f52 */\nfunction tailRecur(n, res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n === 0) return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.ts/* \u5c3e\u9012\u5f52 */\nfunction tailRecur(n: number, res: number): number {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n === 0) return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.dart/* \u5c3e\u9012\u5f52 */\nint tailRecur(int n, int res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0) return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.rs/* \u5c3e\u9012\u5f52 */\nfn tail_recur(n: i32, res: i32) -> i32 {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 0 {\n return res;\n }\n // \u5c3e\u9012\u5f52\u8c03\u7528\n tail_recur(n - 1, res + n)\n}\n
recursion.c/* \u5c3e\u9012\u5f52 */\nint tailRecur(int n, int res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0)\n return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.zig// \u5c3e\u9012\u5f52\u51fd\u6570\nfn tailRecur(n: i32, res: i32) i32 {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0) {\n return res;\n }\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
Visualizing Code Full Screen >
The execution process of tail recursion is shown in the following figure. Comparing regular recursion and tail recursion, the point of the summation operation is different.
Figure 2-5 \u00a0 Tail Recursion Process
Tip
Note that many compilers or interpreters do not support tail recursion optimization. For example, Python does not support tail recursion optimization by default, so even if the function is in the form of tail recursion, it may still encounter stack overflow issues.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#3-recursion-tree","title":"3. \u00a0 Recursion Tree","text":"When dealing with algorithms related to \"divide and conquer\", recursion often offers a more intuitive approach and more readable code than iteration. Take the \"Fibonacci sequence\" as an example.
Question
Given a Fibonacci sequence \\(0, 1, 1, 2, 3, 5, 8, 13, \\dots\\), find the \\(n\\)th number in the sequence.
Let the \\(n\\)th number of the Fibonacci sequence be \\(f(n)\\), it's easy to deduce two conclusions:
Using the recursive relation, and considering the first two numbers as termination conditions, we can write the recursive code. Calling fib(n)
will yield the \\(n\\)th number of the Fibonacci sequence:
def fib(n: int) -> int:\n \"\"\"\u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52\"\"\"\n # \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if n == 1 or n == 2:\n return n - 1\n # \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n res = fib(n - 1) + fib(n - 2)\n # \u8fd4\u56de\u7ed3\u679c f(n)\n return res\n
recursion.cpp/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nint fib(int n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 || n == 2)\n return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n int res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.java/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nint fib(int n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 || n == 2)\n return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n int res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.cs/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nint Fib(int n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 || n == 2)\n return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n int res = Fib(n - 1) + Fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.go/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nfunc fib(n int) int {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if n == 1 || n == 2 {\n return n - 1\n }\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n res := fib(n-1) + fib(n-2)\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res\n}\n
recursion.swift/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nfunc fib(n: Int) -> Int {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if n == 1 || n == 2 {\n return n - 1\n }\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n let res = fib(n: n - 1) + fib(n: n - 2)\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res\n}\n
recursion.js/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nfunction fib(n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n === 1 || n === 2) return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n const res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.ts/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nfunction fib(n: number): number {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n === 1 || n === 2) return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n const res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.dart/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nint fib(int n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 || n == 2) return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n int res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.rs/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nfn fib(n: i32) -> i32 {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if n == 1 || n == 2 {\n return n - 1;\n }\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n let res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c\n res\n}\n
recursion.c/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nint fib(int n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 || n == 2)\n return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n int res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.zig// \u6590\u6ce2\u90a3\u5951\u6570\u5217\nfn fib(n: i32) i32 {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 or n == 2) {\n return n - 1;\n }\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n var res: i32 = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
Visualizing Code Full Screen >
Observing the above code, we see that it recursively calls two functions within itself, meaning that one call generates two branching calls. As illustrated below, this continuous recursive calling eventually creates a \"recursion tree\" with a depth of \\(n\\).
Figure 2-6 \u00a0 Fibonacci Sequence Recursion Tree
Fundamentally, recursion embodies the paradigm of \"breaking down a problem into smaller sub-problems.\" This divide-and-conquer strategy is crucial.
Summarizing the above content, the following table shows the differences between iteration and recursion in terms of implementation, performance, and applicability.
Table: Comparison of Iteration and Recursion Characteristics
Iteration Recursion Approach Loop structure Function calls itself Time Efficiency Generally higher efficiency, no function call overhead Each function call generates overhead Memory Usage Typically uses a fixed size of memory space Accumulative function calls can use a substantial amount of stack frame space Suitable Problems Suitable for simple loop tasks, intuitive and readable code Suitable for problem decomposition, like trees, graphs, divide-and-conquer, backtracking, etc., concise and clear code structureTip
If you find the following content difficult to understand, consider revisiting it after reading the \"Stack\" chapter.
So, what is the intrinsic connection between iteration and recursion? Taking the above recursive function as an example, the summation operation occurs during the recursion's \"return\" phase. This means that the initially called function is actually the last to complete its summation operation, mirroring the \"last in, first out\" principle of a stack.
In fact, recursive terms like \"call stack\" and \"stack frame space\" hint at the close relationship between recursion and stacks.
Therefore, we can use an explicit stack to simulate the behavior of the call stack, thus transforming recursion into an iterative form:
PythonC++JavaC#GoSwiftJSTSDartRustCZig recursion.pydef for_loop_recur(n: int) -> int:\n \"\"\"\u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52\"\"\"\n # \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n stack = []\n res = 0\n # \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for i in range(n, 0, -1):\n # \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.append(i)\n # \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while stack:\n # \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.pop()\n # res = 1+2+3+...+n\n return res\n
recursion.cpp/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nint forLoopRecur(int n) {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n stack<int> stack;\n int res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (int i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (!stack.empty()) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.top();\n stack.pop();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.java/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nint forLoopRecur(int n) {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n Stack<Integer> stack = new Stack<>();\n int res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (int i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (!stack.isEmpty()) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.pop();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.cs/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nint ForLoopRecur(int n) {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n Stack<int> stack = new();\n int res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (int i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.Push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (stack.Count > 0) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.Pop();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.go/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nfunc forLoopRecur(n int) int {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n stack := list.New()\n res := 0\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for i := n; i > 0; i-- {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.PushBack(i)\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n for stack.Len() != 0 {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.Back().Value.(int)\n stack.Remove(stack.Back())\n }\n // res = 1+2+3+...+n\n return res\n}\n
recursion.swift/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nfunc forLoopRecur(n: Int) -> Int {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n var stack: [Int] = []\n var res = 0\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for i in stride(from: n, to: 0, by: -1) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.append(i)\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while !stack.isEmpty {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.removeLast()\n }\n // res = 1+2+3+...+n\n return res\n}\n
recursion.js/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nfunction forLoopRecur(n) {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n const stack = [];\n let res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (let i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (stack.length) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.pop();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.ts/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nfunction forLoopRecur(n: number): number {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808 \n const stack: number[] = [];\n let res: number = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (let i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (stack.length) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.pop();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.dart/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nint forLoopRecur(int n) {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n List<int> stack = [];\n int res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (int i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.add(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (!stack.isEmpty) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.removeLast();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.rs/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nfn for_loop_recur(n: i32) -> i32 {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n let mut stack = Vec::new();\n let mut res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for i in (1..=n).rev() {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while !stack.is_empty() {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.pop().unwrap();\n }\n // res = 1+2+3+...+n\n res\n}\n
recursion.c/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nint forLoopRecur(int n) {\n int stack[1000]; // \u501f\u52a9\u4e00\u4e2a\u5927\u6570\u7ec4\u6765\u6a21\u62df\u6808\n int top = -1; // \u6808\u9876\u7d22\u5f15\n int res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (int i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack[1 + top++] = i;\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (top >= 0) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack[top--];\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.zig// \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52\nfn forLoopRecur(comptime n: i32) i32 {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n var stack: [n]i32 = undefined;\n var res: i32 = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n var i: usize = n;\n while (i > 0) {\n stack[i - 1] = @intCast(i);\n i -= 1;\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n var index: usize = n;\n while (index > 0) {\n index -= 1;\n res += stack[index];\n }\n // res = 1+2+3+...+n\n return res;\n}\n
Visualizing Code Full Screen >
Observing the above code, when recursion is transformed into iteration, the code becomes more complex. Although iteration and recursion can often be transformed into each other, it's not always advisable to do so for two reasons:
In summary, choosing between iteration and recursion depends on the nature of the specific problem. In programming practice, weighing the pros and cons of each and choosing the appropriate method for the situation is essential.
"},{"location":"chapter_computational_complexity/performance_evaluation/","title":"2.1 \u00a0 Algorithm Efficiency Assessment","text":"In algorithm design, we pursue the following two objectives in sequence.
In other words, under the premise of being able to solve the problem, algorithm efficiency has become the main criterion for evaluating the merits of an algorithm, which includes the following two dimensions.
In short, our goal is to design data structures and algorithms that are both fast and memory-efficient. Effectively assessing algorithm efficiency is crucial because only then can we compare various algorithms and guide the process of algorithm design and optimization.
There are mainly two methods of efficiency assessment: actual testing and theoretical estimation.
"},{"location":"chapter_computational_complexity/performance_evaluation/#211-actual-testing","title":"2.1.1 \u00a0 Actual Testing","text":"Suppose we have algorithms A
and B
, both capable of solving the same problem, and we need to compare their efficiencies. The most direct method is to use a computer to run these two algorithms and monitor and record their runtime and memory usage. This assessment method reflects the actual situation but has significant limitations.
On one hand, it's difficult to eliminate interference from the testing environment. Hardware configurations can affect algorithm performance. For example, algorithm A
might run faster than B
on one computer, but the opposite result may occur on another computer with different configurations. This means we would need to test on a variety of machines to calculate average efficiency, which is impractical.
On the other hand, conducting a full test is very resource-intensive. As the volume of input data changes, the efficiency of the algorithms may vary. For example, with smaller data volumes, algorithm A
might run faster than B
, but the opposite might be true with larger data volumes. Therefore, to draw convincing conclusions, we need to test a wide range of input data sizes, which requires significant computational resources.
Due to the significant limitations of actual testing, we can consider evaluating algorithm efficiency solely through calculations. This estimation method is known as \"asymptotic complexity analysis,\" or simply \"complexity analysis.\"
Complexity analysis reflects the relationship between the time and space resources required for algorithm execution and the size of the input data. It describes the trend of growth in the time and space required by the algorithm as the size of the input data increases. This definition might sound complex, but we can break it down into three key points to understand it better.
Complexity analysis overcomes the disadvantages of actual testing methods, reflected in the following aspects:
Tip
If you're still confused about the concept of complexity, don't worry. We will introduce it in detail in subsequent chapters.
Complexity analysis provides us with a \"ruler\" to measure the time and space resources needed to execute an algorithm and compare the efficiency between different algorithms.
Complexity is a mathematical concept and may be abstract and challenging for beginners. From this perspective, complexity analysis might not be the best content to introduce first. However, when discussing the characteristics of a particular data structure or algorithm, it's hard to avoid analyzing its speed and space usage.
In summary, it's recommended that you establish a preliminary understanding of complexity analysis before diving deep into data structures and algorithms, so that you can carry out simple complexity analyses of algorithms.
"},{"location":"chapter_computational_complexity/space_complexity/","title":"2.4 \u00a0 Space Complexity","text":"\"Space complexity\" is used to measure the growth trend of the memory space occupied by an algorithm as the amount of data increases. This concept is very similar to time complexity, except that \"running time\" is replaced with \"occupied memory space\".
"},{"location":"chapter_computational_complexity/space_complexity/#241-space-related-to-algorithms","title":"2.4.1 \u00a0 Space Related to Algorithms","text":"The memory space used by an algorithm during its execution mainly includes the following types.
Generally, the scope of space complexity statistics includes both \"Temporary Space\" and \"Output Space\".
Temporary space can be further divided into three parts.
When analyzing the space complexity of a program, we typically count the Temporary Data, Stack Frame Space, and Output Data, as shown in the Figure 2-15 .
Figure 2-15 \u00a0 Space Types Used in Algorithms
The relevant code is as follows:
PythonC++JavaC#GoSwiftJSTSDartRustCZigclass Node:\n \"\"\"Classes\"\"\"\"\n def __init__(self, x: int):\n self.val: int = x # node value\n self.next: Node | None = None # reference to the next node\n\ndef function() -> int:\n \"\"\"\"Functions\"\"\"\"\"\n # Perform certain operations...\n return 0\n\ndef algorithm(n) -> int: # input data\n A = 0 # temporary data (constant, usually in uppercase)\n b = 0 # temporary data (variable)\n node = Node(0) # temporary data (object)\n c = function() # Stack frame space (call function)\n return A + b + c # output data\n
/* Structures */\nstruct Node {\n int val;\n Node *next;\n Node(int x) : val(x), next(nullptr) {}\n};\n\n/* Functions */\nint func() {\n // Perform certain operations...\n return 0;\n}\n\nint algorithm(int n) { // input data\n const int a = 0; // temporary data (constant)\n int b = 0; // temporary data (variable)\n Node* node = new Node(0); // temporary data (object)\n int c = func(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
/* Classes */\nclass Node {\n int val;\n Node next;\n Node(int x) { val = x; }\n}\n\n/* Functions */\nint function() {\n // Perform certain operations...\n return 0;\n}\n\nint algorithm(int n) { // input data\n final int a = 0; // temporary data (constant)\n int b = 0; // temporary data (variable)\n Node node = new Node(0); // temporary data (object)\n int c = function(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
/* Classes */\nclass Node {\n int val;\n Node next;\n Node(int x) { val = x; }\n}\n\n/* Functions */\nint Function() {\n // Perform certain operations...\n return 0;\n}\n\nint Algorithm(int n) { // input data\n const int a = 0; // temporary data (constant)\n int b = 0; // temporary data (variable)\n Node node = new(0); // temporary data (object)\n int c = Function(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
/* Structures */\ntype node struct {\n val int\n next *node\n}\n\n/* Create node structure */\nfunc newNode(val int) *node {\n return &node{val: val}\n}\n\n/* Functions */\nfunc function() int {\n // Perform certain operations...\n return 0\n}\n\nfunc algorithm(n int) int { // input data\n const a = 0 // temporary data (constant)\n b := 0 // temporary storage of data (variable)\n newNode(0) // temporary data (object)\n c := function() // stack frame space (call function)\n return a + b + c // output data\n}\n
/* Classes */\nclass Node {\n var val: Int\n var next: Node?\n\n init(x: Int) {\n val = x\n }\n}\n\n/* Functions */\nfunc function() -> Int {\n // Perform certain operations...\n return 0\n}\n\nfunc algorithm(n: Int) -> Int { // input data\n let a = 0 // temporary data (constant)\n var b = 0 // temporary data (variable)\n let node = Node(x: 0) // temporary data (object)\n let c = function() // stack frame space (call function)\n return a + b + c // output data\n}\n
/* Classes */\nclass Node {\n val;\n next;\n constructor(val) {\n this.val = val === undefined ? 0 : val; // node value\n this.next = null; // reference to the next node\n }\n}\n\n/* Functions */\nfunction constFunc() {\n // Perform certain operations\n return 0;\n}\n\nfunction algorithm(n) { // input data\n const a = 0; // temporary data (constant)\n let b = 0; // temporary data (variable)\n const node = new Node(0); // temporary data (object)\n const c = constFunc(); // Stack frame space (calling function)\n return a + b + c; // output data\n}\n
/* Classes */\nclass Node {\n val: number;\n next: Node | null;\n constructor(val?: number) {\n this.val = val === undefined ? 0 : val; // node value\n this.next = null; // reference to the next node\n }\n}\n\n/* Functions */\nfunction constFunc(): number {\n // Perform certain operations\n return 0;\n}\n\nfunction algorithm(n: number): number { // input data\n const a = 0; // temporary data (constant)\n let b = 0; // temporary data (variable)\n const node = new Node(0); // temporary data (object)\n const c = constFunc(); // Stack frame space (calling function)\n return a + b + c; // output data\n}\n
/* Classes */\nclass Node {\n int val;\n Node next;\n Node(this.val, [this.next]);\n}\n\n/* Functions */\nint function() {\n // Perform certain operations...\n return 0;\n}\n\nint algorithm(int n) { // input data\n const int a = 0; // temporary data (constant)\n int b = 0; // temporary data (variable)\n Node node = Node(0); // temporary data (object)\n int c = function(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
use std::rc::Rc;\nuse std::cell::RefCell;\n\n/* Structures */\nstruct Node {\n val: i32,\n next: Option<Rc<RefCell<Node>>>,\n}\n\n/* Creating a Node structure */\nimpl Node {\n fn new(val: i32) -> Self {\n Self { val: val, next: None }\n }\n}\n\n/* Functions */\nfn function() -> i32 { \n // Perform certain operations...\n return 0;\n}\n\nfn algorithm(n: i32) -> i32 { // input data\n const a: i32 = 0; // temporary data (constant)\n let mut b = 0; // temporary data (variable)\n let node = Node::new(0); // temporary data (object)\n let c = function(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
/* Functions */\nint func() {\n // Perform certain operations...\n return 0;\n}\n\nint algorithm(int n) { // input data\n const int a = 0; // temporary data (constant)\n int b = 0; // temporary data (variable)\n int c = func(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
\n
"},{"location":"chapter_computational_complexity/space_complexity/#242-calculation-method","title":"2.4.2 \u00a0 Calculation Method","text":"The method for calculating space complexity is roughly similar to that of time complexity, with the only change being the shift of the statistical object from \"number of operations\" to \"size of used space\".
However, unlike time complexity, we usually only focus on the worst-case space complexity. This is because memory space is a hard requirement, and we must ensure that there is enough memory space reserved under all input data.
Consider the following code, the term \"worst-case\" in worst-case space complexity has two meanings.
nums
occupies \\(O(n)\\) space, thus the worst-case space complexity is \\(O(n)\\).nums
, the program occupies \\(O(n)\\) space, hence the worst-case space complexity is \\(O(n)\\).def algorithm(n: int):\n a = 0 # O(1)\n b = [0] * 10000 # O(1)\n if n > 10:\n nums = [0] * n # O(n)\n
void algorithm(int n) {\n int a = 0; // O(1)\n vector<int> b(10000); // O(1)\n if (n > 10)\n vector<int> nums(n); // O(n)\n}\n
void algorithm(int n) {\n int a = 0; // O(1)\n int[] b = new int[10000]; // O(1)\n if (n > 10)\n int[] nums = new int[n]; // O(n)\n}\n
void Algorithm(int n) {\n int a = 0; // O(1)\n int[] b = new int[10000]; // O(1)\n if (n > 10) {\n int[] nums = new int[n]; // O(n)\n }\n}\n
func algorithm(n int) {\n a := 0 // O(1)\n b := make([]int, 10000) // O(1)\n var nums []int\n if n > 10 {\n nums := make([]int, n) // O(n)\n }\n fmt.Println(a, b, nums)\n}\n
func algorithm(n: Int) {\n let a = 0 // O(1)\n let b = Array(repeating: 0, count: 10000) // O(1)\n if n > 10 {\n let nums = Array(repeating: 0, count: n) // O(n)\n }\n}\n
function algorithm(n) {\n const a = 0; // O(1)\n const b = new Array(10000); // O(1)\n if (n > 10) {\n const nums = new Array(n); // O(n)\n }\n}\n
function algorithm(n: number): void {\n const a = 0; // O(1)\n const b = new Array(10000); // O(1)\n if (n > 10) {\n const nums = new Array(n); // O(n)\n }\n}\n
void algorithm(int n) {\n int a = 0; // O(1)\n List<int> b = List.filled(10000, 0); // O(1)\n if (n > 10) {\n List<int> nums = List.filled(n, 0); // O(n)\n }\n}\n
fn algorithm(n: i32) {\n let a = 0; // O(1)\n let b = [0; 10000]; // O(1)\n if n > 10 {\n let nums = vec![0; n as usize]; // O(n)\n }\n}\n
void algorithm(int n) {\n int a = 0; // O(1)\n int b[10000]; // O(1)\n if (n > 10)\n int nums[n] = {0}; // O(n)\n}\n
\n
In recursive functions, stack frame space must be taken into count. Consider the following code:
PythonC++JavaC#GoSwiftJSTSDartRustCZigdef function() -> int:\n # Perform certain operations\n return 0\n\ndef loop(n: int):\n \"\"\"Loop O(1)\"\"\"\"\"\n for _ in range(n):\n function()\n\ndef recur(n: int) -> int:\n \"\"\"Recursion O(n)\"\"\"\"\"\n if n == 1: return\n return recur(n - 1)\n
int func() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nvoid loop(int n) {\n for (int i = 0; i < n; i++) {\n func();\n }\n}\n/* Recursion O(n) */\nvoid recur(int n) {\n if (n == 1) return;\n return recur(n - 1);\n}\n
int function() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nvoid loop(int n) {\n for (int i = 0; i < n; i++) {\n function();\n }\n}\n/* Recursion O(n) */\nvoid recur(int n) {\n if (n == 1) return;\n return recur(n - 1);\n}\n
int Function() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nvoid Loop(int n) {\n for (int i = 0; i < n; i++) {\n Function();\n }\n}\n/* Recursion O(n) */\nint Recur(int n) {\n if (n == 1) return 1;\n return Recur(n - 1);\n}\n
func function() int {\n // Perform certain operations\n return 0\n}\n\n/* Cycle O(1) */\nfunc loop(n int) {\n for i := 0; i < n; i++ {\n function()\n }\n}\n\n/* Recursion O(n) */\nfunc recur(n int) {\n if n == 1 {\n return\n }\n recur(n - 1)\n}\n
@discardableResult\nfunc function() -> Int {\n // Perform certain operations\n return 0\n}\n\n/* Cycle O(1) */\nfunc loop(n: Int) {\n for _ in 0 ..< n {\n function()\n }\n}\n\n/* Recursion O(n) */\nfunc recur(n: Int) {\n if n == 1 {\n return\n }\n recur(n: n - 1)\n}\n
function constFunc() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nfunction loop(n) {\n for (let i = 0; i < n; i++) {\n constFunc();\n }\n}\n/* Recursion O(n) */\nfunction recur(n) {\n if (n === 1) return;\n return recur(n - 1);\n}\n
function constFunc(): number {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nfunction loop(n: number): void {\n for (let i = 0; i < n; i++) {\n constFunc();\n }\n}\n/* Recursion O(n) */\nfunction recur(n: number): void {\n if (n === 1) return;\n return recur(n - 1);\n}\n
int function() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nvoid loop(int n) {\n for (int i = 0; i < n; i++) {\n function();\n }\n}\n/* Recursion O(n) */\nvoid recur(int n) {\n if (n == 1) return;\n return recur(n - 1);\n}\n
fn function() -> i32 {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nfn loop(n: i32) {\n for i in 0..n {\n function();\n }\n}\n/* Recursion O(n) */\nvoid recur(n: i32) {\n if n == 1 {\n return;\n }\n recur(n - 1);\n}\n
int func() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nvoid loop(int n) {\n for (int i = 0; i < n; i++) {\n func();\n }\n}\n/* Recursion O(n) */\nvoid recur(int n) {\n if (n == 1) return;\n return recur(n - 1);\n}\n
\n
The time complexity of both loop()
and recur()
functions is \\(O(n)\\), but their space complexities differ.
loop()
function calls function()
\\(n\\) times in a loop, where each iteration's function()
returns and releases its stack frame space, so the space complexity remains \\(O(1)\\).recur()
will have \\(n\\) instances of unreturned recur()
existing simultaneously during its execution, thus occupying \\(O(n)\\) stack frame space.Let the size of the input data be \\(n\\), the following chart displays common types of space complexities (arranged from low to high).
\\[ \\begin{aligned} O(1) < O(\\log n) < O(n) < O(n^2) < O(2^n) \\newline \\text{Constant Order} < \\text{Logarithmic Order} < \\text{Linear Order} < \\text{Quadratic Order} < \\text{Exponential Order} \\end{aligned} \\]Figure 2-16 \u00a0 Common Types of Space Complexity
"},{"location":"chapter_computational_complexity/space_complexity/#1-constant-order-o1","title":"1. \u00a0 Constant Order \\(O(1)\\)","text":"Constant order is common in constants, variables, objects that are independent of the size of input data \\(n\\).
Note that memory occupied by initializing variables or calling functions in a loop, which is released upon entering the next cycle, does not accumulate over space, thus the space complexity remains \\(O(1)\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig space_complexity.pydef function() -> int:\n \"\"\"\u51fd\u6570\"\"\"\n # \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0\n\ndef constant(n: int):\n \"\"\"\u5e38\u6570\u9636\"\"\"\n # \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n a = 0\n nums = [0] * 10000\n node = ListNode(0)\n # \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for _ in range(n):\n c = 0\n # \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for _ in range(n):\n function()\n
space_complexity.cpp/* \u51fd\u6570 */\nint func() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nvoid constant(int n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const int a = 0;\n int b = 0;\n vector<int> nums(10000);\n ListNode node(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n int c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n func();\n }\n}\n
space_complexity.java/* \u51fd\u6570 */\nint function() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nvoid constant(int n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n final int a = 0;\n int b = 0;\n int[] nums = new int[10000];\n ListNode node = new ListNode(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n int c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n function();\n }\n}\n
space_complexity.cs/* \u51fd\u6570 */\nint Function() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nvoid Constant(int n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n int a = 0;\n int b = 0;\n int[] nums = new int[10000];\n ListNode node = new(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n int c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n Function();\n }\n}\n
space_complexity.go/* \u51fd\u6570 */\nfunc function() int {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c...\n return 0\n}\n\n/* \u5e38\u6570\u9636 */\nfunc spaceConstant(n int) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const a = 0\n b := 0\n nums := make([]int, 10000)\n node := newNode(0)\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n var c int\n for i := 0; i < n; i++ {\n c = 0\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for i := 0; i < n; i++ {\n function()\n }\n b += 0\n c += 0\n nums[0] = 0\n node.val = 0\n}\n
space_complexity.swift/* \u51fd\u6570 */\n@discardableResult\nfunc function() -> Int {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0\n}\n\n/* \u5e38\u6570\u9636 */\nfunc constant(n: Int) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n let a = 0\n var b = 0\n let nums = Array(repeating: 0, count: 10000)\n let node = ListNode(x: 0)\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for _ in 0 ..< n {\n let c = 0\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for _ in 0 ..< n {\n function()\n }\n}\n
space_complexity.js/* \u51fd\u6570 */\nfunction constFunc() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nfunction constant(n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const a = 0;\n const b = 0;\n const nums = new Array(10000);\n const node = new ListNode(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (let i = 0; i < n; i++) {\n const c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (let i = 0; i < n; i++) {\n constFunc();\n }\n}\n
space_complexity.ts/* \u51fd\u6570 */\nfunction constFunc(): number {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nfunction constant(n: number): void {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const a = 0;\n const b = 0;\n const nums = new Array(10000);\n const node = new ListNode(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (let i = 0; i < n; i++) {\n const c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (let i = 0; i < n; i++) {\n constFunc();\n }\n}\n
space_complexity.dart/* \u51fd\u6570 */\nint function() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nvoid constant(int n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n final int a = 0;\n int b = 0;\n List<int> nums = List.filled(10000, 0);\n ListNode node = ListNode(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (var i = 0; i < n; i++) {\n int c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (var i = 0; i < n; i++) {\n function();\n }\n}\n
space_complexity.rs/* \u51fd\u6570 */\nfn function() ->i32 {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\n#[allow(unused)]\nfn constant(n: i32) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const A: i32 = 0;\n let b = 0;\n let nums = vec![0; 10000];\n let node = ListNode::new(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for i in 0..n {\n let c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for i in 0..n {\n function();\n }\n}\n
space_complexity.c/* \u51fd\u6570 */\nint func() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nvoid constant(int n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const int a = 0;\n int b = 0;\n int nums[1000];\n ListNode *node = newListNode(0);\n free(node);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n int c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n func();\n }\n}\n
space_complexity.zig// \u51fd\u6570\nfn function() i32 {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n// \u5e38\u6570\u9636\nfn constant(n: i32) void {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const a: i32 = 0;\n var b: i32 = 0;\n var nums = [_]i32{0}**10000;\n var node = inc.ListNode(i32){.val = 0};\n var i: i32 = 0;\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n while (i < n) : (i += 1) {\n var c: i32 = 0;\n _ = c;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n i = 0;\n while (i < n) : (i += 1) {\n _ = function();\n }\n _ = a;\n _ = b;\n _ = nums;\n _ = node;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_computational_complexity/space_complexity/#2-linear-order-on","title":"2. \u00a0 Linear Order \\(O(n)\\)","text":"Linear order is common in arrays, linked lists, stacks, queues, etc., where the number of elements is proportional to \\(n\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig space_complexity.pydef linear(n: int):\n \"\"\"\u7ebf\u6027\u9636\"\"\"\n # \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n nums = [0] * n\n # \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n hmap = dict[int, str]()\n for i in range(n):\n hmap[i] = str(i)\n
space_complexity.cpp/* \u7ebf\u6027\u9636 */\nvoid linear(int n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n vector<int> nums(n);\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n vector<ListNode> nodes;\n for (int i = 0; i < n; i++) {\n nodes.push_back(ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n unordered_map<int, string> map;\n for (int i = 0; i < n; i++) {\n map[i] = to_string(i);\n }\n}\n
space_complexity.java/* \u7ebf\u6027\u9636 */\nvoid linear(int n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n int[] nums = new int[n];\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n List<ListNode> nodes = new ArrayList<>();\n for (int i = 0; i < n; i++) {\n nodes.add(new ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n Map<Integer, String> map = new HashMap<>();\n for (int i = 0; i < n; i++) {\n map.put(i, String.valueOf(i));\n }\n}\n
space_complexity.cs/* \u7ebf\u6027\u9636 */\nvoid Linear(int n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n int[] nums = new int[n];\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n List<ListNode> nodes = [];\n for (int i = 0; i < n; i++) {\n nodes.Add(new ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n Dictionary<int, string> map = [];\n for (int i = 0; i < n; i++) {\n map.Add(i, i.ToString());\n }\n}\n
space_complexity.go/* \u7ebf\u6027\u9636 */\nfunc spaceLinear(n int) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n _ = make([]int, n)\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n var nodes []*node\n for i := 0; i < n; i++ {\n nodes = append(nodes, newNode(i))\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n m := make(map[int]string, n)\n for i := 0; i < n; i++ {\n m[i] = strconv.Itoa(i)\n }\n}\n
space_complexity.swift/* \u7ebf\u6027\u9636 */\nfunc linear(n: Int) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n let nums = Array(repeating: 0, count: n)\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n let nodes = (0 ..< n).map { ListNode(x: $0) }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n let map = Dictionary(uniqueKeysWithValues: (0 ..< n).map { ($0, \"\\($0)\") })\n}\n
space_complexity.js/* \u7ebf\u6027\u9636 */\nfunction linear(n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n const nums = new Array(n);\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n const nodes = [];\n for (let i = 0; i < n; i++) {\n nodes.push(new ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n const map = new Map();\n for (let i = 0; i < n; i++) {\n map.set(i, i.toString());\n }\n}\n
space_complexity.ts/* \u7ebf\u6027\u9636 */\nfunction linear(n: number): void {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n const nums = new Array(n);\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n const nodes: ListNode[] = [];\n for (let i = 0; i < n; i++) {\n nodes.push(new ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n const map = new Map();\n for (let i = 0; i < n; i++) {\n map.set(i, i.toString());\n }\n}\n
space_complexity.dart/* \u7ebf\u6027\u9636 */\nvoid linear(int n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n List<int> nums = List.filled(n, 0);\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n List<ListNode> nodes = [];\n for (var i = 0; i < n; i++) {\n nodes.add(ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n Map<int, String> map = HashMap();\n for (var i = 0; i < n; i++) {\n map.putIfAbsent(i, () => i.toString());\n }\n}\n
space_complexity.rs/* \u7ebf\u6027\u9636 */\n#[allow(unused)]\nfn linear(n: i32) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n let mut nums = vec![0; n as usize];\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n let mut nodes = Vec::new();\n for i in 0..n {\n nodes.push(ListNode::new(i))\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n let mut map = HashMap::new();\n for i in 0..n {\n map.insert(i, i.to_string());\n }\n}\n
space_complexity.c/* \u54c8\u5e0c\u8868 */\ntypedef struct {\n int key;\n int val;\n UT_hash_handle hh; // \u57fa\u4e8e uthash.h \u5b9e\u73b0\n} HashTable;\n\n/* \u7ebf\u6027\u9636 */\nvoid linear(int n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n int *nums = malloc(sizeof(int) * n);\n free(nums);\n\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n ListNode **nodes = malloc(sizeof(ListNode *) * n);\n for (int i = 0; i < n; i++) {\n nodes[i] = newListNode(i);\n }\n // \u5185\u5b58\u91ca\u653e\n for (int i = 0; i < n; i++) {\n free(nodes[i]);\n }\n free(nodes);\n\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n HashTable *h = NULL;\n for (int i = 0; i < n; i++) {\n HashTable *tmp = malloc(sizeof(HashTable));\n tmp->key = i;\n tmp->val = i;\n HASH_ADD_INT(h, key, tmp);\n }\n\n // \u5185\u5b58\u91ca\u653e\n HashTable *curr, *tmp;\n HASH_ITER(hh, h, curr, tmp) {\n HASH_DEL(h, curr);\n free(curr);\n }\n}\n
space_complexity.zig// \u7ebf\u6027\u9636\nfn linear(comptime n: i32) !void {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n var nums = [_]i32{0}**n;\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n var nodes = std.ArrayList(i32).init(std.heap.page_allocator);\n defer nodes.deinit();\n var i: i32 = 0;\n while (i < n) : (i += 1) {\n try nodes.append(i);\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n var map = std.AutoArrayHashMap(i32, []const u8).init(std.heap.page_allocator);\n defer map.deinit();\n var j: i32 = 0;\n while (j < n) : (j += 1) {\n const string = try std.fmt.allocPrint(std.heap.page_allocator, \"{d}\", .{j});\n defer std.heap.page_allocator.free(string);\n try map.put(i, string);\n }\n _ = nums;\n}\n
Visualizing Code Full Screen >
As shown below, this function's recursive depth is \\(n\\), meaning there are \\(n\\) instances of unreturned linear_recur()
function, using \\(O(n)\\) size of stack frame space:
def linear_recur(n: int):\n \"\"\"\u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\"\"\"\n print(\"\u9012\u5f52 n =\", n)\n if n == 1:\n return\n linear_recur(n - 1)\n
space_complexity.cpp/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nvoid linearRecur(int n) {\n cout << \"\u9012\u5f52 n = \" << n << endl;\n if (n == 1)\n return;\n linearRecur(n - 1);\n}\n
space_complexity.java/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nvoid linearRecur(int n) {\n System.out.println(\"\u9012\u5f52 n = \" + n);\n if (n == 1)\n return;\n linearRecur(n - 1);\n}\n
space_complexity.cs/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nvoid LinearRecur(int n) {\n Console.WriteLine(\"\u9012\u5f52 n = \" + n);\n if (n == 1) return;\n LinearRecur(n - 1);\n}\n
space_complexity.go/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc spaceLinearRecur(n int) {\n fmt.Println(\"\u9012\u5f52 n =\", n)\n if n == 1 {\n return\n }\n spaceLinearRecur(n - 1)\n}\n
space_complexity.swift/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc linearRecur(n: Int) {\n print(\"\u9012\u5f52 n = \\(n)\")\n if n == 1 {\n return\n }\n linearRecur(n: n - 1)\n}\n
space_complexity.js/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction linearRecur(n) {\n console.log(`\u9012\u5f52 n = ${n}`);\n if (n === 1) return;\n linearRecur(n - 1);\n}\n
space_complexity.ts/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction linearRecur(n: number): void {\n console.log(`\u9012\u5f52 n = ${n}`);\n if (n === 1) return;\n linearRecur(n - 1);\n}\n
space_complexity.dart/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nvoid linearRecur(int n) {\n print('\u9012\u5f52 n = $n');\n if (n == 1) return;\n linearRecur(n - 1);\n}\n
space_complexity.rs/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfn linear_recur(n: i32) {\n println!(\"\u9012\u5f52 n = {}\", n);\n if n == 1 {return};\n linear_recur(n - 1);\n}\n
space_complexity.c/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nvoid linearRecur(int n) {\n printf(\"\u9012\u5f52 n = %d\\r\\n\", n);\n if (n == 1)\n return;\n linearRecur(n - 1);\n}\n
space_complexity.zig// \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\nfn linearRecur(comptime n: i32) void {\n std.debug.print(\"\u9012\u5f52 n = {}\\n\", .{n});\n if (n == 1) return;\n linearRecur(n - 1);\n}\n
Visualizing Code Full Screen >
Figure 2-17 \u00a0 Recursive Function Generating Linear Order Space Complexity
"},{"location":"chapter_computational_complexity/space_complexity/#3-quadratic-order-on2","title":"3. \u00a0 Quadratic Order \\(O(n^2)\\)","text":"Quadratic order is common in matrices and graphs, where the number of elements is quadratic to \\(n\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig space_complexity.pydef quadratic(n: int):\n \"\"\"\u5e73\u65b9\u9636\"\"\"\n # \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n num_matrix = [[0] * n for _ in range(n)]\n
space_complexity.cpp/* \u5e73\u65b9\u9636 */\nvoid quadratic(int n) {\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n vector<vector<int>> numMatrix;\n for (int i = 0; i < n; i++) {\n vector<int> tmp;\n for (int j = 0; j < n; j++) {\n tmp.push_back(0);\n }\n numMatrix.push_back(tmp);\n }\n}\n
space_complexity.java/* \u5e73\u65b9\u9636 */\nvoid quadratic(int n) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n int[][] numMatrix = new int[n][n];\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n List<List<Integer>> numList = new ArrayList<>();\n for (int i = 0; i < n; i++) {\n List<Integer> tmp = new ArrayList<>();\n for (int j = 0; j < n; j++) {\n tmp.add(0);\n }\n numList.add(tmp);\n }\n}\n
space_complexity.cs/* \u5e73\u65b9\u9636 */\nvoid Quadratic(int n) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n int[,] numMatrix = new int[n, n];\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n List<List<int>> numList = [];\n for (int i = 0; i < n; i++) {\n List<int> tmp = [];\n for (int j = 0; j < n; j++) {\n tmp.Add(0);\n }\n numList.Add(tmp);\n }\n}\n
space_complexity.go/* \u5e73\u65b9\u9636 */\nfunc spaceQuadratic(n int) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n numMatrix := make([][]int, n)\n for i := 0; i < n; i++ {\n numMatrix[i] = make([]int, n)\n }\n}\n
space_complexity.swift/* \u5e73\u65b9\u9636 */\nfunc quadratic(n: Int) {\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n let numList = Array(repeating: Array(repeating: 0, count: n), count: n)\n}\n
space_complexity.js/* \u5e73\u65b9\u9636 */\nfunction quadratic(n) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n const numMatrix = Array(n)\n .fill(null)\n .map(() => Array(n).fill(null));\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n const numList = [];\n for (let i = 0; i < n; i++) {\n const tmp = [];\n for (let j = 0; j < n; j++) {\n tmp.push(0);\n }\n numList.push(tmp);\n }\n}\n
space_complexity.ts/* \u5e73\u65b9\u9636 */\nfunction quadratic(n: number): void {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n const numMatrix = Array(n)\n .fill(null)\n .map(() => Array(n).fill(null));\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n const numList = [];\n for (let i = 0; i < n; i++) {\n const tmp = [];\n for (let j = 0; j < n; j++) {\n tmp.push(0);\n }\n numList.push(tmp);\n }\n}\n
space_complexity.dart/* \u5e73\u65b9\u9636 */\nvoid quadratic(int n) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n List<List<int>> numMatrix = List.generate(n, (_) => List.filled(n, 0));\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n List<List<int>> numList = [];\n for (var i = 0; i < n; i++) {\n List<int> tmp = [];\n for (int j = 0; j < n; j++) {\n tmp.add(0);\n }\n numList.add(tmp);\n }\n}\n
space_complexity.rs/* \u5e73\u65b9\u9636 */\n#[allow(unused)]\nfn quadratic(n: i32) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n let num_matrix = vec![vec![0; n as usize]; n as usize];\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n let mut num_list = Vec::new();\n for i in 0..n {\n let mut tmp = Vec::new();\n for j in 0..n {\n tmp.push(0);\n }\n num_list.push(tmp);\n }\n}\n
space_complexity.c/* \u5e73\u65b9\u9636 */\nvoid quadratic(int n) {\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n int **numMatrix = malloc(sizeof(int *) * n);\n for (int i = 0; i < n; i++) {\n int *tmp = malloc(sizeof(int) * n);\n for (int j = 0; j < n; j++) {\n tmp[j] = 0;\n }\n numMatrix[i] = tmp;\n }\n\n // \u5185\u5b58\u91ca\u653e\n for (int i = 0; i < n; i++) {\n free(numMatrix[i]);\n }\n free(numMatrix);\n}\n
space_complexity.zig// \u5e73\u65b9\u9636\nfn quadratic(n: i32) !void {\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n var nodes = std.ArrayList(std.ArrayList(i32)).init(std.heap.page_allocator);\n defer nodes.deinit();\n var i: i32 = 0;\n while (i < n) : (i += 1) {\n var tmp = std.ArrayList(i32).init(std.heap.page_allocator);\n defer tmp.deinit();\n var j: i32 = 0;\n while (j < n) : (j += 1) {\n try tmp.append(0);\n }\n try nodes.append(tmp);\n }\n}\n
Visualizing Code Full Screen >
As shown below, the recursive depth of this function is \\(n\\), and in each recursive call, an array is initialized with lengths \\(n\\), \\(n-1\\), \\(\\dots\\), \\(2\\), \\(1\\), averaging \\(n/2\\), thus overall occupying \\(O(n^2)\\) space:
PythonC++JavaC#GoSwiftJSTSDartRustCZig space_complexity.pydef quadratic_recur(n: int) -> int:\n \"\"\"\u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\"\"\"\n if n <= 0:\n return 0\n # \u6570\u7ec4 nums \u957f\u5ea6\u4e3a n, n-1, ..., 2, 1\n nums = [0] * n\n return quadratic_recur(n - 1)\n
space_complexity.cpp/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint quadraticRecur(int n) {\n if (n <= 0)\n return 0;\n vector<int> nums(n);\n cout << \"\u9012\u5f52 n = \" << n << \" \u4e2d\u7684 nums \u957f\u5ea6 = \" << nums.size() << endl;\n return quadraticRecur(n - 1);\n}\n
space_complexity.java/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint quadraticRecur(int n) {\n if (n <= 0)\n return 0;\n // \u6570\u7ec4 nums \u957f\u5ea6\u4e3a n, n-1, ..., 2, 1\n int[] nums = new int[n];\n System.out.println(\"\u9012\u5f52 n = \" + n + \" \u4e2d\u7684 nums \u957f\u5ea6 = \" + nums.length);\n return quadraticRecur(n - 1);\n}\n
space_complexity.cs/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint QuadraticRecur(int n) {\n if (n <= 0) return 0;\n int[] nums = new int[n];\n Console.WriteLine(\"\u9012\u5f52 n = \" + n + \" \u4e2d\u7684 nums \u957f\u5ea6 = \" + nums.Length);\n return QuadraticRecur(n - 1);\n}\n
space_complexity.go/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc spaceQuadraticRecur(n int) int {\n if n <= 0 {\n return 0\n }\n nums := make([]int, n)\n fmt.Printf(\"\u9012\u5f52 n = %d \u4e2d\u7684 nums \u957f\u5ea6 = %d \\n\", n, len(nums))\n return spaceQuadraticRecur(n - 1)\n}\n
space_complexity.swift/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\n@discardableResult\nfunc quadraticRecur(n: Int) -> Int {\n if n <= 0 {\n return 0\n }\n // \u6570\u7ec4 nums \u957f\u5ea6\u4e3a n, n-1, ..., 2, 1\n let nums = Array(repeating: 0, count: n)\n print(\"\u9012\u5f52 n = \\(n) \u4e2d\u7684 nums \u957f\u5ea6 = \\(nums.count)\")\n return quadraticRecur(n: n - 1)\n}\n
space_complexity.js/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction quadraticRecur(n) {\n if (n <= 0) return 0;\n const nums = new Array(n);\n console.log(`\u9012\u5f52 n = ${n} \u4e2d\u7684 nums \u957f\u5ea6 = ${nums.length}`);\n return quadraticRecur(n - 1);\n}\n
space_complexity.ts/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction quadraticRecur(n: number): number {\n if (n <= 0) return 0;\n const nums = new Array(n);\n console.log(`\u9012\u5f52 n = ${n} \u4e2d\u7684 nums \u957f\u5ea6 = ${nums.length}`);\n return quadraticRecur(n - 1);\n}\n
space_complexity.dart/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint quadraticRecur(int n) {\n if (n <= 0) return 0;\n List<int> nums = List.filled(n, 0);\n print('\u9012\u5f52 n = $n \u4e2d\u7684 nums \u957f\u5ea6 = ${nums.length}');\n return quadraticRecur(n - 1);\n}\n
space_complexity.rs/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfn quadratic_recur(n: i32) -> i32 {\n if n <= 0 {return 0};\n // \u6570\u7ec4 nums \u957f\u5ea6\u4e3a n, n-1, ..., 2, 1\n let nums = vec![0; n as usize];\n println!(\"\u9012\u5f52 n = {} \u4e2d\u7684 nums \u957f\u5ea6 = {}\", n, nums.len());\n return quadratic_recur(n - 1);\n}\n
space_complexity.c/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint quadraticRecur(int n) {\n if (n <= 0)\n return 0;\n int *nums = malloc(sizeof(int) * n);\n printf(\"\u9012\u5f52 n = %d \u4e2d\u7684 nums \u957f\u5ea6 = %d\\r\\n\", n, n);\n int res = quadraticRecur(n - 1);\n free(nums);\n return res;\n}\n
space_complexity.zig// \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\nfn quadraticRecur(comptime n: i32) i32 {\n if (n <= 0) return 0;\n var nums = [_]i32{0}**n;\n std.debug.print(\"\u9012\u5f52 n = {} \u4e2d\u7684 nums \u957f\u5ea6 = {}\\n\", .{n, nums.len});\n return quadraticRecur(n - 1);\n}\n
Visualizing Code Full Screen >
Figure 2-18 \u00a0 Recursive Function Generating Quadratic Order Space Complexity
"},{"location":"chapter_computational_complexity/space_complexity/#4-exponential-order-o2n","title":"4. \u00a0 Exponential Order \\(O(2^n)\\)","text":"Exponential order is common in binary trees. Observe the below image, a \"full binary tree\" with \\(n\\) levels has \\(2^n - 1\\) nodes, occupying \\(O(2^n)\\) space:
PythonC++JavaC#GoSwiftJSTSDartRustCZig space_complexity.pydef build_tree(n: int) -> TreeNode | None:\n \"\"\"\u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09\"\"\"\n if n == 0:\n return None\n root = TreeNode(0)\n root.left = build_tree(n - 1)\n root.right = build_tree(n - 1)\n return root\n
space_complexity.cpp/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nTreeNode *buildTree(int n) {\n if (n == 0)\n return nullptr;\n TreeNode *root = new TreeNode(0);\n root->left = buildTree(n - 1);\n root->right = buildTree(n - 1);\n return root;\n}\n
space_complexity.java/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nTreeNode buildTree(int n) {\n if (n == 0)\n return null;\n TreeNode root = new TreeNode(0);\n root.left = buildTree(n - 1);\n root.right = buildTree(n - 1);\n return root;\n}\n
space_complexity.cs/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nTreeNode? BuildTree(int n) {\n if (n == 0) return null;\n TreeNode root = new(0) {\n left = BuildTree(n - 1),\n right = BuildTree(n - 1)\n };\n return root;\n}\n
space_complexity.go/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nfunc buildTree(n int) *TreeNode {\n if n == 0 {\n return nil\n }\n root := NewTreeNode(0)\n root.Left = buildTree(n - 1)\n root.Right = buildTree(n - 1)\n return root\n}\n
space_complexity.swift/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nfunc buildTree(n: Int) -> TreeNode? {\n if n == 0 {\n return nil\n }\n let root = TreeNode(x: 0)\n root.left = buildTree(n: n - 1)\n root.right = buildTree(n: n - 1)\n return root\n}\n
space_complexity.js/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nfunction buildTree(n) {\n if (n === 0) return null;\n const root = new TreeNode(0);\n root.left = buildTree(n - 1);\n root.right = buildTree(n - 1);\n return root;\n}\n
space_complexity.ts/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nfunction buildTree(n: number): TreeNode | null {\n if (n === 0) return null;\n const root = new TreeNode(0);\n root.left = buildTree(n - 1);\n root.right = buildTree(n - 1);\n return root;\n}\n
space_complexity.dart/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nTreeNode? buildTree(int n) {\n if (n == 0) return null;\n TreeNode root = TreeNode(0);\n root.left = buildTree(n - 1);\n root.right = buildTree(n - 1);\n return root;\n}\n
space_complexity.rs/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nfn build_tree(n: i32) -> Option<Rc<RefCell<TreeNode>>> {\n if n == 0 {return None};\n let root = TreeNode::new(0);\n root.borrow_mut().left = build_tree(n - 1);\n root.borrow_mut().right = build_tree(n - 1);\n return Some(root);\n}\n
space_complexity.c/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nTreeNode *buildTree(int n) {\n if (n == 0)\n return NULL;\n TreeNode *root = newTreeNode(0);\n root->left = buildTree(n - 1);\n root->right = buildTree(n - 1);\n return root;\n}\n
space_complexity.zig// \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09\nfn buildTree(mem_allocator: std.mem.Allocator, n: i32) !?*inc.TreeNode(i32) {\n if (n == 0) return null;\n const root = try mem_allocator.create(inc.TreeNode(i32));\n root.init(0);\n root.left = try buildTree(mem_allocator, n - 1);\n root.right = try buildTree(mem_allocator, n - 1);\n return root;\n}\n
Visualizing Code Full Screen >
Figure 2-19 \u00a0 Full Binary Tree Generating Exponential Order Space Complexity
"},{"location":"chapter_computational_complexity/space_complexity/#5-logarithmic-order-olog-n","title":"5. \u00a0 Logarithmic Order \\(O(\\log n)\\)","text":"Logarithmic order is common in divide-and-conquer algorithms. For example, in merge sort, an array of length \\(n\\) is recursively divided in half each round, forming a recursion tree of height \\(\\log n\\), using \\(O(\\log n)\\) stack frame space.
Another example is converting a number to a string. Given a positive integer \\(n\\), its number of digits is \\(\\log_{10} n + 1\\), corresponding to the length of the string, thus the space complexity is \\(O(\\log_{10} n + 1) = O(\\log n)\\).
"},{"location":"chapter_computational_complexity/space_complexity/#244-balancing-time-and-space","title":"2.4.4 \u00a0 Balancing Time and Space","text":"Ideally, we aim for both time complexity and space complexity to be optimal. However, in practice, optimizing both simultaneously is often difficult.
Lowering time complexity usually comes at the cost of increased space complexity, and vice versa. The approach of sacrificing memory space to improve algorithm speed is known as \"space-time tradeoff\"; the reverse is known as \"time-space tradeoff\".
The choice depends on which aspect we value more. In most cases, time is more precious than space, so \"space-time tradeoff\" is often the more common strategy. Of course, controlling space complexity is also very important when dealing with large volumes of data.
"},{"location":"chapter_computational_complexity/summary/","title":"2.5 \u00a0 Summary","text":""},{"location":"chapter_computational_complexity/summary/#1-key-review","title":"1. \u00a0 Key Review","text":"Algorithm Efficiency Assessment
Time Complexity
Space Complexity
Q: Is the space complexity of tail recursion \\(O(1)\\)?
Theoretically, the space complexity of a tail-recursive function can be optimized to \\(O(1)\\). However, most programming languages (such as Java, Python, C++, Go, C#) do not support automatic optimization of tail recursion, so it's generally considered to have a space complexity of \\(O(n)\\).
Q: What is the difference between the terms \"function\" and \"method\"?
A \"function\" can be executed independently, with all parameters passed explicitly. A \"method\" is associated with an object and is implicitly passed to the object calling it, able to operate on the data contained within an instance of a class.
Here are some examples from common programming languages:
Q: Does the \"Common Types of Space Complexity\" figure reflect the absolute size of occupied space?
No, the figure shows space complexities, which reflect growth trends, not the absolute size of the occupied space.
If you take \\(n = 8\\), you might find that the values of each curve don't correspond to their functions. This is because each curve includes a constant term, intended to compress the value range into a visually comfortable range.
In practice, since we usually don't know the \"constant term\" complexity of each method, it's generally not possible to choose the best solution for \\(n = 8\\) based solely on complexity. However, for \\(n = 8^5\\), it's much easier to choose, as the growth trend becomes dominant.
"},{"location":"chapter_computational_complexity/time_complexity/","title":"2.3 \u00a0 Time Complexity","text":"Time complexity is a concept used to measure how the run time of an algorithm increases with the size of the input data. Understanding time complexity is crucial for accurately assessing the efficiency of an algorithm.
+
might take 1 ns, a multiplication operation *
might take 10 ns, a print operation print()
might take 5 ns, etc.For example, consider the following code with an input size of \\(n\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig# Under an operating platform\ndef algorithm(n: int):\n a = 2 # 1 ns\n a = a + 1 # 1 ns\n a = a * 2 # 10 ns\n # Cycle n times\n for _ in range(n): # 1 ns\n print(0) # 5 ns\n
// Under a particular operating platform\nvoid algorithm(int n) {\n int a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for (int i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n cout << 0 << endl; // 5 ns\n }\n}\n
// Under a particular operating platform\nvoid algorithm(int n) {\n int a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for (int i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n System.out.println(0); // 5 ns\n }\n}\n
// Under a particular operating platform\nvoid Algorithm(int n) {\n int a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for (int i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n Console.WriteLine(0); // 5 ns\n }\n}\n
// Under a particular operating platform\nfunc algorithm(n int) {\n a := 2 // 1 ns\n a = a + 1 // 1 ns\n a = a * 2 // 10 ns\n // Loop n times\n for i := 0; i < n; i++ { // 1 ns\n fmt.Println(a) // 5 ns\n }\n}\n
// Under a particular operating platform\nfunc algorithm(n: Int) {\n var a = 2 // 1 ns\n a = a + 1 // 1 ns\n a = a * 2 // 10 ns\n // Loop n times\n for _ in 0 ..< n { // 1 ns\n print(0) // 5 ns\n }\n}\n
// Under a particular operating platform\nfunction algorithm(n) {\n var a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for(let i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n console.log(0); // 5 ns\n }\n}\n
// Under a particular operating platform\nfunction algorithm(n: number): void {\n var a: number = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for(let i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n console.log(0); // 5 ns\n }\n}\n
// Under a particular operating platform\nvoid algorithm(int n) {\n int a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for (int i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n print(0); // 5 ns\n }\n}\n
// Under a particular operating platform\nfn algorithm(n: i32) {\n let mut a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for _ in 0..n { // 1 ns for each round i++\n println!(\"{}\", 0); // 5 ns\n }\n}\n
// Under a particular operating platform\nvoid algorithm(int n) {\n int a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for (int i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n printf(\"%d\", 0); // 5 ns\n }\n}\n
// Under a particular operating platform\nfn algorithm(n: usize) void {\n var a: i32 = 2; // 1 ns\n a += 1; // 1 ns\n a *= 2; // 10 ns\n // Loop n times\n for (0..n) |_| { // 1 ns\n std.debug.print(\"{}\\n\", .{0}); // 5 ns\n }\n}\n
Using the above method, the run time of the algorithm can be calculated as \\((6n + 12)\\) ns:
\\[ 1 + 1 + 10 + (1 + 5) \\times n = 6n + 12 \\]However, in practice, counting the run time of an algorithm is neither practical nor reasonable. First, we don't want to tie the estimated time to the running platform, as algorithms need to run on various platforms. Second, it's challenging to know the run time for each type of operation, making the estimation process difficult.
"},{"location":"chapter_computational_complexity/time_complexity/#231-assessing-time-growth-trend","title":"2.3.1 \u00a0 Assessing Time Growth Trend","text":"Time complexity analysis does not count the algorithm's run time, but rather the growth trend of the run time as the data volume increases.
Let's understand this concept of \"time growth trend\" with an example. Assume the input data size is \\(n\\), and consider three algorithms A
, B
, and C
:
# Time complexity of algorithm A: constant order\ndef algorithm_A(n: int):\n print(0)\n# Time complexity of algorithm B: linear order\ndef algorithm_B(n: int):\n for _ in range(n):\n print(0)\n# Time complexity of algorithm C: constant order\ndef algorithm_C(n: int):\n for _ in range(1000000):\n print(0)\n
// Time complexity of algorithm A: constant order\nvoid algorithm_A(int n) {\n cout << 0 << endl;\n}\n// Time complexity of algorithm B: linear order\nvoid algorithm_B(int n) {\n for (int i = 0; i < n; i++) {\n cout << 0 << endl;\n }\n}\n// Time complexity of algorithm C: constant order\nvoid algorithm_C(int n) {\n for (int i = 0; i < 1000000; i++) {\n cout << 0 << endl;\n }\n}\n
// Time complexity of algorithm A: constant order\nvoid algorithm_A(int n) {\n System.out.println(0);\n}\n// Time complexity of algorithm B: linear order\nvoid algorithm_B(int n) {\n for (int i = 0; i < n; i++) {\n System.out.println(0);\n }\n}\n// Time complexity of algorithm C: constant order\nvoid algorithm_C(int n) {\n for (int i = 0; i < 1000000; i++) {\n System.out.println(0);\n }\n}\n
// Time complexity of algorithm A: constant order\nvoid AlgorithmA(int n) {\n Console.WriteLine(0);\n}\n// Time complexity of algorithm B: linear order\nvoid AlgorithmB(int n) {\n for (int i = 0; i < n; i++) {\n Console.WriteLine(0);\n }\n}\n// Time complexity of algorithm C: constant order\nvoid AlgorithmC(int n) {\n for (int i = 0; i < 1000000; i++) {\n Console.WriteLine(0);\n }\n}\n
// Time complexity of algorithm A: constant order\nfunc algorithm_A(n int) {\n fmt.Println(0)\n}\n// Time complexity of algorithm B: linear order\nfunc algorithm_B(n int) {\n for i := 0; i < n; i++ {\n fmt.Println(0)\n }\n}\n// Time complexity of algorithm C: constant order\nfunc algorithm_C(n int) {\n for i := 0; i < 1000000; i++ {\n fmt.Println(0)\n }\n}\n
// Time complexity of algorithm A: constant order\nfunc algorithmA(n: Int) {\n print(0)\n}\n\n// Time complexity of algorithm B: linear order\nfunc algorithmB(n: Int) {\n for _ in 0 ..< n {\n print(0)\n }\n}\n\n// Time complexity of algorithm C: constant order\nfunc algorithmC(n: Int) {\n for _ in 0 ..< 1000000 {\n print(0)\n }\n}\n
// Time complexity of algorithm A: constant order\nfunction algorithm_A(n) {\n console.log(0);\n}\n// Time complexity of algorithm B: linear order\nfunction algorithm_B(n) {\n for (let i = 0; i < n; i++) {\n console.log(0);\n }\n}\n// Time complexity of algorithm C: constant order\nfunction algorithm_C(n) {\n for (let i = 0; i < 1000000; i++) {\n console.log(0);\n }\n}\n
// Time complexity of algorithm A: constant order\nfunction algorithm_A(n: number): void {\n console.log(0);\n}\n// Time complexity of algorithm B: linear order\nfunction algorithm_B(n: number): void {\n for (let i = 0; i < n; i++) {\n console.log(0);\n }\n}\n// Time complexity of algorithm C: constant order\nfunction algorithm_C(n: number): void {\n for (let i = 0; i < 1000000; i++) {\n console.log(0);\n }\n}\n
// Time complexity of algorithm A: constant order\nvoid algorithmA(int n) {\n print(0);\n}\n// Time complexity of algorithm B: linear order\nvoid algorithmB(int n) {\n for (int i = 0; i < n; i++) {\n print(0);\n }\n}\n// Time complexity of algorithm C: constant order\nvoid algorithmC(int n) {\n for (int i = 0; i < 1000000; i++) {\n print(0);\n }\n}\n
// Time complexity of algorithm A: constant order\nfn algorithm_A(n: i32) {\n println!(\"{}\", 0);\n}\n// Time complexity of algorithm B: linear order\nfn algorithm_B(n: i32) {\n for _ in 0..n {\n println!(\"{}\", 0);\n }\n}\n// Time complexity of algorithm C: constant order\nfn algorithm_C(n: i32) {\n for _ in 0..1000000 {\n println!(\"{}\", 0);\n }\n}\n
// Time complexity of algorithm A: constant order\nvoid algorithm_A(int n) {\n printf(\"%d\", 0);\n}\n// Time complexity of algorithm B: linear order\nvoid algorithm_B(int n) {\n for (int i = 0; i < n; i++) {\n printf(\"%d\", 0);\n }\n}\n// Time complexity of algorithm C: constant order\nvoid algorithm_C(int n) {\n for (int i = 0; i < 1000000; i++) {\n printf(\"%d\", 0);\n }\n}\n
// Time complexity of algorithm A: constant order\nfn algorithm_A(n: usize) void {\n _ = n;\n std.debug.print(\"{}\\n\", .{0});\n}\n// Time complexity of algorithm B: linear order\nfn algorithm_B(n: i32) void {\n for (0..n) |_| {\n std.debug.print(\"{}\\n\", .{0});\n }\n}\n// Time complexity of algorithm C: constant order\nfn algorithm_C(n: i32) void {\n _ = n;\n for (0..1000000) |_| {\n std.debug.print(\"{}\\n\", .{0});\n }\n}\n
The following figure shows the time complexities of these three algorithms.
A
has just one print operation, and its run time does not grow with \\(n\\). Its time complexity is considered \"constant order.\"B
involves a print operation looping \\(n\\) times, and its run time grows linearly with \\(n\\). Its time complexity is \"linear order.\"C
has a print operation looping 1,000,000 times. Although it takes a long time, it is independent of the input data size \\(n\\). Therefore, the time complexity of C
is the same as A
, which is \"constant order.\"Figure 2-7 \u00a0 Time Growth Trend of Algorithms A, B, and C
Compared to directly counting the run time of an algorithm, what are the characteristics of time complexity analysis?
B
has linearly growing run time, which is slower than algorithm A
when \\(n > 1\\) and slower than C
when \\(n > 1,000,000\\). In fact, as long as the input data size \\(n\\) is sufficiently large, a \"constant order\" complexity algorithm will always be better than a \"linear order\" one, demonstrating the essence of time growth trend.A
and C
have the same time complexity, their actual run times can be quite different. Similarly, even though algorithm B
has a higher time complexity than C
, it is clearly superior when the input data size \\(n\\) is small. In these cases, it's difficult to judge the efficiency of algorithms based solely on time complexity. Nonetheless, despite these issues, complexity analysis remains the most effective and commonly used method for evaluating algorithm efficiency.Consider a function with an input size of \\(n\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZigdef algorithm(n: int):\n a = 1 # +1\n a = a + 1 # +1\n a = a * 2 # +1\n # Cycle n times\n for i in range(n): # +1\n print(0) # +1\n
void algorithm(int n) {\n int a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n // Loop n times\n for (int i = 0; i < n; i++) { // +1 (execute i ++ every round)\n cout << 0 << endl; // +1\n }\n}\n
void algorithm(int n) {\n int a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n // Loop n times\n for (int i = 0; i < n; i++) { // +1 (execute i ++ every round)\n System.out.println(0); // +1\n }\n}\n
void Algorithm(int n) {\n int a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n // Loop n times\n for (int i = 0; i < n; i++) { // +1 (execute i ++ every round)\n Console.WriteLine(0); // +1\n }\n}\n
func algorithm(n int) {\n a := 1 // +1\n a = a + 1 // +1\n a = a * 2 // +1\n // Loop n times\n for i := 0; i < n; i++ { // +1\n fmt.Println(a) // +1\n }\n}\n
func algorithm(n: Int) {\n var a = 1 // +1\n a = a + 1 // +1\n a = a * 2 // +1\n // Loop n times\n for _ in 0 ..< n { // +1\n print(0) // +1\n }\n}\n
function algorithm(n) {\n var a = 1; // +1\n a += 1; // +1\n a *= 2; // +1\n // Loop n times\n for(let i = 0; i < n; i++){ // +1 (execute i ++ every round)\n console.log(0); // +1\n }\n}\n
function algorithm(n: number): void{\n var a: number = 1; // +1\n a += 1; // +1\n a *= 2; // +1\n // Loop n times\n for(let i = 0; i < n; i++){ // +1 (execute i ++ every round)\n console.log(0); // +1\n }\n}\n
void algorithm(int n) {\n int a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n // Loop n times\n for (int i = 0; i < n; i++) { // +1 (execute i ++ every round)\n print(0); // +1\n }\n}\n
fn algorithm(n: i32) {\n let mut a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n\n // Loop n times\n for _ in 0..n { // +1 (execute i ++ every round)\n println!(\"{}\", 0); // +1\n }\n}\n
void algorithm(int n) {\n int a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n // Loop n times\n for (int i = 0; i < n; i++) { // +1 (execute i ++ every round)\n printf(\"%d\", 0); // +1\n }\n} \n
fn algorithm(n: usize) void {\n var a: i32 = 1; // +1\n a += 1; // +1\n a *= 2; // +1\n // Loop n times\n for (0..n) |_| { // +1 (execute i ++ every round)\n std.debug.print(\"{}\\n\", .{0}); // +1\n }\n}\n
Given a function that represents the number of operations of an algorithm as a function of the input size \\(n\\), denoted as \\(T(n)\\), consider the following example:
\\[ T(n) = 3 + 2n \\]Since \\(T(n)\\) is a linear function, its growth trend is linear, and therefore, its time complexity is of linear order, denoted as \\(O(n)\\). This mathematical notation, known as \"big-O notation,\" represents the \"asymptotic upper bound\" of the function \\(T(n)\\).
In essence, time complexity analysis is about finding the asymptotic upper bound of the \"number of operations \\(T(n)\\)\". It has a precise mathematical definition.
Asymptotic Upper Bound
If there exist positive real numbers \\(c\\) and \\(n_0\\) such that for all \\(n > n_0\\), \\(T(n) \\leq c \\cdot f(n)\\), then \\(f(n)\\) is considered an asymptotic upper bound of \\(T(n)\\), denoted as \\(T(n) = O(f(n))\\).
As illustrated below, calculating the asymptotic upper bound involves finding a function \\(f(n)\\) such that, as \\(n\\) approaches infinity, \\(T(n)\\) and \\(f(n)\\) have the same growth order, differing only by a constant factor \\(c\\).
Figure 2-8 \u00a0 Asymptotic Upper Bound of a Function
"},{"location":"chapter_computational_complexity/time_complexity/#233-calculation-method","title":"2.3.3 \u00a0 Calculation Method","text":"While the concept of asymptotic upper bound might seem mathematically dense, you don't need to fully grasp it right away. Let's first understand the method of calculation, which can be practiced and comprehended over time.
Once \\(f(n)\\) is determined, we obtain the time complexity \\(O(f(n))\\). But how do we determine the asymptotic upper bound \\(f(n)\\)? This process generally involves two steps: counting the number of operations and determining the asymptotic upper bound.
"},{"location":"chapter_computational_complexity/time_complexity/#1-step-1-counting-the-number-of-operations","title":"1. \u00a0 Step 1: Counting the Number of Operations","text":"This step involves going through the code line by line. However, due to the presence of the constant \\(c\\) in \\(c \\cdot f(n)\\), all coefficients and constant terms in \\(T(n)\\) can be ignored. This principle allows for simplification techniques in counting operations.
Given a function, we can use these techniques to count operations:
PythonC++JavaC#GoSwiftJSTSDartRustCZigdef algorithm(n: int):\n a = 1 # +0 (trick 1)\n a = a + n # +0 (trick 1)\n # +n (technique 2)\n for i in range(5 * n + 1):\n print(0)\n # +n*n (technique 3)\n for i in range(2 * n):\n for j in range(n + 1):\n print(0)\n
void algorithm(int n) {\n int a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (int i = 0; i < 5 * n + 1; i++) {\n cout << 0 << endl;\n }\n // +n*n (technique 3)\n for (int i = 0; i < 2 * n; i++) {\n for (int j = 0; j < n + 1; j++) {\n cout << 0 << endl;\n }\n }\n}\n
void algorithm(int n) {\n int a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (int i = 0; i < 5 * n + 1; i++) {\n System.out.println(0);\n }\n // +n*n (technique 3)\n for (int i = 0; i < 2 * n; i++) {\n for (int j = 0; j < n + 1; j++) {\n System.out.println(0);\n }\n }\n}\n
void Algorithm(int n) {\n int a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (int i = 0; i < 5 * n + 1; i++) {\n Console.WriteLine(0);\n }\n // +n*n (technique 3)\n for (int i = 0; i < 2 * n; i++) {\n for (int j = 0; j < n + 1; j++) {\n Console.WriteLine(0);\n }\n }\n}\n
func algorithm(n int) {\n a := 1 // +0 (trick 1)\n a = a + n // +0 (trick 1)\n // +n (technique 2)\n for i := 0; i < 5 * n + 1; i++ {\n fmt.Println(0)\n }\n // +n*n (technique 3)\n for i := 0; i < 2 * n; i++ {\n for j := 0; j < n + 1; j++ {\n fmt.Println(0)\n }\n }\n}\n
func algorithm(n: Int) {\n var a = 1 // +0 (trick 1)\n a = a + n // +0 (trick 1)\n // +n (technique 2)\n for _ in 0 ..< (5 * n + 1) {\n print(0)\n }\n // +n*n (technique 3)\n for _ in 0 ..< (2 * n) {\n for _ in 0 ..< (n + 1) {\n print(0)\n }\n }\n}\n
function algorithm(n) {\n let a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (let i = 0; i < 5 * n + 1; i++) {\n console.log(0);\n }\n // +n*n (technique 3)\n for (let i = 0; i < 2 * n; i++) {\n for (let j = 0; j < n + 1; j++) {\n console.log(0);\n }\n }\n}\n
function algorithm(n: number): void {\n let a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (let i = 0; i < 5 * n + 1; i++) {\n console.log(0);\n }\n // +n*n (technique 3)\n for (let i = 0; i < 2 * n; i++) {\n for (let j = 0; j < n + 1; j++) {\n console.log(0);\n }\n }\n}\n
void algorithm(int n) {\n int a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (int i = 0; i < 5 * n + 1; i++) {\n print(0);\n }\n // +n*n (technique 3)\n for (int i = 0; i < 2 * n; i++) {\n for (int j = 0; j < n + 1; j++) {\n print(0);\n }\n }\n}\n
fn algorithm(n: i32) {\n let mut a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n\n // +n (technique 2)\n for i in 0..(5 * n + 1) {\n println!(\"{}\", 0);\n }\n\n // +n*n (technique 3)\n for i in 0..(2 * n) {\n for j in 0..(n + 1) {\n println!(\"{}\", 0);\n }\n }\n}\n
void algorithm(int n) {\n int a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (int i = 0; i < 5 * n + 1; i++) {\n printf(\"%d\", 0);\n }\n // +n*n (technique 3)\n for (int i = 0; i < 2 * n; i++) {\n for (int j = 0; j < n + 1; j++) {\n printf(\"%d\", 0);\n }\n }\n}\n
fn algorithm(n: usize) void {\n var a: i32 = 1; // +0 (trick 1)\n a = a + @as(i32, @intCast(n)); // +0 (trick 1)\n\n // +n (technique 2)\n for(0..(5 * n + 1)) |_| {\n std.debug.print(\"{}\\n\", .{0});\n }\n\n // +n*n (technique 3)\n for(0..(2 * n)) |_| {\n for(0..(n + 1)) |_| {\n std.debug.print(\"{}\\n\", .{0});\n }\n }\n}\n
The formula below shows the counting results before and after simplification, both leading to a time complexity of \\(O(n^2)\\):
\\[ \\begin{aligned} T(n) & = 2n(n + 1) + (5n + 1) + 2 & \\text{Complete Count (-.-|||)} \\newline & = 2n^2 + 7n + 3 \\newline T(n) & = n^2 + n & \\text{Simplified Count (o.O)} \\end{aligned} \\]"},{"location":"chapter_computational_complexity/time_complexity/#2-step-2-determining-the-asymptotic-upper-bound","title":"2. \u00a0 Step 2: Determining the Asymptotic Upper Bound","text":"The time complexity is determined by the highest order term in \\(T(n)\\). This is because, as \\(n\\) approaches infinity, the highest order term dominates, rendering the influence of other terms negligible.
The following table illustrates examples of different operation counts and their corresponding time complexities. Some exaggerated values are used to emphasize that coefficients cannot alter the order of growth. When \\(n\\) becomes very large, these constants become insignificant.
Table: Time Complexity for Different Operation Counts
Operation Count \\(T(n)\\) Time Complexity \\(O(f(n))\\) \\(100000\\) \\(O(1)\\) \\(3n + 2\\) \\(O(n)\\) \\(2n^2 + 3n + 2\\) \\(O(n^2)\\) \\(n^3 + 10000n^2\\) \\(O(n^3)\\) \\(2^n + 10000n^{10000}\\) \\(O(2^n)\\)"},{"location":"chapter_computational_complexity/time_complexity/#234-common-types-of-time-complexity","title":"2.3.4 \u00a0 Common Types of Time Complexity","text":"Let's consider the input data size as \\(n\\). The common types of time complexities are illustrated below, arranged from lowest to highest:
\\[ \\begin{aligned} O(1) < O(\\log n) < O(n) < O(n \\log n) < O(n^2) < O(2^n) < O(n!) \\newline \\text{Constant Order} < \\text{Logarithmic Order} < \\text{Linear Order} < \\text{Linear-Logarithmic Order} < \\text{Quadratic Order} < \\text{Exponential Order} < \\text{Factorial Order} \\end{aligned} \\]Figure 2-9 \u00a0 Common Types of Time Complexity
"},{"location":"chapter_computational_complexity/time_complexity/#1-constant-order-o1","title":"1. \u00a0 Constant Order \\(O(1)\\)","text":"Constant order means the number of operations is independent of the input data size \\(n\\). In the following function, although the number of operations size
might be large, the time complexity remains \\(O(1)\\) as it's unrelated to \\(n\\):
def constant(n: int) -> int:\n \"\"\"\u5e38\u6570\u9636\"\"\"\n count = 0\n size = 100000\n for _ in range(size):\n count += 1\n return count\n
time_complexity.cpp/* \u5e38\u6570\u9636 */\nint constant(int n) {\n int count = 0;\n int size = 100000;\n for (int i = 0; i < size; i++)\n count++;\n return count;\n}\n
time_complexity.java/* \u5e38\u6570\u9636 */\nint constant(int n) {\n int count = 0;\n int size = 100000;\n for (int i = 0; i < size; i++)\n count++;\n return count;\n}\n
time_complexity.cs/* \u5e38\u6570\u9636 */\nint Constant(int n) {\n int count = 0;\n int size = 100000;\n for (int i = 0; i < size; i++)\n count++;\n return count;\n}\n
time_complexity.go/* \u5e38\u6570\u9636 */\nfunc constant(n int) int {\n count := 0\n size := 100000\n for i := 0; i < size; i++ {\n count++\n }\n return count\n}\n
time_complexity.swift/* \u5e38\u6570\u9636 */\nfunc constant(n: Int) -> Int {\n var count = 0\n let size = 100_000\n for _ in 0 ..< size {\n count += 1\n }\n return count\n}\n
time_complexity.js/* \u5e38\u6570\u9636 */\nfunction constant(n) {\n let count = 0;\n const size = 100000;\n for (let i = 0; i < size; i++) count++;\n return count;\n}\n
time_complexity.ts/* \u5e38\u6570\u9636 */\nfunction constant(n: number): number {\n let count = 0;\n const size = 100000;\n for (let i = 0; i < size; i++) count++;\n return count;\n}\n
time_complexity.dart/* \u5e38\u6570\u9636 */\nint constant(int n) {\n int count = 0;\n int size = 100000;\n for (var i = 0; i < size; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.rs/* \u5e38\u6570\u9636 */\nfn constant(n: i32) -> i32 {\n _ = n;\n let mut count = 0;\n let size = 100_000;\n for _ in 0..size {\n count += 1;\n }\n count\n}\n
time_complexity.c/* \u5e38\u6570\u9636 */\nint constant(int n) {\n int count = 0;\n int size = 100000;\n int i = 0;\n for (int i = 0; i < size; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.zig// \u5e38\u6570\u9636\nfn constant(n: i32) i32 {\n _ = n;\n var count: i32 = 0;\n const size: i32 = 100_000;\n var i: i32 = 0;\n while(i<size) : (i += 1) {\n count += 1;\n }\n return count;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_computational_complexity/time_complexity/#2-linear-order-on","title":"2. \u00a0 Linear Order \\(O(n)\\)","text":"Linear order indicates the number of operations grows linearly with the input data size \\(n\\). Linear order commonly appears in single-loop structures:
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef linear(n: int) -> int:\n \"\"\"\u7ebf\u6027\u9636\"\"\"\n count = 0\n for _ in range(n):\n count += 1\n return count\n
time_complexity.cpp/* \u7ebf\u6027\u9636 */\nint linear(int n) {\n int count = 0;\n for (int i = 0; i < n; i++)\n count++;\n return count;\n}\n
time_complexity.java/* \u7ebf\u6027\u9636 */\nint linear(int n) {\n int count = 0;\n for (int i = 0; i < n; i++)\n count++;\n return count;\n}\n
time_complexity.cs/* \u7ebf\u6027\u9636 */\nint Linear(int n) {\n int count = 0;\n for (int i = 0; i < n; i++)\n count++;\n return count;\n}\n
time_complexity.go/* \u7ebf\u6027\u9636 */\nfunc linear(n int) int {\n count := 0\n for i := 0; i < n; i++ {\n count++\n }\n return count\n}\n
time_complexity.swift/* \u7ebf\u6027\u9636 */\nfunc linear(n: Int) -> Int {\n var count = 0\n for _ in 0 ..< n {\n count += 1\n }\n return count\n}\n
time_complexity.js/* \u7ebf\u6027\u9636 */\nfunction linear(n) {\n let count = 0;\n for (let i = 0; i < n; i++) count++;\n return count;\n}\n
time_complexity.ts/* \u7ebf\u6027\u9636 */\nfunction linear(n: number): number {\n let count = 0;\n for (let i = 0; i < n; i++) count++;\n return count;\n}\n
time_complexity.dart/* \u7ebf\u6027\u9636 */\nint linear(int n) {\n int count = 0;\n for (var i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.rs/* \u7ebf\u6027\u9636 */\nfn linear(n: i32) -> i32 {\n let mut count = 0;\n for _ in 0..n {\n count += 1;\n }\n count\n}\n
time_complexity.c/* \u7ebf\u6027\u9636 */\nint linear(int n) {\n int count = 0;\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.zig// \u7ebf\u6027\u9636\nfn linear(n: i32) i32 {\n var count: i32 = 0;\n var i: i32 = 0;\n while (i < n) : (i += 1) {\n count += 1;\n }\n return count;\n}\n
Visualizing Code Full Screen >
Operations like array traversal and linked list traversal have a time complexity of \\(O(n)\\), where \\(n\\) is the length of the array or list:
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef array_traversal(nums: list[int]) -> int:\n \"\"\"\u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09\"\"\"\n count = 0\n # \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for num in nums:\n count += 1\n return count\n
time_complexity.cpp/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nint arrayTraversal(vector<int> &nums) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (int num : nums) {\n count++;\n }\n return count;\n}\n
time_complexity.java/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nint arrayTraversal(int[] nums) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (int num : nums) {\n count++;\n }\n return count;\n}\n
time_complexity.cs/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nint ArrayTraversal(int[] nums) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n foreach (int num in nums) {\n count++;\n }\n return count;\n}\n
time_complexity.go/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nfunc arrayTraversal(nums []int) int {\n count := 0\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for range nums {\n count++\n }\n return count\n}\n
time_complexity.swift/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nfunc arrayTraversal(nums: [Int]) -> Int {\n var count = 0\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for _ in nums {\n count += 1\n }\n return count\n}\n
time_complexity.js/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nfunction arrayTraversal(nums) {\n let count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (let i = 0; i < nums.length; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.ts/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nfunction arrayTraversal(nums: number[]): number {\n let count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (let i = 0; i < nums.length; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.dart/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nint arrayTraversal(List<int> nums) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (var _num in nums) {\n count++;\n }\n return count;\n}\n
time_complexity.rs/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nfn array_traversal(nums: &[i32]) -> i32 {\n let mut count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for _ in nums {\n count += 1;\n }\n count\n}\n
time_complexity.c/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nint arrayTraversal(int *nums, int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.zig// \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09\nfn arrayTraversal(nums: []i32) i32 {\n var count: i32 = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (nums) |_| {\n count += 1;\n }\n return count;\n}\n
Visualizing Code Full Screen >
It's important to note that the input data size \\(n\\) should be determined based on the type of input data. For example, in the first example, \\(n\\) represents the input data size, while in the second example, the length of the array \\(n\\) is the data size.
"},{"location":"chapter_computational_complexity/time_complexity/#3-quadratic-order-on2","title":"3. \u00a0 Quadratic Order \\(O(n^2)\\)","text":"Quadratic order means the number of operations grows quadratically with the input data size \\(n\\). Quadratic order typically appears in nested loops, where both the outer and inner loops have a time complexity of \\(O(n)\\), resulting in an overall complexity of \\(O(n^2)\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef quadratic(n: int) -> int:\n \"\"\"\u5e73\u65b9\u9636\"\"\"\n count = 0\n # \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for i in range(n):\n for j in range(n):\n count += 1\n return count\n
time_complexity.cpp/* \u5e73\u65b9\u9636 */\nint quadratic(int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.java/* \u5e73\u65b9\u9636 */\nint quadratic(int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.cs/* \u5e73\u65b9\u9636 */\nint Quadratic(int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.go/* \u5e73\u65b9\u9636 */\nfunc quadratic(n int) int {\n count := 0\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for i := 0; i < n; i++ {\n for j := 0; j < n; j++ {\n count++\n }\n }\n return count\n}\n
time_complexity.swift/* \u5e73\u65b9\u9636 */\nfunc quadratic(n: Int) -> Int {\n var count = 0\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for _ in 0 ..< n {\n for _ in 0 ..< n {\n count += 1\n }\n }\n return count\n}\n
time_complexity.js/* \u5e73\u65b9\u9636 */\nfunction quadratic(n) {\n let count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.ts/* \u5e73\u65b9\u9636 */\nfunction quadratic(n: number): number {\n let count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.dart/* \u5e73\u65b9\u9636 */\nint quadratic(int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.rs/* \u5e73\u65b9\u9636 */\nfn quadratic(n: i32) -> i32 {\n let mut count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for _ in 0..n {\n for _ in 0..n {\n count += 1;\n }\n }\n count\n}\n
time_complexity.c/* \u5e73\u65b9\u9636 */\nint quadratic(int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.zig// \u5e73\u65b9\u9636\nfn quadratic(n: i32) i32 {\n var count: i32 = 0;\n var i: i32 = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n while (i < n) : (i += 1) {\n var j: i32 = 0;\n while (j < n) : (j += 1) {\n count += 1;\n }\n }\n return count;\n}\n
Visualizing Code Full Screen >
The following image compares constant order, linear order, and quadratic order time complexities.
Figure 2-10 \u00a0 Constant, Linear, and Quadratic Order Time Complexities
For instance, in bubble sort, the outer loop runs \\(n - 1\\) times, and the inner loop runs \\(n-1\\), \\(n-2\\), ..., \\(2\\), \\(1\\) times, averaging \\(n / 2\\) times, resulting in a time complexity of \\(O((n - 1) n / 2) = O(n^2)\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef bubble_sort(nums: list[int]) -> int:\n \"\"\"\u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09\"\"\"\n count = 0 # \u8ba1\u6570\u5668\n # \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for i in range(len(nums) - 1, 0, -1):\n # \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for j in range(i):\n if nums[j] > nums[j + 1]:\n # \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n tmp: int = nums[j]\n nums[j] = nums[j + 1]\n nums[j + 1] = tmp\n count += 3 # \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n return count\n
time_complexity.cpp/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nint bubbleSort(vector<int> &nums) {\n int count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (int i = nums.size() - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (int j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n int tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.java/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nint bubbleSort(int[] nums) {\n int count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (int i = nums.length - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (int j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n int tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.cs/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nint BubbleSort(int[] nums) {\n int count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (int i = nums.Length - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef \n for (int j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]);\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.go/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nfunc bubbleSort(nums []int) int {\n count := 0 // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for i := len(nums) - 1; i > 0; i-- {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for j := 0; j < i; j++ {\n if nums[j] > nums[j+1] {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n tmp := nums[j]\n nums[j] = nums[j+1]\n nums[j+1] = tmp\n count += 3 // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count\n}\n
time_complexity.swift/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nfunc bubbleSort(nums: inout [Int]) -> Int {\n var count = 0 // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for i in stride(from: nums.count - 1, to: 0, by: -1) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef \n for j in 0 ..< i {\n if nums[j] > nums[j + 1] {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n let tmp = nums[j]\n nums[j] = nums[j + 1]\n nums[j + 1] = tmp\n count += 3 // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count\n}\n
time_complexity.js/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nfunction bubbleSort(nums) {\n let count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (let i = nums.length - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (let j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n let tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.ts/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nfunction bubbleSort(nums: number[]): number {\n let count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (let i = nums.length - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (let j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n let tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.dart/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nint bubbleSort(List<int> nums) {\n int count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (var i = nums.length - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (var j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n int tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.rs/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nfn bubble_sort(nums: &mut [i32]) -> i32 {\n let mut count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for i in (1..nums.len()).rev() {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef \n for j in 0..i {\n if nums[j] > nums[j + 1] {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n let tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n count\n}\n
time_complexity.c/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nint bubbleSort(int *nums, int n) {\n int count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (int i = n - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (int j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n int tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.zig// \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09\nfn bubbleSort(nums: []i32) i32 {\n var count: i32 = 0; // \u8ba1\u6570\u5668 \n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n var i: i32 = @as(i32, @intCast(nums.len)) - 1;\n while (i > 0) : (i -= 1) {\n var j: usize = 0;\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef \n while (j < i) : (j += 1) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n var tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_computational_complexity/time_complexity/#4-exponential-order-o2n","title":"4. \u00a0 Exponential Order \\(O(2^n)\\)","text":"Biological \"cell division\" is a classic example of exponential order growth: starting with one cell, it becomes two after one division, four after two divisions, and so on, resulting in \\(2^n\\) cells after \\(n\\) divisions.
The following image and code simulate the cell division process, with a time complexity of \\(O(2^n)\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef exponential(n: int) -> int:\n \"\"\"\u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09\"\"\"\n count = 0\n base = 1\n # \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for _ in range(n):\n for _ in range(base):\n count += 1\n base *= 2\n # count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count\n
time_complexity.cpp/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint exponential(int n) {\n int count = 0, base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < base; j++) {\n count++;\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.java/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint exponential(int n) {\n int count = 0, base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < base; j++) {\n count++;\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.cs/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint Exponential(int n) {\n int count = 0, bas = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < bas; j++) {\n count++;\n }\n bas *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.go/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09*/\nfunc exponential(n int) int {\n count, base := 0, 1\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for i := 0; i < n; i++ {\n for j := 0; j < base; j++ {\n count++\n }\n base *= 2\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count\n}\n
time_complexity.swift/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunc exponential(n: Int) -> Int {\n var count = 0\n var base = 1\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for _ in 0 ..< n {\n for _ in 0 ..< base {\n count += 1\n }\n base *= 2\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count\n}\n
time_complexity.js/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunction exponential(n) {\n let count = 0,\n base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < base; j++) {\n count++;\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.ts/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunction exponential(n: number): number {\n let count = 0,\n base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < base; j++) {\n count++;\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.dart/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint exponential(int n) {\n int count = 0, base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (var i = 0; i < n; i++) {\n for (var j = 0; j < base; j++) {\n count++;\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.rs/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfn exponential(n: i32) -> i32 {\n let mut count = 0;\n let mut base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for _ in 0..n {\n for _ in 0..base {\n count += 1\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n count\n}\n
time_complexity.c/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint exponential(int n) {\n int count = 0;\n int bas = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < bas; j++) {\n count++;\n }\n bas *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.zig// \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09\nfn exponential(n: i32) i32 {\n var count: i32 = 0;\n var bas: i32 = 1;\n var i: i32 = 0;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n while (i < n) : (i += 1) {\n var j: i32 = 0;\n while (j < bas) : (j += 1) {\n count += 1;\n }\n bas *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
Visualizing Code Full Screen >
Figure 2-11 \u00a0 Exponential Order Time Complexity
In practice, exponential order often appears in recursive functions. For example, in the code below, it recursively splits into two halves, stopping after \\(n\\) divisions:
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef exp_recur(n: int) -> int:\n \"\"\"\u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\"\"\"\n if n == 1:\n return 1\n return exp_recur(n - 1) + exp_recur(n - 1) + 1\n
time_complexity.cpp/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint expRecur(int n) {\n if (n == 1)\n return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.java/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint expRecur(int n) {\n if (n == 1)\n return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.cs/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint ExpRecur(int n) {\n if (n == 1) return 1;\n return ExpRecur(n - 1) + ExpRecur(n - 1) + 1;\n}\n
time_complexity.go/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09*/\nfunc expRecur(n int) int {\n if n == 1 {\n return 1\n }\n return expRecur(n-1) + expRecur(n-1) + 1\n}\n
time_complexity.swift/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc expRecur(n: Int) -> Int {\n if n == 1 {\n return 1\n }\n return expRecur(n: n - 1) + expRecur(n: n - 1) + 1\n}\n
time_complexity.js/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction expRecur(n) {\n if (n === 1) return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.ts/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction expRecur(n: number): number {\n if (n === 1) return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.dart/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint expRecur(int n) {\n if (n == 1) return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.rs/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfn exp_recur(n: i32) -> i32 {\n if n == 1 {\n return 1;\n }\n exp_recur(n - 1) + exp_recur(n - 1) + 1\n}\n
time_complexity.c/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint expRecur(int n) {\n if (n == 1)\n return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.zig// \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\nfn expRecur(n: i32) i32 {\n if (n == 1) return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
Visualizing Code Full Screen >
Exponential order growth is extremely rapid and is commonly seen in exhaustive search methods (brute force, backtracking, etc.). For large-scale problems, exponential order is unacceptable, often requiring dynamic programming or greedy algorithms as solutions.
"},{"location":"chapter_computational_complexity/time_complexity/#5-logarithmic-order-olog-n","title":"5. \u00a0 Logarithmic Order \\(O(\\log n)\\)","text":"In contrast to exponential order, logarithmic order reflects situations where \"the size is halved each round.\" Given an input data size \\(n\\), since the size is halved each round, the number of iterations is \\(\\log_2 n\\), the inverse function of \\(2^n\\).
The following image and code simulate the \"halving each round\" process, with a time complexity of \\(O(\\log_2 n)\\), commonly abbreviated as \\(O(\\log n)\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef logarithmic(n: float) -> int:\n \"\"\"\u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09\"\"\"\n count = 0\n while n > 1:\n n = n / 2\n count += 1\n return count\n
time_complexity.cpp/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint logarithmic(float n) {\n int count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.java/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint logarithmic(float n) {\n int count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.cs/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint Logarithmic(float n) {\n int count = 0;\n while (n > 1) {\n n /= 2;\n count++;\n }\n return count;\n}\n
time_complexity.go/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09*/\nfunc logarithmic(n float64) int {\n count := 0\n for n > 1 {\n n = n / 2\n count++\n }\n return count\n}\n
time_complexity.swift/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunc logarithmic(n: Double) -> Int {\n var count = 0\n var n = n\n while n > 1 {\n n = n / 2\n count += 1\n }\n return count\n}\n
time_complexity.js/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunction logarithmic(n) {\n let count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.ts/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunction logarithmic(n: number): number {\n let count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.dart/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint logarithmic(num n) {\n int count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.rs/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfn logarithmic(mut n: f32) -> i32 {\n let mut count = 0;\n while n > 1.0 {\n n = n / 2.0;\n count += 1;\n }\n count\n}\n
time_complexity.c/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint logarithmic(float n) {\n int count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.zig// \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09\nfn logarithmic(n: f32) i32 {\n var count: i32 = 0;\n var n_var = n;\n while (n_var > 1)\n {\n n_var = n_var / 2;\n count +=1;\n }\n return count;\n}\n
Visualizing Code Full Screen >
Figure 2-12 \u00a0 Logarithmic Order Time Complexity
Like exponential order, logarithmic order also frequently appears in recursive functions. The code below forms a recursive tree of height \\(\\log_2 n\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef log_recur(n: float) -> int:\n \"\"\"\u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\"\"\"\n if n <= 1:\n return 0\n return log_recur(n / 2) + 1\n
time_complexity.cpp/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint logRecur(float n) {\n if (n <= 1)\n return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.java/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint logRecur(float n) {\n if (n <= 1)\n return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.cs/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint LogRecur(float n) {\n if (n <= 1) return 0;\n return LogRecur(n / 2) + 1;\n}\n
time_complexity.go/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09*/\nfunc logRecur(n float64) int {\n if n <= 1 {\n return 0\n }\n return logRecur(n/2) + 1\n}\n
time_complexity.swift/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc logRecur(n: Double) -> Int {\n if n <= 1 {\n return 0\n }\n return logRecur(n: n / 2) + 1\n}\n
time_complexity.js/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction logRecur(n) {\n if (n <= 1) return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.ts/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction logRecur(n: number): number {\n if (n <= 1) return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.dart/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint logRecur(num n) {\n if (n <= 1) return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.rs/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfn log_recur(n: f32) -> i32 {\n if n <= 1.0 {\n return 0;\n }\n log_recur(n / 2.0) + 1\n}\n
time_complexity.c/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint logRecur(float n) {\n if (n <= 1)\n return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.zig// \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\nfn logRecur(n: f32) i32 {\n if (n <= 1) return 0;\n return logRecur(n / 2) + 1;\n}\n
Visualizing Code Full Screen >
Logarithmic order is typical in algorithms based on the divide-and-conquer strategy, embodying the \"split into many\" and \"simplify complex problems\" approach. It's slow-growing and is the most ideal time complexity after constant order.
What is the base of \\(O(\\log n)\\)?
Technically, \"splitting into \\(m\\)\" corresponds to a time complexity of \\(O(\\log_m n)\\). Using the logarithm base change formula, we can equate different logarithmic complexities:
\\[ O(\\log_m n) = O(\\log_k n / \\log_k m) = O(\\log_k n) \\]This means the base \\(m\\) can be changed without affecting the complexity. Therefore, we often omit the base \\(m\\) and simply denote logarithmic order as \\(O(\\log n)\\).
"},{"location":"chapter_computational_complexity/time_complexity/#6-linear-logarithmic-order-on-log-n","title":"6. \u00a0 Linear-Logarithmic Order \\(O(n \\log n)\\)","text":"Linear-logarithmic order often appears in nested loops, with the complexities of the two loops being \\(O(\\log n)\\) and \\(O(n)\\) respectively. The related code is as follows:
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef linear_log_recur(n: float) -> int:\n \"\"\"\u7ebf\u6027\u5bf9\u6570\u9636\"\"\"\n if n <= 1:\n return 1\n count: int = linear_log_recur(n // 2) + linear_log_recur(n // 2)\n for _ in range(n):\n count += 1\n return count\n
time_complexity.cpp/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nint linearLogRecur(float n) {\n if (n <= 1)\n return 1;\n int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.java/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nint linearLogRecur(float n) {\n if (n <= 1)\n return 1;\n int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.cs/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nint LinearLogRecur(float n) {\n if (n <= 1) return 1;\n int count = LinearLogRecur(n / 2) + LinearLogRecur(n / 2);\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.go/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nfunc linearLogRecur(n float64) int {\n if n <= 1 {\n return 1\n }\n count := linearLogRecur(n/2) + linearLogRecur(n/2)\n for i := 0.0; i < n; i++ {\n count++\n }\n return count\n}\n
time_complexity.swift/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nfunc linearLogRecur(n: Double) -> Int {\n if n <= 1 {\n return 1\n }\n var count = linearLogRecur(n: n / 2) + linearLogRecur(n: n / 2)\n for _ in stride(from: 0, to: n, by: 1) {\n count += 1\n }\n return count\n}\n
time_complexity.js/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nfunction linearLogRecur(n) {\n if (n <= 1) return 1;\n let count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (let i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.ts/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nfunction linearLogRecur(n: number): number {\n if (n <= 1) return 1;\n let count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (let i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.dart/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nint linearLogRecur(num n) {\n if (n <= 1) return 1;\n int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (var i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.rs/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nfn linear_log_recur(n: f32) -> i32 {\n if n <= 1.0 {\n return 1;\n }\n let mut count = linear_log_recur(n / 2.0) + linear_log_recur(n / 2.0);\n for _ in 0 ..n as i32 {\n count += 1;\n }\n return count\n}\n
time_complexity.c/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nint linearLogRecur(float n) {\n if (n <= 1)\n return 1;\n int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.zig// \u7ebf\u6027\u5bf9\u6570\u9636\nfn linearLogRecur(n: f32) i32 {\n if (n <= 1) return 1;\n var count: i32 = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n var i: f32 = 0;\n while (i < n) : (i += 1) {\n count += 1;\n }\n return count;\n}\n
Visualizing Code Full Screen >
The image below demonstrates how linear-logarithmic order is generated. Each level of a binary tree has \\(n\\) operations, and the tree has \\(\\log_2 n + 1\\) levels, resulting in a time complexity of \\(O(n \\log n)\\).
Figure 2-13 \u00a0 Linear-Logarithmic Order Time Complexity
Mainstream sorting algorithms typically have a time complexity of \\(O(n \\log n)\\), such as quicksort, mergesort, and heapsort.
"},{"location":"chapter_computational_complexity/time_complexity/#7-factorial-order-on","title":"7. \u00a0 Factorial Order \\(O(n!)\\)","text":"Factorial order corresponds to the mathematical problem of \"full permutation.\" Given \\(n\\) distinct elements, the total number of possible permutations is:
\\[ n! = n \\times (n - 1) \\times (n - 2) \\times \\dots \\times 2 \\times 1 \\]Factorials are typically implemented using recursion. As shown in the image and code below, the first level splits into \\(n\\) branches, the second level into \\(n - 1\\) branches, and so on, stopping after the \\(n\\)th level:
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef factorial_recur(n: int) -> int:\n \"\"\"\u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\"\"\"\n if n == 0:\n return 1\n count = 0\n # \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for _ in range(n):\n count += factorial_recur(n - 1)\n return count\n
time_complexity.cpp/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint factorialRecur(int n) {\n if (n == 0)\n return 1;\n int count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (int i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.java/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint factorialRecur(int n) {\n if (n == 0)\n return 1;\n int count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (int i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.cs/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint FactorialRecur(int n) {\n if (n == 0) return 1;\n int count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (int i = 0; i < n; i++) {\n count += FactorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.go/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc factorialRecur(n int) int {\n if n == 0 {\n return 1\n }\n count := 0\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for i := 0; i < n; i++ {\n count += factorialRecur(n - 1)\n }\n return count\n}\n
time_complexity.swift/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc factorialRecur(n: Int) -> Int {\n if n == 0 {\n return 1\n }\n var count = 0\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for _ in 0 ..< n {\n count += factorialRecur(n: n - 1)\n }\n return count\n}\n
time_complexity.js/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction factorialRecur(n) {\n if (n === 0) return 1;\n let count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (let i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.ts/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction factorialRecur(n: number): number {\n if (n === 0) return 1;\n let count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (let i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.dart/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint factorialRecur(int n) {\n if (n == 0) return 1;\n int count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (var i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.rs/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfn factorial_recur(n: i32) -> i32 {\n if n == 0 {\n return 1;\n }\n let mut count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for _ in 0..n {\n count += factorial_recur(n - 1);\n }\n count\n}\n
time_complexity.c/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint factorialRecur(int n) {\n if (n == 0)\n return 1;\n int count = 0;\n for (int i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.zig// \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\nfn factorialRecur(n: i32) i32 {\n if (n == 0) return 1;\n var count: i32 = 0;\n var i: i32 = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n while (i < n) : (i += 1) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
Visualizing Code Full Screen >
Figure 2-14 \u00a0 Factorial Order Time Complexity
Note that factorial order grows even faster than exponential order; it's unacceptable for larger \\(n\\) values.
"},{"location":"chapter_computational_complexity/time_complexity/#235-worst-best-and-average-time-complexities","title":"2.3.5 \u00a0 Worst, Best, and Average Time Complexities","text":"The time efficiency of an algorithm is often not fixed but depends on the distribution of the input data. Assume we have an array nums
of length \\(n\\), consisting of numbers from \\(1\\) to \\(n\\), each appearing only once, but in a randomly shuffled order. The task is to return the index of the element \\(1\\). We can draw the following conclusions:
nums = [?, ?, ..., 1]
, that is, when the last element is \\(1\\), it requires a complete traversal of the array, achieving the worst-case time complexity of \\(O(n)\\).nums = [1, ?, ?, ...]
, that is, when the first element is \\(1\\), no matter the length of the array, no further traversal is needed, achieving the best-case time complexity of \\(\\Omega(1)\\).The \"worst-case time complexity\" corresponds to the asymptotic upper bound, denoted by the big \\(O\\) notation. Correspondingly, the \"best-case time complexity\" corresponds to the asymptotic lower bound, denoted by \\(\\Omega\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig worst_best_time_complexity.pydef random_numbers(n: int) -> list[int]:\n \"\"\"\u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a: 1, 2, ..., n \uff0c\u987a\u5e8f\u88ab\u6253\u4e71\"\"\"\n # \u751f\u6210\u6570\u7ec4 nums =: 1, 2, 3, ..., n\n nums = [i for i in range(1, n + 1)]\n # \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n random.shuffle(nums)\n return nums\n\ndef find_one(nums: list[int]) -> int:\n \"\"\"\u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15\"\"\"\n for i in range(len(nums)):\n # \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n # \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if nums[i] == 1:\n return i\n return -1\n
worst_best_time_complexity.cpp/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nvector<int> randomNumbers(int n) {\n vector<int> nums(n);\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (int i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u4f7f\u7528\u7cfb\u7edf\u65f6\u95f4\u751f\u6210\u968f\u673a\u79cd\u5b50\n unsigned seed = chrono::system_clock::now().time_since_epoch().count();\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n shuffle(nums.begin(), nums.end(), default_random_engine(seed));\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nint findOne(vector<int> &nums) {\n for (int i = 0; i < nums.size(); i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] == 1)\n return i;\n }\n return -1;\n}\n
worst_best_time_complexity.java/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nint[] randomNumbers(int n) {\n Integer[] nums = new Integer[n];\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (int i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n Collections.shuffle(Arrays.asList(nums));\n // Integer[] -> int[]\n int[] res = new int[n];\n for (int i = 0; i < n; i++) {\n res[i] = nums[i];\n }\n return res;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nint findOne(int[] nums) {\n for (int i = 0; i < nums.length; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] == 1)\n return i;\n }\n return -1;\n}\n
worst_best_time_complexity.cs/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nint[] RandomNumbers(int n) {\n int[] nums = new int[n];\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (int i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n for (int i = 0; i < nums.Length; i++) {\n int index = new Random().Next(i, nums.Length);\n (nums[i], nums[index]) = (nums[index], nums[i]);\n }\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nint FindOne(int[] nums) {\n for (int i = 0; i < nums.Length; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] == 1)\n return i;\n }\n return -1;\n}\n
worst_best_time_complexity.go/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nfunc randomNumbers(n int) []int {\n nums := make([]int, n)\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for i := 0; i < n; i++ {\n nums[i] = i + 1\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n rand.Shuffle(len(nums), func(i, j int) {\n nums[i], nums[j] = nums[j], nums[i]\n })\n return nums\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nfunc findOne(nums []int) int {\n for i := 0; i < len(nums); i++ {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if nums[i] == 1 {\n return i\n }\n }\n return -1\n}\n
worst_best_time_complexity.swift/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nfunc randomNumbers(n: Int) -> [Int] {\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n var nums = Array(1 ... n)\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n nums.shuffle()\n return nums\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nfunc findOne(nums: [Int]) -> Int {\n for i in nums.indices {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if nums[i] == 1 {\n return i\n }\n }\n return -1\n}\n
worst_best_time_complexity.js/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nfunction randomNumbers(n) {\n const nums = Array(n);\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (let i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n for (let i = 0; i < n; i++) {\n const r = Math.floor(Math.random() * (i + 1));\n const temp = nums[i];\n nums[i] = nums[r];\n nums[r] = temp;\n }\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nfunction findOne(nums) {\n for (let i = 0; i < nums.length; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] === 1) {\n return i;\n }\n }\n return -1;\n}\n
worst_best_time_complexity.ts/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nfunction randomNumbers(n: number): number[] {\n const nums = Array(n);\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (let i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n for (let i = 0; i < n; i++) {\n const r = Math.floor(Math.random() * (i + 1));\n const temp = nums[i];\n nums[i] = nums[r];\n nums[r] = temp;\n }\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nfunction findOne(nums: number[]): number {\n for (let i = 0; i < nums.length; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] === 1) {\n return i;\n }\n }\n return -1;\n}\n
worst_best_time_complexity.dart/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nList<int> randomNumbers(int n) {\n final nums = List.filled(n, 0);\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (var i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n nums.shuffle();\n\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nint findOne(List<int> nums) {\n for (var i = 0; i < nums.length; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] == 1) return i;\n }\n\n return -1;\n}\n
worst_best_time_complexity.rs/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nfn random_numbers(n: i32) -> Vec<i32> {\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n let mut nums = (1..=n).collect::<Vec<i32>>();\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n nums.shuffle(&mut thread_rng());\n nums\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nfn find_one(nums: &[i32]) -> Option<usize> {\n for i in 0..nums.len() {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if nums[i] == 1 {\n return Some(i);\n }\n }\n None\n}\n
worst_best_time_complexity.c/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nint *randomNumbers(int n) {\n // \u5206\u914d\u5806\u533a\u5185\u5b58\uff08\u521b\u5efa\u4e00\u7ef4\u53ef\u53d8\u957f\u6570\u7ec4\uff1a\u6570\u7ec4\u4e2d\u5143\u7d20\u6570\u91cf\u4e3a n \uff0c\u5143\u7d20\u7c7b\u578b\u4e3a int \uff09\n int *nums = (int *)malloc(n * sizeof(int));\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (int i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n for (int i = n - 1; i > 0; i--) {\n int j = rand() % (i + 1);\n int temp = nums[i];\n nums[i] = nums[j];\n nums[j] = temp;\n }\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nint findOne(int *nums, int n) {\n for (int i = 0; i < n; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] == 1)\n return i;\n }\n return -1;\n}\n
worst_best_time_complexity.zig// \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71\nfn randomNumbers(comptime n: usize) [n]i32 {\n var nums: [n]i32 = undefined;\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (&nums, 0..) |*num, i| {\n num.* = @as(i32, @intCast(i)) + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n const rand = std.crypto.random;\n rand.shuffle(i32, &nums);\n return nums;\n}\n\n// \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15\nfn findOne(nums: []i32) i32 {\n for (nums, 0..) |num, i| {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (num == 1) return @intCast(i);\n }\n return -1;\n}\n
Visualizing Code Full Screen >
It's important to note that the best-case time complexity is rarely used in practice, as it is usually only achievable under very low probabilities and might be misleading. The worst-case time complexity is more practical as it provides a safety value for efficiency, allowing us to confidently use the algorithm.
From the above example, it's clear that both the worst-case and best-case time complexities only occur under \"special data distributions,\" which may have a small probability of occurrence and may not accurately reflect the algorithm's run efficiency. In contrast, the average time complexity can reflect the algorithm's efficiency under random input data, denoted by the \\(\\Theta\\) notation.
For some algorithms, we can simply estimate the average case under a random data distribution. For example, in the aforementioned example, since the input array is shuffled, the probability of element \\(1\\) appearing at any index is equal. Therefore, the average number of loops for the algorithm is half the length of the array \\(n / 2\\), giving an average time complexity of \\(\\Theta(n / 2) = \\Theta(n)\\).
However, calculating the average time complexity for more complex algorithms can be quite difficult, as it's challenging to analyze the overall mathematical expectation under the data distribution. In such cases, we usually use the worst-case time complexity as the standard for judging the efficiency of the algorithm.
Why is the \\(\\Theta\\) symbol rarely seen?
Possibly because the \\(O\\) notation is more commonly spoken, it is often used to represent the average time complexity. However, strictly speaking, this practice is not accurate. In this book and other materials, if you encounter statements like \"average time complexity \\(O(n)\\)\", please understand it directly as \\(\\Theta(n)\\).
"},{"location":"chapter_data_structure/","title":"Chapter 3. \u00a0 Data Structures","text":"Abstract
Data structures serve as a robust and diverse framework.
They offer a blueprint for the orderly organization of data, upon which algorithms come to life.
"},{"location":"chapter_data_structure/#chapter-contents","title":"Chapter Contents","text":"When discussing data in computers, various forms like text, images, videos, voice and 3D models comes to mind. Despite their different organizational forms, they are all composed of various basic data types.
Basic data types are those that the CPU can directly operate on and are directly used in algorithms, mainly including the following.
byte
, short
, int
, long
.float
, double
, used to represent decimals.char
, used to represent letters, punctuation, and even emojis in various languages.bool
, used to represent \"yes\" or \"no\" decisions.Basic data types are stored in computers in binary form. One binary digit is 1 bit. In most modern operating systems, 1 byte consists of 8 bits.
The range of values for basic data types depends on the size of the space they occupy. Below, we take Java as an example.
byte
occupies 1 byte = 8 bits and can represent \\(2^8\\) numbers.int
occupies 4 bytes = 32 bits and can represent \\(2^{32}\\) numbers.The following table lists the space occupied, value range, and default values of various basic data types in Java. While memorizing this table isn't necessary, having a general understanding of it and referencing it when required is recommended.
Table 3-1 \u00a0 Space Occupied and Value Range of Basic Data Types
Type Symbol Space Occupied Minimum Value Maximum Value Default Value Integerbyte
1 byte \\(-2^7\\) (\\(-128\\)) \\(2^7 - 1\\) (\\(127\\)) 0 short
2 bytes \\(-2^{15}\\) \\(2^{15} - 1\\) 0 int
4 bytes \\(-2^{31}\\) \\(2^{31} - 1\\) 0 long
8 bytes \\(-2^{63}\\) \\(2^{63} - 1\\) 0 Float float
4 bytes \\(1.175 \\times 10^{-38}\\) \\(3.403 \\times 10^{38}\\) \\(0.0\\text{f}\\) double
8 bytes \\(2.225 \\times 10^{-308}\\) \\(1.798 \\times 10^{308}\\) 0.0 Char char
2 bytes 0 \\(2^{16} - 1\\) 0 Boolean bool
1 byte \\(\\text{false}\\) \\(\\text{true}\\) \\(\\text{false}\\) Please note that the above table is specific to Java's basic data types. Every programming language has its own data type definitions, which might differ in space occupied, value ranges, and default values.
int
can be of any size, limited only by available memory; the floating-point float
is double precision 64-bit; there is no char
type, as a single character is actually a string str
of length 1.char
in C and C++ is 1 byte, while in most programming languages, it depends on the specific character encoding method, as detailed in the \"Character Encoding\" chapter.So, what is the connection between basic data types and data structures? We know that data structures are ways to organize and store data in computers. The focus here is on \"structure\" rather than \"data\".
If we want to represent \"a row of numbers\", we naturally think of using an array. This is because the linear structure of an array can represent the adjacency and the ordering of the numbers, but whether the stored content is an integer int
, a decimal float
, or a character char
, is irrelevant to the \"data structure\".
In other words, basic data types provide the \"content type\" of data, while data structures provide the \"way of organizing\" data. For example, in the following code, we use the same data structure (array) to store and represent different basic data types, including int
, float
, char
, bool
, etc.
# Using various basic data types to initialize arrays\nnumbers: list[int] = [0] * 5\ndecimals: list[float] = [0.0] * 5\n# Python's characters are actually strings of length 1\ncharacters: list[str] = ['0'] * 5\nbools: list[bool] = [False] * 5\n# Python's lists can freely store various basic data types and object references\ndata = [0, 0.0, 'a', False, ListNode(0)]\n
// Using various basic data types to initialize arrays\nint numbers[5];\nfloat decimals[5];\nchar characters[5];\nbool bools[5];\n
// Using various basic data types to initialize arrays\nint[] numbers = new int[5];\nfloat[] decimals = new float[5];\nchar[] characters = new char[5];\nboolean[] bools = new boolean[5];\n
// Using various basic data types to initialize arrays\nint[] numbers = new int[5];\nfloat[] decimals = new float[5];\nchar[] characters = new char[5];\nbool[] bools = new bool[5];\n
// Using various basic data types to initialize arrays\nvar numbers = [5]int{}\nvar decimals = [5]float64{}\nvar characters = [5]byte{}\nvar bools = [5]bool{}\n
// Using various basic data types to initialize arrays\nlet numbers = Array(repeating: Int(), count: 5)\nlet decimals = Array(repeating: Double(), count: 5)\nlet characters = Array(repeating: Character(\"a\"), count: 5)\nlet bools = Array(repeating: Bool(), count: 5)\n
// JavaScript's arrays can freely store various basic data types and objects\nconst array = [0, 0.0, 'a', false];\n
// Using various basic data types to initialize arrays\nconst numbers: number[] = [];\nconst characters: string[] = [];\nconst bools: boolean[] = [];\n
// Using various basic data types to initialize arrays\nList<int> numbers = List.filled(5, 0);\nList<double> decimals = List.filled(5, 0.0);\nList<String> characters = List.filled(5, 'a');\nList<bool> bools = List.filled(5, false);\n
// Using various basic data types to initialize arrays\nlet numbers: Vec<i32> = vec![0; 5];\nlet decimals: Vec<f32> = vec![0.0, 5];\nlet characters: Vec<char> = vec!['0'; 5];\nlet bools: Vec<bool> = vec![false; 5];\n
// Using various basic data types to initialize arrays\nint numbers[10];\nfloat decimals[10];\nchar characters[10];\nbool bools[10];\n
// Using various basic data types to initialize arrays\nvar numbers: [5]i32 = undefined;\nvar decimals: [5]f32 = undefined;\nvar characters: [5]u8 = undefined;\nvar bools: [5]bool = undefined;\n
"},{"location":"chapter_data_structure/character_encoding/","title":"3.4 \u00a0 Character Encoding *","text":"In computers, all data is stored in binary form, and the character char
is no exception. To represent characters, we need to establish a \"character set\" that defines a one-to-one correspondence between each character and binary numbers. With a character set, computers can convert binary numbers to characters by looking up a table.
The \"ASCII code\" is one of the earliest character sets, officially known as the American Standard Code for Information Interchange. It uses 7 binary digits (the lower 7 bits of a byte) to represent a character, allowing for a maximum of 128 different characters. As shown in the Figure 3-6 , ASCII includes uppercase and lowercase English letters, numbers 0 ~ 9, some punctuation marks, and some control characters (such as newline and tab).
Figure 3-6 \u00a0 ASCII Code
However, ASCII can only represent English characters. With the globalization of computers, a character set called \"EASCII\" was developed to represent more languages. It expands on the 7-bit basis of ASCII to 8 bits, enabling the representation of 256 different characters.
Globally, a series of EASCII character sets for different regions emerged. The first 128 characters of these sets are uniformly ASCII, while the remaining 128 characters are defined differently to cater to various language requirements.
"},{"location":"chapter_data_structure/character_encoding/#342-gbk-character-set","title":"3.4.2 \u00a0 GBK Character Set","text":"Later, it was found that EASCII still could not meet the character requirements of many languages. For instance, there are nearly a hundred thousand Chinese characters, with several thousand used in everyday life. In 1980, China's National Standards Bureau released the \"GB2312\" character set, which included 6763 Chinese characters, essentially meeting the computer processing needs for Chinese.
However, GB2312 could not handle some rare and traditional characters. The \"GBK\" character set, an expansion of GB2312, includes a total of 21886 Chinese characters. In the GBK encoding scheme, ASCII characters are represented with one byte, while Chinese characters use two bytes.
"},{"location":"chapter_data_structure/character_encoding/#343-unicode-character-set","title":"3.4.3 \u00a0 Unicode Character Set","text":"With the rapid development of computer technology and a plethora of character sets and encoding standards, numerous problems arose. On one hand, these character sets generally only defined characters for specific languages and could not function properly in multilingual environments. On the other hand, the existence of multiple character set standards for the same language caused garbled text when information was exchanged between computers using different encoding standards.
Researchers of that era thought: What if we introduced a comprehensive character set that included all languages and symbols worldwide, wouldn't that solve the problems of cross-language environments and garbled text? Driven by this idea, the extensive character set, Unicode, was born.
The Chinese name for \"Unicode\" is \"\u7edf\u4e00\u7801\" (Unified Code), theoretically capable of accommodating over a million characters. It aims to incorporate characters from all over the world into a single set, providing a universal character set for processing and displaying various languages and reducing the issues of garbled text due to different encoding standards.
Since its release in 1991, Unicode has continually expanded to include new languages and characters. As of September 2022, Unicode contains 149,186 characters, including characters, symbols, and even emojis from various languages. In the vast Unicode character set, commonly used characters occupy 2 bytes, while some rare characters take up 3 or even 4 bytes.
Unicode is a universal character set that assigns a number (called a \"code point\") to each character, but it does not specify how these character code points should be stored in a computer. One might ask: When Unicode code points of varying lengths appear in a text, how does the system parse the characters? For example, given a 2-byte code, how does the system determine if it represents a single 2-byte character or two 1-byte characters?
A straightforward solution to this problem is to store all characters as equal-length encodings. As shown in the Figure 3-7 , each character in \"Hello\" occupies 1 byte, while each character in \"\u7b97\u6cd5\" (algorithm) occupies 2 bytes. We could encode all characters in \"Hello \u7b97\u6cd5\" as 2 bytes by padding the higher bits with zeros. This way, the system can parse a character every 2 bytes, recovering the content of the phrase.
Figure 3-7 \u00a0 Unicode Encoding Example
However, as ASCII has shown us, encoding English only requires 1 byte. Using the above approach would double the space occupied by English text compared to ASCII encoding, which is a waste of memory space. Therefore, a more efficient Unicode encoding method is needed.
"},{"location":"chapter_data_structure/character_encoding/#344-utf-8-encoding","title":"3.4.4 \u00a0 UTF-8 Encoding","text":"Currently, UTF-8 has become the most widely used Unicode encoding method internationally. It is a variable-length encoding, using 1 to 4 bytes to represent a character, depending on the complexity of the character. ASCII characters need only 1 byte, Latin and Greek letters require 2 bytes, commonly used Chinese characters need 3 bytes, and some other rare characters need 4 bytes.
The encoding rules for UTF-8 are not complex and can be divided into two cases:
The Figure 3-8 shows the UTF-8 encoding for \"Hello\u7b97\u6cd5\". It can be observed that since the highest \\(n\\) bits are set to \\(1\\), the system can determine the length of the character as \\(n\\) by counting the number of highest bits set to \\(1\\).
But why set the highest 2 bits of the remaining bytes to \\(10\\)? Actually, this \\(10\\) serves as a kind of checksum. If the system starts parsing text from an incorrect byte, the \\(10\\) at the beginning of the byte can help the system quickly detect an anomaly.
The reason for using \\(10\\) as a checksum is that, under UTF-8 encoding rules, it's impossible for the highest two bits of a character to be \\(10\\). This can be proven by contradiction: If the highest two bits of a character are \\(10\\), it indicates that the character's length is \\(1\\), corresponding to ASCII. However, the highest bit of an ASCII character should be \\(0\\), contradicting the assumption.
Figure 3-8 \u00a0 UTF-8 Encoding Example
Apart from UTF-8, other common encoding methods include:
From the perspective of storage space, UTF-8 is highly efficient for representing English characters, requiring only 1 byte; UTF-16 might be more efficient for encoding some non-English characters (like Chinese), as it requires only 2 bytes, while UTF-8 might need 3 bytes.
From a compatibility standpoint, UTF-8 is the most versatile, with many tools and libraries supporting UTF-8 as a priority.
"},{"location":"chapter_data_structure/character_encoding/#345-character-encoding-in-programming-languages","title":"3.4.5 \u00a0 Character Encoding in Programming Languages","text":"In many classic programming languages, strings during program execution are encoded using fixed-length encodings like UTF-16 or UTF-32. This allows strings to be treated as arrays, offering several advantages:
The design of character encoding schemes in programming languages is an interesting topic involving various factors:
String
type uses UTF-16 encoding, with each character occupying 2 bytes. This was based on the initial belief that 16 bits were sufficient to represent all possible characters, a judgment later proven incorrect. As the Unicode standard expanded beyond 16 bits, characters in Java may now be represented by a pair of 16-bit values, known as \u201csurrogate pairs.\u201dDue to the underestimation of character counts, these languages had to resort to using \"surrogate pairs\" to represent Unicode characters exceeding 16 bits. This approach has its drawbacks: strings containing surrogate pairs may have characters occupying 2 or 4 bytes, losing the advantage of fixed-length encoding, and handling surrogate pairs adds to the complexity and debugging difficulty of programming.
Owing to these reasons, some programming languages have adopted different encoding schemes:
str
type uses Unicode encoding with a flexible representation where the storage length of characters depends on the largest Unicode code point in the string. If all characters are ASCII, each character occupies 1 byte; if characters exceed ASCII but are within the Basic Multilingual Plane (BMP), each occupies 2 bytes; if characters exceed the BMP, each occupies 4 bytes.string
type internally uses UTF-8 encoding. Go also provides the rune
type for representing individual Unicode code points.str
and String
types use UTF-8 encoding internally. Rust also offers the char
type for individual Unicode code points.It\u2019s important to note that the above discussion pertains to how strings are stored in programming languages, which is a different issue from how strings are stored in files or transmitted over networks. For file storage or network transmission, strings are usually encoded in UTF-8 format for optimal compatibility and space efficiency.
"},{"location":"chapter_data_structure/classification_of_data_structure/","title":"3.1 \u00a0 Classification of Data Structures","text":"Common data structures include arrays, linked lists, stacks, queues, hash tables, trees, heaps, and graphs. They can be classified into \"logical structure\" and \"physical structure\".
"},{"location":"chapter_data_structure/classification_of_data_structure/#311-logical-structure-linear-and-non-linear","title":"3.1.1 \u00a0 Logical Structure: Linear and Non-Linear","text":"The logical structures reveal the logical relationships between data elements. In arrays and linked lists, data are arranged in a specific sequence, demonstrating the linear relationship between data; while in trees, data are arranged hierarchically from the top down, showing the derived relationship between \"ancestors\" and \"descendants\"; and graphs are composed of nodes and edges, reflecting the intricate network relationship.
As shown in the Figure 3-1 , logical structures can be divided into two major categories: \"linear\" and \"non-linear\". Linear structures are more intuitive, indicating data is arranged linearly in logical relationships; non-linear structures, conversely, are arranged non-linearly.
Figure 3-1 \u00a0 Linear and Non-Linear Data Structures
Non-linear data structures can be further divided into tree structures and network structures.
During the execution of an algorithm, the data being processed is stored in memory. The Figure 3-2 shows a computer memory stick where each black square is a physical memory space. We can think of memory as a vast Excel spreadsheet, with each cell capable of storing a certain amount of data.
The system accesses the data at the target location by means of a memory address. As shown in the Figure 3-2 , the computer assigns a unique identifier to each cell in the table according to specific rules, ensuring that each memory space has a unique memory address. With these addresses, the program can access the data stored in memory.
Figure 3-2 \u00a0 Memory Stick, Memory Spaces, Memory Addresses
Tip
It's worth noting that comparing memory to an Excel spreadsheet is a simplified analogy. The actual working mechanism of memory is more complex, involving concepts like address space, memory management, cache mechanisms, virtual memory, and physical memory.
Memory is a shared resource for all programs. When a block of memory is occupied by one program, it cannot be simultaneously used by other programs. Therefore, considering memory resources is crucial in designing data structures and algorithms. For instance, the algorithm's peak memory usage should not exceed the remaining free memory of the system; if there is a lack of contiguous memory blocks, then the data structure chosen must be able to be stored in non-contiguous memory blocks.
As illustrated in the Figure 3-3 , the physical structure reflects the way data is stored in computer memory and it can be divided into contiguous space storage (arrays) and non-contiguous space storage (linked lists). The two types of physical structures exhibit complementary characteristics in terms of time efficiency and space efficiency.
Figure 3-3 \u00a0 Contiguous Space Storage and Dispersed Space Storage
It is worth noting that all data structures are implemented based on arrays, linked lists, or a combination of both. For example, stacks and queues can be implemented using either arrays or linked lists; while implementations of hash tables may involve both arrays and linked lists. - Array-based implementations: Stacks, Queues, Hash Tables, Trees, Heaps, Graphs, Matrices, Tensors (arrays with dimensions \\(\\geq 3\\)). - Linked-list-based implementations: Stacks, Queues, Hash Tables, Trees, Heaps, Graphs, etc.
Data structures implemented based on arrays are also called \u201cStatic Data Structures,\u201d meaning their length cannot be changed after initialization. Conversely, those based on linked lists are called \u201cDynamic Data Structures,\u201d which can still adjust their size during program execution.
Tip
If you find it challenging to comprehend the physical structure, it is recommended that you read the next chapter, \"Arrays and Linked Lists,\" and revisit this section later.
"},{"location":"chapter_data_structure/number_encoding/","title":"3.3 \u00a0 Number Encoding *","text":"Note
In this book, chapters marked with an asterisk '*' are optional readings. If you are short on time or find them challenging, you may skip these initially and return to them after completing the essential chapters.
"},{"location":"chapter_data_structure/number_encoding/#331-integer-encoding","title":"3.3.1 \u00a0 Integer Encoding","text":"In the table from the previous section, we observed that all integer types can represent one more negative number than positive numbers, such as the byte
range of \\([-128, 127]\\). This phenomenon seems counterintuitive, and its underlying reason involves knowledge of sign-magnitude, one's complement, and two's complement encoding.
Firstly, it's important to note that numbers are stored in computers using the two's complement form. Before analyzing why this is the case, let's define these three encoding methods:
The following diagram illustrates the conversions among sign-magnitude, one's complement, and two's complement:
Figure 3-4 \u00a0 Conversions between Sign-Magnitude, One's Complement, and Two's Complement
Although sign-magnitude is the most intuitive, it has limitations. For one, negative numbers in sign-magnitude cannot be directly used in calculations. For example, in sign-magnitude, calculating \\(1 + (-2)\\) results in \\(-3\\), which is incorrect.
\\[ \\begin{aligned} & 1 + (-2) \\newline & \\rightarrow 0000 \\; 0001 + 1000 \\; 0010 \\newline & = 1000 \\; 0011 \\newline & \\rightarrow -3 \\end{aligned} \\]To address this, computers introduced the one's complement. If we convert to one's complement and calculate \\(1 + (-2)\\), then convert the result back to sign-magnitude, we get the correct result of \\(-1\\).
\\[ \\begin{aligned} & 1 + (-2) \\newline & \\rightarrow 0000 \\; 0001 \\; \\text{(Sign-magnitude)} + 1000 \\; 0010 \\; \\text{(Sign-magnitude)} \\newline & = 0000 \\; 0001 \\; \\text{(One's complement)} + 1111 \\; 1101 \\; \\text{(One's complement)} \\newline & = 1111 \\; 1110 \\; \\text{(One's complement)} \\newline & = 1000 \\; 0001 \\; \\text{(Sign-magnitude)} \\newline & \\rightarrow -1 \\end{aligned} \\]Additionally, there are two representations of zero in sign-magnitude: \\(+0\\) and \\(-0\\). This means two different binary encodings for zero, which could lead to ambiguity. For example, in conditional checks, not differentiating between positive and negative zero might result in incorrect outcomes. Addressing this ambiguity would require additional checks, potentially reducing computational efficiency.
\\[ \\begin{aligned} +0 & \\rightarrow 0000 \\; 0000 \\newline -0 & \\rightarrow 1000 \\; 0000 \\end{aligned} \\]Like sign-magnitude, one's complement also suffers from the positive and negative zero ambiguity. Therefore, computers further introduced the two's complement. Let's observe the conversion process for negative zero in sign-magnitude, one's complement, and two's complement:
\\[ \\begin{aligned} -0 \\rightarrow \\; & 1000 \\; 0000 \\; \\text{(Sign-magnitude)} \\newline = \\; & 1111 \\; 1111 \\; \\text{(One's complement)} \\newline = 1 \\; & 0000 \\; 0000 \\; \\text{(Two's complement)} \\newline \\end{aligned} \\]Adding \\(1\\) to the one's complement of negative zero produces a carry, but with byte
length being only 8 bits, the carried-over \\(1\\) to the 9th bit is discarded. Therefore, the two's complement of negative zero is \\(0000 \\; 0000\\), the same as positive zero, thus resolving the ambiguity.
One last puzzle is the \\([-128, 127]\\) range for byte
, with an additional negative number, \\(-128\\). We observe that for the interval \\([-127, +127]\\), all integers have corresponding sign-magnitude, one's complement, and two's complement, allowing for mutual conversion between them.
However, the two's complement \\(1000 \\; 0000\\) is an exception without a corresponding sign-magnitude. According to the conversion method, its sign-magnitude would be \\(0000 \\; 0000\\), indicating zero. This presents a contradiction because its two's complement should represent itself. Computers designate this special two's complement \\(1000 \\; 0000\\) as representing \\(-128\\). In fact, the calculation of \\((-1) + (-127)\\) in two's complement results in \\(-128\\).
\\[ \\begin{aligned} & (-127) + (-1) \\newline & \\rightarrow 1111 \\; 1111 \\; \\text{(Sign-magnitude)} + 1000 \\; 0001 \\; \\text{(Sign-magnitude)} \\newline & = 1000 \\; 0000 \\; \\text{(One's complement)} + 1111 \\; 1110 \\; \\text{(One's complement)} \\newline & = 1000 \\; 0001 \\; \\text{(Two's complement)} + 1111 \\; 1111 \\; \\text{(Two's complement)} \\newline & = 1000 \\; 0000 \\; \\text{(Two's complement)} \\newline & \\rightarrow -128 \\end{aligned} \\]As you might have noticed, all these calculations are additions, hinting at an important fact: computers' internal hardware circuits are primarily designed around addition operations. This is because addition is simpler to implement in hardware compared to other operations like multiplication, division, and subtraction, allowing for easier parallelization and faster computation.
It's important to note that this doesn't mean computers can only perform addition. By combining addition with basic logical operations, computers can execute a variety of other mathematical operations. For example, the subtraction \\(a - b\\) can be translated into \\(a + (-b)\\); multiplication and division can be translated into multiple additions or subtractions.
We can now summarize the reason for using two's complement in computers: with two's complement representation, computers can use the same circuits and operations to handle both positive and negative number addition, eliminating the need for special hardware circuits for subtraction and avoiding the ambiguity of positive and negative zero. This greatly simplifies hardware design and enhances computational efficiency.
The design of two's complement is quite ingenious, and due to space constraints, we'll stop here. Interested readers are encouraged to explore further.
"},{"location":"chapter_data_structure/number_encoding/#332-floating-point-number-encoding","title":"3.3.2 \u00a0 Floating-Point Number Encoding","text":"You might have noticed something intriguing: despite having the same length of 4 bytes, why does a float
have a much larger range of values compared to an int
? This seems counterintuitive, as one would expect the range to shrink for float
since it needs to represent fractions.
In fact, this is due to the different representation method used by floating-point numbers (float
). Let's consider a 32-bit binary number as:
According to the IEEE 754 standard, a 32-bit float
consists of the following three parts:
The value of a binary float
number is calculated as:
Converted to a decimal formula, this becomes:
\\[ \\text{val} = (-1)^{\\mathrm{S}} \\times 2^{\\mathrm{E} - 127} \\times (1 + \\mathrm{N}) \\]The range of each component is:
\\[ \\begin{aligned} \\mathrm{S} \\in & \\{ 0, 1\\}, \\quad \\mathrm{E} \\in \\{ 1, 2, \\dots, 254 \\} \\newline (1 + \\mathrm{N}) = & (1 + \\sum_{i=1}^{23} b_{23-i} \\times 2^{-i}) \\subset [1, 2 - 2^{-23}] \\end{aligned} \\]Figure 3-5 \u00a0 Example Calculation of a float in IEEE 754 Standard
Observing the diagram, given an example data \\(\\mathrm{S} = 0\\), \\(\\mathrm{E} = 124\\), \\(\\mathrm{N} = 2^{-2} + 2^{-3} = 0.375\\), we have:
\\[ \\text{val} = (-1)^0 \\times 2^{124 - 127} \\times (1 + 0.375) = 0.171875 \\]Now we can answer the initial question: The representation of float
includes an exponent bit, leading to a much larger range than int
. Based on the above calculation, the maximum positive number representable by float
is approximately \\(2^{254 - 127} \\times (2 - 2^{-23}) \\approx 3.4 \\times 10^{38}\\), and the minimum negative number is obtained by switching the sign bit.
However, the trade-off for float
's expanded range is a sacrifice in precision. The integer type int
uses all 32 bits to represent the number, with values evenly distributed; but due to the exponent bit, the larger the value of a float
, the greater the difference between adjacent numbers.
As shown in the Table 3-2 , exponent bits \\(E = 0\\) and \\(E = 255\\) have special meanings, used to represent zero, infinity, \\(\\mathrm{NaN}\\), etc.
Table 3-2 \u00a0 Meaning of Exponent Bits
Exponent Bit E Fraction Bit \\(\\mathrm{N} = 0\\) Fraction Bit \\(\\mathrm{N} \\ne 0\\) Calculation Formula \\(0\\) \\(\\pm 0\\) Subnormal Numbers \\((-1)^{\\mathrm{S}} \\times 2^{-126} \\times (0.\\mathrm{N})\\) \\(1, 2, \\dots, 254\\) Normal Numbers Normal Numbers \\((-1)^{\\mathrm{S}} \\times 2^{(\\mathrm{E} -127)} \\times (1.\\mathrm{N})\\) \\(255\\) \\(\\pm \\infty\\) \\(\\mathrm{NaN}\\)It's worth noting that subnormal numbers significantly improve the precision of floating-point numbers. The smallest positive normal number is \\(2^{-126}\\), and the smallest positive subnormal number is \\(2^{-126} \\times 2^{-23}\\).
Double-precision double
also uses a similar representation method to float
, which is not elaborated here for brevity.
byte
, short
, int
, long
), floating-point numbers (float
, double
), characters (char
), and booleans (boolean
). Their range depends on the size of the space occupied and the representation method.Q: Why does a hash table contain both linear and non-linear data structures?
The underlying structure of a hash table is an array. To resolve hash collisions, we may use \"chaining\": each bucket in the array points to a linked list, which, when exceeding a certain threshold, might be transformed into a tree (usually a red-black tree). From a storage perspective, the foundation of a hash table is an array, where each bucket slot might contain a value, a linked list, or a tree. Therefore, hash tables may contain both linear data structures (arrays, linked lists) and non-linear data structures (trees).
Q: Is the length of the char
type 1 byte?
The length of the char
type is determined by the encoding method used by the programming language. For example, Java, JavaScript, TypeScript, and C# all use UTF-16 encoding (to save Unicode code points), so the length of the char type is 2 bytes.
Q: Is there ambiguity in calling data structures based on arrays \"static data structures\"? Because operations like push and pop on stacks are \"dynamic\".
While stacks indeed allow for dynamic data operations, the data structure itself remains \"static\" (with unchangeable length). Even though data structures based on arrays can dynamically add or remove elements, their capacity is fixed. If the data volume exceeds the pre-allocated size, a new, larger array needs to be created, and the contents of the old array copied into it.
Q: When building stacks (queues) without specifying their size, why are they considered \"static data structures\"?
In high-level programming languages, we don't need to manually specify the initial capacity of stacks (queues); this task is automatically handled internally by the class. For example, the initial capacity of Java's ArrayList is usually 10. Furthermore, the expansion operation is also implemented automatically. See the subsequent \"List\" chapter for details.
"},{"location":"chapter_introduction/","title":"Chapter 1. \u00a0 Introduction to Algorithms","text":"Abstract
A graceful maiden dances, intertwined with the data, her skirt swaying to the melody of algorithms.
She invites you to a dance, follow her steps, and enter the world of algorithms full of logic and beauty.
"},{"location":"chapter_introduction/#chapter-contents","title":"Chapter Contents","text":"When we hear the word \"algorithm,\" we naturally think of mathematics. However, many algorithms do not involve complex mathematics but rely more on basic logic, which can be seen everywhere in our daily lives.
Before formally discussing algorithms, there's an interesting fact worth sharing: you have already unconsciously learned many algorithms and have become accustomed to applying them in your daily life. Here, I will give a few specific examples to prove this point.
Example 1: Looking Up a Dictionary. In an English dictionary, words are listed alphabetically. Suppose we're searching for a word that starts with the letter \\(r\\). This is typically done in the following way:
1.
and 2.
until you find the page where the word starts with \\(r\\).Figure 1-1 \u00a0 Process of Looking Up a Dictionary
This essential skill for elementary students, looking up a dictionary, is actually the famous \"Binary Search\" algorithm. From a data structure perspective, we can consider the dictionary as a sorted \"array\"; from an algorithmic perspective, the series of actions taken to look up a word in the dictionary can be viewed as \"Binary Search.\"
Example 2: Organizing Playing Cards. When playing cards, we need to arrange the cards in our hand in ascending order, as shown in the following process.
2.
until all cards are in order.Figure 1-2 \u00a0 Playing Cards Sorting Process
The above method of organizing playing cards is essentially the \"Insertion Sort\" algorithm, which is very efficient for small datasets. Many programming languages' sorting functions include the insertion sort.
Example 3: Making Change. Suppose we buy goods worth \\(69\\) yuan at a supermarket and give the cashier \\(100\\) yuan, then the cashier needs to give us \\(31\\) yuan in change. They would naturally complete the thought process as shown below.
Figure 1-3 \u00a0 Change making process
In the above steps, we make the best choice at each step (using the largest denomination possible), ultimately resulting in a feasible change-making plan. From the perspective of data structures and algorithms, this method is essentially a \"Greedy\" algorithm.
From cooking a meal to interstellar travel, almost all problem-solving involves algorithms. The advent of computers allows us to store data structures in memory and write code to call the CPU and GPU to execute algorithms. In this way, we can transfer real-life problems to computers, solving various complex issues more efficiently.
Tip
If concepts such as data structures, algorithms, arrays, and binary search still seem somewhat obsecure, I encourage you to continue reading. This book will gently guide you into the realm of understanding data structures and algorithms.
"},{"location":"chapter_introduction/summary/","title":"1.3 \u00a0 Summary","text":"An \"algorithm\" is a set of instructions or steps to solve a specific problem within a finite amount of time. It has the following characteristics:
A \"data structure\" is a way of organizing and storing data in a computer, with the following design goals:
Designing data structures is a balancing act, often requiring trade-offs. If you want to improve in one aspect, you often need to compromise in another. Here are two examples:
As shown in the Figure 1-4 , data structures and algorithms are highly related and closely integrated, specifically in the following three aspects:
Figure 1-4 \u00a0 Relationship between data structures and algorithms
Data structures and algorithms can be likened to a set of building blocks, as illustrated in the Figure 1-5 . A building block set includes numerous pieces, accompanied by detailed assembly instructions. Following these instructions step by step allows us to construct an intricate block model.
Figure 1-5 \u00a0 Assembling blocks
The detailed correspondence between the two is shown in the Table 1-1 .
Table 1-1 \u00a0 Comparing Data Structures and Algorithms to Building Blocks
Data Structures and Algorithms Building Blocks Input data Unassembled blocks Data structure Organization of blocks, including shape, size, connections, etc Algorithm A series of steps to assemble the blocks into the desired shape Output data Completed Block modelIt's worth noting that data structures and algorithms are independent of programming languages. For this reason, this book is able to provide implementations in multiple programming languages.
Conventional Abbreviation
In real-life discussions, we often refer to \"Data Structures and Algorithms\" simply as \"Algorithms\". For example, the well-known LeetCode algorithm problems actually test both data structure and algorithm knowledge.
"},{"location":"chapter_preface/","title":"Chapter 0. \u00a0 Preface","text":"Abstract
Algorithms are like a beautiful symphony, with each line of code flowing like a rhythm.
May this book ring softly in your mind, leaving a unique and profound melody.
"},{"location":"chapter_preface/#chapter-contents","title":"Chapter Contents","text":"This open-source project aims to create a free, and beginner-friendly crash course on data structures and algorithms.
If you are new to algorithms with limited exposure, or you have accumulated some experience in algorithms, but you only have a vague understanding of data structures and algorithms, and you are constantly jumping between \"yep\" and \"hmm\", then this book is for you!
If you have already accumulated a certain amount of problem-solving experience, and are familiar with most types of problems, then this book can help you review and organize your algorithm knowledge system. The repository's source code can be used as a \"problem-solving toolkit\" or an \"algorithm cheat sheet\".
If you are an algorithm expert, we look forward to receiving your valuable suggestions, or join us and collaborate.
Prerequisites
You should know how to write and read simple code in at least one programming language.
"},{"location":"chapter_preface/about_the_book/#012-content-structure","title":"0.1.2 \u00a0 Content Structure","text":"The main content of the book is shown in the following figure.
Figure 0-1 \u00a0 Main Content of the Book
"},{"location":"chapter_preface/about_the_book/#013-acknowledgements","title":"0.1.3 \u00a0 Acknowledgements","text":"This book is continuously improved with the joint efforts of many contributors from the open-source community. Thanks to each writer who invested their time and energy, listed in the order generated by GitHub: krahets, codingonion, nuomi1, Gonglja, Reanon, justin-tse, danielsss, hpstory, S-N-O-R-L-A-X, night-cruise, msk397, gvenusleo, RiverTwilight, gyt95, zhuoqinyue, Zuoxun, Xia-Sang, mingXta, FangYuan33, GN-Yu, IsChristina, xBLACKICEx, guowei-gong, Cathay-Chen, mgisr, JoseHung, qualifier1024, pengchzn, Guanngxu, longsizhuo, L-Super, what-is-me, yuan0221, lhxsm, Slone123c, WSL0809, longranger2, theNefelibatas, xiongsp, JeffersonHuang, hongyun-robot, K3v123, yuelinxin, a16su, gaofer, malone6, Wonderdch, xjr7670, DullSword, Horbin-Magician, NI-SW, reeswell, XC-Zero, XiaChuerwu, yd-j, iron-irax, huawuque404, MolDuM, Nigh, KorsChen, foursevenlove, 52coder, bubble9um, youshaoXG, curly210102, gltianwen, fanchenggang, Transmigration-zhou, FloranceYeh, FreddieLi, ShiMaRing, lipusheng, Javesun99, JackYang-hellobobo, shanghai-Jerry, 0130w, Keynman, psychelzh, logan-qiu, ZnYang2018, MwumLi, 1ch0, Phoenix0415, qingpeng9802, Richard-Zhang1019, QiLOL, Suremotoo, Turing-1024-Lee, Evilrabbit520, GaochaoZhu, ZJKung, linzeyan, hezhizhen, ZongYangL, beintentional, czruby, coderlef, dshlstarr, szu17dmy, fbigm, gledfish, hts0000, boloboloda, iStig, jiaxianhua, wenjianmin, keshida, kilikilikid, lclc6, lwbaptx, liuxjerry, lucaswangdev, lyl625760, chadyi, noobcodemaker, selear, siqyka, syd168, 4yDX3906, tao363, wangwang105, weibk, yabo083, yi427, yishangzhang, zhouLion, baagod, ElaBosak233, xb534, luluxia, yanedie, thomasq0, YangXuanyi and th1nk3r-ing.
The code review work for this book was completed by codingonion, Gonglja, gvenusleo, hpstory, justin\u2010tse, krahets, night-cruise, nuomi1, and Reanon (listed in alphabetical order). Thanks to them for their time and effort, ensuring the standardization and uniformity of the code in various languages.
Throughout the creation of this book, numerous individuals provided invaluable assistance, including but not limited to:
Throughout the writing journey, I delved into numerous textbooks and articles on data structures and algorithms. These works served as exemplary models, ensuring the accuracy and quality of this book's content. I extend my gratitude to all who preceded me for their invaluable contributions!
This book advocates a combination of hands-on and minds-on learning, inspired in this regard by \"Dive into Deep Learning\". I highly recommend this excellent book to all readers.
Heartfelt thanks to my parents, whose ongoing support and encouragement have allowed me to do this interesting work.
"},{"location":"chapter_preface/suggestions/","title":"0.2 \u00a0 How to Read","text":"Tip
For the best reading experience, it is recommended that you read through this section.
"},{"location":"chapter_preface/suggestions/#021-writing-conventions","title":"0.2.1 \u00a0 Writing Conventions","text":"\"\"\"Header comments for labeling functions, classes, test samples, etc\"\"\"\"\n\n# Comments for explaining details\n\n\"\"\"\nMultiline\ncomments\n\"\"\"\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
// Header comments for labeling functions, classes, test samples, etc\n\n// Comments for explaining details.\n\n// Multiline\n// comments\n
"},{"location":"chapter_preface/suggestions/#022-efficient-learning-via-animated-illustrations","title":"0.2.2 \u00a0 Efficient Learning via Animated Illustrations","text":"Compared with text, videos and pictures have a higher density of information and are more structured, making them easier to understand. In this book, key and difficult concepts are mainly presented through animations and illustrations, with text serving as explanations and supplements.
When encountering content with animations or illustrations as shown in the Figure 0-2 , prioritize understanding the figure, with text as supplementary, integrating both for a comprehensive understanding.
Figure 0-2 \u00a0 Animated Illustration Example
"},{"location":"chapter_preface/suggestions/#023-deepen-understanding-through-coding-practice","title":"0.2.3 \u00a0 Deepen Understanding through Coding Practice","text":"The source code of this book is hosted on the GitHub Repository. As shown in the Figure 0-3 , the source code comes with test examples and can be executed with just a single click.
If time permits, it's recommended to type out the code yourself. If pressed for time, at least read and run all the codes.
Compared to just reading code, writing code often yields more learning. Learning by doing is the real way to learn.
Figure 0-3 \u00a0 Running Code Example
Setting up to run the code involves three main steps.
Step 1: Install a local programming environment. Follow the tutorial in the appendix for installation, or skip this step if already installed.
Step 2: Clone or download the code repository. Visit the GitHub Repository.
If Git is installed, use the following command to clone the repository:
git clone https://github.com/krahets/hello-algo.git\n
Alternatively, you can also click the \"Download ZIP\" button at the location shown in the Figure 0-4 to directly download the code as a compressed ZIP file. Then, you can simply extract it locally.
Figure 0-4 \u00a0 Cloning Repository and Downloading Code
Step 3: Run the source code. As shown in the Figure 0-5 , for the code block labeled with the file name at the top, we can find the corresponding source code file in the codes
folder of the repository. These files can be executed with a single click, which will help you save unnecessary debugging time and allow you to focus on learning.
Figure 0-5 \u00a0 Code Block and Corresponding Source Code File
"},{"location":"chapter_preface/suggestions/#024-learning-together-in-discussion","title":"0.2.4 \u00a0 Learning Together in Discussion","text":"While reading this book, please don't skip over the points that you didn't learn. Feel free to post your questions in the comment section. We will be happy to answer them and can usually respond within two days.
As illustrated in the Figure 0-6 , each chapter features a comment section at the bottom. I encourage you to pay attention to these comments. They not only expose you to others' encountered problems, aiding in identifying knowledge gaps and sparking deeper contemplation, but also invite you to generously contribute by answering fellow readers' inquiries, sharing insights, and fostering mutual improvement.
Figure 0-6 \u00a0 Comment Section Example
"},{"location":"chapter_preface/suggestions/#025-algorithm-learning-path","title":"0.2.5 \u00a0 Algorithm Learning Path","text":"Overall, the journey of mastering data structures and algorithms can be divided into three stages:
As shown in the Figure 0-7 , this book mainly covers \u201cStage 1,\u201d aiming to help you more efficiently embark on Stages 2 and 3.
Figure 0-7 \u00a0 Algorithm Learning Path
"},{"location":"chapter_preface/summary/","title":"0.3 \u00a0 Summary","text":"Abstract
Stacks are like stacking cats, while queues are like cats lining up.
They respectively represent the logical relationships of Last-In-First-Out (LIFO) and First-In-First-Out (FIFO).
"},{"location":"chapter_stack_and_queue/#chapter-contents","title":"Chapter Contents","text":"In a regular queue, we can only delete elements from the head or add elements to the tail. As shown in the Figure 5-7 , a \"double-ended queue (deque)\" offers more flexibility, allowing the addition or removal of elements at both the head and the tail.
Figure 5-7 \u00a0 Operations in Double-Ended Queue
"},{"location":"chapter_stack_and_queue/deque/#531-common-operations-in-double-ended-queue","title":"5.3.1 \u00a0 Common Operations in Double-Ended Queue","text":"The common operations in a double-ended queue are listed below, and the specific method names depend on the programming language used.
Table 5-3 \u00a0 Efficiency of Double-Ended Queue Operations
Method Name Description Time ComplexitypushFirst()
Add an element to the front \\(O(1)\\) pushLast()
Add an element to the rear \\(O(1)\\) popFirst()
Remove the front element \\(O(1)\\) popLast()
Remove the rear element \\(O(1)\\) peekFirst()
Access the front element \\(O(1)\\) peekLast()
Access the rear element \\(O(1)\\) Similarly, we can directly use the double-ended queue classes implemented in programming languages:
PythonC++JavaC#GoSwiftJSTSDartRustCZig deque.pyfrom collections import deque\n\n# Initialize the deque\ndeque: deque[int] = deque()\n\n# Enqueue elements\ndeque.append(2) # Add to the rear\ndeque.append(5)\ndeque.append(4)\ndeque.appendleft(3) # Add to the front\ndeque.appendleft(1)\n\n# Access elements\nfront: int = deque[0] # Front element\nrear: int = deque[-1] # Rear element\n\n# Dequeue elements\npop_front: int = deque.popleft() # Front element dequeued\npop_rear: int = deque.pop() # Rear element dequeued\n\n# Get the length of the deque\nsize: int = len(deque)\n\n# Check if the deque is empty\nis_empty: bool = len(deque) == 0\n
deque.cpp/* Initialize the deque */\ndeque<int> deque;\n\n/* Enqueue elements */\ndeque.push_back(2); // Add to the rear\ndeque.push_back(5);\ndeque.push_back(4);\ndeque.push_front(3); // Add to the front\ndeque.push_front(1);\n\n/* Access elements */\nint front = deque.front(); // Front element\nint back = deque.back(); // Rear element\n\n/* Dequeue elements */\ndeque.pop_front(); // Front element dequeued\ndeque.pop_back(); // Rear element dequeued\n\n/* Get the length of the deque */\nint size = deque.size();\n\n/* Check if the deque is empty */\nbool empty = deque.empty();\n
deque.java/* Initialize the deque */\nDeque<Integer> deque = new LinkedList<>();\n\n/* Enqueue elements */\ndeque.offerLast(2); // Add to the rear\ndeque.offerLast(5);\ndeque.offerLast(4);\ndeque.offerFirst(3); // Add to the front\ndeque.offerFirst(1);\n\n/* Access elements */\nint peekFirst = deque.peekFirst(); // Front element\nint peekLast = deque.peekLast(); // Rear element\n\n/* Dequeue elements */\nint popFirst = deque.pollFirst(); // Front element dequeued\nint popLast = deque.pollLast(); // Rear element dequeued\n\n/* Get the length of the deque */\nint size = deque.size();\n\n/* Check if the deque is empty */\nboolean isEmpty = deque.isEmpty();\n
deque.cs/* Initialize the deque */\n// In C#, LinkedList is used as a deque\nLinkedList<int> deque = new();\n\n/* Enqueue elements */\ndeque.AddLast(2); // Add to the rear\ndeque.AddLast(5);\ndeque.AddLast(4);\ndeque.AddFirst(3); // Add to the front\ndeque.AddFirst(1);\n\n/* Access elements */\nint peekFirst = deque.First.Value; // Front element\nint peekLast = deque.Last.Value; // Rear element\n\n/* Dequeue elements */\ndeque.RemoveFirst(); // Front element dequeued\ndeque.RemoveLast(); // Rear element dequeued\n\n/* Get the length of the deque */\nint size = deque.Count;\n\n/* Check if the deque is empty */\nbool isEmpty = deque.Count == 0;\n
deque_test.go/* Initialize the deque */\n// In Go, use list as a deque\ndeque := list.New()\n\n/* Enqueue elements */\ndeque.PushBack(2) // Add to the rear\ndeque.PushBack(5)\ndeque.PushBack(4)\ndeque.PushFront(3) // Add to the front\ndeque.PushFront(1)\n\n/* Access elements */\nfront := deque.Front() // Front element\nrear := deque.Back() // Rear element\n\n/* Dequeue elements */\ndeque.Remove(front) // Front element dequeued\ndeque.Remove(rear) // Rear element dequeued\n\n/* Get the length of the deque */\nsize := deque.Len()\n\n/* Check if the deque is empty */\nisEmpty := deque.Len() == 0\n
deque.swift/* Initialize the deque */\n// Swift does not have a built-in deque class, so Array can be used as a deque\nvar deque: [Int] = []\n\n/* Enqueue elements */\ndeque.append(2) // Add to the rear\ndeque.append(5)\ndeque.append(4)\ndeque.insert(3, at: 0) // Add to the front\ndeque.insert(1, at: 0)\n\n/* Access elements */\nlet peekFirst = deque.first! // Front element\nlet peekLast = deque.last! // Rear element\n\n/* Dequeue elements */\n// Using Array, popFirst has a complexity of O(n)\nlet popFirst = deque.removeFirst() // Front element dequeued\nlet popLast = deque.removeLast() // Rear element dequeued\n\n/* Get the length of the deque */\nlet size = deque.count\n\n/* Check if the deque is empty */\nlet isEmpty = deque.isEmpty\n
deque.js/* Initialize the deque */\n// JavaScript does not have a built-in deque, so Array is used as a deque\nconst deque = [];\n\n/* Enqueue elements */\ndeque.push(2);\ndeque.push(5);\ndeque.push(4);\n// Note that unshift() has a time complexity of O(n) as it's an array\ndeque.unshift(3);\ndeque.unshift(1);\n\n/* Access elements */\nconst peekFirst = deque[0]; // Front element\nconst peekLast = deque[deque.length - 1]; // Rear element\n\n/* Dequeue elements */\n// Note that shift() has a time complexity of O(n) as it's an array\nconst popFront = deque.shift(); // Front element dequeued\nconst popBack = deque.pop(); // Rear element dequeued\n\n/* Get the length of the deque */\nconst size = deque.length;\n\n/* Check if the deque is empty */\nconst isEmpty = size === 0;\n
deque.ts/* Initialize the deque */\n// TypeScript does not have a built-in deque, so Array is used as a deque\nconst deque: number[] = [];\n\n/* Enqueue elements */\ndeque.push(2);\ndeque.push(5);\ndeque.push(4);\n// Note that unshift() has a time complexity of O(n) as it's an array\ndeque.unshift(3);\ndeque.unshift(1);\n\n/* Access elements */\nconst peekFirst: number = deque[0]; // Front element\nconst peekLast: number = deque[deque.length - 1]; // Rear element\n\n/* Dequeue elements */\n// Note that shift() has a time complexity of O(n) as it's an array\nconst popFront: number = deque.shift() as number; // Front element dequeued\nconst popBack: number = deque.pop() as number; // Rear element dequeued\n\n/* Get the length of the deque */\nconst size: number = deque.length;\n\n/* Check if the deque is empty */\nconst isEmpty: boolean = size === 0;\n
deque.dart/* Initialize the deque */\n// In Dart, Queue is defined as a deque\nQueue<int> deque = Queue<int>();\n\n/* Enqueue elements */\ndeque.addLast(2); // Add to the rear\ndeque.addLast(5);\ndeque.addLast(4);\ndeque.addFirst(3); // Add to the front\ndeque.addFirst(1);\n\n/* Access elements */\nint peekFirst = deque.first; // Front element\nint peekLast = deque.last; // Rear element\n\n/* Dequeue elements */\nint popFirst = deque.removeFirst(); // Front element dequeued\nint popLast = deque.removeLast(); // Rear element dequeued\n\n/* Get the length of the deque */\nint size = deque.length;\n\n/* Check if the deque is empty */\nbool isEmpty = deque.isEmpty;\n
deque.rs/* Initialize the deque */\nlet mut deque: VecDeque<u32> = VecDeque::new();\n\n/* Enqueue elements */\ndeque.push_back(2); // Add to the rear\ndeque.push_back(5);\ndeque.push_back(4);\ndeque.push_front(3); // Add to the front\ndeque.push_front(1);\n\n/* Access elements */\nif let Some(front) = deque.front() { // Front element\n}\nif let Some(rear) = deque.back() { // Rear element\n}\n\n/* Dequeue elements */\nif let Some(pop_front) = deque.pop_front() { // Front element dequeued\n}\nif let Some(pop_rear) = deque.pop_back() { // Rear element dequeued\n}\n\n/* Get the length of the deque */\nlet size = deque.len();\n\n/* Check if the deque is empty */\nlet is_empty = deque.is_empty();\n
deque.c// C does not provide a built-in deque\n
deque.zig\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/deque/#532-implementing-a-double-ended-queue","title":"5.3.2 \u00a0 Implementing a Double-Ended Queue *","text":"The implementation of a double-ended queue is similar to that of a regular queue, with the choice of either linked lists or arrays as the underlying data structure.
"},{"location":"chapter_stack_and_queue/deque/#1-implementation-based-on-doubly-linked-list","title":"1. \u00a0 Implementation Based on Doubly Linked List","text":"Recall from the previous section that we used a regular singly linked list to implement a queue, as it conveniently allows for deleting the head node (corresponding to dequeue operation) and adding new nodes after the tail node (corresponding to enqueue operation).
For a double-ended queue, both the head and the tail can perform enqueue and dequeue operations. In other words, a double-ended queue needs to implement another symmetric direction of operations. For this, we use a \"doubly linked list\" as the underlying data structure of the double-ended queue.
As shown in the Figure 5-8 , we treat the head and tail nodes of the doubly linked list as the front and rear of the double-ended queue, respectively, and implement the functionality to add and remove nodes at both ends.
LinkedListDequepushLast()pushFirst()popLast()popFirst()Figure 5-8 \u00a0 Implementing Double-Ended Queue with Doubly Linked List for Enqueue and Dequeue Operations
The implementation code is as follows:
PythonC++JavaC#GoSwiftJSTSDartRustCZig linkedlist_deque.pyclass ListNode:\n \"\"\"\u53cc\u5411\u94fe\u8868\u8282\u70b9\"\"\"\n\n def __init__(self, val: int):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self.val: int = val\n self.next: ListNode | None = None # \u540e\u7ee7\u8282\u70b9\u5f15\u7528\n self.prev: ListNode | None = None # \u524d\u9a71\u8282\u70b9\u5f15\u7528\n\nclass LinkedListDeque:\n \"\"\"\u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217\"\"\"\n\n def __init__(self):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._front: ListNode | None = None # \u5934\u8282\u70b9 front\n self._rear: ListNode | None = None # \u5c3e\u8282\u70b9 rear\n self._size: int = 0 # \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\"\"\"\n return self._size\n\n def is_empty(self) -> bool:\n \"\"\"\u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a\"\"\"\n return self.size() == 0\n\n def push(self, num: int, is_front: bool):\n \"\"\"\u5165\u961f\u64cd\u4f5c\"\"\"\n node = ListNode(num)\n # \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if self.is_empty():\n self._front = self._rear = node\n # \u961f\u9996\u5165\u961f\u64cd\u4f5c\n elif is_front:\n # \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n self._front.prev = node\n node.next = self._front\n self._front = node # \u66f4\u65b0\u5934\u8282\u70b9\n # \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n else:\n # \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n self._rear.next = node\n node.prev = self._rear\n self._rear = node # \u66f4\u65b0\u5c3e\u8282\u70b9\n self._size += 1 # \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n\n def push_first(self, num: int):\n \"\"\"\u961f\u9996\u5165\u961f\"\"\"\n self.push(num, True)\n\n def push_last(self, num: int):\n \"\"\"\u961f\u5c3e\u5165\u961f\"\"\"\n self.push(num, False)\n\n def pop(self, is_front: bool) -> int:\n \"\"\"\u51fa\u961f\u64cd\u4f5c\"\"\"\n if self.is_empty():\n raise IndexError(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\")\n # \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if is_front:\n val: int = self._front.val # \u6682\u5b58\u5934\u8282\u70b9\u503c\n # \u5220\u9664\u5934\u8282\u70b9\n fnext: ListNode | None = self._front.next\n if fnext != None:\n fnext.prev = None\n self._front.next = None\n self._front = fnext # \u66f4\u65b0\u5934\u8282\u70b9\n # \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n else:\n val: int = self._rear.val # \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n # \u5220\u9664\u5c3e\u8282\u70b9\n rprev: ListNode | None = self._rear.prev\n if rprev != None:\n rprev.next = None\n self._rear.prev = None\n self._rear = rprev # \u66f4\u65b0\u5c3e\u8282\u70b9\n self._size -= 1 # \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val\n\n def pop_first(self) -> int:\n \"\"\"\u961f\u9996\u51fa\u961f\"\"\"\n return self.pop(True)\n\n def pop_last(self) -> int:\n \"\"\"\u961f\u5c3e\u51fa\u961f\"\"\"\n return self.pop(False)\n\n def peek_first(self) -> int:\n \"\"\"\u8bbf\u95ee\u961f\u9996\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\")\n return self._front.val\n\n def peek_last(self) -> int:\n \"\"\"\u8bbf\u95ee\u961f\u5c3e\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\")\n return self._rear.val\n\n def to_array(self) -> list[int]:\n \"\"\"\u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370\"\"\"\n node = self._front\n res = [0] * self.size()\n for i in range(self.size()):\n res[i] = node.val\n node = node.next\n return res\n
linkedlist_deque.cpp/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nstruct DoublyListNode {\n int val; // \u8282\u70b9\u503c\n DoublyListNode *next; // \u540e\u7ee7\u8282\u70b9\u6307\u9488\n DoublyListNode *prev; // \u524d\u9a71\u8282\u70b9\u6307\u9488\n DoublyListNode(int val) : val(val), prev(nullptr), next(nullptr) {\n }\n};\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n private:\n DoublyListNode *front, *rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear\n int queSize = 0; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n public:\n /* \u6784\u9020\u65b9\u6cd5 */\n LinkedListDeque() : front(nullptr), rear(nullptr) {\n }\n\n /* \u6790\u6784\u65b9\u6cd5 */\n ~LinkedListDeque() {\n // \u904d\u5386\u94fe\u8868\u5220\u9664\u8282\u70b9\uff0c\u91ca\u653e\u5185\u5b58\n DoublyListNode *pre, *cur = front;\n while (cur != nullptr) {\n pre = cur;\n cur = cur->next;\n delete pre;\n }\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n void push(int num, bool isFront) {\n DoublyListNode *node = new DoublyListNode(num);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (isEmpty())\n front = rear = node;\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n else if (isFront) {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n front->prev = node;\n node->next = front;\n front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n rear->next = node;\n node->prev = rear;\n rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize++; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n void pushFirst(int num) {\n push(num, true);\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n void pushLast(int num) {\n push(num, false);\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n int pop(bool isFront) {\n if (isEmpty())\n throw out_of_range(\"\u961f\u5217\u4e3a\u7a7a\");\n int val;\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if (isFront) {\n val = front->val; // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n DoublyListNode *fNext = front->next;\n if (fNext != nullptr) {\n fNext->prev = nullptr;\n front->next = nullptr;\n delete front;\n }\n front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n } else {\n val = rear->val; // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n DoublyListNode *rPrev = rear->prev;\n if (rPrev != nullptr) {\n rPrev->next = nullptr;\n rear->prev = nullptr;\n delete rear;\n }\n rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize--; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n }\n\n /* \u961f\u9996\u51fa\u961f */\n int popFirst() {\n return pop(true);\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n int popLast() {\n return pop(false);\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int peekFirst() {\n if (isEmpty())\n throw out_of_range(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\");\n return front->val;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n int peekLast() {\n if (isEmpty())\n throw out_of_range(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\");\n return rear->val;\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n vector<int> toVector() {\n DoublyListNode *node = front;\n vector<int> res(size());\n for (int i = 0; i < res.size(); i++) {\n res[i] = node->val;\n node = node->next;\n }\n return res;\n }\n};\n
linkedlist_deque.java/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode {\n int val; // \u8282\u70b9\u503c\n ListNode next; // \u540e\u7ee7\u8282\u70b9\u5f15\u7528\n ListNode prev; // \u524d\u9a71\u8282\u70b9\u5f15\u7528\n\n ListNode(int val) {\n this.val = val;\n prev = next = null;\n }\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n private ListNode front, rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear\n private int queSize = 0; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n public LinkedListDeque() {\n front = rear = null;\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n public int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public boolean isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n private void push(int num, boolean isFront) {\n ListNode node = new ListNode(num);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (isEmpty())\n front = rear = node;\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n else if (isFront) {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n front.prev = node;\n node.next = front;\n front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n rear.next = node;\n node.prev = rear;\n rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize++; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n public void pushFirst(int num) {\n push(num, true);\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n public void pushLast(int num) {\n push(num, false);\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n private int pop(boolean isFront) {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n int val;\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if (isFront) {\n val = front.val; // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n ListNode fNext = front.next;\n if (fNext != null) {\n fNext.prev = null;\n front.next = null;\n }\n front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n } else {\n val = rear.val; // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n ListNode rPrev = rear.prev;\n if (rPrev != null) {\n rPrev.next = null;\n rear.prev = null;\n }\n rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize--; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n }\n\n /* \u961f\u9996\u51fa\u961f */\n public int popFirst() {\n return pop(true);\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n public int popLast() {\n return pop(false);\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int peekFirst() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return front.val;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n public int peekLast() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return rear.val;\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n public int[] toArray() {\n ListNode node = front;\n int[] res = new int[size()];\n for (int i = 0; i < res.length; i++) {\n res[i] = node.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_deque.cs/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode(int val) {\n public int val = val; // \u8282\u70b9\u503c\n public ListNode? next = null; // \u540e\u7ee7\u8282\u70b9\u5f15\u7528\n public ListNode? prev = null; // \u524d\u9a71\u8282\u70b9\u5f15\u7528\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n ListNode? front, rear; // \u5934\u8282\u70b9 front, \u5c3e\u8282\u70b9 rear\n int queSize = 0; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n public LinkedListDeque() {\n front = null;\n rear = null;\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n public int Size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public bool IsEmpty() {\n return Size() == 0;\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n void Push(int num, bool isFront) {\n ListNode node = new(num);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (IsEmpty()) {\n front = node;\n rear = node;\n }\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n else if (isFront) {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n front!.prev = node;\n node.next = front;\n front = node; // \u66f4\u65b0\u5934\u8282\u70b9 \n }\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n rear!.next = node;\n node.prev = rear;\n rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n\n queSize++; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n public void PushFirst(int num) {\n Push(num, true);\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n public void PushLast(int num) {\n Push(num, false);\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n int? Pop(bool isFront) {\n if (IsEmpty())\n throw new Exception();\n int? val;\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if (isFront) {\n val = front?.val; // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n ListNode? fNext = front?.next;\n if (fNext != null) {\n fNext.prev = null;\n front!.next = null;\n }\n front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n }\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n else {\n val = rear?.val; // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n ListNode? rPrev = rear?.prev;\n if (rPrev != null) {\n rPrev.next = null;\n rear!.prev = null;\n }\n rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n\n queSize--; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n }\n\n /* \u961f\u9996\u51fa\u961f */\n public int? PopFirst() {\n return Pop(true);\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n public int? PopLast() {\n return Pop(false);\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int? PeekFirst() {\n if (IsEmpty())\n throw new Exception();\n return front?.val;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n public int? PeekLast() {\n if (IsEmpty())\n throw new Exception();\n return rear?.val;\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n public int?[] ToArray() {\n ListNode? node = front;\n int?[] res = new int?[Size()];\n for (int i = 0; i < res.Length; i++) {\n res[i] = node?.val;\n node = node?.next;\n }\n\n return res;\n }\n}\n
linkedlist_deque.go/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\ntype linkedListDeque struct {\n // \u4f7f\u7528\u5185\u7f6e\u5305 list\n data *list.List\n}\n\n/* \u521d\u59cb\u5316\u53cc\u7aef\u961f\u5217 */\nfunc newLinkedListDeque() *linkedListDeque {\n return &linkedListDeque{\n data: list.New(),\n }\n}\n\n/* \u961f\u9996\u5143\u7d20\u5165\u961f */\nfunc (s *linkedListDeque) pushFirst(value any) {\n s.data.PushFront(value)\n}\n\n/* \u961f\u5c3e\u5143\u7d20\u5165\u961f */\nfunc (s *linkedListDeque) pushLast(value any) {\n s.data.PushBack(value)\n}\n\n/* \u961f\u9996\u5143\u7d20\u51fa\u961f */\nfunc (s *linkedListDeque) popFirst() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Front()\n s.data.Remove(e)\n return e.Value\n}\n\n/* \u961f\u5c3e\u5143\u7d20\u51fa\u961f */\nfunc (s *linkedListDeque) popLast() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Back()\n s.data.Remove(e)\n return e.Value\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nfunc (s *linkedListDeque) peekFirst() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Front()\n return e.Value\n}\n\n/* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\nfunc (s *linkedListDeque) peekLast() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Back()\n return e.Value\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nfunc (s *linkedListDeque) size() int {\n return s.data.Len()\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nfunc (s *linkedListDeque) isEmpty() bool {\n return s.data.Len() == 0\n}\n\n/* \u83b7\u53d6 List \u7528\u4e8e\u6253\u5370 */\nfunc (s *linkedListDeque) toList() *list.List {\n return s.data\n}\n
linkedlist_deque.swift/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode {\n var val: Int // \u8282\u70b9\u503c\n var next: ListNode? // \u540e\u7ee7\u8282\u70b9\u5f15\u7528\n weak var prev: ListNode? // \u524d\u9a71\u8282\u70b9\u5f15\u7528\n\n init(val: Int) {\n self.val = val\n }\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n private var front: ListNode? // \u5934\u8282\u70b9 front\n private var rear: ListNode? // \u5c3e\u8282\u70b9 rear\n private var queSize: Int // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n init() {\n queSize = 0\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n func size() -> Int {\n queSize\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n func isEmpty() -> Bool {\n size() == 0\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n private func push(num: Int, isFront: Bool) {\n let node = ListNode(val: num)\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if isEmpty() {\n front = node\n rear = node\n }\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n else if isFront {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n front?.prev = node\n node.next = front\n front = node // \u66f4\u65b0\u5934\u8282\u70b9\n }\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n rear?.next = node\n node.prev = rear\n rear = node // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize += 1 // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n func pushFirst(num: Int) {\n push(num: num, isFront: true)\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n func pushLast(num: Int) {\n push(num: num, isFront: false)\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n private func pop(isFront: Bool) -> Int {\n if isEmpty() {\n fatalError(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\")\n }\n let val: Int\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if isFront {\n val = front!.val // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n let fNext = front?.next\n if fNext != nil {\n fNext?.prev = nil\n front?.next = nil\n }\n front = fNext // \u66f4\u65b0\u5934\u8282\u70b9\n }\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n else {\n val = rear!.val // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n let rPrev = rear?.prev\n if rPrev != nil {\n rPrev?.next = nil\n rear?.prev = nil\n }\n rear = rPrev // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize -= 1 // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val\n }\n\n /* \u961f\u9996\u51fa\u961f */\n func popFirst() -> Int {\n pop(isFront: true)\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n func popLast() -> Int {\n pop(isFront: false)\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n func peekFirst() -> Int? {\n isEmpty() ? nil : front?.val\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n func peekLast() -> Int? {\n isEmpty() ? nil : rear?.val\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n func toArray() -> [Int] {\n var node = front\n var res = Array(repeating: 0, count: size())\n for i in res.indices {\n res[i] = node!.val\n node = node?.next\n }\n return res\n }\n}\n
linkedlist_deque.js/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode {\n prev; // \u524d\u9a71\u8282\u70b9\u5f15\u7528 (\u6307\u9488)\n next; // \u540e\u7ee7\u8282\u70b9\u5f15\u7528 (\u6307\u9488)\n val; // \u8282\u70b9\u503c\n\n constructor(val) {\n this.val = val;\n this.next = null;\n this.prev = null;\n }\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n #front; // \u5934\u8282\u70b9 front\n #rear; // \u5c3e\u8282\u70b9 rear\n #queSize; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n constructor() {\n this.#front = null;\n this.#rear = null;\n this.#queSize = 0;\n }\n\n /* \u961f\u5c3e\u5165\u961f\u64cd\u4f5c */\n pushLast(val) {\n const node = new ListNode(val);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (this.#queSize === 0) {\n this.#front = node;\n this.#rear = node;\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n this.#rear.next = node;\n node.prev = this.#rear;\n this.#rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n this.#queSize++;\n }\n\n /* \u961f\u9996\u5165\u961f\u64cd\u4f5c */\n pushFirst(val) {\n const node = new ListNode(val);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (this.#queSize === 0) {\n this.#front = node;\n this.#rear = node;\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n this.#front.prev = node;\n node.next = this.#front;\n this.#front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n }\n this.#queSize++;\n }\n\n /* \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c */\n popLast() {\n if (this.#queSize === 0) {\n return null;\n }\n const value = this.#rear.val; // \u5b58\u50a8\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n let temp = this.#rear.prev;\n if (temp !== null) {\n temp.next = null;\n this.#rear.prev = null;\n }\n this.#rear = temp; // \u66f4\u65b0\u5c3e\u8282\u70b9\n this.#queSize--;\n return value;\n }\n\n /* \u961f\u9996\u51fa\u961f\u64cd\u4f5c */\n popFirst() {\n if (this.#queSize === 0) {\n return null;\n }\n const value = this.#front.val; // \u5b58\u50a8\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n let temp = this.#front.next;\n if (temp !== null) {\n temp.prev = null;\n this.#front.next = null;\n }\n this.#front = temp; // \u66f4\u65b0\u5934\u8282\u70b9\n this.#queSize--;\n return value;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n peekLast() {\n return this.#queSize === 0 ? null : this.#rear.val;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peekFirst() {\n return this.#queSize === 0 ? null : this.#front.val;\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n size() {\n return this.#queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty() {\n return this.#queSize === 0;\n }\n\n /* \u6253\u5370\u53cc\u5411\u961f\u5217 */\n print() {\n const arr = [];\n let temp = this.#front;\n while (temp !== null) {\n arr.push(temp.val);\n temp = temp.next;\n }\n console.log('[' + arr.join(', ') + ']');\n }\n}\n
linkedlist_deque.ts/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode {\n prev: ListNode; // \u524d\u9a71\u8282\u70b9\u5f15\u7528 (\u6307\u9488)\n next: ListNode; // \u540e\u7ee7\u8282\u70b9\u5f15\u7528 (\u6307\u9488)\n val: number; // \u8282\u70b9\u503c\n\n constructor(val: number) {\n this.val = val;\n this.next = null;\n this.prev = null;\n }\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n private front: ListNode; // \u5934\u8282\u70b9 front\n private rear: ListNode; // \u5c3e\u8282\u70b9 rear\n private queSize: number; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n constructor() {\n this.front = null;\n this.rear = null;\n this.queSize = 0;\n }\n\n /* \u961f\u5c3e\u5165\u961f\u64cd\u4f5c */\n pushLast(val: number): void {\n const node: ListNode = new ListNode(val);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (this.queSize === 0) {\n this.front = node;\n this.rear = node;\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n this.rear.next = node;\n node.prev = this.rear;\n this.rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n this.queSize++;\n }\n\n /* \u961f\u9996\u5165\u961f\u64cd\u4f5c */\n pushFirst(val: number): void {\n const node: ListNode = new ListNode(val);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (this.queSize === 0) {\n this.front = node;\n this.rear = node;\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n this.front.prev = node;\n node.next = this.front;\n this.front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n }\n this.queSize++;\n }\n\n /* \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c */\n popLast(): number {\n if (this.queSize === 0) {\n return null;\n }\n const value: number = this.rear.val; // \u5b58\u50a8\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n let temp: ListNode = this.rear.prev;\n if (temp !== null) {\n temp.next = null;\n this.rear.prev = null;\n }\n this.rear = temp; // \u66f4\u65b0\u5c3e\u8282\u70b9\n this.queSize--;\n return value;\n }\n\n /* \u961f\u9996\u51fa\u961f\u64cd\u4f5c */\n popFirst(): number {\n if (this.queSize === 0) {\n return null;\n }\n const value: number = this.front.val; // \u5b58\u50a8\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n let temp: ListNode = this.front.next;\n if (temp !== null) {\n temp.prev = null;\n this.front.next = null;\n }\n this.front = temp; // \u66f4\u65b0\u5934\u8282\u70b9\n this.queSize--;\n return value;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n peekLast(): number {\n return this.queSize === 0 ? null : this.rear.val;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peekFirst(): number {\n return this.queSize === 0 ? null : this.front.val;\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n size(): number {\n return this.queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty(): boolean {\n return this.queSize === 0;\n }\n\n /* \u6253\u5370\u53cc\u5411\u961f\u5217 */\n print(): void {\n const arr: number[] = [];\n let temp: ListNode = this.front;\n while (temp !== null) {\n arr.push(temp.val);\n temp = temp.next;\n }\n console.log('[' + arr.join(', ') + ']');\n }\n}\n
linkedlist_deque.dart/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode {\n int val; // \u8282\u70b9\u503c\n ListNode? next; // \u540e\u7ee7\u8282\u70b9\u5f15\u7528\n ListNode? prev; // \u524d\u9a71\u8282\u70b9\u5f15\u7528\n\n ListNode(this.val, {this.next, this.prev});\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u5bf9\u5217 */\nclass LinkedListDeque {\n late ListNode? _front; // \u5934\u8282\u70b9 _front\n late ListNode? _rear; // \u5c3e\u8282\u70b9 _rear\n int _queSize = 0; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n LinkedListDeque() {\n this._front = null;\n this._rear = null;\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u957f\u5ea6 */\n int size() {\n return this._queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n void push(int _num, bool isFront) {\n final ListNode node = ListNode(_num);\n if (isEmpty()) {\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 _front \u548c _rear \u90fd\u6307\u5411 node\n _front = _rear = node;\n } else if (isFront) {\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n _front!.prev = node;\n node.next = _front;\n _front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n } else {\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n _rear!.next = node;\n node.prev = _rear;\n _rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n _queSize++; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n void pushFirst(int _num) {\n push(_num, true);\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n void pushLast(int _num) {\n push(_num, false);\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n int? pop(bool isFront) {\n // \u82e5\u961f\u5217\u4e3a\u7a7a\uff0c\u76f4\u63a5\u8fd4\u56de null\n if (isEmpty()) {\n return null;\n }\n final int val;\n if (isFront) {\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n val = _front!.val; // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n ListNode? fNext = _front!.next;\n if (fNext != null) {\n fNext.prev = null;\n _front!.next = null;\n }\n _front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n } else {\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n val = _rear!.val; // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n ListNode? rPrev = _rear!.prev;\n if (rPrev != null) {\n rPrev.next = null;\n _rear!.prev = null;\n }\n _rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n _queSize--; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n }\n\n /* \u961f\u9996\u51fa\u961f */\n int? popFirst() {\n return pop(true);\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n int? popLast() {\n return pop(false);\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int? peekFirst() {\n return _front?.val;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n int? peekLast() {\n return _rear?.val;\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n List<int> toArray() {\n ListNode? node = _front;\n final List<int> res = [];\n for (int i = 0; i < _queSize; i++) {\n res.add(node!.val);\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_deque.rs/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\npub struct ListNode<T> {\n pub val: T, // \u8282\u70b9\u503c\n pub next: Option<Rc<RefCell<ListNode<T>>>>, // \u540e\u7ee7\u8282\u70b9\u6307\u9488\n pub prev: Option<Rc<RefCell<ListNode<T>>>>, // \u524d\u9a71\u8282\u70b9\u6307\u9488\n}\n\nimpl<T> ListNode<T> {\n pub fn new(val: T) -> Rc<RefCell<ListNode<T>>> {\n Rc::new(RefCell::new(ListNode {\n val,\n next: None,\n prev: None,\n }))\n }\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\n#[allow(dead_code)]\npub struct LinkedListDeque<T> {\n front: Option<Rc<RefCell<ListNode<T>>>>, // \u5934\u8282\u70b9 front\n rear: Option<Rc<RefCell<ListNode<T>>>>, // \u5c3e\u8282\u70b9 rear \n que_size: usize, // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n}\n\nimpl<T: Copy> LinkedListDeque<T> {\n pub fn new() -> Self {\n Self {\n front: None,\n rear: None,\n que_size: 0, \n }\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n pub fn size(&self) -> usize {\n return self.que_size;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n pub fn is_empty(&self) -> bool {\n return self.size() == 0;\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n pub fn push(&mut self, num: T, is_front: bool) {\n let node = ListNode::new(num);\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n if is_front {\n match self.front.take() {\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n None => {\n self.rear = Some(node.clone());\n self.front = Some(node);\n }\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n Some(old_front) => {\n old_front.borrow_mut().prev = Some(node.clone());\n node.borrow_mut().next = Some(old_front);\n self.front = Some(node); // \u66f4\u65b0\u5934\u8282\u70b9\n }\n }\n } \n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n else {\n match self.rear.take() {\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n None => {\n self.front = Some(node.clone());\n self.rear = Some(node);\n }\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n Some(old_rear) => {\n old_rear.borrow_mut().next = Some(node.clone());\n node.borrow_mut().prev = Some(old_rear);\n self.rear = Some(node); // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n }\n }\n self.que_size += 1; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n pub fn push_first(&mut self, num: T) {\n self.push(num, true);\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n pub fn push_last(&mut self, num: T) {\n self.push(num, false);\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n pub fn pop(&mut self, is_front: bool) -> Option<T> {\n // \u82e5\u961f\u5217\u4e3a\u7a7a\uff0c\u76f4\u63a5\u8fd4\u56de None\n if self.is_empty() { \n return None \n };\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if is_front {\n self.front.take().map(|old_front| {\n match old_front.borrow_mut().next.take() {\n Some(new_front) => {\n new_front.borrow_mut().prev.take();\n self.front = Some(new_front); // \u66f4\u65b0\u5934\u8282\u70b9\n }\n None => {\n self.rear.take();\n }\n }\n self.que_size -= 1; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n Rc::try_unwrap(old_front).ok().unwrap().into_inner().val\n })\n\n } \n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n else {\n self.rear.take().map(|old_rear| {\n match old_rear.borrow_mut().prev.take() {\n Some(new_rear) => {\n new_rear.borrow_mut().next.take();\n self.rear = Some(new_rear); // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n None => {\n self.front.take();\n }\n }\n self.que_size -= 1; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n Rc::try_unwrap(old_rear).ok().unwrap().into_inner().val\n })\n }\n }\n\n /* \u961f\u9996\u51fa\u961f */\n pub fn pop_first(&mut self) -> Option<T> {\n return self.pop(true);\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n pub fn pop_last(&mut self) -> Option<T> {\n return self.pop(false);\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n pub fn peek_first(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {\n self.front.as_ref()\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n pub fn peek_last(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {\n self.rear.as_ref()\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n pub fn to_array(&self, head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {\n if let Some(node) = head {\n let mut nums = self.to_array(node.borrow().next.as_ref());\n nums.insert(0, node.borrow().val);\n return nums;\n }\n return Vec::new();\n }\n}\n
linkedlist_deque.c/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\ntypedef struct DoublyListNode {\n int val; // \u8282\u70b9\u503c\n struct DoublyListNode *next; // \u540e\u7ee7\u8282\u70b9\n struct DoublyListNode *prev; // \u524d\u9a71\u8282\u70b9\n} DoublyListNode;\n\n/* \u6784\u9020\u51fd\u6570 */\nDoublyListNode *newDoublyListNode(int num) {\n DoublyListNode *new = (DoublyListNode *)malloc(sizeof(DoublyListNode));\n new->val = num;\n new->next = NULL;\n new->prev = NULL;\n return new;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delDoublyListNode(DoublyListNode *node) {\n free(node);\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\ntypedef struct {\n DoublyListNode *front, *rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear\n int queSize; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n} LinkedListDeque;\n\n/* \u6784\u9020\u51fd\u6570 */\nLinkedListDeque *newLinkedListDeque() {\n LinkedListDeque *deque = (LinkedListDeque *)malloc(sizeof(LinkedListDeque));\n deque->front = NULL;\n deque->rear = NULL;\n deque->queSize = 0;\n return deque;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delLinkedListdeque(LinkedListDeque *deque) {\n // \u91ca\u653e\u6240\u6709\u8282\u70b9\n for (int i = 0; i < deque->queSize && deque->front != NULL; i++) {\n DoublyListNode *tmp = deque->front;\n deque->front = deque->front->next;\n free(tmp);\n }\n // \u91ca\u653e deque \u7ed3\u6784\u4f53\n free(deque);\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nint size(LinkedListDeque *deque) {\n return deque->queSize;\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nbool empty(LinkedListDeque *deque) {\n return (size(deque) == 0);\n}\n\n/* \u5165\u961f */\nvoid push(LinkedListDeque *deque, int num, bool isFront) {\n DoublyListNode *node = newDoublyListNode(num);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411node\n if (empty(deque)) {\n deque->front = deque->rear = node;\n }\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n else if (isFront) {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n deque->front->prev = node;\n node->next = deque->front;\n deque->front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n }\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n deque->rear->next = node;\n node->prev = deque->rear;\n deque->rear = node;\n }\n deque->queSize++; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n}\n\n/* \u961f\u9996\u5165\u961f */\nvoid pushFirst(LinkedListDeque *deque, int num) {\n push(deque, num, true);\n}\n\n/* \u961f\u5c3e\u5165\u961f */\nvoid pushLast(LinkedListDeque *deque, int num) {\n push(deque, num, false);\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nint peekFirst(LinkedListDeque *deque) {\n assert(size(deque) && deque->front);\n return deque->front->val;\n}\n\n/* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\nint peekLast(LinkedListDeque *deque) {\n assert(size(deque) && deque->rear);\n return deque->rear->val;\n}\n\n/* \u51fa\u961f */\nint pop(LinkedListDeque *deque, bool isFront) {\n if (empty(deque))\n return -1;\n int val;\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if (isFront) {\n val = peekFirst(deque); // \u6682\u5b58\u5934\u8282\u70b9\u503c\n DoublyListNode *fNext = deque->front->next;\n if (fNext) {\n fNext->prev = NULL;\n deque->front->next = NULL;\n delDoublyListNode(deque->front);\n }\n deque->front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n }\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n else {\n val = peekLast(deque); // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n DoublyListNode *rPrev = deque->rear->prev;\n if (rPrev) {\n rPrev->next = NULL;\n deque->rear->prev = NULL;\n delDoublyListNode(deque->rear);\n }\n deque->rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n deque->queSize--; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n}\n\n/* \u961f\u9996\u51fa\u961f */\nint popFirst(LinkedListDeque *deque) {\n return pop(deque, true);\n}\n\n/* \u961f\u5c3e\u51fa\u961f */\nint popLast(LinkedListDeque *deque) {\n return pop(deque, false);\n}\n\n/* \u6253\u5370\u961f\u5217 */\nvoid printLinkedListDeque(LinkedListDeque *deque) {\n int *arr = malloc(sizeof(int) * deque->queSize);\n // \u62f7\u8d1d\u94fe\u8868\u4e2d\u7684\u6570\u636e\u5230\u6570\u7ec4\n int i;\n DoublyListNode *node;\n for (i = 0, node = deque->front; i < deque->queSize; i++) {\n arr[i] = node->val;\n node = node->next;\n }\n printArray(arr, deque->queSize);\n free(arr);\n}\n
linkedlist_deque.zig// \u53cc\u5411\u94fe\u8868\u8282\u70b9\nfn ListNode(comptime T: type) type {\n return struct {\n const Self = @This();\n\n val: T = undefined, // \u8282\u70b9\u503c\n next: ?*Self = null, // \u540e\u7ee7\u8282\u70b9\u6307\u9488\n prev: ?*Self = null, // \u524d\u9a71\u8282\u70b9\u6307\u9488\n\n // Initialize a list node with specific value\n pub fn init(self: *Self, x: i32) void {\n self.val = x;\n self.next = null;\n self.prev = null;\n }\n };\n}\n\n// \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217\nfn LinkedListDeque(comptime T: type) type {\n return struct {\n const Self = @This();\n\n front: ?*ListNode(T) = null, // \u5934\u8282\u70b9 front\n rear: ?*ListNode(T) = null, // \u5c3e\u8282\u70b9 rear\n que_size: usize = 0, // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n mem_arena: ?std.heap.ArenaAllocator = null,\n mem_allocator: std.mem.Allocator = undefined, // \u5185\u5b58\u5206\u914d\u5668\n\n // \u6784\u9020\u51fd\u6570\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u961f\u5217\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator) !void {\n if (self.mem_arena == null) {\n self.mem_arena = std.heap.ArenaAllocator.init(allocator);\n self.mem_allocator = self.mem_arena.?.allocator();\n }\n self.front = null;\n self.rear = null;\n self.que_size = 0;\n }\n\n // \u6790\u6784\u51fd\u6570\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.mem_arena == null) return;\n self.mem_arena.?.deinit();\n }\n\n // \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n pub fn size(self: *Self) usize {\n return self.que_size;\n }\n\n // \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a\n pub fn isEmpty(self: *Self) bool {\n return self.size() == 0;\n }\n\n // \u5165\u961f\u64cd\u4f5c\n pub fn push(self: *Self, num: T, is_front: bool) !void {\n var node = try self.mem_allocator.create(ListNode(T));\n node.init(num);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (self.isEmpty()) {\n self.front = node;\n self.rear = node;\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n } else if (is_front) {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n self.front.?.prev = node;\n node.next = self.front;\n self.front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n self.rear.?.next = node;\n node.prev = self.rear;\n self.rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n self.que_size += 1; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n } \n\n // \u961f\u9996\u5165\u961f\n pub fn pushFirst(self: *Self, num: T) !void {\n try self.push(num, true);\n } \n\n // \u961f\u5c3e\u5165\u961f\n pub fn pushLast(self: *Self, num: T) !void {\n try self.push(num, false);\n } \n\n // \u51fa\u961f\u64cd\u4f5c\n pub fn pop(self: *Self, is_front: bool) T {\n if (self.isEmpty()) @panic(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\");\n var val: T = undefined;\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if (is_front) {\n val = self.front.?.val; // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n var fNext = self.front.?.next;\n if (fNext != null) {\n fNext.?.prev = null;\n self.front.?.next = null;\n }\n self.front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n } else {\n val = self.rear.?.val; // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n var rPrev = self.rear.?.prev;\n if (rPrev != null) {\n rPrev.?.next = null;\n self.rear.?.prev = null;\n }\n self.rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n self.que_size -= 1; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n } \n\n // \u961f\u9996\u51fa\u961f\n pub fn popFirst(self: *Self) T {\n return self.pop(true);\n } \n\n // \u961f\u5c3e\u51fa\u961f\n pub fn popLast(self: *Self) T {\n return self.pop(false);\n } \n\n // \u8bbf\u95ee\u961f\u9996\u5143\u7d20\n pub fn peekFirst(self: *Self) T {\n if (self.isEmpty()) @panic(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\");\n return self.front.?.val;\n } \n\n // \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20\n pub fn peekLast(self: *Self) T {\n if (self.isEmpty()) @panic(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\");\n return self.rear.?.val;\n }\n\n // \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370\n pub fn toArray(self: *Self) ![]T {\n var node = self.front;\n var res = try self.mem_allocator.alloc(T, self.size());\n @memset(res, @as(T, 0));\n var i: usize = 0;\n while (i < res.len) : (i += 1) {\n res[i] = node.?.val;\n node = node.?.next;\n }\n return res;\n }\n };\n}\n
"},{"location":"chapter_stack_and_queue/deque/#2-implementation-based-on-array","title":"2. \u00a0 Implementation Based on Array","text":"As shown in the Figure 5-9 , similar to implementing a queue with an array, we can also use a circular array to implement a double-ended queue.
ArrayDequepushLast()pushFirst()popLast()popFirst()Figure 5-9 \u00a0 Implementing Double-Ended Queue with Array for Enqueue and Dequeue Operations
The implementation only needs to add methods for \"front enqueue\" and \"rear dequeue\":
[file]{array_deque}-[func]{}\n
"},{"location":"chapter_stack_and_queue/deque/#533-applications-of-double-ended-queue","title":"5.3.3 \u00a0 Applications of Double-Ended Queue","text":"The double-ended queue combines the logic of both stacks and queues, thus it can implement all the application scenarios of these two, while offering greater flexibility.
We know that the \"undo\" feature in software is typically implemented using a stack: the system pushes
each change operation onto the stack, and then pops
to implement undoing. However, considering the limitations of system resources, software often restricts the number of undo steps (for example, only allowing the last 50 steps). When the length of the stack exceeds 50, the software needs to perform a deletion operation at the bottom of the stack (the front of the queue). But a regular stack cannot perform this function, which is where a double-ended queue becomes necessary. Note that the core logic of \"undo\" still follows the Last-In-First-Out principle of a stack, but a double-ended queue can more flexibly implement some additional logic.
\"Queue\" is a linear data structure that follows the First-In-First-Out (FIFO) rule. As the name suggests, a queue simulates the phenomenon of lining up, where newcomers join the back of the queue, and people at the front of the queue leave one by one.
As shown in the Figure 5-4 , we call the front of the queue the \"head\" and the back the \"tail.\" The operation of adding elements to the tail of the queue is termed \"enqueue,\" and the operation of removing elements from the head is termed \"dequeue.\"
Figure 5-4 \u00a0 Queue's First-In-First-Out Rule
"},{"location":"chapter_stack_and_queue/queue/#521-common-operations-on-queue","title":"5.2.1 \u00a0 Common Operations on Queue","text":"The common operations on a queue are shown in the Table 5-2 . Note that method names may vary across different programming languages. Here, we adopt the same naming convention as used for stacks.
Table 5-2 \u00a0 Efficiency of Queue Operations
Method Name Description Time Complexitypush()
Enqueue an element, add it to the tail \\(O(1)\\) pop()
Dequeue the head element \\(O(1)\\) peek()
Access the head element \\(O(1)\\) We can directly use the ready-made queue classes in programming languages:
PythonC++JavaC#GoSwiftJSTSDartRustCZig queue.pyfrom collections import deque\n\n# Initialize the queue\n# In Python, we generally use the deque class as a queue\n# Although queue.Queue() is a pure queue class, it's not very user-friendly, so it's not recommended\nque: deque[int] = deque()\n\n# Enqueue elements\nque.append(1)\nque.append(3)\nque.append(2)\nque.append(5)\nque.append(4)\n\n# Access the front element\nfront: int = que[0]\n\n# Dequeue an element\npop: int = que.popleft()\n\n# Get the length of the queue\nsize: int = len(que)\n\n# Check if the queue is empty\nis_empty: bool = len(que) == 0\n
queue.cpp/* Initialize the queue */\nqueue<int> queue;\n\n/* Enqueue elements */\nqueue.push(1);\nqueue.push(3);\nqueue.push(2);\nqueue.push(5);\nqueue.push(4);\n\n/* Access the front element */\nint front = queue.front();\n\n/* Dequeue an element */\nqueue.pop();\n\n/* Get the length of the queue */\nint size = queue.size();\n\n/* Check if the queue is empty */\nbool empty = queue.empty();\n
queue.java/* Initialize the queue */\nQueue<Integer> queue = new LinkedList<>();\n\n/* Enqueue elements */\nqueue.offer(1);\nqueue.offer(3);\nqueue.offer(2);\nqueue.offer(5);\nqueue.offer(4);\n\n/* Access the front element */\nint peek = queue.peek();\n\n/* Dequeue an element */\nint pop = queue.poll();\n\n/* Get the length of the queue */\nint size = queue.size();\n\n/* Check if the queue is empty */\nboolean isEmpty = queue.isEmpty();\n
queue.cs/* Initialize the queue */\nQueue<int> queue = new();\n\n/* Enqueue elements */\nqueue.Enqueue(1);\nqueue.Enqueue(3);\nqueue.Enqueue(2);\nqueue.Enqueue(5);\nqueue.Enqueue(4);\n\n/* Access the front element */\nint peek = queue.Peek();\n\n/* Dequeue an element */\nint pop = queue.Dequeue();\n\n/* Get the length of the queue */\nint size = queue.Count;\n\n/* Check if the queue is empty */\nbool isEmpty = queue.Count == 0;\n
queue_test.go/* Initialize the queue */\n// In Go, use list as a queue\nqueue := list.New()\n\n/* Enqueue elements */\nqueue.PushBack(1)\nqueue.PushBack(3)\nqueue.PushBack(2)\nqueue.PushBack(5)\nqueue.PushBack(4)\n\n/* Access the front element */\npeek := queue.Front()\n\n/* Dequeue an element */\npop := queue.Front()\nqueue.Remove(pop)\n\n/* Get the length of the queue */\nsize := queue.Len()\n\n/* Check if the queue is empty */\nisEmpty := queue.Len() == 0\n
queue.swift/* Initialize the queue */\n// Swift does not have a built-in queue class, so Array can be used as a queue\nvar queue: [Int] = []\n\n/* Enqueue elements */\nqueue.append(1)\nqueue.append(3)\nqueue.append(2)\nqueue.append(5)\nqueue.append(4)\n\n/* Access the front element */\nlet peek = queue.first!\n\n/* Dequeue an element */\n// Since it's an array, removeFirst has a complexity of O(n)\nlet pool = queue.removeFirst()\n\n/* Get the length of the queue */\nlet size = queue.count\n\n/* Check if the queue is empty */\nlet isEmpty = queue.isEmpty\n
queue.js/* Initialize the queue */\n// JavaScript does not have a built-in queue, so Array can be used as a queue\nconst queue = [];\n\n/* Enqueue elements */\nqueue.push(1);\nqueue.push(3);\nqueue.push(2);\nqueue.push(5);\nqueue.push(4);\n\n/* Access the front element */\nconst peek = queue[0];\n\n/* Dequeue an element */\n// Since the underlying structure is an array, shift() method has a time complexity of O(n)\nconst pop = queue.shift();\n\n/* Get the length of the queue */\nconst size = queue.length;\n\n/* Check if the queue is empty */\nconst empty = queue.length === 0;\n
queue.ts/* Initialize the queue */\n// TypeScript does not have a built-in queue, so Array can be used as a queue \nconst queue: number[] = [];\n\n/* Enqueue elements */\nqueue.push(1);\nqueue.push(3);\nqueue.push(2);\nqueue.push(5);\nqueue.push(4);\n\n/* Access the front element */\nconst peek = queue[0];\n\n/* Dequeue an element */\n// Since the underlying structure is an array, shift() method has a time complexity of O(n)\nconst pop = queue.shift();\n\n/* Get the length of the queue */\nconst size = queue.length;\n\n/* Check if the queue is empty */\nconst empty = queue.length === 0;\n
queue.dart/* Initialize the queue */\n// In Dart, the Queue class is a double-ended queue but can be used as a queue\nQueue<int> queue = Queue();\n\n/* Enqueue elements */\nqueue.add(1);\nqueue.add(3);\nqueue.add(2);\nqueue.add(5);\nqueue.add(4);\n\n/* Access the front element */\nint peek = queue.first;\n\n/* Dequeue an element */\nint pop = queue.removeFirst();\n\n/* Get the length of the queue */\nint size = queue.length;\n\n/* Check if the queue is empty */\nbool isEmpty = queue.isEmpty;\n
queue.rs/* Initialize the double-ended queue */\n// In Rust, use a double-ended queue as a regular queue\nlet mut deque: VecDeque<u32> = VecDeque::new();\n\n/* Enqueue elements */\ndeque.push_back(1);\ndeque.push_back(3);\ndeque.push_back(2);\ndeque.push_back(5);\ndeque.push_back(4);\n\n/* Access the front element */\nif let Some(front) = deque.front() {\n}\n\n/* Dequeue an element */\nif let Some(pop) = deque.pop_front() {\n}\n\n/* Get the length of the queue */\nlet size = deque.len();\n\n/* Check if the queue is empty */\nlet is_empty = deque.is_empty();\n
queue.c// C does not provide a built-in queue\n
queue.zig\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/queue/#522-implementing-a-queue","title":"5.2.2 \u00a0 Implementing a Queue","text":"To implement a queue, we need a data structure that allows adding elements at one end and removing them at the other. Both linked lists and arrays meet this requirement.
"},{"location":"chapter_stack_and_queue/queue/#1-implementation-based-on-linked-list","title":"1. \u00a0 Implementation Based on Linked List","text":"As shown in the Figure 5-5 , we can consider the \"head node\" and \"tail node\" of a linked list as the \"head\" and \"tail\" of the queue, respectively. We restrict the operations so that nodes can only be added at the tail and removed at the head.
LinkedListQueuepush()pop()Figure 5-5 \u00a0 Implementing Queue with Linked List for Enqueue and Dequeue Operations
Below is the code for implementing a queue using a linked list:
PythonC++JavaC#GoSwiftJSTSDartRustCZig linkedlist_queue.pyclass LinkedListQueue:\n \"\"\"\u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217\"\"\"\n\n def __init__(self):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._front: ListNode | None = None # \u5934\u8282\u70b9 front\n self._rear: ListNode | None = None # \u5c3e\u8282\u70b9 rear\n self._size: int = 0\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6\"\"\"\n return self._size\n\n def is_empty(self) -> bool:\n \"\"\"\u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a\"\"\"\n return not self._front\n\n def push(self, num: int):\n \"\"\"\u5165\u961f\"\"\"\n # \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n node = ListNode(num)\n # \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if self._front is None:\n self._front = node\n self._rear = node\n # \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n else:\n self._rear.next = node\n self._rear = node\n self._size += 1\n\n def pop(self) -> int:\n \"\"\"\u51fa\u961f\"\"\"\n num = self.peek()\n # \u5220\u9664\u5934\u8282\u70b9\n self._front = self._front.next\n self._size -= 1\n return num\n\n def peek(self) -> int:\n \"\"\"\u8bbf\u95ee\u961f\u9996\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u961f\u5217\u4e3a\u7a7a\")\n return self._front.val\n\n def to_list(self) -> list[int]:\n \"\"\"\u8f6c\u5316\u4e3a\u5217\u8868\u7528\u4e8e\u6253\u5370\"\"\"\n queue = []\n temp = self._front\n while temp:\n queue.append(temp.val)\n temp = temp.next\n return queue\n
linkedlist_queue.cpp/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n private:\n ListNode *front, *rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear\n int queSize;\n\n public:\n LinkedListQueue() {\n front = nullptr;\n rear = nullptr;\n queSize = 0;\n }\n\n ~LinkedListQueue() {\n // \u904d\u5386\u94fe\u8868\u5220\u9664\u8282\u70b9\uff0c\u91ca\u653e\u5185\u5b58\n freeMemoryLinkedList(front);\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return queSize == 0;\n }\n\n /* \u5165\u961f */\n void push(int num) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n ListNode *node = new ListNode(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (front == nullptr) {\n front = node;\n rear = node;\n }\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n else {\n rear->next = node;\n rear = node;\n }\n queSize++;\n }\n\n /* \u51fa\u961f */\n int pop() {\n int num = peek();\n // \u5220\u9664\u5934\u8282\u70b9\n ListNode *tmp = front;\n front = front->next;\n // \u91ca\u653e\u5185\u5b58\n delete tmp;\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int peek() {\n if (size() == 0)\n throw out_of_range(\"\u961f\u5217\u4e3a\u7a7a\");\n return front->val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Vector \u5e76\u8fd4\u56de */\n vector<int> toVector() {\n ListNode *node = front;\n vector<int> res(size());\n for (int i = 0; i < res.size(); i++) {\n res[i] = node->val;\n node = node->next;\n }\n return res;\n }\n};\n
linkedlist_queue.java/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n private ListNode front, rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear\n private int queSize = 0;\n\n public LinkedListQueue() {\n front = null;\n rear = null;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n public int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public boolean isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u961f */\n public void push(int num) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n ListNode node = new ListNode(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (front == null) {\n front = node;\n rear = node;\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n } else {\n rear.next = node;\n rear = node;\n }\n queSize++;\n }\n\n /* \u51fa\u961f */\n public int pop() {\n int num = peek();\n // \u5220\u9664\u5934\u8282\u70b9\n front = front.next;\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int peek() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return front.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public int[] toArray() {\n ListNode node = front;\n int[] res = new int[size()];\n for (int i = 0; i < res.length; i++) {\n res[i] = node.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_queue.cs/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n ListNode? front, rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear \n int queSize = 0;\n\n public LinkedListQueue() {\n front = null;\n rear = null;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n public int Size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public bool IsEmpty() {\n return Size() == 0;\n }\n\n /* \u5165\u961f */\n public void Push(int num) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n ListNode node = new(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (front == null) {\n front = node;\n rear = node;\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n } else if (rear != null) {\n rear.next = node;\n rear = node;\n }\n queSize++;\n }\n\n /* \u51fa\u961f */\n public int Pop() {\n int num = Peek();\n // \u5220\u9664\u5934\u8282\u70b9\n front = front?.next;\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int Peek() {\n if (IsEmpty())\n throw new Exception();\n return front!.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public int[] ToArray() {\n if (front == null)\n return [];\n\n ListNode? node = front;\n int[] res = new int[Size()];\n for (int i = 0; i < res.Length; i++) {\n res[i] = node!.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_queue.go/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\ntype linkedListQueue struct {\n // \u4f7f\u7528\u5185\u7f6e\u5305 list \u6765\u5b9e\u73b0\u961f\u5217\n data *list.List\n}\n\n/* \u521d\u59cb\u5316\u961f\u5217 */\nfunc newLinkedListQueue() *linkedListQueue {\n return &linkedListQueue{\n data: list.New(),\n }\n}\n\n/* \u5165\u961f */\nfunc (s *linkedListQueue) push(value any) {\n s.data.PushBack(value)\n}\n\n/* \u51fa\u961f */\nfunc (s *linkedListQueue) pop() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Front()\n s.data.Remove(e)\n return e.Value\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nfunc (s *linkedListQueue) peek() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Front()\n return e.Value\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nfunc (s *linkedListQueue) size() int {\n return s.data.Len()\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nfunc (s *linkedListQueue) isEmpty() bool {\n return s.data.Len() == 0\n}\n\n/* \u83b7\u53d6 List \u7528\u4e8e\u6253\u5370 */\nfunc (s *linkedListQueue) toList() *list.List {\n return s.data\n}\n
linkedlist_queue.swift/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n private var front: ListNode? // \u5934\u8282\u70b9\n private var rear: ListNode? // \u5c3e\u8282\u70b9\n private var _size = 0\n\n init() {}\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n func size() -> Int {\n _size\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n func isEmpty() -> Bool {\n size() == 0\n }\n\n /* \u5165\u961f */\n func push(num: Int) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n let node = ListNode(x: num)\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if front == nil {\n front = node\n rear = node\n }\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n else {\n rear?.next = node\n rear = node\n }\n _size += 1\n }\n\n /* \u51fa\u961f */\n @discardableResult\n func pop() -> Int {\n let num = peek()\n // \u5220\u9664\u5934\u8282\u70b9\n front = front?.next\n _size -= 1\n return num\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n func peek() -> Int {\n if isEmpty() {\n fatalError(\"\u961f\u5217\u4e3a\u7a7a\")\n }\n return front!.val\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n func toArray() -> [Int] {\n var node = front\n var res = Array(repeating: 0, count: size())\n for i in res.indices {\n res[i] = node!.val\n node = node?.next\n }\n return res\n }\n}\n
linkedlist_queue.js/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n #front; // \u5934\u8282\u70b9 #front\n #rear; // \u5c3e\u8282\u70b9 #rear\n #queSize = 0;\n\n constructor() {\n this.#front = null;\n this.#rear = null;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n get size() {\n return this.#queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty() {\n return this.size === 0;\n }\n\n /* \u5165\u961f */\n push(num) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n const node = new ListNode(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (!this.#front) {\n this.#front = node;\n this.#rear = node;\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n } else {\n this.#rear.next = node;\n this.#rear = node;\n }\n this.#queSize++;\n }\n\n /* \u51fa\u961f */\n pop() {\n const num = this.peek();\n // \u5220\u9664\u5934\u8282\u70b9\n this.#front = this.#front.next;\n this.#queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peek() {\n if (this.size === 0) throw new Error('\u961f\u5217\u4e3a\u7a7a');\n return this.#front.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n toArray() {\n let node = this.#front;\n const res = new Array(this.size);\n for (let i = 0; i < res.length; i++) {\n res[i] = node.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_queue.ts/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n private front: ListNode | null; // \u5934\u8282\u70b9 front\n private rear: ListNode | null; // \u5c3e\u8282\u70b9 rear\n private queSize: number = 0;\n\n constructor() {\n this.front = null;\n this.rear = null;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n get size(): number {\n return this.queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty(): boolean {\n return this.size === 0;\n }\n\n /* \u5165\u961f */\n push(num: number): void {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n const node = new ListNode(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (!this.front) {\n this.front = node;\n this.rear = node;\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n } else {\n this.rear!.next = node;\n this.rear = node;\n }\n this.queSize++;\n }\n\n /* \u51fa\u961f */\n pop(): number {\n const num = this.peek();\n if (!this.front) throw new Error('\u961f\u5217\u4e3a\u7a7a');\n // \u5220\u9664\u5934\u8282\u70b9\n this.front = this.front.next;\n this.queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peek(): number {\n if (this.size === 0) throw new Error('\u961f\u5217\u4e3a\u7a7a');\n return this.front!.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n toArray(): number[] {\n let node = this.front;\n const res = new Array<number>(this.size);\n for (let i = 0; i < res.length; i++) {\n res[i] = node!.val;\n node = node!.next;\n }\n return res;\n }\n}\n
linkedlist_queue.dart/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n ListNode? _front; // \u5934\u8282\u70b9 _front\n ListNode? _rear; // \u5c3e\u8282\u70b9 _rear\n int _queSize = 0; // \u961f\u5217\u957f\u5ea6\n\n LinkedListQueue() {\n _front = null;\n _rear = null;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n int size() {\n return _queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return _queSize == 0;\n }\n\n /* \u5165\u961f */\n void push(int _num) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 _num\n final node = ListNode(_num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (_front == null) {\n _front = node;\n _rear = node;\n } else {\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n _rear!.next = node;\n _rear = node;\n }\n _queSize++;\n }\n\n /* \u51fa\u961f */\n int pop() {\n final int _num = peek();\n // \u5220\u9664\u5934\u8282\u70b9\n _front = _front!.next;\n _queSize--;\n return _num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int peek() {\n if (_queSize == 0) {\n throw Exception('\u961f\u5217\u4e3a\u7a7a');\n }\n return _front!.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n List<int> toArray() {\n ListNode? node = _front;\n final List<int> queue = [];\n while (node != null) {\n queue.add(node.val);\n node = node.next;\n }\n return queue;\n }\n}\n
linkedlist_queue.rs/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\n#[allow(dead_code)]\npub struct LinkedListQueue<T> {\n front: Option<Rc<RefCell<ListNode<T>>>>, // \u5934\u8282\u70b9 front\n rear: Option<Rc<RefCell<ListNode<T>>>>, // \u5c3e\u8282\u70b9 rear \n que_size: usize, // \u961f\u5217\u7684\u957f\u5ea6\n}\n\nimpl<T: Copy> LinkedListQueue<T> {\n pub fn new() -> Self {\n Self {\n front: None,\n rear: None,\n que_size: 0, \n }\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n pub fn size(&self) -> usize {\n return self.que_size;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n pub fn is_empty(&self) -> bool {\n return self.size() == 0;\n }\n\n /* \u5165\u961f */\n pub fn push(&mut self, num: T) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n let new_rear = ListNode::new(num);\n match self.rear.take() {\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n Some(old_rear) => {\n old_rear.borrow_mut().next = Some(new_rear.clone());\n self.rear = Some(new_rear);\n }\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n None => {\n self.front = Some(new_rear.clone());\n self.rear = Some(new_rear);\n }\n }\n self.que_size += 1;\n }\n\n /* \u51fa\u961f */\n pub fn pop(&mut self) -> Option<T> {\n self.front.take().map(|old_front| {\n match old_front.borrow_mut().next.take() {\n Some(new_front) => {\n self.front = Some(new_front);\n }\n None => {\n self.rear.take();\n }\n }\n self.que_size -= 1;\n Rc::try_unwrap(old_front).ok().unwrap().into_inner().val\n })\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n pub fn peek(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {\n self.front.as_ref()\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n pub fn to_array(&self, head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {\n if let Some(node) = head {\n let mut nums = self.to_array(node.borrow().next.as_ref());\n nums.insert(0, node.borrow().val);\n return nums;\n }\n return Vec::new();\n }\n}\n
linkedlist_queue.c/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\ntypedef struct {\n ListNode *front, *rear;\n int queSize;\n} LinkedListQueue;\n\n/* \u6784\u9020\u51fd\u6570 */\nLinkedListQueue *newLinkedListQueue() {\n LinkedListQueue *queue = (LinkedListQueue *)malloc(sizeof(LinkedListQueue));\n queue->front = NULL;\n queue->rear = NULL;\n queue->queSize = 0;\n return queue;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delLinkedListQueue(LinkedListQueue *queue) {\n // \u91ca\u653e\u6240\u6709\u8282\u70b9\n for (int i = 0; i < queue->queSize && queue->front != NULL; i++) {\n ListNode *tmp = queue->front;\n queue->front = queue->front->next;\n free(tmp);\n }\n // \u91ca\u653e queue \u7ed3\u6784\u4f53\n free(queue);\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nint size(LinkedListQueue *queue) {\n return queue->queSize;\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nbool empty(LinkedListQueue *queue) {\n return (size(queue) == 0);\n}\n\n/* \u5165\u961f */\nvoid push(LinkedListQueue *queue, int num) {\n // \u5c3e\u8282\u70b9\u5904\u6dfb\u52a0 node\n ListNode *node = newListNode(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (queue->front == NULL) {\n queue->front = node;\n queue->rear = node;\n }\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n else {\n queue->rear->next = node;\n queue->rear = node;\n }\n queue->queSize++;\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nint peek(LinkedListQueue *queue) {\n assert(size(queue) && queue->front);\n return queue->front->val;\n}\n\n/* \u51fa\u961f */\nint pop(LinkedListQueue *queue) {\n int num = peek(queue);\n ListNode *tmp = queue->front;\n queue->front = queue->front->next;\n free(tmp);\n queue->queSize--;\n return num;\n}\n\n/* \u6253\u5370\u961f\u5217 */\nvoid printLinkedListQueue(LinkedListQueue *queue) {\n int *arr = malloc(sizeof(int) * queue->queSize);\n // \u62f7\u8d1d\u94fe\u8868\u4e2d\u7684\u6570\u636e\u5230\u6570\u7ec4\n int i;\n ListNode *node;\n for (i = 0, node = queue->front; i < queue->queSize; i++) {\n arr[i] = node->val;\n node = node->next;\n }\n printArray(arr, queue->queSize);\n free(arr);\n}\n
linkedlist_queue.zig// \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217\nfn LinkedListQueue(comptime T: type) type {\n return struct {\n const Self = @This();\n\n front: ?*inc.ListNode(T) = null, // \u5934\u8282\u70b9 front\n rear: ?*inc.ListNode(T) = null, // \u5c3e\u8282\u70b9 rear\n que_size: usize = 0, // \u961f\u5217\u7684\u957f\u5ea6\n mem_arena: ?std.heap.ArenaAllocator = null,\n mem_allocator: std.mem.Allocator = undefined, // \u5185\u5b58\u5206\u914d\u5668\n\n // \u6784\u9020\u51fd\u6570\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u961f\u5217\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator) !void {\n if (self.mem_arena == null) {\n self.mem_arena = std.heap.ArenaAllocator.init(allocator);\n self.mem_allocator = self.mem_arena.?.allocator();\n }\n self.front = null;\n self.rear = null;\n self.que_size = 0;\n }\n\n // \u6790\u6784\u51fd\u6570\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.mem_arena == null) return;\n self.mem_arena.?.deinit();\n }\n\n // \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6\n pub fn size(self: *Self) usize {\n return self.que_size;\n }\n\n // \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a\n pub fn isEmpty(self: *Self) bool {\n return self.size() == 0;\n }\n\n // \u8bbf\u95ee\u961f\u9996\u5143\u7d20\n pub fn peek(self: *Self) T {\n if (self.size() == 0) @panic(\"\u961f\u5217\u4e3a\u7a7a\");\n return self.front.?.val;\n } \n\n // \u5165\u961f\n pub fn push(self: *Self, num: T) !void {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n var node = try self.mem_allocator.create(inc.ListNode(T));\n node.init(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (self.front == null) {\n self.front = node;\n self.rear = node;\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n } else {\n self.rear.?.next = node;\n self.rear = node;\n }\n self.que_size += 1;\n } \n\n // \u51fa\u961f\n pub fn pop(self: *Self) T {\n var num = self.peek();\n // \u5220\u9664\u5934\u8282\u70b9\n self.front = self.front.?.next;\n self.que_size -= 1;\n return num;\n } \n\n // \u5c06\u94fe\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4\n pub fn toArray(self: *Self) ![]T {\n var node = self.front;\n var res = try self.mem_allocator.alloc(T, self.size());\n @memset(res, @as(T, 0));\n var i: usize = 0;\n while (i < res.len) : (i += 1) {\n res[i] = node.?.val;\n node = node.?.next;\n }\n return res;\n }\n };\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/queue/#2-implementation-based-on-array","title":"2. \u00a0 Implementation Based on Array","text":"Deleting the first element in an array has a time complexity of \\(O(n)\\), which would make the dequeue operation inefficient. However, this problem can be cleverly avoided as follows.
We can use a variable front
to point to the index of the head element and maintain a size
variable to record the length of the queue. Define rear = front + size
, which points to the position right after the tail element.
With this design, the effective interval of elements in the array is [front, rear - 1]
. The implementation methods for various operations are shown in the Figure 5-6 .
rear
index and increase size
by 1.front
by 1 and decrease size
by 1.Both enqueue and dequeue operations only require a single operation, each with a time complexity of \\(O(1)\\).
ArrayQueuepush()pop()Figure 5-6 \u00a0 Implementing Queue with Array for Enqueue and Dequeue Operations
You might notice a problem: as enqueue and dequeue operations are continuously performed, both front
and rear
move to the right and will eventually reach the end of the array and can't move further. To resolve this issue, we can treat the array as a \"circular array.\"
For a circular array, front
or rear
needs to loop back to the start of the array upon reaching the end. This cyclical pattern can be achieved with a \"modulo operation,\" as shown in the code below:
class ArrayQueue:\n \"\"\"\u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217\"\"\"\n\n def __init__(self, size: int):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._nums: list[int] = [0] * size # \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n self._front: int = 0 # \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n self._size: int = 0 # \u961f\u5217\u957f\u5ea6\n\n def capacity(self) -> int:\n \"\"\"\u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf\"\"\"\n return len(self._nums)\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6\"\"\"\n return self._size\n\n def is_empty(self) -> bool:\n \"\"\"\u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a\"\"\"\n return self._size == 0\n\n def push(self, num: int):\n \"\"\"\u5165\u961f\"\"\"\n if self._size == self.capacity():\n raise IndexError(\"\u961f\u5217\u5df2\u6ee1\")\n # \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n # \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n rear: int = (self._front + self._size) % self.capacity()\n # \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n self._nums[rear] = num\n self._size += 1\n\n def pop(self) -> int:\n \"\"\"\u51fa\u961f\"\"\"\n num: int = self.peek()\n # \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n self._front = (self._front + 1) % self.capacity()\n self._size -= 1\n return num\n\n def peek(self) -> int:\n \"\"\"\u8bbf\u95ee\u961f\u9996\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u961f\u5217\u4e3a\u7a7a\")\n return self._nums[self._front]\n\n def to_list(self) -> list[int]:\n \"\"\"\u8fd4\u56de\u5217\u8868\u7528\u4e8e\u6253\u5370\"\"\"\n res = [0] * self.size()\n j: int = self._front\n for i in range(self.size()):\n res[i] = self._nums[(j % self.capacity())]\n j += 1\n return res\n
array_queue.cpp/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n private:\n int *nums; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n int front; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n int queSize; // \u961f\u5217\u957f\u5ea6\n int queCapacity; // \u961f\u5217\u5bb9\u91cf\n\n public:\n ArrayQueue(int capacity) {\n // \u521d\u59cb\u5316\u6570\u7ec4\n nums = new int[capacity];\n queCapacity = capacity;\n front = queSize = 0;\n }\n\n ~ArrayQueue() {\n delete[] nums;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n int capacity() {\n return queCapacity;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u961f */\n void push(int num) {\n if (queSize == queCapacity) {\n cout << \"\u961f\u5217\u5df2\u6ee1\" << endl;\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n int rear = (front + queSize) % queCapacity;\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n nums[rear] = num;\n queSize++;\n }\n\n /* \u51fa\u961f */\n int pop() {\n int num = peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n front = (front + 1) % queCapacity;\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int peek() {\n if (isEmpty())\n throw out_of_range(\"\u961f\u5217\u4e3a\u7a7a\");\n return nums[front];\n }\n\n /* \u5c06\u6570\u7ec4\u8f6c\u5316\u4e3a Vector \u5e76\u8fd4\u56de */\n vector<int> toVector() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n vector<int> arr(queSize);\n for (int i = 0, j = front; i < queSize; i++, j++) {\n arr[i] = nums[j % queCapacity];\n }\n return arr;\n }\n};\n
array_queue.java/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n private int[] nums; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n private int front; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n private int queSize; // \u961f\u5217\u957f\u5ea6\n\n public ArrayQueue(int capacity) {\n nums = new int[capacity];\n front = queSize = 0;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n public int capacity() {\n return nums.length;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n public int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public boolean isEmpty() {\n return queSize == 0;\n }\n\n /* \u5165\u961f */\n public void push(int num) {\n if (queSize == capacity()) {\n System.out.println(\"\u961f\u5217\u5df2\u6ee1\");\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n int rear = (front + queSize) % capacity();\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n nums[rear] = num;\n queSize++;\n }\n\n /* \u51fa\u961f */\n public int pop() {\n int num = peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n front = (front + 1) % capacity();\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int peek() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return nums[front];\n }\n\n /* \u8fd4\u56de\u6570\u7ec4 */\n public int[] toArray() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n int[] res = new int[queSize];\n for (int i = 0, j = front; i < queSize; i++, j++) {\n res[i] = nums[j % capacity()];\n }\n return res;\n }\n}\n
array_queue.cs/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n int[] nums; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n int front; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n int queSize; // \u961f\u5217\u957f\u5ea6\n\n public ArrayQueue(int capacity) {\n nums = new int[capacity];\n front = queSize = 0;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n int Capacity() {\n return nums.Length;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n public int Size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public bool IsEmpty() {\n return queSize == 0;\n }\n\n /* \u5165\u961f */\n public void Push(int num) {\n if (queSize == Capacity()) {\n Console.WriteLine(\"\u961f\u5217\u5df2\u6ee1\");\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n int rear = (front + queSize) % Capacity();\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n nums[rear] = num;\n queSize++;\n }\n\n /* \u51fa\u961f */\n public int Pop() {\n int num = Peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n front = (front + 1) % Capacity();\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int Peek() {\n if (IsEmpty())\n throw new Exception();\n return nums[front];\n }\n\n /* \u8fd4\u56de\u6570\u7ec4 */\n public int[] ToArray() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n int[] res = new int[queSize];\n for (int i = 0, j = front; i < queSize; i++, j++) {\n res[i] = nums[j % this.Capacity()];\n }\n return res;\n }\n}\n
array_queue.go/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\ntype arrayQueue struct {\n nums []int // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n front int // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n queSize int // \u961f\u5217\u957f\u5ea6\n queCapacity int // \u961f\u5217\u5bb9\u91cf\uff08\u5373\u6700\u5927\u5bb9\u7eb3\u5143\u7d20\u6570\u91cf\uff09\n}\n\n/* \u521d\u59cb\u5316\u961f\u5217 */\nfunc newArrayQueue(queCapacity int) *arrayQueue {\n return &arrayQueue{\n nums: make([]int, queCapacity),\n queCapacity: queCapacity,\n front: 0,\n queSize: 0,\n }\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nfunc (q *arrayQueue) size() int {\n return q.queSize\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nfunc (q *arrayQueue) isEmpty() bool {\n return q.queSize == 0\n}\n\n/* \u5165\u961f */\nfunc (q *arrayQueue) push(num int) {\n // \u5f53 rear == queCapacity \u8868\u793a\u961f\u5217\u5df2\u6ee1\n if q.queSize == q.queCapacity {\n return\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n rear := (q.front + q.queSize) % q.queCapacity\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n q.nums[rear] = num\n q.queSize++\n}\n\n/* \u51fa\u961f */\nfunc (q *arrayQueue) pop() any {\n num := q.peek()\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n q.front = (q.front + 1) % q.queCapacity\n q.queSize--\n return num\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nfunc (q *arrayQueue) peek() any {\n if q.isEmpty() {\n return nil\n }\n return q.nums[q.front]\n}\n\n/* \u83b7\u53d6 Slice \u7528\u4e8e\u6253\u5370 */\nfunc (q *arrayQueue) toSlice() []int {\n rear := (q.front + q.queSize)\n if rear >= q.queCapacity {\n rear %= q.queCapacity\n return append(q.nums[q.front:], q.nums[:rear]...)\n }\n return q.nums[q.front:rear]\n}\n
array_queue.swift/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n private var nums: [Int] // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n private var front = 0 // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n private var queSize = 0 // \u961f\u5217\u957f\u5ea6\n\n init(capacity: Int) {\n // \u521d\u59cb\u5316\u6570\u7ec4\n nums = Array(repeating: 0, count: capacity)\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n func capacity() -> Int {\n nums.count\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n func size() -> Int {\n queSize\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n func isEmpty() -> Bool {\n queSize == 0\n }\n\n /* \u5165\u961f */\n func push(num: Int) {\n if size() == capacity() {\n print(\"\u961f\u5217\u5df2\u6ee1\")\n return\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n let rear = (front + queSize) % capacity()\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n nums[rear] = num\n queSize += 1\n }\n\n /* \u51fa\u961f */\n @discardableResult\n func pop() -> Int {\n let num = peek()\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n front = (front + 1) % capacity()\n queSize -= 1\n return num\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n func peek() -> Int {\n if isEmpty() {\n fatalError(\"\u961f\u5217\u4e3a\u7a7a\")\n }\n return nums[front]\n }\n\n /* \u8fd4\u56de\u6570\u7ec4 */\n func toArray() -> [Int] {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n var res = Array(repeating: 0, count: queSize)\n for (i, j) in sequence(first: (0, front), next: { $0 < self.queSize - 1 ? ($0 + 1, $1 + 1) : nil }) {\n res[i] = nums[j % capacity()]\n }\n return res\n }\n}\n
array_queue.js/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n #nums; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n #front = 0; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n #queSize = 0; // \u961f\u5217\u957f\u5ea6\n\n constructor(capacity) {\n this.#nums = new Array(capacity);\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n get capacity() {\n return this.#nums.length;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n get size() {\n return this.#queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty() {\n return this.#queSize === 0;\n }\n\n /* \u5165\u961f */\n push(num) {\n if (this.size === this.capacity) {\n console.log('\u961f\u5217\u5df2\u6ee1');\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n const rear = (this.#front + this.size) % this.capacity;\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n this.#nums[rear] = num;\n this.#queSize++;\n }\n\n /* \u51fa\u961f */\n pop() {\n const num = this.peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n this.#front = (this.#front + 1) % this.capacity;\n this.#queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peek() {\n if (this.isEmpty()) throw new Error('\u961f\u5217\u4e3a\u7a7a');\n return this.#nums[this.#front];\n }\n\n /* \u8fd4\u56de Array */\n toArray() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n const arr = new Array(this.size);\n for (let i = 0, j = this.#front; i < this.size; i++, j++) {\n arr[i] = this.#nums[j % this.capacity];\n }\n return arr;\n }\n}\n
array_queue.ts/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n private nums: number[]; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n private front: number; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n private queSize: number; // \u961f\u5217\u957f\u5ea6\n\n constructor(capacity: number) {\n this.nums = new Array(capacity);\n this.front = this.queSize = 0;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n get capacity(): number {\n return this.nums.length;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n get size(): number {\n return this.queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty(): boolean {\n return this.queSize === 0;\n }\n\n /* \u5165\u961f */\n push(num: number): void {\n if (this.size === this.capacity) {\n console.log('\u961f\u5217\u5df2\u6ee1');\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n const rear = (this.front + this.queSize) % this.capacity;\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n this.nums[rear] = num;\n this.queSize++;\n }\n\n /* \u51fa\u961f */\n pop(): number {\n const num = this.peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n this.front = (this.front + 1) % this.capacity;\n this.queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peek(): number {\n if (this.isEmpty()) throw new Error('\u961f\u5217\u4e3a\u7a7a');\n return this.nums[this.front];\n }\n\n /* \u8fd4\u56de Array */\n toArray(): number[] {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n const arr = new Array(this.size);\n for (let i = 0, j = this.front; i < this.size; i++, j++) {\n arr[i] = this.nums[j % this.capacity];\n }\n return arr;\n }\n}\n
array_queue.dart/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n late List<int> _nums; // \u7528\u4e8e\u50a8\u5b58\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n late int _front; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n late int _queSize; // \u961f\u5217\u957f\u5ea6\n\n ArrayQueue(int capacity) {\n _nums = List.filled(capacity, 0);\n _front = _queSize = 0;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n int capaCity() {\n return _nums.length;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n int size() {\n return _queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return _queSize == 0;\n }\n\n /* \u5165\u961f */\n void push(int _num) {\n if (_queSize == capaCity()) {\n throw Exception(\"\u961f\u5217\u5df2\u6ee1\");\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n int rear = (_front + _queSize) % capaCity();\n // \u5c06 _num \u6dfb\u52a0\u81f3\u961f\u5c3e\n _nums[rear] = _num;\n _queSize++;\n }\n\n /* \u51fa\u961f */\n int pop() {\n int _num = peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n _front = (_front + 1) % capaCity();\n _queSize--;\n return _num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int peek() {\n if (isEmpty()) {\n throw Exception(\"\u961f\u5217\u4e3a\u7a7a\");\n }\n return _nums[_front];\n }\n\n /* \u8fd4\u56de Array */\n List<int> toArray() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n final List<int> res = List.filled(_queSize, 0);\n for (int i = 0, j = _front; i < _queSize; i++, j++) {\n res[i] = _nums[j % capaCity()];\n }\n return res;\n }\n}\n
array_queue.rs/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nstruct ArrayQueue {\n nums: Vec<i32>, // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n front: i32, // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n que_size: i32, // \u961f\u5217\u957f\u5ea6\n que_capacity: i32, // \u961f\u5217\u5bb9\u91cf\n}\n\nimpl ArrayQueue {\n /* \u6784\u9020\u65b9\u6cd5 */\n fn new(capacity: i32) -> ArrayQueue {\n ArrayQueue {\n nums: vec![0; capacity as usize],\n front: 0,\n que_size: 0,\n que_capacity: capacity,\n }\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n fn capacity(&self) -> i32 {\n self.que_capacity\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n fn size(&self) -> i32 {\n self.que_size\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n fn is_empty(&self) -> bool {\n self.que_size == 0\n }\n\n /* \u5165\u961f */\n fn push(&mut self, num: i32) {\n if self.que_size == self.capacity() {\n println!(\"\u961f\u5217\u5df2\u6ee1\");\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n let rear = (self.front + self.que_size) % self.que_capacity;\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n self.nums[rear as usize] = num;\n self.que_size += 1;\n }\n\n /* \u51fa\u961f */\n fn pop(&mut self) -> i32 {\n let num = self.peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n self.front = (self.front + 1) % self.que_capacity;\n self.que_size -= 1;\n num\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n fn peek(&self) -> i32 {\n if self.is_empty() {\n panic!(\"index out of bounds\");\n }\n self.nums[self.front as usize]\n }\n\n /* \u8fd4\u56de\u6570\u7ec4 */\n fn to_vector(&self) -> Vec<i32> {\n let cap = self.que_capacity;\n let mut j = self.front;\n let mut arr = vec![0; self.que_size as usize];\n for i in 0..self.que_size {\n arr[i as usize] = self.nums[(j % cap) as usize];\n j += 1;\n }\n arr\n }\n}\n
array_queue.c/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\ntypedef struct {\n int *nums; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n int front; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n int queSize; // \u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e + 1\n int queCapacity; // \u961f\u5217\u5bb9\u91cf\n} ArrayQueue;\n\n/* \u6784\u9020\u51fd\u6570 */\nArrayQueue *newArrayQueue(int capacity) {\n ArrayQueue *queue = (ArrayQueue *)malloc(sizeof(ArrayQueue));\n // \u521d\u59cb\u5316\u6570\u7ec4\n queue->queCapacity = capacity;\n queue->nums = (int *)malloc(sizeof(int) * queue->queCapacity);\n queue->front = queue->queSize = 0;\n return queue;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delArrayQueue(ArrayQueue *queue) {\n free(queue->nums);\n free(queue);\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\nint capacity(ArrayQueue *queue) {\n return queue->queCapacity;\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nint size(ArrayQueue *queue) {\n return queue->queSize;\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nbool empty(ArrayQueue *queue) {\n return queue->queSize == 0;\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nint peek(ArrayQueue *queue) {\n assert(size(queue) != 0);\n return queue->nums[queue->front];\n}\n\n/* \u5165\u961f */\nvoid push(ArrayQueue *queue, int num) {\n if (size(queue) == capacity(queue)) {\n printf(\"\u961f\u5217\u5df2\u6ee1\\r\\n\");\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n int rear = (queue->front + queue->queSize) % queue->queCapacity;\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n queue->nums[rear] = num;\n queue->queSize++;\n}\n\n/* \u51fa\u961f */\nint pop(ArrayQueue *queue) {\n int num = peek(queue);\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n queue->front = (queue->front + 1) % queue->queCapacity;\n queue->queSize--;\n return num;\n}\n
array_queue.zig// \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217\nfn ArrayQueue(comptime T: type) type {\n return struct {\n const Self = @This();\n\n nums: []T = undefined, // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4 \n cap: usize = 0, // \u961f\u5217\u5bb9\u91cf\n front: usize = 0, // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n queSize: usize = 0, // \u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e + 1\n mem_arena: ?std.heap.ArenaAllocator = null,\n mem_allocator: std.mem.Allocator = undefined, // \u5185\u5b58\u5206\u914d\u5668\n\n // \u6784\u9020\u51fd\u6570\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u6570\u7ec4\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator, cap: usize) !void {\n if (self.mem_arena == null) {\n self.mem_arena = std.heap.ArenaAllocator.init(allocator);\n self.mem_allocator = self.mem_arena.?.allocator();\n }\n self.cap = cap;\n self.nums = try self.mem_allocator.alloc(T, self.cap);\n @memset(self.nums, @as(T, 0));\n }\n\n // \u6790\u6784\u51fd\u6570\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.mem_arena == null) return;\n self.mem_arena.?.deinit();\n }\n\n // \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf\n pub fn capacity(self: *Self) usize {\n return self.cap;\n }\n\n // \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6\n pub fn size(self: *Self) usize {\n return self.queSize;\n }\n\n // \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a\n pub fn isEmpty(self: *Self) bool {\n return self.queSize == 0;\n }\n\n // \u5165\u961f\n pub fn push(self: *Self, num: T) !void {\n if (self.size() == self.capacity()) {\n std.debug.print(\"\u961f\u5217\u5df2\u6ee1\\n\", .{});\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n var rear = (self.front + self.queSize) % self.capacity();\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n self.nums[rear] = num;\n self.queSize += 1;\n } \n\n // \u51fa\u961f\n pub fn pop(self: *Self) T {\n var num = self.peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n self.front = (self.front + 1) % self.capacity();\n self.queSize -= 1;\n return num;\n } \n\n // \u8bbf\u95ee\u961f\u9996\u5143\u7d20\n pub fn peek(self: *Self) T {\n if (self.isEmpty()) @panic(\"\u961f\u5217\u4e3a\u7a7a\");\n return self.nums[self.front];\n } \n\n // \u8fd4\u56de\u6570\u7ec4\n pub fn toArray(self: *Self) ![]T {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n var res = try self.mem_allocator.alloc(T, self.size());\n @memset(res, @as(T, 0));\n var i: usize = 0;\n var j: usize = self.front;\n while (i < self.size()) : ({ i += 1; j += 1; }) {\n res[i] = self.nums[j % self.capacity()];\n }\n return res;\n }\n };\n}\n
Visualizing Code Full Screen >
The above implementation of the queue still has limitations: its length is fixed. However, this issue is not difficult to resolve. We can replace the array with a dynamic array to introduce an expansion mechanism. Interested readers can try to implement this themselves.
The comparison of the two implementations is consistent with that of the stack and is not repeated here.
"},{"location":"chapter_stack_and_queue/queue/#523-typical-applications-of-queue","title":"5.2.3 \u00a0 Typical Applications of Queue","text":"\"Stack\" is a linear data structure that follows the principle of Last-In-First-Out (LIFO).
We can compare a stack to a pile of plates on a table. To access the bottom plate, one must remove the plates on top. If we replace the plates with various types of elements (such as integers, characters, objects, etc.), we obtain the data structure known as a stack.
As shown in the following figure, we refer to the top of the pile of elements as the \"top of the stack\" and the bottom as the \"bottom of the stack.\" The operation of adding elements to the top of the stack is called \"push,\" and the operation of removing the top element is called \"pop.\"
Figure 5-1 \u00a0 Stack's Last-In-First-Out Rule
"},{"location":"chapter_stack_and_queue/stack/#511-common-operations-on-stack","title":"5.1.1 \u00a0 Common Operations on Stack","text":"The common operations on a stack are shown in the Table 5-1 . The specific method names depend on the programming language used. Here, we use push()
, pop()
, and peek()
as examples.
Table 5-1 \u00a0 Efficiency of Stack Operations
Method Description Time Complexitypush()
Push an element onto the stack (add to the top) \\(O(1)\\) pop()
Pop the top element from the stack \\(O(1)\\) peek()
Access the top element of the stack \\(O(1)\\) Typically, we can directly use the stack class built into the programming language. However, some languages may not specifically provide a stack class. In these cases, we can use the language's \"array\" or \"linked list\" as a stack and ignore operations that are not related to stack logic in the program.
PythonC++JavaC#GoSwiftJSTSDartRustCZig stack.py# Initialize the stack\n# Python does not have a built-in stack class, so a list can be used as a stack\nstack: list[int] = []\n\n# Push elements onto the stack\nstack.append(1)\nstack.append(3)\nstack.append(2)\nstack.append(5)\nstack.append(4)\n\n# Access the top element of the stack\npeek: int = stack[-1]\n\n# Pop an element from the stack\npop: int = stack.pop()\n\n# Get the length of the stack\nsize: int = len(stack)\n\n# Check if the stack is empty\nis_empty: bool = len(stack) == 0\n
stack.cpp/* Initialize the stack */\nstack<int> stack;\n\n/* Push elements onto the stack */\nstack.push(1);\nstack.push(3);\nstack.push(2);\nstack.push(5);\nstack.push(4);\n\n/* Access the top element of the stack */\nint top = stack.top();\n\n/* Pop an element from the stack */\nstack.pop(); // No return value\n\n/* Get the length of the stack */\nint size = stack.size();\n\n/* Check if the stack is empty */\nbool empty = stack.empty();\n
stack.java/* Initialize the stack */\nStack<Integer> stack = new Stack<>();\n\n/* Push elements onto the stack */\nstack.push(1);\nstack.push(3);\nstack.push(2);\nstack.push(5);\nstack.push(4);\n\n/* Access the top element of the stack */\nint peek = stack.peek();\n\n/* Pop an element from the stack */\nint pop = stack.pop();\n\n/* Get the length of the stack */\nint size = stack.size();\n\n/* Check if the stack is empty */\nboolean isEmpty = stack.isEmpty();\n
stack.cs/* Initialize the stack */\nStack<int> stack = new();\n\n/* Push elements onto the stack */\nstack.Push(1);\nstack.Push(3);\nstack.Push(2);\nstack.Push(5);\nstack.Push(4);\n\n/* Access the top element of the stack */\nint peek = stack.Peek();\n\n/* Pop an element from the stack */\nint pop = stack.Pop();\n\n/* Get the length of the stack */\nint size = stack.Count;\n\n/* Check if the stack is empty */\nbool isEmpty = stack.Count == 0;\n
stack_test.go/* Initialize the stack */\n// In Go, it is recommended to use a Slice as a stack\nvar stack []int\n\n/* Push elements onto the stack */\nstack = append(stack, 1)\nstack = append(stack, 3)\nstack = append(stack, 2)\nstack = append(stack, 5)\nstack = append(stack, 4)\n\n/* Access the top element of the stack */\npeek := stack[len(stack)-1]\n\n/* Pop an element from the stack */\npop := stack[len(stack)-1]\nstack = stack[:len(stack)-1]\n\n/* Get the length of the stack */\nsize := len(stack)\n\n/* Check if the stack is empty */\nisEmpty := len(stack) == 0\n
stack.swift/* Initialize the stack */\n// Swift does not have a built-in stack class, so Array can be used as a stack\nvar stack: [Int] = []\n\n/* Push elements onto the stack */\nstack.append(1)\nstack.append(3)\nstack.append(2)\nstack.append(5)\nstack.append(4)\n\n/* Access the top element of the stack */\nlet peek = stack.last!\n\n/* Pop an element from the stack */\nlet pop = stack.removeLast()\n\n/* Get the length of the stack */\nlet size = stack.count\n\n/* Check if the stack is empty */\nlet isEmpty = stack.isEmpty\n
stack.js/* Initialize the stack */\n// JavaScript does not have a built-in stack class, so Array can be used as a stack\nconst stack = [];\n\n/* Push elements onto the stack */\nstack.push(1);\nstack.push(3);\nstack.push(2);\nstack.push(5);\nstack.push(4);\n\n/* Access the top element of the stack */\nconst peek = stack[stack.length-1];\n\n/* Pop an element from the stack */\nconst pop = stack.pop();\n\n/* Get the length of the stack */\nconst size = stack.length;\n\n/* Check if the stack is empty */\nconst is_empty = stack.length === 0;\n
stack.ts/* Initialize the stack */\n// TypeScript does not have a built-in stack class, so Array can be used as a stack\nconst stack: number[] = [];\n\n/* Push elements onto the stack */\nstack.push(1);\nstack.push(3);\nstack.push(2);\nstack.push(5);\nstack.push(4);\n\n/* Access the top element of the stack */\nconst peek = stack[stack.length - 1];\n\n/* Pop an element from the stack */\nconst pop = stack.pop();\n\n/* Get the length of the stack */\nconst size = stack.length;\n\n/* Check if the stack is empty */\nconst is_empty = stack.length === 0;\n
stack.dart/* Initialize the stack */\n// Dart does not have a built-in stack class, so List can be used as a stack\nList<int> stack = [];\n\n/* Push elements onto the stack */\nstack.add(1);\nstack.add(3);\nstack.add(2);\nstack.add(5);\nstack.add(4);\n\n/* Access the top element of the stack */\nint peek = stack.last;\n\n/* Pop an element from the stack */\nint pop = stack.removeLast();\n\n/* Get the length of the stack */\nint size = stack.length;\n\n/* Check if the stack is empty */\nbool isEmpty = stack.isEmpty;\n
stack.rs/* Initialize the stack */\n// Use Vec as a stack\nlet mut stack: Vec<i32> = Vec::new();\n\n/* Push elements onto the stack */\nstack.push(1);\nstack.push(3);\nstack.push(2);\nstack.push(5);\nstack.push(4);\n\n/* Access the top element of the stack */\nlet top = stack.last().unwrap();\n\n/* Pop an element from the stack */\nlet pop = stack.pop().unwrap();\n\n/* Get the length of the stack */\nlet size = stack.len();\n\n/* Check if the stack is empty */\nlet is_empty = stack.is_empty();\n
stack.c// C does not provide a built-in stack\n
stack.zig\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/stack/#512-implementing-a-stack","title":"5.1.2 \u00a0 Implementing a Stack","text":"To understand the mechanics of a stack more deeply, let's try implementing a stack class ourselves.
A stack follows the principle of Last-In-First-Out, which means we can only add or remove elements at the top of the stack. However, both arrays and linked lists allow adding and removing elements at any position, therefore a stack can be seen as a restricted array or linked list. In other words, we can \"mask\" some unrelated operations of arrays or linked lists to make their logic conform to the characteristics of a stack.
"},{"location":"chapter_stack_and_queue/stack/#1-implementation-based-on-linked-list","title":"1. \u00a0 Implementation Based on Linked List","text":"When implementing a stack using a linked list, we can consider the head node of the list as the top of the stack and the tail node as the bottom of the stack.
As shown in the Figure 5-2 , for the push operation, we simply insert elements at the head of the linked list. This method of node insertion is known as \"head insertion.\" For the pop operation, we just need to remove the head node from the list.
LinkedListStackpush()pop()Figure 5-2 \u00a0 Implementing Stack with Linked List for Push and Pop Operations
Below is an example code for implementing a stack based on a linked list:
PythonC++JavaC#GoSwiftJSTSDartRustCZig linkedlist_stack.pyclass LinkedListStack:\n \"\"\"\u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808\"\"\"\n\n def __init__(self):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._peek: ListNode | None = None\n self._size: int = 0\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u6808\u7684\u957f\u5ea6\"\"\"\n return self._size\n\n def is_empty(self) -> bool:\n \"\"\"\u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a\"\"\"\n return not self._peek\n\n def push(self, val: int):\n \"\"\"\u5165\u6808\"\"\"\n node = ListNode(val)\n node.next = self._peek\n self._peek = node\n self._size += 1\n\n def pop(self) -> int:\n \"\"\"\u51fa\u6808\"\"\"\n num = self.peek()\n self._peek = self._peek.next\n self._size -= 1\n return num\n\n def peek(self) -> int:\n \"\"\"\u8bbf\u95ee\u6808\u9876\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u6808\u4e3a\u7a7a\")\n return self._peek.val\n\n def to_list(self) -> list[int]:\n \"\"\"\u8f6c\u5316\u4e3a\u5217\u8868\u7528\u4e8e\u6253\u5370\"\"\"\n arr = []\n node = self._peek\n while node:\n arr.append(node.val)\n node = node.next\n arr.reverse()\n return arr\n
linkedlist_stack.cpp/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n private:\n ListNode *stackTop; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n int stkSize; // \u6808\u7684\u957f\u5ea6\n\n public:\n LinkedListStack() {\n stackTop = nullptr;\n stkSize = 0;\n }\n\n ~LinkedListStack() {\n // \u904d\u5386\u94fe\u8868\u5220\u9664\u8282\u70b9\uff0c\u91ca\u653e\u5185\u5b58\n freeMemoryLinkedList(stackTop);\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n int size() {\n return stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u6808 */\n void push(int num) {\n ListNode *node = new ListNode(num);\n node->next = stackTop;\n stackTop = node;\n stkSize++;\n }\n\n /* \u51fa\u6808 */\n int pop() {\n int num = top();\n ListNode *tmp = stackTop;\n stackTop = stackTop->next;\n // \u91ca\u653e\u5185\u5b58\n delete tmp;\n stkSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n int top() {\n if (isEmpty())\n throw out_of_range(\"\u6808\u4e3a\u7a7a\");\n return stackTop->val;\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n vector<int> toVector() {\n ListNode *node = stackTop;\n vector<int> res(size());\n for (int i = res.size() - 1; i >= 0; i--) {\n res[i] = node->val;\n node = node->next;\n }\n return res;\n }\n};\n
linkedlist_stack.java/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n private ListNode stackPeek; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n private int stkSize = 0; // \u6808\u7684\u957f\u5ea6\n\n public LinkedListStack() {\n stackPeek = null;\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n public int size() {\n return stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n public boolean isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u6808 */\n public void push(int num) {\n ListNode node = new ListNode(num);\n node.next = stackPeek;\n stackPeek = node;\n stkSize++;\n }\n\n /* \u51fa\u6808 */\n public int pop() {\n int num = peek();\n stackPeek = stackPeek.next;\n stkSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n public int peek() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return stackPeek.val;\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public int[] toArray() {\n ListNode node = stackPeek;\n int[] res = new int[size()];\n for (int i = res.length - 1; i >= 0; i--) {\n res[i] = node.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_stack.cs/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n ListNode? stackPeek; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n int stkSize = 0; // \u6808\u7684\u957f\u5ea6\n\n public LinkedListStack() {\n stackPeek = null;\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n public int Size() {\n return stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n public bool IsEmpty() {\n return Size() == 0;\n }\n\n /* \u5165\u6808 */\n public void Push(int num) {\n ListNode node = new(num) {\n next = stackPeek\n };\n stackPeek = node;\n stkSize++;\n }\n\n /* \u51fa\u6808 */\n public int Pop() {\n int num = Peek();\n stackPeek = stackPeek!.next;\n stkSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n public int Peek() {\n if (IsEmpty())\n throw new Exception();\n return stackPeek!.val;\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public int[] ToArray() {\n if (stackPeek == null)\n return [];\n\n ListNode? node = stackPeek;\n int[] res = new int[Size()];\n for (int i = res.Length - 1; i >= 0; i--) {\n res[i] = node!.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_stack.go/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\ntype linkedListStack struct {\n // \u4f7f\u7528\u5185\u7f6e\u5305 list \u6765\u5b9e\u73b0\u6808\n data *list.List\n}\n\n/* \u521d\u59cb\u5316\u6808 */\nfunc newLinkedListStack() *linkedListStack {\n return &linkedListStack{\n data: list.New(),\n }\n}\n\n/* \u5165\u6808 */\nfunc (s *linkedListStack) push(value int) {\n s.data.PushBack(value)\n}\n\n/* \u51fa\u6808 */\nfunc (s *linkedListStack) pop() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Back()\n s.data.Remove(e)\n return e.Value\n}\n\n/* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\nfunc (s *linkedListStack) peek() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Back()\n return e.Value\n}\n\n/* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\nfunc (s *linkedListStack) size() int {\n return s.data.Len()\n}\n\n/* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\nfunc (s *linkedListStack) isEmpty() bool {\n return s.data.Len() == 0\n}\n\n/* \u83b7\u53d6 List \u7528\u4e8e\u6253\u5370 */\nfunc (s *linkedListStack) toList() *list.List {\n return s.data\n}\n
linkedlist_stack.swift/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n private var _peek: ListNode? // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n private var _size = 0 // \u6808\u7684\u957f\u5ea6\n\n init() {}\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n func size() -> Int {\n _size\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n func isEmpty() -> Bool {\n size() == 0\n }\n\n /* \u5165\u6808 */\n func push(num: Int) {\n let node = ListNode(x: num)\n node.next = _peek\n _peek = node\n _size += 1\n }\n\n /* \u51fa\u6808 */\n @discardableResult\n func pop() -> Int {\n let num = peek()\n _peek = _peek?.next\n _size -= 1\n return num\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n func peek() -> Int {\n if isEmpty() {\n fatalError(\"\u6808\u4e3a\u7a7a\")\n }\n return _peek!.val\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n func toArray() -> [Int] {\n var node = _peek\n var res = Array(repeating: 0, count: _size)\n for i in sequence(first: res.count - 1, next: { $0 >= 0 + 1 ? $0 - 1 : nil }) {\n res[i] = node!.val\n node = node?.next\n }\n return res\n }\n}\n
linkedlist_stack.js/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n #stackPeek; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n #stkSize = 0; // \u6808\u7684\u957f\u5ea6\n\n constructor() {\n this.#stackPeek = null;\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n get size() {\n return this.#stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n isEmpty() {\n return this.size === 0;\n }\n\n /* \u5165\u6808 */\n push(num) {\n const node = new ListNode(num);\n node.next = this.#stackPeek;\n this.#stackPeek = node;\n this.#stkSize++;\n }\n\n /* \u51fa\u6808 */\n pop() {\n const num = this.peek();\n this.#stackPeek = this.#stackPeek.next;\n this.#stkSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n peek() {\n if (!this.#stackPeek) throw new Error('\u6808\u4e3a\u7a7a');\n return this.#stackPeek.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n toArray() {\n let node = this.#stackPeek;\n const res = new Array(this.size);\n for (let i = res.length - 1; i >= 0; i--) {\n res[i] = node.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_stack.ts/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n private stackPeek: ListNode | null; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n private stkSize: number = 0; // \u6808\u7684\u957f\u5ea6\n\n constructor() {\n this.stackPeek = null;\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n get size(): number {\n return this.stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n isEmpty(): boolean {\n return this.size === 0;\n }\n\n /* \u5165\u6808 */\n push(num: number): void {\n const node = new ListNode(num);\n node.next = this.stackPeek;\n this.stackPeek = node;\n this.stkSize++;\n }\n\n /* \u51fa\u6808 */\n pop(): number {\n const num = this.peek();\n if (!this.stackPeek) throw new Error('\u6808\u4e3a\u7a7a');\n this.stackPeek = this.stackPeek.next;\n this.stkSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n peek(): number {\n if (!this.stackPeek) throw new Error('\u6808\u4e3a\u7a7a');\n return this.stackPeek.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n toArray(): number[] {\n let node = this.stackPeek;\n const res = new Array<number>(this.size);\n for (let i = res.length - 1; i >= 0; i--) {\n res[i] = node!.val;\n node = node!.next;\n }\n return res;\n }\n}\n
linkedlist_stack.dart/* \u57fa\u4e8e\u94fe\u8868\u7c7b\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n ListNode? _stackPeek; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n int _stkSize = 0; // \u6808\u7684\u957f\u5ea6\n\n LinkedListStack() {\n _stackPeek = null;\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n int size() {\n return _stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return _stkSize == 0;\n }\n\n /* \u5165\u6808 */\n void push(int _num) {\n final ListNode node = ListNode(_num);\n node.next = _stackPeek;\n _stackPeek = node;\n _stkSize++;\n }\n\n /* \u51fa\u6808 */\n int pop() {\n final int _num = peek();\n _stackPeek = _stackPeek!.next;\n _stkSize--;\n return _num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n int peek() {\n if (_stackPeek == null) {\n throw Exception(\"\u6808\u4e3a\u7a7a\");\n }\n return _stackPeek!.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a List \u5e76\u8fd4\u56de */\n List<int> toList() {\n ListNode? node = _stackPeek;\n List<int> list = [];\n while (node != null) {\n list.add(node.val);\n node = node.next;\n }\n list = list.reversed.toList();\n return list;\n }\n}\n
linkedlist_stack.rs/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\n#[allow(dead_code)]\npub struct LinkedListStack<T> {\n stack_peek: Option<Rc<RefCell<ListNode<T>>>>, // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n stk_size: usize, // \u6808\u7684\u957f\u5ea6\n}\n\nimpl<T: Copy> LinkedListStack<T> {\n pub fn new() -> Self {\n Self {\n stack_peek: None,\n stk_size: 0,\n }\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n pub fn size(&self) -> usize {\n return self.stk_size;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n pub fn is_empty(&self) -> bool {\n return self.size() == 0;\n }\n\n /* \u5165\u6808 */\n pub fn push(&mut self, num: T) {\n let node = ListNode::new(num);\n node.borrow_mut().next = self.stack_peek.take();\n self.stack_peek = Some(node);\n self.stk_size += 1;\n }\n\n /* \u51fa\u6808 */\n pub fn pop(&mut self) -> Option<T> {\n self.stack_peek.take().map(|old_head| {\n match old_head.borrow_mut().next.take() {\n Some(new_head) => {\n self.stack_peek = Some(new_head);\n }\n None => {\n self.stack_peek = None;\n }\n }\n self.stk_size -= 1;\n Rc::try_unwrap(old_head).ok().unwrap().into_inner().val\n })\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n pub fn peek(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {\n self.stack_peek.as_ref()\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n pub fn to_array(&self, head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {\n if let Some(node) = head {\n let mut nums = self.to_array(node.borrow().next.as_ref());\n nums.push(node.borrow().val);\n return nums;\n }\n return Vec::new();\n }\n}\n
linkedlist_stack.c/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\ntypedef struct {\n ListNode *top; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n int size; // \u6808\u7684\u957f\u5ea6\n} LinkedListStack;\n\n/* \u6784\u9020\u51fd\u6570 */\nLinkedListStack *newLinkedListStack() {\n LinkedListStack *s = malloc(sizeof(LinkedListStack));\n s->top = NULL;\n s->size = 0;\n return s;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delLinkedListStack(LinkedListStack *s) {\n while (s->top) {\n ListNode *n = s->top->next;\n free(s->top);\n s->top = n;\n }\n free(s);\n}\n\n/* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\nint size(LinkedListStack *s) {\n return s->size;\n}\n\n/* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\nbool isEmpty(LinkedListStack *s) {\n return size(s) == 0;\n}\n\n/* \u5165\u6808 */\nvoid push(LinkedListStack *s, int num) {\n ListNode *node = (ListNode *)malloc(sizeof(ListNode));\n node->next = s->top; // \u66f4\u65b0\u65b0\u52a0\u8282\u70b9\u6307\u9488\u57df\n node->val = num; // \u66f4\u65b0\u65b0\u52a0\u8282\u70b9\u6570\u636e\u57df\n s->top = node; // \u66f4\u65b0\u6808\u9876\n s->size++; // \u66f4\u65b0\u6808\u5927\u5c0f\n}\n\n/* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\nint peek(LinkedListStack *s) {\n if (s->size == 0) {\n printf(\"\u6808\u4e3a\u7a7a\\n\");\n return INT_MAX;\n }\n return s->top->val;\n}\n\n/* \u51fa\u6808 */\nint pop(LinkedListStack *s) {\n int val = peek(s);\n ListNode *tmp = s->top;\n s->top = s->top->next;\n // \u91ca\u653e\u5185\u5b58\n free(tmp);\n s->size--;\n return val;\n}\n
linkedlist_stack.zig// \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808\nfn LinkedListStack(comptime T: type) type {\n return struct {\n const Self = @This();\n\n stack_top: ?*inc.ListNode(T) = null, // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n stk_size: usize = 0, // \u6808\u7684\u957f\u5ea6\n mem_arena: ?std.heap.ArenaAllocator = null,\n mem_allocator: std.mem.Allocator = undefined, // \u5185\u5b58\u5206\u914d\u5668\n\n // \u6784\u9020\u51fd\u6570\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u6808\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator) !void {\n if (self.mem_arena == null) {\n self.mem_arena = std.heap.ArenaAllocator.init(allocator);\n self.mem_allocator = self.mem_arena.?.allocator();\n }\n self.stack_top = null;\n self.stk_size = 0;\n }\n\n // \u6790\u6784\u51fd\u6570\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.mem_arena == null) return;\n self.mem_arena.?.deinit();\n }\n\n // \u83b7\u53d6\u6808\u7684\u957f\u5ea6\n pub fn size(self: *Self) usize {\n return self.stk_size;\n }\n\n // \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a\n pub fn isEmpty(self: *Self) bool {\n return self.size() == 0;\n }\n\n // \u8bbf\u95ee\u6808\u9876\u5143\u7d20\n pub fn peek(self: *Self) T {\n if (self.size() == 0) @panic(\"\u6808\u4e3a\u7a7a\");\n return self.stack_top.?.val;\n } \n\n // \u5165\u6808\n pub fn push(self: *Self, num: T) !void {\n var node = try self.mem_allocator.create(inc.ListNode(T));\n node.init(num);\n node.next = self.stack_top;\n self.stack_top = node;\n self.stk_size += 1;\n } \n\n // \u51fa\u6808\n pub fn pop(self: *Self) T {\n var num = self.peek();\n self.stack_top = self.stack_top.?.next;\n self.stk_size -= 1;\n return num;\n } \n\n // \u5c06\u6808\u8f6c\u6362\u4e3a\u6570\u7ec4\n pub fn toArray(self: *Self) ![]T {\n var node = self.stack_top;\n var res = try self.mem_allocator.alloc(T, self.size());\n @memset(res, @as(T, 0));\n var i: usize = 0;\n while (i < res.len) : (i += 1) {\n res[res.len - i - 1] = node.?.val;\n node = node.?.next;\n }\n return res;\n }\n };\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/stack/#2-implementation-based-on-array","title":"2. \u00a0 Implementation Based on Array","text":"When implementing a stack using an array, we can consider the end of the array as the top of the stack. As shown in the Figure 5-3 , push and pop operations correspond to adding and removing elements at the end of the array, respectively, both with a time complexity of \\(O(1)\\).
ArrayStackpush()pop()Figure 5-3 \u00a0 Implementing Stack with Array for Push and Pop Operations
Since the elements to be pushed onto the stack may continuously increase, we can use a dynamic array, thus avoiding the need to handle array expansion ourselves. Here is an example code:
PythonC++JavaC#GoSwiftJSTSDartRustCZig array_stack.pyclass ArrayStack:\n \"\"\"\u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808\"\"\"\n\n def __init__(self):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._stack: list[int] = []\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u6808\u7684\u957f\u5ea6\"\"\"\n return len(self._stack)\n\n def is_empty(self) -> bool:\n \"\"\"\u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a\"\"\"\n return self._stack == []\n\n def push(self, item: int):\n \"\"\"\u5165\u6808\"\"\"\n self._stack.append(item)\n\n def pop(self) -> int:\n \"\"\"\u51fa\u6808\"\"\"\n if self.is_empty():\n raise IndexError(\"\u6808\u4e3a\u7a7a\")\n return self._stack.pop()\n\n def peek(self) -> int:\n \"\"\"\u8bbf\u95ee\u6808\u9876\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u6808\u4e3a\u7a7a\")\n return self._stack[-1]\n\n def to_list(self) -> list[int]:\n \"\"\"\u8fd4\u56de\u5217\u8868\u7528\u4e8e\u6253\u5370\"\"\"\n return self._stack\n
array_stack.cpp/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n private:\n vector<int> stack;\n\n public:\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n int size() {\n return stack.size();\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return stack.size() == 0;\n }\n\n /* \u5165\u6808 */\n void push(int num) {\n stack.push_back(num);\n }\n\n /* \u51fa\u6808 */\n int pop() {\n int num = top();\n stack.pop_back();\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n int top() {\n if (isEmpty())\n throw out_of_range(\"\u6808\u4e3a\u7a7a\");\n return stack.back();\n }\n\n /* \u8fd4\u56de Vector */\n vector<int> toVector() {\n return stack;\n }\n};\n
array_stack.java/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n private ArrayList<Integer> stack;\n\n public ArrayStack() {\n // \u521d\u59cb\u5316\u5217\u8868\uff08\u52a8\u6001\u6570\u7ec4\uff09\n stack = new ArrayList<>();\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n public int size() {\n return stack.size();\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n public boolean isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u6808 */\n public void push(int num) {\n stack.add(num);\n }\n\n /* \u51fa\u6808 */\n public int pop() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return stack.remove(size() - 1);\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n public int peek() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return stack.get(size() - 1);\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public Object[] toArray() {\n return stack.toArray();\n }\n}\n
array_stack.cs/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n List<int> stack;\n public ArrayStack() {\n // \u521d\u59cb\u5316\u5217\u8868\uff08\u52a8\u6001\u6570\u7ec4\uff09\n stack = [];\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n public int Size() {\n return stack.Count;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n public bool IsEmpty() {\n return Size() == 0;\n }\n\n /* \u5165\u6808 */\n public void Push(int num) {\n stack.Add(num);\n }\n\n /* \u51fa\u6808 */\n public int Pop() {\n if (IsEmpty())\n throw new Exception();\n var val = Peek();\n stack.RemoveAt(Size() - 1);\n return val;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n public int Peek() {\n if (IsEmpty())\n throw new Exception();\n return stack[Size() - 1];\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public int[] ToArray() {\n return [.. stack];\n }\n}\n
array_stack.go/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\ntype arrayStack struct {\n data []int // \u6570\u636e\n}\n\n/* \u521d\u59cb\u5316\u6808 */\nfunc newArrayStack() *arrayStack {\n return &arrayStack{\n // \u8bbe\u7f6e\u6808\u7684\u957f\u5ea6\u4e3a 0\uff0c\u5bb9\u91cf\u4e3a 16\n data: make([]int, 0, 16),\n }\n}\n\n/* \u6808\u7684\u957f\u5ea6 */\nfunc (s *arrayStack) size() int {\n return len(s.data)\n}\n\n/* \u6808\u662f\u5426\u4e3a\u7a7a */\nfunc (s *arrayStack) isEmpty() bool {\n return s.size() == 0\n}\n\n/* \u5165\u6808 */\nfunc (s *arrayStack) push(v int) {\n // \u5207\u7247\u4f1a\u81ea\u52a8\u6269\u5bb9\n s.data = append(s.data, v)\n}\n\n/* \u51fa\u6808 */\nfunc (s *arrayStack) pop() any {\n val := s.peek()\n s.data = s.data[:len(s.data)-1]\n return val\n}\n\n/* \u83b7\u53d6\u6808\u9876\u5143\u7d20 */\nfunc (s *arrayStack) peek() any {\n if s.isEmpty() {\n return nil\n }\n val := s.data[len(s.data)-1]\n return val\n}\n\n/* \u83b7\u53d6 Slice \u7528\u4e8e\u6253\u5370 */\nfunc (s *arrayStack) toSlice() []int {\n return s.data\n}\n
array_stack.swift/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n private var stack: [Int]\n\n init() {\n // \u521d\u59cb\u5316\u5217\u8868\uff08\u52a8\u6001\u6570\u7ec4\uff09\n stack = []\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n func size() -> Int {\n stack.count\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n func isEmpty() -> Bool {\n stack.isEmpty\n }\n\n /* \u5165\u6808 */\n func push(num: Int) {\n stack.append(num)\n }\n\n /* \u51fa\u6808 */\n @discardableResult\n func pop() -> Int {\n if isEmpty() {\n fatalError(\"\u6808\u4e3a\u7a7a\")\n }\n return stack.removeLast()\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n func peek() -> Int {\n if isEmpty() {\n fatalError(\"\u6808\u4e3a\u7a7a\")\n }\n return stack.last!\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n func toArray() -> [Int] {\n stack\n }\n}\n
array_stack.js/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n #stack;\n constructor() {\n this.#stack = [];\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n get size() {\n return this.#stack.length;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n isEmpty() {\n return this.#stack.length === 0;\n }\n\n /* \u5165\u6808 */\n push(num) {\n this.#stack.push(num);\n }\n\n /* \u51fa\u6808 */\n pop() {\n if (this.isEmpty()) throw new Error('\u6808\u4e3a\u7a7a');\n return this.#stack.pop();\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n top() {\n if (this.isEmpty()) throw new Error('\u6808\u4e3a\u7a7a');\n return this.#stack[this.#stack.length - 1];\n }\n\n /* \u8fd4\u56de Array */\n toArray() {\n return this.#stack;\n }\n}\n
array_stack.ts/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n private stack: number[];\n constructor() {\n this.stack = [];\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n get size(): number {\n return this.stack.length;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n isEmpty(): boolean {\n return this.stack.length === 0;\n }\n\n /* \u5165\u6808 */\n push(num: number): void {\n this.stack.push(num);\n }\n\n /* \u51fa\u6808 */\n pop(): number | undefined {\n if (this.isEmpty()) throw new Error('\u6808\u4e3a\u7a7a');\n return this.stack.pop();\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n top(): number | undefined {\n if (this.isEmpty()) throw new Error('\u6808\u4e3a\u7a7a');\n return this.stack[this.stack.length - 1];\n }\n\n /* \u8fd4\u56de Array */\n toArray() {\n return this.stack;\n }\n}\n
array_stack.dart/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n late List<int> _stack;\n ArrayStack() {\n _stack = [];\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n int size() {\n return _stack.length;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return _stack.isEmpty;\n }\n\n /* \u5165\u6808 */\n void push(int _num) {\n _stack.add(_num);\n }\n\n /* \u51fa\u6808 */\n int pop() {\n if (isEmpty()) {\n throw Exception(\"\u6808\u4e3a\u7a7a\");\n }\n return _stack.removeLast();\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n int peek() {\n if (isEmpty()) {\n throw Exception(\"\u6808\u4e3a\u7a7a\");\n }\n return _stack.last;\n }\n\n /* \u5c06\u6808\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n List<int> toArray() => _stack;\n}\n
array_stack.rs/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nstruct ArrayStack<T> {\n stack: Vec<T>,\n}\n\nimpl<T> ArrayStack<T> {\n /* \u521d\u59cb\u5316\u6808 */\n fn new() -> ArrayStack<T> {\n ArrayStack::<T> { stack: Vec::<T>::new() }\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n fn size(&self) -> usize {\n self.stack.len()\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n fn is_empty(&self) -> bool {\n self.size() == 0\n }\n\n /* \u5165\u6808 */\n fn push(&mut self, num: T) {\n self.stack.push(num);\n }\n\n /* \u51fa\u6808 */\n fn pop(&mut self) -> Option<T> {\n match self.stack.pop() {\n Some(num) => Some(num),\n None => None,\n }\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n fn peek(&self) -> Option<&T> {\n if self.is_empty() { panic!(\"\u6808\u4e3a\u7a7a\") };\n self.stack.last()\n }\n\n /* \u8fd4\u56de &Vec */\n fn to_array(&self) -> &Vec<T> {\n &self.stack\n }\n}\n
array_stack.c/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\ntypedef struct {\n int *data;\n int size;\n} ArrayStack;\n\n/* \u6784\u9020\u51fd\u6570 */\nArrayStack *newArrayStack() {\n ArrayStack *stack = malloc(sizeof(ArrayStack));\n // \u521d\u59cb\u5316\u4e00\u4e2a\u5927\u5bb9\u91cf\uff0c\u907f\u514d\u6269\u5bb9\n stack->data = malloc(sizeof(int) * MAX_SIZE);\n stack->size = 0;\n return stack;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delArrayStack(ArrayStack *stack) {\n free(stack->data);\n free(stack);\n}\n\n/* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\nint size(ArrayStack *stack) {\n return stack->size;\n}\n\n/* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\nbool isEmpty(ArrayStack *stack) {\n return stack->size == 0;\n}\n\n/* \u5165\u6808 */\nvoid push(ArrayStack *stack, int num) {\n if (stack->size == MAX_SIZE) {\n printf(\"\u6808\u5df2\u6ee1\\n\");\n return;\n }\n stack->data[stack->size] = num;\n stack->size++;\n}\n\n/* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\nint peek(ArrayStack *stack) {\n if (stack->size == 0) {\n printf(\"\u6808\u4e3a\u7a7a\\n\");\n return INT_MAX;\n }\n return stack->data[stack->size - 1];\n}\n\n/* \u51fa\u6808 */\nint pop(ArrayStack *stack) {\n int val = peek(stack);\n stack->size--;\n return val;\n}\n
array_stack.zig// \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808\nfn ArrayStack(comptime T: type) type {\n return struct {\n const Self = @This();\n\n stack: ?std.ArrayList(T) = null, \n\n // \u6784\u9020\u65b9\u6cd5\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u6808\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator) void {\n if (self.stack == null) {\n self.stack = std.ArrayList(T).init(allocator);\n }\n }\n\n // \u6790\u6784\u65b9\u6cd5\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.stack == null) return;\n self.stack.?.deinit();\n }\n\n // \u83b7\u53d6\u6808\u7684\u957f\u5ea6\n pub fn size(self: *Self) usize {\n return self.stack.?.items.len;\n }\n\n // \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a\n pub fn isEmpty(self: *Self) bool {\n return self.size() == 0;\n }\n\n // \u8bbf\u95ee\u6808\u9876\u5143\u7d20\n pub fn peek(self: *Self) T {\n if (self.isEmpty()) @panic(\"\u6808\u4e3a\u7a7a\");\n return self.stack.?.items[self.size() - 1];\n } \n\n // \u5165\u6808\n pub fn push(self: *Self, num: T) !void {\n try self.stack.?.append(num);\n } \n\n // \u51fa\u6808\n pub fn pop(self: *Self) T {\n var num = self.stack.?.pop();\n return num;\n } \n\n // \u8fd4\u56de ArrayList\n pub fn toList(self: *Self) std.ArrayList(T) {\n return self.stack.?;\n }\n };\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/stack/#513-comparison-of-the-two-implementations","title":"5.1.3 \u00a0 Comparison of the Two Implementations","text":"Supported Operations
Both implementations support all the operations defined in a stack. The array implementation additionally supports random access, but this is beyond the scope of a stack definition and is generally not used.
Time Efficiency
In the array-based implementation, both push and pop operations occur in pre-allocated continuous memory, which has good cache locality and therefore higher efficiency. However, if the push operation exceeds the array capacity, it triggers a resizing mechanism, making the time complexity of that push operation \\(O(n)\\).
In the linked list implementation, list expansion is very flexible, and there is no efficiency decrease issue as in array expansion. However, the push operation requires initializing a node object and modifying pointers, so its efficiency is relatively lower. If the elements being pushed are already node objects, then the initialization step can be skipped, improving efficiency.
Thus, when the elements for push and pop operations are basic data types like int
or double
, we can draw the following conclusions:
Space Efficiency
When initializing a list, the system allocates an \"initial capacity,\" which might exceed the actual need; moreover, the expansion mechanism usually increases capacity by a specific factor (like doubling), which may also exceed the actual need. Therefore, the array-based stack might waste some space.
However, since linked list nodes require extra space for storing pointers, the space occupied by linked list nodes is relatively larger.
In summary, we cannot simply determine which implementation is more memory-efficient. It requires analysis based on specific circumstances.
"},{"location":"chapter_stack_and_queue/stack/#514-typical-applications-of-stack","title":"5.1.4 \u00a0 Typical Applications of Stack","text":"Q: Is the browser's forward and backward functionality implemented with a doubly linked list?
The forward and backward functionality of a browser fundamentally represents the \"stack\" concept. When a user visits a new page, it is added to the top of the stack; when they click the back button, the page is popped from the top. A double-ended queue can conveniently implement some additional operations, as mentioned in the \"Double-Ended Queue\" section.
Q: After popping from a stack, is it necessary to free the memory of the popped node?
If the popped node will still be used later, it's not necessary to free its memory. In languages like Java and Python that have automatic garbage collection, manual memory release isn't required; in C and C++, manual memory release is necessary if the node will no longer be used.
Q: A double-ended queue seems like two stacks joined together. What are its uses?
A double-ended queue is essentially a combination of a stack and a queue, or like two stacks joined together. It exhibits both stack and queue logic, therefore enabling the implementation of all applications of stacks and queues with added flexibility.
Q: How exactly are undo and redo implemented?
Undo and redo are implemented using two stacks: Stack A for undo and Stack B for redo.
Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code
Dive In Clone Repo Get PDF
The English edition is brewing...
Feel free to engage in Chinese-to-English translation and pull request review! For guidelines, please see #914.
Quote
\"An easy-to-understand book on data structures and algorithms, which guides readers to learn by minds-on and hands-on. Strongly recommended for algorithm beginners!\"
\u2014\u2014 Junhui Deng, Professor of Computer Science, Tsinghua University
Quote
\"If I had 'Hello Algo' when I was learning data structures and algorithms, it would have been 10 times easier!\"
\u2014\u2014 Mu Li, Senior Principal Scientist, Amazon
Animated illustrationsEasy to understandSmooth learning curve
\"A picture is worth a thousand words.\"
Off-the-Shelf CodeMulti programming languagesRun with one click
\"Talk is cheap. Show me the code.\"
Learning TogetherDiscussion and questions welcomeReaders progress together
\"Knowledge increases by sharing.\"
PrefaceTwo years ago, I shared the \"Sword Offer\" series of problem solutions on LeetCode, which received much love and support from many students. During my interactions with readers, the most common question I encountered was \"How to get started with algorithms.\" Gradually, I developed a deep interest in this question.
Blindly solving problems seems to be the most popular method, being simple, direct, and effective. However, problem-solving is like playing a \"Minesweeper\" game, where students with strong self-learning abilities can successfully clear the mines one by one, but those with insufficient foundations may end up bruised from explosions, retreating step by step in frustration. Thoroughly reading textbooks is also common, but for students aiming for job applications, the energy consumed by graduation, resume submissions, and preparing for written tests and interviews makes tackling thick books a daunting challenge.
If you are facing similar troubles, then you are lucky to have found this book. This book is my answer to this question, not necessarily the best solution, but at least an active attempt. Although this book won't directly land you an Offer, it will guide you through the \"knowledge map\" of data structures and algorithms, help you understand the shape, size, and distribution of different \"mines,\" and equip you with various \"demining methods.\" With these skills, I believe you can more comfortably solve problems and read literature, gradually building a complete knowledge system.
I deeply agree with Professor Feynman's saying: \"Knowledge isn't free. You have to pay attention.\" In this sense, this book is not entirely \"free.\" To not disappoint the precious \"attention\" you pay to this book, I will do my utmost, investing the greatest \"attention\" to complete the creation of this book.
AuthorYudong Jin(Krahets), Senior Algorithm Engineer in a top tech company, Master's degree from Shanghai Jiao Tong University. The highest-read blogger across the entire LeetCode, his published \"Illustration of Algorithm Data Structures\" has been subscribed to by over 300k.
ContributionThis book is continuously improved with the joint efforts of many contributors from the open-source community. Thanks to each writer who invested their time and energy, listed in the order generated by GitHub:
The code review work for this book was completed by codingonion, Gonglja, gvenusleo, hpstory, justin\u2010tse, krahets, night-cruise, nuomi1, and Reanon (listed in alphabetical order). Thanks to them for their time and effort, ensuring the standardization and uniformity of the code in various languages.
codingonionRust, Zig GongljaC, C++ gvenusleoDart hpstoryC# justin-tseJS, TS krahetsJava, Python night-cruiseRust nuomi1Swift ReanonGo, C"},{"location":"chapter_array_and_linkedlist/","title":"Chapter 4. \u00a0 Arrays and Linked Lists","text":"Abstract
The world of data structures resembles a sturdy brick wall.
In arrays, envision bricks snugly aligned, each resting seamlessly beside the next, creating a unified formation. Meanwhile, in linked lists, these bricks disperse freely, embraced by vines gracefully knitting connections between them.
"},{"location":"chapter_array_and_linkedlist/#chapter-contents","title":"Chapter Contents","text":"An \"array\" is a linear data structure that operates as a lineup of similar items, stored together in a computer's memory in contiguous spaces. It's like a sequence that maintains organized storage. Each item in this lineup has its unique 'spot' known as an \"index\". Please refer to the Figure 4-1 to observe how arrays work and grasp these key terms.
Figure 4-1 \u00a0 Array Definition and Storage Method
"},{"location":"chapter_array_and_linkedlist/array/#411-common-operations-on-arrays","title":"4.1.1 \u00a0 Common Operations on Arrays","text":""},{"location":"chapter_array_and_linkedlist/array/#1-initializing-arrays","title":"1. \u00a0 Initializing Arrays","text":"Arrays can be initialized in two ways depending on the needs: either without initial values or with specified initial values. When initial values are not specified, most programming languages will set the array elements to \\(0\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.py# Initialize array\narr: list[int] = [0] * 5 # [ 0, 0, 0, 0, 0 ]\nnums: list[int] = [1, 3, 2, 5, 4]\n
array.cpp/* Initialize array */\n// Stored on stack\nint arr[5];\nint nums[5] = { 1, 3, 2, 5, 4 };\n// Stored on heap (manual memory release needed)\nint* arr1 = new int[5];\nint* nums1 = new int[5] { 1, 3, 2, 5, 4 };\n
array.java/* Initialize array */\nint[] arr = new int[5]; // { 0, 0, 0, 0, 0 }\nint[] nums = { 1, 3, 2, 5, 4 };\n
array.cs/* Initialize array */\nint[] arr = new int[5]; // [ 0, 0, 0, 0, 0 ]\nint[] nums = [1, 3, 2, 5, 4];\n
array.go/* Initialize array */\nvar arr [5]int\n// In Go, specifying the length ([5]int) denotes an array, while not specifying it ([]int) denotes a slice.\n// Since Go's arrays are designed to have compile-time fixed length, only constants can be used to specify the length.\n// For convenience in implementing the extend() method, the Slice will be considered as an Array here.\nnums := []int{1, 3, 2, 5, 4}\n
array.swift/* Initialize array */\nlet arr = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0]\nlet nums = [1, 3, 2, 5, 4]\n
array.js/* Initialize array */\nvar arr = new Array(5).fill(0);\nvar nums = [1, 3, 2, 5, 4];\n
array.ts/* Initialize array */\nlet arr: number[] = new Array(5).fill(0);\nlet nums: number[] = [1, 3, 2, 5, 4];\n
array.dart/* Initialize array */\nList<int> arr = List.filled(5, 0); // [0, 0, 0, 0, 0]\nList<int> nums = [1, 3, 2, 5, 4];\n
array.rs/* Initialize array */\nlet arr: Vec<i32> = vec![0; 5]; // [0, 0, 0, 0, 0]\nlet nums: Vec<i32> = vec![1, 3, 2, 5, 4];\n
array.c/* Initialize array */\nint arr[5] = { 0 }; // { 0, 0, 0, 0, 0 }\nint nums[5] = { 1, 3, 2, 5, 4 };\n
array.zig// Initialize array\nvar arr = [_]i32{0} ** 5; // { 0, 0, 0, 0, 0 }\nvar nums = [_]i32{ 1, 3, 2, 5, 4 };\n
"},{"location":"chapter_array_and_linkedlist/array/#2-accessing-elements","title":"2. \u00a0 Accessing Elements","text":"Elements in an array are stored in contiguous memory spaces, making it simpler to compute each element's memory address. The formula shown in the Figure below aids in determining an element's memory address, utilizing the array's memory address (specifically, the first element's address) and the element's index. This computation streamlines direct access to the desired element.
Figure 4-2 \u00a0 Memory Address Calculation for Array Elements
As observed in the above illustration, array indexing conventionally begins at \\(0\\). While this might appear counterintuitive, considering counting usually starts at \\(1\\), within the address calculation formula, an index is essentially an offset from the memory address. For the first element's address, this offset is \\(0\\), validating its index as \\(0\\).
Accessing elements in an array is highly efficient, allowing us to randomly access any element in \\(O(1)\\) time.
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef random_access(nums: list[int]) -> int:\n \"\"\"\u968f\u673a\u8bbf\u95ee\u5143\u7d20\"\"\"\n # \u5728\u533a\u95f4 [0, len(nums)-1] \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n random_index = random.randint(0, len(nums) - 1)\n # \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n random_num = nums[random_index]\n return random_num\n
array.cpp/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nint randomAccess(int *nums, int size) {\n // \u5728\u533a\u95f4 [0, size) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n int randomIndex = rand() % size;\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n int randomNum = nums[randomIndex];\n return randomNum;\n}\n
array.java/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nint randomAccess(int[] nums) {\n // \u5728\u533a\u95f4 [0, nums.length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n int randomIndex = ThreadLocalRandom.current().nextInt(0, nums.length);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n int randomNum = nums[randomIndex];\n return randomNum;\n}\n
array.cs/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nint RandomAccess(int[] nums) {\n Random random = new();\n // \u5728\u533a\u95f4 [0, nums.Length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n int randomIndex = random.Next(nums.Length);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n int randomNum = nums[randomIndex];\n return randomNum;\n}\n
array.go/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nfunc randomAccess(nums []int) (randomNum int) {\n // \u5728\u533a\u95f4 [0, nums.length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n randomIndex := rand.Intn(len(nums))\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n randomNum = nums[randomIndex]\n return\n}\n
array.swift/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nfunc randomAccess(nums: [Int]) -> Int {\n // \u5728\u533a\u95f4 [0, nums.count) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n let randomIndex = nums.indices.randomElement()!\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n let randomNum = nums[randomIndex]\n return randomNum\n}\n
array.js/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nfunction randomAccess(nums) {\n // \u5728\u533a\u95f4 [0, nums.length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n const random_index = Math.floor(Math.random() * nums.length);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n const random_num = nums[random_index];\n return random_num;\n}\n
array.ts/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nfunction randomAccess(nums: number[]): number {\n // \u5728\u533a\u95f4 [0, nums.length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n const random_index = Math.floor(Math.random() * nums.length);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n const random_num = nums[random_index];\n return random_num;\n}\n
array.dart/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nint randomAccess(List<int> nums) {\n // \u5728\u533a\u95f4 [0, nums.length) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n int randomIndex = Random().nextInt(nums.length);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n int randomNum = nums[randomIndex];\n return randomNum;\n}\n
array.rs/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nfn random_access(nums: &[i32]) -> i32 {\n // \u5728\u533a\u95f4 [0, nums.len()) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n let random_index = rand::thread_rng().gen_range(0..nums.len());\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n let random_num = nums[random_index];\n random_num\n}\n
array.c/* \u968f\u673a\u8bbf\u95ee\u5143\u7d20 */\nint randomAccess(int *nums, int size) {\n // \u5728\u533a\u95f4 [0, size) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6570\u5b57\n int randomIndex = rand() % size;\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n int randomNum = nums[randomIndex];\n return randomNum;\n}\n
array.zig// \u968f\u673a\u8bbf\u95ee\u5143\u7d20\nfn randomAccess(nums: []i32) i32 {\n // \u5728\u533a\u95f4 [0, nums.len) \u4e2d\u968f\u673a\u62bd\u53d6\u4e00\u4e2a\u6574\u6570\n var randomIndex = std.crypto.random.intRangeLessThan(usize, 0, nums.len);\n // \u83b7\u53d6\u5e76\u8fd4\u56de\u968f\u673a\u5143\u7d20\n var randomNum = nums[randomIndex];\n return randomNum;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/array/#3-inserting-elements","title":"3. \u00a0 Inserting Elements","text":"Array elements are tightly packed in memory, with no space available to accommodate additional data between them. Illustrated in Figure below, inserting an element in the middle of an array requires shifting all subsequent elements back by one position to create room for the new element.
Figure 4-3 \u00a0 Array Element Insertion Example
It's important to note that due to the fixed length of an array, inserting an element will unavoidably result in the loss of the last element in the array. Solutions to address this issue will be explored in the \"List\" chapter.
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef insert(nums: list[int], num: int, index: int):\n \"\"\"\u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num\"\"\"\n # \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for i in range(len(nums) - 1, index, -1):\n nums[i] = nums[i - 1]\n # \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num\n
array.cpp/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nvoid insert(int *nums, int size, int num, int index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int i = size - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.java/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nvoid insert(int[] nums, int num, int index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int i = nums.length - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.cs/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nvoid Insert(int[] nums, int num, int index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int i = nums.Length - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.go/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nfunc insert(nums []int, num int, index int) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for i := len(nums) - 1; i > index; i-- {\n nums[i] = nums[i-1]\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num\n}\n
array.swift/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nfunc insert(nums: inout [Int], num: Int, index: Int) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for i in nums.indices.dropFirst(index).reversed() {\n nums[i] = nums[i - 1]\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num\n}\n
array.js/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nfunction insert(nums, num, index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (let i = nums.length - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.ts/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nfunction insert(nums: number[], num: number, index: number): void {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (let i = nums.length - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.dart/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 _num */\nvoid insert(List<int> nums, int _num, int index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (var i = nums.length - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 _num \u8d4b\u7ed9 index \u5904\u5143\u7d20\n nums[index] = _num;\n}\n
array.rs/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nfn insert(nums: &mut Vec<i32>, num: i32, index: usize) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for i in (index + 1..nums.len()).rev() {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.c/* \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num */\nvoid insert(int *nums, int size, int num, int index) {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int i = size - 1; i > index; i--) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
array.zig// \u5728\u6570\u7ec4\u7684\u7d22\u5f15 index \u5904\u63d2\u5165\u5143\u7d20 num\nfn insert(nums: []i32, num: i32, index: usize) void {\n // \u628a\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n var i = nums.len - 1;\n while (i > index) : (i -= 1) {\n nums[i] = nums[i - 1];\n }\n // \u5c06 num \u8d4b\u7ed9 index \u5904\u7684\u5143\u7d20\n nums[index] = num;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/array/#4-deleting-elements","title":"4. \u00a0 Deleting Elements","text":"Similarly, as depicted in the Figure 4-4 , to delete an element at index \\(i\\), all elements following index \\(i\\) must be moved forward by one position.
Figure 4-4 \u00a0 Array Element Deletion Example
Please note that after deletion, the former last element becomes \"meaningless,\" hence requiring no specific modification.
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef remove(nums: list[int], index: int):\n \"\"\"\u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20\"\"\"\n # \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for i in range(index, len(nums) - 1):\n nums[i] = nums[i + 1]\n
array.cpp/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nvoid remove(int *nums, int size, int index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int i = index; i < size - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.java/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nvoid remove(int[] nums, int index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int i = index; i < nums.length - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.cs/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nvoid Remove(int[] nums, int index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int i = index; i < nums.Length - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.go/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nfunc remove(nums []int, index int) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for i := index; i < len(nums)-1; i++ {\n nums[i] = nums[i+1]\n }\n}\n
array.swift/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nfunc remove(nums: inout [Int], index: Int) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for i in nums.indices.dropFirst(index).dropLast() {\n nums[i] = nums[i + 1]\n }\n}\n
array.js/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nfunction remove(nums, index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (let i = index; i < nums.length - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.ts/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nfunction remove(nums: number[], index: number): void {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (let i = index; i < nums.length - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.dart/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nvoid remove(List<int> nums, int index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (var i = index; i < nums.length - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.rs/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\nfn remove(nums: &mut Vec<i32>, index: usize) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for i in index..nums.len() - 1 {\n nums[i] = nums[i + 1];\n }\n}\n
array.c/* \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20 */\n// \u6ce8\u610f\uff1astdio.h \u5360\u7528\u4e86 remove \u5173\u952e\u8bcd\nvoid removeItem(int *nums, int size, int index) {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int i = index; i < size - 1; i++) {\n nums[i] = nums[i + 1];\n }\n}\n
array.zig// \u5220\u9664\u7d22\u5f15 index \u5904\u7684\u5143\u7d20\nfn remove(nums: []i32, index: usize) void {\n // \u628a\u7d22\u5f15 index \u4e4b\u540e\u7684\u6240\u6709\u5143\u7d20\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n var i = index;\n while (i < nums.len - 1) : (i += 1) {\n nums[i] = nums[i + 1];\n }\n}\n
Visualizing Code Full Screen >
In summary, the insertion and deletion operations in arrays present the following disadvantages:
In most programming languages, we can traverse an array either by using indices or by directly iterating over each element:
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef traverse(nums: list[int]):\n \"\"\"\u904d\u5386\u6570\u7ec4\"\"\"\n count = 0\n # \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for i in range(len(nums)):\n count += nums[i]\n # \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for num in nums:\n count += num\n # \u540c\u65f6\u904d\u5386\u6570\u636e\u7d22\u5f15\u548c\u5143\u7d20\n for i, num in enumerate(nums):\n count += nums[i]\n count += num\n
array.cpp/* \u904d\u5386\u6570\u7ec4 */\nvoid traverse(int *nums, int size) {\n int count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (int i = 0; i < size; i++) {\n count += nums[i];\n }\n}\n
array.java/* \u904d\u5386\u6570\u7ec4 */\nvoid traverse(int[] nums) {\n int count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (int i = 0; i < nums.length; i++) {\n count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for (int num : nums) {\n count += num;\n }\n}\n
array.cs/* \u904d\u5386\u6570\u7ec4 */\nvoid Traverse(int[] nums) {\n int count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (int i = 0; i < nums.Length; i++) {\n count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n foreach (int num in nums) {\n count += num;\n }\n}\n
array.go/* \u904d\u5386\u6570\u7ec4 */\nfunc traverse(nums []int) {\n count := 0\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for i := 0; i < len(nums); i++ {\n count += nums[i]\n }\n count = 0\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for _, num := range nums {\n count += num\n }\n // \u540c\u65f6\u904d\u5386\u6570\u636e\u7d22\u5f15\u548c\u5143\u7d20\n for i, num := range nums {\n count += nums[i]\n count += num\n }\n}\n
array.swift/* \u904d\u5386\u6570\u7ec4 */\nfunc traverse(nums: [Int]) {\n var count = 0\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for i in nums.indices {\n count += nums[i]\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for num in nums {\n count += num\n }\n}\n
array.js/* \u904d\u5386\u6570\u7ec4 */\nfunction traverse(nums) {\n let count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (let i = 0; i < nums.length; i++) {\n count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for (const num of nums) {\n count += num;\n }\n}\n
array.ts/* \u904d\u5386\u6570\u7ec4 */\nfunction traverse(nums: number[]): void {\n let count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (let i = 0; i < nums.length; i++) {\n count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for (const num of nums) {\n count += num;\n }\n}\n
array.dart/* \u904d\u5386\u6570\u7ec4\u5143\u7d20 */\nvoid traverse(List<int> nums) {\n int count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (var i = 0; i < nums.length; i++) {\n count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for (int _num in nums) {\n count += _num;\n }\n // \u901a\u8fc7 forEach \u65b9\u6cd5\u904d\u5386\u6570\u7ec4\n nums.forEach((_num) {\n count += _num;\n });\n}\n
array.rs/* \u904d\u5386\u6570\u7ec4 */\nfn traverse(nums: &[i32]) {\n let mut _count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for i in 0..nums.len() {\n _count += nums[i];\n }\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for num in nums {\n _count += num;\n }\n}\n
array.c/* \u904d\u5386\u6570\u7ec4 */\nvoid traverse(int *nums, int size) {\n int count = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n for (int i = 0; i < size; i++) {\n count += nums[i];\n }\n}\n
array.zig// \u904d\u5386\u6570\u7ec4\nfn traverse(nums: []i32) void {\n var count: i32 = 0;\n // \u901a\u8fc7\u7d22\u5f15\u904d\u5386\u6570\u7ec4\n var i: i32 = 0;\n while (i < nums.len) : (i += 1) {\n count += nums[i];\n }\n count = 0;\n // \u76f4\u63a5\u904d\u5386\u6570\u7ec4\u5143\u7d20\n for (nums) |num| {\n count += num;\n }\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/array/#6-finding-elements","title":"6. \u00a0 Finding Elements","text":"Locating a specific element within an array involves iterating through the array, checking each element to determine if it matches the desired value.
Because arrays are linear data structures, this operation is commonly referred to as \"linear search.\"
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef find(nums: list[int], target: int) -> int:\n \"\"\"\u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20\"\"\"\n for i in range(len(nums)):\n if nums[i] == target:\n return i\n return -1\n
array.cpp/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nint find(int *nums, int size, int target) {\n for (int i = 0; i < size; i++) {\n if (nums[i] == target)\n return i;\n }\n return -1;\n}\n
array.java/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nint find(int[] nums, int target) {\n for (int i = 0; i < nums.length; i++) {\n if (nums[i] == target)\n return i;\n }\n return -1;\n}\n
array.cs/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nint Find(int[] nums, int target) {\n for (int i = 0; i < nums.Length; i++) {\n if (nums[i] == target)\n return i;\n }\n return -1;\n}\n
array.go/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nfunc find(nums []int, target int) (index int) {\n index = -1\n for i := 0; i < len(nums); i++ {\n if nums[i] == target {\n index = i\n break\n }\n }\n return\n}\n
array.swift/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nfunc find(nums: [Int], target: Int) -> Int {\n for i in nums.indices {\n if nums[i] == target {\n return i\n }\n }\n return -1\n}\n
array.js/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nfunction find(nums, target) {\n for (let i = 0; i < nums.length; i++) {\n if (nums[i] === target) return i;\n }\n return -1;\n}\n
array.ts/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nfunction find(nums: number[], target: number): number {\n for (let i = 0; i < nums.length; i++) {\n if (nums[i] === target) {\n return i;\n }\n }\n return -1;\n}\n
array.dart/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nint find(List<int> nums, int target) {\n for (var i = 0; i < nums.length; i++) {\n if (nums[i] == target) return i;\n }\n return -1;\n}\n
array.rs/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nfn find(nums: &[i32], target: i32) -> Option<usize> {\n for i in 0..nums.len() {\n if nums[i] == target {\n return Some(i);\n }\n }\n None\n}\n
array.c/* \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20 */\nint find(int *nums, int size, int target) {\n for (int i = 0; i < size; i++) {\n if (nums[i] == target)\n return i;\n }\n return -1;\n}\n
array.zig// \u5728\u6570\u7ec4\u4e2d\u67e5\u627e\u6307\u5b9a\u5143\u7d20\nfn find(nums: []i32, target: i32) i32 {\n for (nums, 0..) |num, i| {\n if (num == target) return @intCast(i);\n }\n return -1;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/array/#7-expanding-arrays","title":"7. \u00a0 Expanding Arrays","text":"In complex system environments, ensuring the availability of memory space after an array for safe capacity extension becomes challenging. Consequently, in most programming languages, the length of an array is immutable.
To expand an array, it's necessary to create a larger array and then copy the elements from the original array. This operation has a time complexity of \\(O(n)\\) and can be time-consuming for large arrays. The code are as follows:
PythonC++JavaC#GoSwiftJSTSDartRustCZig array.pydef extend(nums: list[int], enlarge: int) -> list[int]:\n \"\"\"\u6269\u5c55\u6570\u7ec4\u957f\u5ea6\"\"\"\n # \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n res = [0] * (len(nums) + enlarge)\n # \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for i in range(len(nums)):\n res[i] = nums[i]\n # \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res\n
array.cpp/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nint *extend(int *nums, int size, int enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n int *res = new int[size + enlarge];\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (int i = 0; i < size; i++) {\n res[i] = nums[i];\n }\n // \u91ca\u653e\u5185\u5b58\n delete[] nums;\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.java/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nint[] extend(int[] nums, int enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n int[] res = new int[nums.length + enlarge];\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (int i = 0; i < nums.length; i++) {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.cs/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nint[] Extend(int[] nums, int enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n int[] res = new int[nums.Length + enlarge];\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (int i = 0; i < nums.Length; i++) {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.go/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nfunc extend(nums []int, enlarge int) []int {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n res := make([]int, len(nums)+enlarge)\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for i, num := range nums {\n res[i] = num\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res\n}\n
array.swift/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nfunc extend(nums: [Int], enlarge: Int) -> [Int] {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n var res = Array(repeating: 0, count: nums.count + enlarge)\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for i in nums.indices {\n res[i] = nums[i]\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res\n}\n
array.js/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\n// \u8bf7\u6ce8\u610f\uff0cJavaScript \u7684 Array \u662f\u52a8\u6001\u6570\u7ec4\uff0c\u53ef\u4ee5\u76f4\u63a5\u6269\u5c55\n// \u4e3a\u4e86\u65b9\u4fbf\u5b66\u4e60\uff0c\u672c\u51fd\u6570\u5c06 Array \u770b\u4f5c\u957f\u5ea6\u4e0d\u53ef\u53d8\u7684\u6570\u7ec4\nfunction extend(nums, enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n const res = new Array(nums.length + enlarge).fill(0);\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (let i = 0; i < nums.length; i++) {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.ts/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\n// \u8bf7\u6ce8\u610f\uff0cTypeScript \u7684 Array \u662f\u52a8\u6001\u6570\u7ec4\uff0c\u53ef\u4ee5\u76f4\u63a5\u6269\u5c55\n// \u4e3a\u4e86\u65b9\u4fbf\u5b66\u4e60\uff0c\u672c\u51fd\u6570\u5c06 Array \u770b\u4f5c\u957f\u5ea6\u4e0d\u53ef\u53d8\u7684\u6570\u7ec4\nfunction extend(nums: number[], enlarge: number): number[] {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n const res = new Array(nums.length + enlarge).fill(0);\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (let i = 0; i < nums.length; i++) {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.dart/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nList<int> extend(List<int> nums, int enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n List<int> res = List.filled(nums.length + enlarge, 0);\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (var i = 0; i < nums.length; i++) {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.rs/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nfn extend(nums: Vec<i32>, enlarge: usize) -> Vec<i32> {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n let mut res: Vec<i32> = vec![0; nums.len() + enlarge];\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\n for i in 0..nums.len() {\n res[i] = nums[i];\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n res\n}\n
array.c/* \u6269\u5c55\u6570\u7ec4\u957f\u5ea6 */\nint *extend(int *nums, int size, int enlarge) {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n int *res = (int *)malloc(sizeof(int) * (size + enlarge));\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (int i = 0; i < size; i++) {\n res[i] = nums[i];\n }\n // \u521d\u59cb\u5316\u6269\u5c55\u540e\u7684\u7a7a\u95f4\n for (int i = size; i < size + enlarge; i++) {\n res[i] = 0;\n }\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
array.zig// \u6269\u5c55\u6570\u7ec4\u957f\u5ea6\nfn extend(mem_allocator: std.mem.Allocator, nums: []i32, enlarge: usize) ![]i32 {\n // \u521d\u59cb\u5316\u4e00\u4e2a\u6269\u5c55\u957f\u5ea6\u540e\u7684\u6570\u7ec4\n var res = try mem_allocator.alloc(i32, nums.len + enlarge);\n @memset(res, 0);\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n std.mem.copy(i32, res, nums);\n // \u8fd4\u56de\u6269\u5c55\u540e\u7684\u65b0\u6570\u7ec4\n return res;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/array/#412-advantages-and-limitations-of-arrays","title":"4.1.2 \u00a0 Advantages and Limitations of Arrays","text":"Arrays are stored in contiguous memory spaces and consist of elements of the same type. This approach provides substantial prior information that systems can leverage to optimize the efficiency of data structure operations.
However, continuous space storage is a double-edged sword, with the following limitations:
Arrays are fundamental and widely used data structures. They find frequent application in various algorithms and serve in the implementation of complex data structures.
Memory space is a common resource for all programs. In a complex system environment, free memory space can be scattered throughout memory. We know that the memory space for storing an array must be contiguous, and when the array is very large, it may not be possible to provide such a large contiguous space. This is where the flexibility advantage of linked lists becomes apparent.
A \"linked list\" is a linear data structure where each element is a node object, and the nodes are connected via \"references\". A reference records the memory address of the next node, allowing access to the next node from the current one.
The design of a linked list allows its nodes to be scattered throughout memory, with no need for contiguous memory addresses.
Figure 4-5 \u00a0 Linked List Definition and Storage Method
Observing the image above, the fundamental unit of a linked list is the \"node\" object. Each node contains two pieces of data: the \"value\" of the node and the \"reference\" to the next node.
null
in Java, nullptr
in C++, and None
in Python.As shown in the following code, a linked list node ListNode
, apart from containing a value, also needs to store a reference (pointer). Therefore, a linked list consumes more memory space than an array for the same amount of data.
class ListNode:\n \"\"\"Linked List Node Class\"\"\"\n def __init__(self, val: int):\n self.val: int = val # Node value\n self.next: ListNode | None = None # Reference to the next node\n
/* Linked List Node Structure */\nstruct ListNode {\n int val; // Node value\n ListNode *next; // Pointer to the next node\n ListNode(int x) : val(x), next(nullptr) {} // Constructor\n};\n
/* Linked List Node Class */\nclass ListNode {\n int val; // Node value\n ListNode next; // Reference to the next node\n ListNode(int x) { val = x; } // Constructor\n}\n
/* Linked List Node Class */\nclass ListNode(int x) { // Constructor\n int val = x; // Node value\n ListNode? next; // Reference to the next node\n}\n
/* Linked List Node Structure */\ntype ListNode struct {\n Val int // Node value\n Next *ListNode // Pointer to the next node\n}\n\n// NewListNode Constructor, creates a new linked list\nfunc NewListNode(val int) *ListNode {\n return &ListNode{\n Val: val,\n Next: nil,\n }\n}\n
/* Linked List Node Class */\nclass ListNode {\n var val: Int // Node value\n var next: ListNode? // Reference to the next node\n\n init(x: Int) { // Constructor\n val = x\n }\n}\n
/* Linked List Node Class */\nclass ListNode {\n constructor(val, next) {\n this.val = (val === undefined ? 0 : val); // Node value\n this.next = (next === undefined ? null : next); // Reference to the next node\n }\n}\n
/* Linked List Node Class */\nclass ListNode {\n val: number;\n next: ListNode | null;\n constructor(val?: number, next?: ListNode | null) {\n this.val = val === undefined ? 0 : val; // Node value\n this.next = next === undefined ? null : next; // Reference to the next node\n }\n}\n
/* \u94fe\u8868\u8282\u70b9\u7c7b */\nclass ListNode {\n int val; // Node value\n ListNode? next; // Reference to the next node\n ListNode(this.val, [this.next]); // Constructor\n}\n
use std::rc::Rc;\nuse std::cell::RefCell;\n/* Linked List Node Class */\n#[derive(Debug)]\nstruct ListNode {\n val: i32, // Node value\n next: Option<Rc<RefCell<ListNode>>>, // Pointer to the next node\n}\n
/* Linked List Node Structure */\ntypedef struct ListNode {\n int val; // Node value\n struct ListNode *next; // Pointer to the next node\n} ListNode;\n\n/* Constructor */\nListNode *newListNode(int val) {\n ListNode *node;\n node = (ListNode *) malloc(sizeof(ListNode));\n node->val = val;\n node->next = NULL;\n return node;\n}\n
// Linked List Node Class\npub fn ListNode(comptime T: type) type {\n return struct {\n const Self = @This();\n\n val: T = 0, // Node value\n next: ?*Self = null, // Pointer to the next node\n\n // Constructor\n pub fn init(self: *Self, x: i32) void {\n self.val = x;\n self.next = null;\n }\n };\n}\n
"},{"location":"chapter_array_and_linkedlist/linked_list/#421-common-operations-on-linked-lists","title":"4.2.1 \u00a0 Common Operations on Linked Lists","text":""},{"location":"chapter_array_and_linkedlist/linked_list/#1-initializing-a-linked-list","title":"1. \u00a0 Initializing a Linked List","text":"Building a linked list involves two steps: initializing each node object and then establishing the references between nodes. Once initialized, we can access all nodes sequentially from the head node via the next
reference.
# Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4\n# Initialize each node\nn0 = ListNode(1)\nn1 = ListNode(3)\nn2 = ListNode(2)\nn3 = ListNode(5)\nn4 = ListNode(4)\n# Build references between nodes\nn0.next = n1\nn1.next = n2\nn2.next = n3\nn3.next = n4\n
linked_list.cpp/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nListNode* n0 = new ListNode(1);\nListNode* n1 = new ListNode(3);\nListNode* n2 = new ListNode(2);\nListNode* n3 = new ListNode(5);\nListNode* n4 = new ListNode(4);\n// Build references between nodes\nn0->next = n1;\nn1->next = n2;\nn2->next = n3;\nn3->next = n4;\n
linked_list.java/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nListNode n0 = new ListNode(1);\nListNode n1 = new ListNode(3);\nListNode n2 = new ListNode(2);\nListNode n3 = new ListNode(5);\nListNode n4 = new ListNode(4);\n// Build references between nodes\nn0.next = n1;\nn1.next = n2;\nn2.next = n3;\nn3.next = n4;\n
linked_list.cs/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nListNode n0 = new(1);\nListNode n1 = new(3);\nListNode n2 = new(2);\nListNode n3 = new(5);\nListNode n4 = new(4);\n// Build references between nodes\nn0.next = n1;\nn1.next = n2;\nn2.next = n3;\nn3.next = n4;\n
linked_list.go/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nn0 := NewListNode(1)\nn1 := NewListNode(3)\nn2 := NewListNode(2)\nn3 := NewListNode(5)\nn4 := NewListNode(4)\n// Build references between nodes\nn0.Next = n1\nn1.Next = n2\nn2.Next = n3\nn3.Next = n4\n
linked_list.swift/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nlet n0 = ListNode(x: 1)\nlet n1 = ListNode(x: 3)\nlet n2 = ListNode(x: 2)\nlet n3 = ListNode(x: 5)\nlet n4 = ListNode(x: 4)\n// Build references between nodes\nn0.next = n1\nn1.next = n2\nn2.next = n3\nn3.next = n4\n
linked_list.js/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nconst n0 = new ListNode(1);\nconst n1 = new ListNode(3);\nconst n2 = new ListNode(2);\nconst n3 = new ListNode(5);\nconst n4 = new ListNode(4);\n// Build references between nodes\nn0.next = n1;\nn1.next = n2;\nn2.next = n3;\nn3.next = n4;\n
linked_list.ts/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nconst n0 = new ListNode(1);\nconst n1 = new ListNode(3);\nconst n2 = new ListNode(2);\nconst n3 = new ListNode(5);\nconst n4 = new ListNode(4);\n// Build references between nodes\nn0.next = n1;\nn1.next = n2;\nn2.next = n3;\nn3.next = n4;\n
linked_list.dart/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nListNode n0 = ListNode(1);\nListNode n1 = ListNode(3);\nListNode n2 = ListNode(2);\nListNode n3 = ListNode(5);\nListNode n4 = ListNode(4);\n// Build references between nodes\nn0.next = n1;\nn1.next = n2;\nn2.next = n3;\nn3.next = n4;\n
linked_list.rs/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nlet n0 = Rc::new(RefCell::new(ListNode { val: 1, next: None }));\nlet n1 = Rc::new(RefCell::new(ListNode { val: 3, next: None }));\nlet n2 = Rc::new(RefCell::new(ListNode { val: 2, next: None }));\nlet n3 = Rc::new(RefCell::new(ListNode { val: 5, next: None }));\nlet n4 = Rc::new(RefCell::new(ListNode { val: 4, next: None }));\n\n// Build references between nodes\nn0.borrow_mut().next = Some(n1.clone());\nn1.borrow_mut().next = Some(n2.clone());\nn2.borrow_mut().next = Some(n3.clone());\nn3.borrow_mut().next = Some(n4.clone());\n
linked_list.c/* Initialize linked list: 1 -> 3 -> 2 -> 5 -> 4 */\n// Initialize each node\nListNode* n0 = newListNode(1);\nListNode* n1 = newListNode(3);\nListNode* n2 = newListNode(2);\nListNode* n3 = newListNode(5);\nListNode* n4 = newListNode(4);\n// Build references between nodes\nn0->next = n1;\nn1->next = n2;\nn2->next = n3;\nn3->next = n4;\n
linked_list.zig// Initialize linked list\n// Initialize each node\nvar n0 = inc.ListNode(i32){.val = 1};\nvar n1 = inc.ListNode(i32){.val = 3};\nvar n2 = inc.ListNode(i32){.val = 2};\nvar n3 = inc.ListNode(i32){.val = 5};\nvar n4 = inc.ListNode(i32){.val = 4};\n// Build references between nodes\nn0.next = &n1;\nn1.next = &n2;\nn2.next = &n3;\nn3.next = &n4;\n
An array is a single variable, such as the array nums
containing elements nums[0]
, nums[1]
, etc., while a linked list is composed of multiple independent node objects. We usually refer to the linked list by its head node, as in the linked list n0
in the above code.
Inserting a node in a linked list is very easy. As shown in the image below, suppose we want to insert a new node P
between two adjacent nodes n0
and n1
. This requires changing only two node references (pointers), with a time complexity of \\(O(1)\\).
In contrast, the time complexity of inserting an element in an array is \\(O(n)\\), which is less efficient with large data volumes.
Figure 4-6 \u00a0 Linked List Node Insertion Example
PythonC++JavaC#GoSwiftJSTSDartRustCZig linked_list.pydef insert(n0: ListNode, P: ListNode):\n \"\"\"\u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P\"\"\"\n n1 = n0.next\n P.next = n1\n n0.next = P\n
linked_list.cpp/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nvoid insert(ListNode *n0, ListNode *P) {\n ListNode *n1 = n0->next;\n P->next = n1;\n n0->next = P;\n}\n
linked_list.java/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nvoid insert(ListNode n0, ListNode P) {\n ListNode n1 = n0.next;\n P.next = n1;\n n0.next = P;\n}\n
linked_list.cs/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nvoid Insert(ListNode n0, ListNode P) {\n ListNode? n1 = n0.next;\n P.next = n1;\n n0.next = P;\n}\n
linked_list.go/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nfunc insertNode(n0 *ListNode, P *ListNode) {\n n1 := n0.Next\n P.Next = n1\n n0.Next = P\n}\n
linked_list.swift/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nfunc insert(n0: ListNode, P: ListNode) {\n let n1 = n0.next\n P.next = n1\n n0.next = P\n}\n
linked_list.js/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nfunction insert(n0, P) {\n const n1 = n0.next;\n P.next = n1;\n n0.next = P;\n}\n
linked_list.ts/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nfunction insert(n0: ListNode, P: ListNode): void {\n const n1 = n0.next;\n P.next = n1;\n n0.next = P;\n}\n
linked_list.dart/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nvoid insert(ListNode n0, ListNode P) {\n ListNode? n1 = n0.next;\n P.next = n1;\n n0.next = P;\n}\n
linked_list.rs/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\n#[allow(non_snake_case)]\npub fn insert<T>(n0: &Rc<RefCell<ListNode<T>>>, P: Rc<RefCell<ListNode<T>>>) {\n let n1 = n0.borrow_mut().next.take();\n P.borrow_mut().next = n1;\n n0.borrow_mut().next = Some(P);\n}\n
linked_list.c/* \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P */\nvoid insert(ListNode *n0, ListNode *P) {\n ListNode *n1 = n0->next;\n P->next = n1;\n n0->next = P;\n}\n
linked_list.zig// \u5728\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u63d2\u5165\u8282\u70b9 P\nfn insert(n0: ?*inc.ListNode(i32), P: ?*inc.ListNode(i32)) void {\n var n1 = n0.?.next;\n P.?.next = n1;\n n0.?.next = P;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/linked_list/#3-deleting-a-node","title":"3. \u00a0 Deleting a Node","text":"As shown below, deleting a node in a linked list is also very convenient, requiring only the change of one node's reference (pointer).
Note that although node P
still points to n1
after the deletion operation is completed, it is no longer accessible when traversing the list, meaning P
is no longer part of the list.
Figure 4-7 \u00a0 Linked List Node Deletion
PythonC++JavaC#GoSwiftJSTSDartRustCZig linked_list.pydef remove(n0: ListNode):\n \"\"\"\u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9\"\"\"\n if not n0.next:\n return\n # n0 -> P -> n1\n P = n0.next\n n1 = P.next\n n0.next = n1\n
linked_list.cpp/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nvoid remove(ListNode *n0) {\n if (n0->next == nullptr)\n return;\n // n0 -> P -> n1\n ListNode *P = n0->next;\n ListNode *n1 = P->next;\n n0->next = n1;\n // \u91ca\u653e\u5185\u5b58\n delete P;\n}\n
linked_list.java/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nvoid remove(ListNode n0) {\n if (n0.next == null)\n return;\n // n0 -> P -> n1\n ListNode P = n0.next;\n ListNode n1 = P.next;\n n0.next = n1;\n}\n
linked_list.cs/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nvoid Remove(ListNode n0) {\n if (n0.next == null)\n return;\n // n0 -> P -> n1\n ListNode P = n0.next;\n ListNode? n1 = P.next;\n n0.next = n1;\n}\n
linked_list.go/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nfunc removeItem(n0 *ListNode) {\n if n0.Next == nil {\n return\n }\n // n0 -> P -> n1\n P := n0.Next\n n1 := P.Next\n n0.Next = n1\n}\n
linked_list.swift/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nfunc remove(n0: ListNode) {\n if n0.next == nil {\n return\n }\n // n0 -> P -> n1\n let P = n0.next\n let n1 = P?.next\n n0.next = n1\n P?.next = nil\n}\n
linked_list.js/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nfunction remove(n0) {\n if (!n0.next) return;\n // n0 -> P -> n1\n const P = n0.next;\n const n1 = P.next;\n n0.next = n1;\n}\n
linked_list.ts/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nfunction remove(n0: ListNode): void {\n if (!n0.next) {\n return;\n }\n // n0 -> P -> n1\n const P = n0.next;\n const n1 = P.next;\n n0.next = n1;\n}\n
linked_list.dart/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\nvoid remove(ListNode n0) {\n if (n0.next == null) return;\n // n0 -> P -> n1\n ListNode P = n0.next!;\n ListNode? n1 = P.next;\n n0.next = n1;\n}\n
linked_list.rs/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\n#[allow(non_snake_case)]\npub fn remove<T>(n0: &Rc<RefCell<ListNode<T>>>) {\n if n0.borrow().next.is_none() {return};\n // n0 -> P -> n1\n let P = n0.borrow_mut().next.take();\n if let Some(node) = P {\n let n1 = node.borrow_mut().next.take();\n n0.borrow_mut().next = n1;\n }\n}\n
linked_list.c/* \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9 */\n// \u6ce8\u610f\uff1astdio.h \u5360\u7528\u4e86 remove \u5173\u952e\u8bcd\nvoid removeItem(ListNode *n0) {\n if (!n0->next)\n return;\n // n0 -> P -> n1\n ListNode *P = n0->next;\n ListNode *n1 = P->next;\n n0->next = n1;\n // \u91ca\u653e\u5185\u5b58\n free(P);\n}\n
linked_list.zig// \u5220\u9664\u94fe\u8868\u7684\u8282\u70b9 n0 \u4e4b\u540e\u7684\u9996\u4e2a\u8282\u70b9\nfn remove(n0: ?*inc.ListNode(i32)) void {\n if (n0.?.next == null) return;\n // n0 -> P -> n1\n var P = n0.?.next;\n var n1 = P.?.next;\n n0.?.next = n1;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/linked_list/#4-accessing-nodes","title":"4. \u00a0 Accessing Nodes","text":"Accessing nodes in a linked list is less efficient. As mentioned earlier, any element in an array can be accessed in \\(O(1)\\) time. However, in a linked list, the program needs to start from the head node and traverse each node sequentially until it finds the target node. That is, accessing the \\(i\\)-th node of a linked list requires \\(i - 1\\) iterations, with a time complexity of \\(O(n)\\).
PythonC++JavaC#GoSwiftJSTSDartRustCZig linked_list.pydef access(head: ListNode, index: int) -> ListNode | None:\n \"\"\"\u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9\"\"\"\n for _ in range(index):\n if not head:\n return None\n head = head.next\n return head\n
linked_list.cpp/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nListNode *access(ListNode *head, int index) {\n for (int i = 0; i < index; i++) {\n if (head == nullptr)\n return nullptr;\n head = head->next;\n }\n return head;\n}\n
linked_list.java/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nListNode access(ListNode head, int index) {\n for (int i = 0; i < index; i++) {\n if (head == null)\n return null;\n head = head.next;\n }\n return head;\n}\n
linked_list.cs/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nListNode? Access(ListNode? head, int index) {\n for (int i = 0; i < index; i++) {\n if (head == null)\n return null;\n head = head.next;\n }\n return head;\n}\n
linked_list.go/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nfunc access(head *ListNode, index int) *ListNode {\n for i := 0; i < index; i++ {\n if head == nil {\n return nil\n }\n head = head.Next\n }\n return head\n}\n
linked_list.swift/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nfunc access(head: ListNode, index: Int) -> ListNode? {\n var head: ListNode? = head\n for _ in 0 ..< index {\n if head == nil {\n return nil\n }\n head = head?.next\n }\n return head\n}\n
linked_list.js/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nfunction access(head, index) {\n for (let i = 0; i < index; i++) {\n if (!head) {\n return null;\n }\n head = head.next;\n }\n return head;\n}\n
linked_list.ts/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nfunction access(head: ListNode | null, index: number): ListNode | null {\n for (let i = 0; i < index; i++) {\n if (!head) {\n return null;\n }\n head = head.next;\n }\n return head;\n}\n
linked_list.dart/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nListNode? access(ListNode? head, int index) {\n for (var i = 0; i < index; i++) {\n if (head == null) return null;\n head = head.next;\n }\n return head;\n}\n
linked_list.rs/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\npub fn access<T>(head: Rc<RefCell<ListNode<T>>>, index: i32) -> Rc<RefCell<ListNode<T>>> {\n if index <= 0 {return head};\n if let Some(node) = &head.borrow_mut().next {\n return access(node.clone(), index - 1);\n }\n return head;\n}\n
linked_list.c/* \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9 */\nListNode *access(ListNode *head, int index) {\n for (int i = 0; i < index; i++) {\n if (head == NULL)\n return NULL;\n head = head->next;\n }\n return head;\n}\n
linked_list.zig// \u8bbf\u95ee\u94fe\u8868\u4e2d\u7d22\u5f15\u4e3a index \u7684\u8282\u70b9\nfn access(node: ?*inc.ListNode(i32), index: i32) ?*inc.ListNode(i32) {\n var head = node;\n var i: i32 = 0;\n while (i < index) : (i += 1) {\n head = head.?.next;\n if (head == null) return null;\n }\n return head;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/linked_list/#5-finding-nodes","title":"5. \u00a0 Finding Nodes","text":"Traverse the linked list to find a node with a value equal to target
, and output the index of that node in the linked list. This process also falls under linear search. The code is as follows:
def find(head: ListNode, target: int) -> int:\n \"\"\"\u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9\"\"\"\n index = 0\n while head:\n if head.val == target:\n return index\n head = head.next\n index += 1\n return -1\n
linked_list.cpp/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nint find(ListNode *head, int target) {\n int index = 0;\n while (head != nullptr) {\n if (head->val == target)\n return index;\n head = head->next;\n index++;\n }\n return -1;\n}\n
linked_list.java/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nint find(ListNode head, int target) {\n int index = 0;\n while (head != null) {\n if (head.val == target)\n return index;\n head = head.next;\n index++;\n }\n return -1;\n}\n
linked_list.cs/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nint Find(ListNode? head, int target) {\n int index = 0;\n while (head != null) {\n if (head.val == target)\n return index;\n head = head.next;\n index++;\n }\n return -1;\n}\n
linked_list.go/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nfunc findNode(head *ListNode, target int) int {\n index := 0\n for head != nil {\n if head.Val == target {\n return index\n }\n head = head.Next\n index++\n }\n return -1\n}\n
linked_list.swift/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nfunc find(head: ListNode, target: Int) -> Int {\n var head: ListNode? = head\n var index = 0\n while head != nil {\n if head?.val == target {\n return index\n }\n head = head?.next\n index += 1\n }\n return -1\n}\n
linked_list.js/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nfunction find(head, target) {\n let index = 0;\n while (head !== null) {\n if (head.val === target) {\n return index;\n }\n head = head.next;\n index += 1;\n }\n return -1;\n}\n
linked_list.ts/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nfunction find(head: ListNode | null, target: number): number {\n let index = 0;\n while (head !== null) {\n if (head.val === target) {\n return index;\n }\n head = head.next;\n index += 1;\n }\n return -1;\n}\n
linked_list.dart/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nint find(ListNode? head, int target) {\n int index = 0;\n while (head != null) {\n if (head.val == target) {\n return index;\n }\n head = head.next;\n index++;\n }\n return -1;\n}\n
linked_list.rs/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\npub fn find<T: PartialEq>(head: Rc<RefCell<ListNode<T>>>, target: T, index: i32) -> i32 {\n if head.borrow().val == target {return index};\n if let Some(node) = &head.borrow_mut().next {\n return find(node.clone(), target, index + 1);\n }\n return -1;\n}\n
linked_list.c/* \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9 */\nint find(ListNode *head, int target) {\n int index = 0;\n while (head) {\n if (head->val == target)\n return index;\n head = head->next;\n index++;\n }\n return -1;\n}\n
linked_list.zig// \u5728\u94fe\u8868\u4e2d\u67e5\u627e\u503c\u4e3a target \u7684\u9996\u4e2a\u8282\u70b9\nfn find(node: ?*inc.ListNode(i32), target: i32) i32 {\n var head = node;\n var index: i32 = 0;\n while (head != null) {\n if (head.?.val == target) return index;\n head = head.?.next;\n index += 1;\n }\n return -1;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/linked_list/#422-arrays-vs-linked-lists","title":"4.2.2 \u00a0 Arrays vs. Linked Lists","text":"The following table summarizes the characteristics of arrays and linked lists and compares their operational efficiencies. Since they employ two opposite storage strategies, their properties and operational efficiencies also show contrasting features.
Table 4-1 \u00a0 Efficiency Comparison of Arrays and Linked Lists
Arrays Linked Lists Storage Contiguous Memory Space Dispersed Memory Space Capacity Expansion Fixed Length Flexible Expansion Memory Efficiency Less Memory per Element, Potential Space Wastage More Memory per Element Accessing Elements \\(O(1)\\) \\(O(n)\\) Adding Elements \\(O(n)\\) \\(O(1)\\) Deleting Elements \\(O(n)\\) \\(O(1)\\)"},{"location":"chapter_array_and_linkedlist/linked_list/#423-common-types-of-linked-lists","title":"4.2.3 \u00a0 Common Types of Linked Lists","text":"As shown in the following image, there are three common types of linked lists.
None
), is the tail node.class ListNode:\n \"\"\"Bidirectional linked list node class\"\"\"\"\n def __init__(self, val: int):\n self.val: int = val # Node value\n self.next: ListNode | None = None # Reference to the successor node\n self.prev: ListNode | None = None # Reference to a predecessor node\n
/* Bidirectional linked list node structure */\nstruct ListNode {\n int val; // Node value\n ListNode *next; // Pointer to the successor node\n ListNode *prev; // Pointer to the predecessor node\n ListNode(int x) : val(x), next(nullptr), prev(nullptr) {} // Constructor\n};\n
/* Bidirectional linked list node class */\nclass ListNode {\n int val; // Node value\n ListNode next; // Reference to the next node\n ListNode prev; // Reference to the predecessor node\n ListNode(int x) { val = x; } // Constructor\n}\n
/* Bidirectional linked list node class */\nclass ListNode(int x) { // Constructor\n int val = x; // Node value\n ListNode next; // Reference to the next node\n ListNode prev; // Reference to the predecessor node\n}\n
/* Bidirectional linked list node structure */\ntype DoublyListNode struct {\n Val int // Node value\n Next *DoublyListNode // Pointer to the successor node\n Prev *DoublyListNode // Pointer to the predecessor node\n}\n\n// NewDoublyListNode initialization\nfunc NewDoublyListNode(val int) *DoublyListNode {\n return &DoublyListNode{\n Val: val,\n Next: nil,\n Prev: nil,\n }\n}\n
/* Bidirectional linked list node class */\nclass ListNode {\n var val: Int // Node value\n var next: ListNode? // Reference to the next node\n var prev: ListNode? // Reference to the predecessor node\n\n init(x: Int) { // Constructor\n val = x\n }\n}\n
/* Bidirectional linked list node class */\nclass ListNode {\n constructor(val, next, prev) {\n this.val = val === undefined ? 0 : val; // Node value\n this.next = next === undefined ? null : next; // Reference to the successor node\n this.prev = prev === undefined ? null : prev; // Reference to the predecessor node\n }\n}\n
/* Bidirectional linked list node class */\nclass ListNode {\n val: number;\n next: ListNode | null;\n prev: ListNode | null;\n constructor(val?: number, next?: ListNode | null, prev?: ListNode | null) {\n this.val = val === undefined ? 0 : val; // Node value\n this.next = next === undefined ? null : next; // Reference to the successor node\n this.prev = prev === undefined ? null : prev; // Reference to the predecessor node\n }\n}\n
/* Bidirectional linked list node class */\nclass ListNode {\n int val; // Node value\n ListNode next; // Reference to the next node\n ListNode prev; // Reference to the predecessor node\n ListNode(this.val, [this.next, this.prev]); // Constructor\n}\n
use std::rc::Rc;\nuse std::cell::RefCell;\n\n/* Bidirectional linked list node type */\n#[derive(Debug)]\nstruct ListNode {\n val: i32, // Node value\n next: Option<Rc<RefCell<ListNode>>>, // Pointer to successor node\n prev: Option<Rc<RefCell<ListNode>>>, // Pointer to predecessor node\n}\n\n/* Constructors */\nimpl ListNode {\n fn new(val: i32) -> Self {\n ListNode {\n val,\n next: None,\n prev: None,\n }\n }\n}\n
/* Bidirectional linked list node structure */\ntypedef struct ListNode {\n int val; // Node value\n struct ListNode *next; // Pointer to the successor node\n struct ListNode *prev; // Pointer to the predecessor node\n} ListNode;\n\n/* Constructors */\nListNode *newListNode(int val) {\n ListNode *node, *next;\n node = (ListNode *) malloc(sizeof(ListNode));\n node->val = val;\n node->next = NULL;\n node->prev = NULL;\n return node;\n}\n
// Bidirectional linked list node class\npub fn ListNode(comptime T: type) type {\n return struct {\n const Self = @This();\n\n val: T = 0, // Node value\n next: ?*Self = null, // Pointer to the successor node\n prev: ?*Self = null, // Pointer to the predecessor node\n\n // Constructor\n pub fn init(self: *Self, x: i32) void {\n self.val = x;\n self.next = null;\n self.prev = null;\n }\n };\n}\n
Figure 4-8 \u00a0 Common Types of Linked Lists
"},{"location":"chapter_array_and_linkedlist/linked_list/#424-typical-applications-of-linked-lists","title":"4.2.4 \u00a0 Typical Applications of Linked Lists","text":"Singly linked lists are commonly used to implement stacks, queues, hash tables, and graphs.
Doubly linked lists are commonly used in scenarios that require quick access to the previous and next elements.
Circular linked lists are commonly used in scenarios requiring periodic operations, such as resource scheduling in operating systems.
A \"list\" is an abstract data structure concept, representing an ordered collection of elements. It supports operations like element access, modification, addition, deletion, and traversal, without requiring users to consider capacity limitations. Lists can be implemented based on linked lists or arrays.
When using arrays to implement lists, the fixed length property reduces the practicality of the list. This is because we often cannot determine in advance how much data needs to be stored, making it difficult to choose an appropriate list length. If the length is too small, it may not meet the requirements; if too large, it may waste memory space.
To solve this problem, we can use a \"dynamic array\" to implement lists. It inherits the advantages of arrays and can dynamically expand during program execution.
In fact, many programming languages' standard libraries implement lists using dynamic arrays, such as Python's list
, Java's ArrayList
, C++'s vector
, and C#'s List
. In the following discussion, we will consider \"list\" and \"dynamic array\" as synonymous concepts.
We typically use two methods of initialization: \"without initial values\" and \"with initial values\".
PythonC++JavaC#GoSwiftJSTSDartRustCZig list.py# Initialize list\n# Without initial values\nnums1: list[int] = []\n# With initial values\nnums: list[int] = [1, 3, 2, 5, 4]\n
list.cpp/* Initialize list */\n// Note, in C++ the vector is the equivalent of nums described here\n// Without initial values\nvector<int> nums1;\n// With initial values\nvector<int> nums = { 1, 3, 2, 5, 4 };\n
list.java/* Initialize list */\n// Without initial values\nList<Integer> nums1 = new ArrayList<>();\n// With initial values (note the element type should be the wrapper class Integer[] for int[])\nInteger[] numbers = new Integer[] { 1, 3, 2, 5, 4 };\nList<Integer> nums = new ArrayList<>(Arrays.asList(numbers));\n
list.cs/* Initialize list */\n// Without initial values\nList<int> nums1 = [];\n// With initial values\nint[] numbers = [1, 3, 2, 5, 4];\nList<int> nums = [.. numbers];\n
list_test.go/* Initialize list */\n// Without initial values\nnums1 := []int{}\n// With initial values\nnums := []int{1, 3, 2, 5, 4}\n
list.swift/* Initialize list */\n// Without initial values\nlet nums1: [Int] = []\n// With initial values\nvar nums = [1, 3, 2, 5, 4]\n
list.js/* Initialize list */\n// Without initial values\nconst nums1 = [];\n// With initial values\nconst nums = [1, 3, 2, 5, 4];\n
list.ts/* Initialize list */\n// Without initial values\nconst nums1: number[] = [];\n// With initial values\nconst nums: number[] = [1, 3, 2, 5, 4];\n
list.dart/* Initialize list */\n// Without initial values\nList<int> nums1 = [];\n// With initial values\nList<int> nums = [1, 3, 2, 5, 4];\n
list.rs/* Initialize list */\n// Without initial values\nlet nums1: Vec<i32> = Vec::new();\n// With initial values\nlet nums: Vec<i32> = vec![1, 3, 2, 5, 4];\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Initialize list\nvar nums = std.ArrayList(i32).init(std.heap.page_allocator);\ndefer nums.deinit();\ntry nums.appendSlice(&[_]i32{ 1, 3, 2, 5, 4 });\n
"},{"location":"chapter_array_and_linkedlist/list/#2-accessing-elements","title":"2. \u00a0 Accessing Elements","text":"Lists are essentially arrays, so accessing and updating elements can be done in \\(O(1)\\) time, which is very efficient.
PythonC++JavaC#GoSwiftJSTSDartRustCZig list.py# Access elements\nnum: int = nums[1] # Access the element at index 1\n\n# Update elements\nnums[1] = 0 # Update the element at index 1 to 0\n
list.cpp/* Access elements */\nint num = nums[1]; // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list.java/* Access elements */\nint num = nums.get(1); // Access the element at index 1\n\n/* Update elements */\nnums.set(1, 0); // Update the element at index 1 to 0\n
list.cs/* Access elements */\nint num = nums[1]; // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list_test.go/* Access elements */\nnum := nums[1] // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0 // Update the element at index 1 to 0\n
list.swift/* Access elements */\nlet num = nums[1] // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0 // Update the element at index 1 to 0\n
list.js/* Access elements */\nconst num = nums[1]; // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list.ts/* Access elements */\nconst num: number = nums[1]; // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list.dart/* Access elements */\nint num = nums[1]; // Access the element at index 1\n\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list.rs/* Access elements */\nlet num: i32 = nums[1]; // Access the element at index 1\n/* Update elements */\nnums[1] = 0; // Update the element at index 1 to 0\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Access elements\nvar num = nums.items[1]; // Access the element at index 1\n\n// Update elements\nnums.items[1] = 0; // Update the element at index 1 to 0 \n
"},{"location":"chapter_array_and_linkedlist/list/#3-inserting-and-deleting-elements","title":"3. \u00a0 Inserting and Deleting Elements","text":"Compared to arrays, lists can freely add and remove elements. Adding elements at the end of the list has a time complexity of \\(O(1)\\), but the efficiency of inserting and deleting elements is still the same as in arrays, with a time complexity of \\(O(n)\\).
PythonC++JavaC#GoSwiftJSTSDartRustCZig list.py# Clear list\nnums.clear()\n\n# Append elements at the end\nnums.append(1)\nnums.append(3)\nnums.append(2)\nnums.append(5)\nnums.append(4)\n\n# Insert element in the middle\nnums.insert(3, 6) # Insert number 6 at index 3\n\n# Remove elements\nnums.pop(3) # Remove the element at index 3\n
list.cpp/* Clear list */\nnums.clear();\n\n/* Append elements at the end */\nnums.push_back(1);\nnums.push_back(3);\nnums.push_back(2);\nnums.push_back(5);\nnums.push_back(4);\n\n/* Insert element in the middle */\nnums.insert(nums.begin() + 3, 6); // Insert number 6 at index 3\n\n/* Remove elements */\nnums.erase(nums.begin() + 3); // Remove the element at index 3\n
list.java/* Clear list */\nnums.clear();\n\n/* Append elements at the end */\nnums.add(1);\nnums.add(3);\nnums.add(2);\nnums.add(5);\nnums.add(4);\n\n/* Insert element in the middle */\nnums.add(3, 6); // Insert number 6 at index 3\n\n/* Remove elements */\nnums.remove(3); // Remove the element at index 3\n
list.cs/* Clear list */\nnums.Clear();\n\n/* Append elements at the end */\nnums.Add(1);\nnums.Add(3);\nnums.Add(2);\nnums.Add(5);\nnums.Add(4);\n\n/* Insert element in the middle */\nnums.Insert(3, 6);\n\n/* Remove elements */\nnums.RemoveAt(3);\n
list_test.go/* Clear list */\nnums = nil\n\n/* Append elements at the end */\nnums = append(nums, 1)\nnums = append(nums, 3)\nnums = append(nums, 2)\nnums = append(nums, 5)\nnums = append(nums, 4)\n\n/* Insert element in the middle */\nnums = append(nums[:3], append([]int{6}, nums[3:]...)...) // Insert number 6 at index 3\n\n/* Remove elements */\nnums = append(nums[:3], nums[4:]...) // Remove the element at index 3\n
list.swift/* Clear list */\nnums.removeAll()\n\n/* Append elements at the end */\nnums.append(1)\nnums.append(3)\nnums.append(2)\nnums.append(5)\nnums.append(4)\n\n/* Insert element in the middle */\nnums.insert(6, at: 3) // Insert number 6 at index 3\n\n/* Remove elements */\nnums.remove(at: 3) // Remove the element at index 3\n
list.js/* Clear list */\nnums.length = 0;\n\n/* Append elements at the end */\nnums.push(1);\nnums.push(3);\nnums.push(2);\nnums.push(5);\nnums.push(4);\n\n/* Insert element in the middle */\nnums.splice(3, 0, 6);\n\n/* Remove elements */\nnums.splice(3, 1);\n
list.ts/* Clear list */\nnums.length = 0;\n\n/* Append elements at the end */\nnums.push(1);\nnums.push(3);\nnums.push(2);\nnums.push(5);\nnums.push(4);\n\n/* Insert element in the middle */\nnums.splice(3, 0, 6);\n\n/* Remove elements */\nnums.splice(3, 1);\n
list.dart/* Clear list */\nnums.clear();\n\n/* Append elements at the end */\nnums.add(1);\nnums.add(3);\nnums.add(2);\nnums.add(5);\nnums.add(4);\n\n/* Insert element in the middle */\nnums.insert(3, 6); // Insert number 6 at index 3\n\n/* Remove elements */\nnums.removeAt(3); // Remove the element at index 3\n
list.rs/* Clear list */\nnums.clear();\n\n/* Append elements at the end */\nnums.push(1);\nnums.push(3);\nnums.push(2);\nnums.push(5);\nnums.push(4);\n\n/* Insert element in the middle */\nnums.insert(3, 6); // Insert number 6 at index 3\n\n/* Remove elements */\nnums.remove(3); // Remove the element at index 3\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Clear list\nnums.clearRetainingCapacity();\n\n// Append elements at the end\ntry nums.append(1);\ntry nums.append(3);\ntry nums.append(2);\ntry nums.append(5);\ntry nums.append(4);\n\n// Insert element in the middle\ntry nums.insert(3, 6); // Insert number 6 at index 3\n\n// Remove elements\n_ = nums.orderedRemove(3); // Remove the element at index 3\n
"},{"location":"chapter_array_and_linkedlist/list/#4-traversing-the-list","title":"4. \u00a0 Traversing the List","text":"Like arrays, lists can be traversed based on index, or by directly iterating over each element.
PythonC++JavaC#GoSwiftJSTSDartRustCZig list.py# Iterate through the list by index\ncount = 0\nfor i in range(len(nums)):\n count += nums[i]\n\n# Iterate directly through list elements\nfor num in nums:\n count += num\n
list.cpp/* Iterate through the list by index */\nint count = 0;\nfor (int i = 0; i < nums.size(); i++) {\n count += nums[i];\n}\n\n/* Iterate directly through list elements */\ncount = 0;\nfor (int num : nums) {\n count += num;\n}\n
list.java/* Iterate through the list by index */\nint count = 0;\nfor (int i = 0; i < nums.size(); i++) {\n count += nums.get(i);\n}\n\n/* Iterate directly through list elements */\nfor (int num : nums) {\n count += num;\n}\n
list.cs/* Iterate through the list by index */\nint count = 0;\nfor (int i = 0; i < nums.Count; i++) {\n count += nums[i];\n}\n\n/* Iterate directly through list elements */\ncount = 0;\nforeach (int num in nums) {\n count += num;\n}\n
list_test.go/* Iterate through the list by index */\ncount := 0\nfor i := 0; i < len(nums); i++ {\n count += nums[i]\n}\n\n/* Iterate directly through list elements */\ncount = 0\nfor _, num := range nums {\n count += num\n}\n
list.swift/* Iterate through the list by index */\nvar count = 0\nfor i in nums.indices {\n count += nums[i]\n}\n\n/* Iterate directly through list elements */\ncount = 0\nfor num in nums {\n count += num\n}\n
list.js/* Iterate through the list by index */\nlet count = 0;\nfor (let i = 0; i < nums.length; i++) {\n count += nums[i];\n}\n\n/* Iterate directly through list elements */\ncount = 0;\nfor (const num of nums) {\n count += num;\n}\n
list.ts/* Iterate through the list by index */\nlet count = 0;\nfor (let i = 0; i < nums.length; i++) {\n count += nums[i];\n}\n\n/* Iterate directly through list elements */\ncount = 0;\nfor (const num of nums) {\n count += num;\n}\n
list.dart/* Iterate through the list by index */\nint count = 0;\nfor (var i = 0; i < nums.length; i++) {\n count += nums[i];\n}\n\n/* Iterate directly through list elements */\ncount = 0;\nfor (var num in nums) {\n count += num;\n}\n
list.rs// Iterate through the list by index\nlet mut _count = 0;\nfor i in 0..nums.len() {\n _count += nums[i];\n}\n\n// Iterate directly through list elements\n_count = 0;\nfor num in &nums {\n _count += num;\n}\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Iterate through the list by index\nvar count: i32 = 0;\nvar i: i32 = 0;\nwhile (i < nums.items.len) : (i += 1) {\n count += nums[i];\n}\n\n// Iterate directly through list elements\ncount = 0;\nfor (nums.items) |num| {\n count += num;\n}\n
"},{"location":"chapter_array_and_linkedlist/list/#5-concatenating-lists","title":"5. \u00a0 Concatenating Lists","text":"Given a new list nums1
, we can append it to the end of the original list.
# Concatenate two lists\nnums1: list[int] = [6, 8, 7, 10, 9]\nnums += nums1 # Concatenate nums1 to the end of nums\n
list.cpp/* Concatenate two lists */\nvector<int> nums1 = { 6, 8, 7, 10, 9 };\n// Concatenate nums1 to the end of nums\nnums.insert(nums.end(), nums1.begin(), nums1.end());\n
list.java/* Concatenate two lists */\nList<Integer> nums1 = new ArrayList<>(Arrays.asList(new Integer[] { 6, 8, 7, 10, 9 }));\nnums.addAll(nums1); // Concatenate nums1 to the end of nums\n
list.cs/* Concatenate two lists */\nList<int> nums1 = [6, 8, 7, 10, 9];\nnums.AddRange(nums1); // Concatenate nums1 to the end of nums\n
list_test.go/* Concatenate two lists */\nnums1 := []int{6, 8, 7, 10, 9}\nnums = append(nums, nums1...) // Concatenate nums1 to the end of nums\n
list.swift/* Concatenate two lists */\nlet nums1 = [6, 8, 7, 10, 9]\nnums.append(contentsOf: nums1) // Concatenate nums1 to the end of nums\n
list.js/* Concatenate two lists */\nconst nums1 = [6, 8, 7, 10, 9];\nnums.push(...nums1); // Concatenate nums1 to the end of nums\n
list.ts/* Concatenate two lists */\nconst nums1: number[] = [6, 8, 7, 10, 9];\nnums.push(...nums1); // Concatenate nums1 to the end of nums\n
list.dart/* Concatenate two lists */\nList<int> nums1 = [6, 8, 7, 10, 9];\nnums.addAll(nums1); // Concatenate nums1 to the end of nums\n
list.rs/* Concatenate two lists */\nlet nums1: Vec<i32> = vec![6, 8, 7, 10, 9];\nnums.extend(nums1);\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Concatenate two lists\nvar nums1 = std.ArrayList(i32).init(std.heap.page_allocator);\ndefer nums1.deinit();\ntry nums1.appendSlice(&[_]i32{ 6, 8, 7, 10, 9 });\ntry nums.insertSlice(nums.items.len, nums1.items); // Concatenate nums1 to the end of nums\n
"},{"location":"chapter_array_and_linkedlist/list/#6-sorting-the-list","title":"6. \u00a0 Sorting the List","text":"After sorting the list, we can use algorithms often tested in array-related algorithm problems, such as \"binary search\" and \"two-pointer\" algorithms.
PythonC++JavaC#GoSwiftJSTSDartRustCZig list.py# Sort the list\nnums.sort() # After sorting, the list elements are in ascending order\n
list.cpp/* Sort the list */\nsort(nums.begin(), nums.end()); // After sorting, the list elements are in ascending order\n
list.java/* Sort the list */\nCollections.sort(nums); // After sorting, the list elements are in ascending order\n
list.cs/* Sort the list */\nnums.Sort(); // After sorting, the list elements are in ascending order\n
list_test.go/* Sort the list */\nsort.Ints(nums) // After sorting, the list elements are in ascending order\n
list.swift/* Sort the list */\nnums.sort() // After sorting, the list elements are in ascending order\n
list.js/* Sort the list */ \nnums.sort((a, b) => a - b); // After sorting, the list elements are in ascending order\n
list.ts/* Sort the list */\nnums.sort((a, b) => a - b); // After sorting, the list elements are in ascending order\n
list.dart/* Sort the list */\nnums.sort(); // After sorting, the list elements are in ascending order\n
list.rs/* Sort the list */\nnums.sort(); // After sorting, the list elements are in ascending order\n
list.c// C does not provide built-in dynamic arrays\n
list.zig// Sort the list\nstd.sort.sort(i32, nums.items, {}, comptime std.sort.asc(i32));\n
"},{"location":"chapter_array_and_linkedlist/list/#432-list-implementation","title":"4.3.2 \u00a0 List Implementation","text":"Many programming languages have built-in lists, such as Java, C++, Python, etc. Their implementations are quite complex, with very meticulous settings for parameters such as initial capacity and expansion multiplier. Interested readers can refer to the source code for learning.
To deepen the understanding of how lists work, let's try implementing a simple version of a list, focusing on three key designs.
size
to record the current number of elements in the list, updating in real-time with element insertion and deletion. With this variable, we can locate the end of the list and determine whether expansion is needed.class MyList:\n \"\"\"\u5217\u8868\u7c7b\"\"\"\n\n def __init__(self):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._capacity: int = 10 # \u5217\u8868\u5bb9\u91cf\n self._arr: list[int] = [0] * self._capacity # \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n self._size: int = 0 # \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n self._extend_ratio: int = 2 # \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\"\"\"\n return self._size\n\n def capacity(self) -> int:\n \"\"\"\u83b7\u53d6\u5217\u8868\u5bb9\u91cf\"\"\"\n return self._capacity\n\n def get(self, index: int) -> int:\n \"\"\"\u8bbf\u95ee\u5143\u7d20\"\"\"\n # \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if index < 0 or index >= self._size:\n raise IndexError(\"\u7d22\u5f15\u8d8a\u754c\")\n return self._arr[index]\n\n def set(self, num: int, index: int):\n \"\"\"\u66f4\u65b0\u5143\u7d20\"\"\"\n if index < 0 or index >= self._size:\n raise IndexError(\"\u7d22\u5f15\u8d8a\u754c\")\n self._arr[index] = num\n\n def add(self, num: int):\n \"\"\"\u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20\"\"\"\n # \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if self.size() == self.capacity():\n self.extend_capacity()\n self._arr[self._size] = num\n self._size += 1\n\n def insert(self, num: int, index: int):\n \"\"\"\u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20\"\"\"\n if index < 0 or index >= self._size:\n raise IndexError(\"\u7d22\u5f15\u8d8a\u754c\")\n # \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if self._size == self.capacity():\n self.extend_capacity()\n # \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for j in range(self._size - 1, index - 1, -1):\n self._arr[j + 1] = self._arr[j]\n self._arr[index] = num\n # \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self._size += 1\n\n def remove(self, index: int) -> int:\n \"\"\"\u5220\u9664\u5143\u7d20\"\"\"\n if index < 0 or index >= self._size:\n raise IndexError(\"\u7d22\u5f15\u8d8a\u754c\")\n num = self._arr[index]\n # \u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for j in range(index, self._size - 1):\n self._arr[j] = self._arr[j + 1]\n # \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self._size -= 1\n # \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num\n\n def extend_capacity(self):\n \"\"\"\u5217\u8868\u6269\u5bb9\"\"\"\n # \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 _extend_ratio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1)\n # \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n self._capacity = len(self._arr)\n\n def to_array(self) -> list[int]:\n \"\"\"\u8fd4\u56de\u6709\u6548\u957f\u5ea6\u7684\u5217\u8868\"\"\"\n return self._arr[: self._size]\n
my_list.cpp/* \u5217\u8868\u7c7b */\nclass MyList {\n private:\n int *arr; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n int arrCapacity = 10; // \u5217\u8868\u5bb9\u91cf\n int arrSize = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n int extendRatio = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n public:\n /* \u6784\u9020\u65b9\u6cd5 */\n MyList() {\n arr = new int[arrCapacity];\n }\n\n /* \u6790\u6784\u65b9\u6cd5 */\n ~MyList() {\n delete[] arr;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n int size() {\n return arrSize;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n int capacity() {\n return arrCapacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n int get(int index) {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 || index >= size())\n throw out_of_range(\"\u7d22\u5f15\u8d8a\u754c\");\n return arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n void set(int index, int num) {\n if (index < 0 || index >= size())\n throw out_of_range(\"\u7d22\u5f15\u8d8a\u754c\");\n arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n void add(int num) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (size() == capacity())\n extendCapacity();\n arr[size()] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n void insert(int index, int num) {\n if (index < 0 || index >= size())\n throw out_of_range(\"\u7d22\u5f15\u8d8a\u754c\");\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (size() == capacity())\n extendCapacity();\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int j = size() - 1; j >= index; j--) {\n arr[j + 1] = arr[j];\n }\n arr[index] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n int remove(int index) {\n if (index < 0 || index >= size())\n throw out_of_range(\"\u7d22\u5f15\u8d8a\u754c\");\n int num = arr[index];\n // \u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int j = index; j < size() - 1; j++) {\n arr[j] = arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n void extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extendRatio \u500d\u7684\u65b0\u6570\u7ec4\n int newCapacity = capacity() * extendRatio;\n int *tmp = arr;\n arr = new int[newCapacity];\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n for (int i = 0; i < size(); i++) {\n arr[i] = tmp[i];\n }\n // \u91ca\u653e\u5185\u5b58\n delete[] tmp;\n arrCapacity = newCapacity;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a Vector \u7528\u4e8e\u6253\u5370 */\n vector<int> toVector() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n vector<int> vec(size());\n for (int i = 0; i < size(); i++) {\n vec[i] = arr[i];\n }\n return vec;\n }\n};\n
my_list.java/* \u5217\u8868\u7c7b */\nclass MyList {\n private int[] arr; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n private int capacity = 10; // \u5217\u8868\u5bb9\u91cf\n private int size = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n private int extendRatio = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n public MyList() {\n arr = new int[capacity];\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09 */\n public int size() {\n return size;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n public int capacity() {\n return capacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n public int get(int index) {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 || index >= size)\n throw new IndexOutOfBoundsException(\"\u7d22\u5f15\u8d8a\u754c\");\n return arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n public void set(int index, int num) {\n if (index < 0 || index >= size)\n throw new IndexOutOfBoundsException(\"\u7d22\u5f15\u8d8a\u754c\");\n arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n public void add(int num) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (size == capacity())\n extendCapacity();\n arr[size] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n size++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n public void insert(int index, int num) {\n if (index < 0 || index >= size)\n throw new IndexOutOfBoundsException(\"\u7d22\u5f15\u8d8a\u754c\");\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (size == capacity())\n extendCapacity();\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int j = size - 1; j >= index; j--) {\n arr[j + 1] = arr[j];\n }\n arr[index] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n size++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n public int remove(int index) {\n if (index < 0 || index >= size)\n throw new IndexOutOfBoundsException(\"\u7d22\u5f15\u8d8a\u754c\");\n int num = arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int j = index; j < size - 1; j++) {\n arr[j] = arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n size--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n public void extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extendRatio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n arr = Arrays.copyOf(arr, capacity() * extendRatio);\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n capacity = arr.length;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n public int[] toArray() {\n int size = size();\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n int[] arr = new int[size];\n for (int i = 0; i < size; i++) {\n arr[i] = get(i);\n }\n return arr;\n }\n}\n
my_list.cs/* \u5217\u8868\u7c7b */\nclass MyList {\n private int[] arr; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n private int arrCapacity = 10; // \u5217\u8868\u5bb9\u91cf\n private int arrSize = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n private readonly int extendRatio = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n public MyList() {\n arr = new int[arrCapacity];\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n public int Size() {\n return arrSize;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n public int Capacity() {\n return arrCapacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n public int Get(int index) {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 || index >= arrSize)\n throw new IndexOutOfRangeException(\"\u7d22\u5f15\u8d8a\u754c\");\n return arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n public void Set(int index, int num) {\n if (index < 0 || index >= arrSize)\n throw new IndexOutOfRangeException(\"\u7d22\u5f15\u8d8a\u754c\");\n arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n public void Add(int num) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (arrSize == arrCapacity)\n ExtendCapacity();\n arr[arrSize] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n public void Insert(int index, int num) {\n if (index < 0 || index >= arrSize)\n throw new IndexOutOfRangeException(\"\u7d22\u5f15\u8d8a\u754c\");\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (arrSize == arrCapacity)\n ExtendCapacity();\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (int j = arrSize - 1; j >= index; j--) {\n arr[j + 1] = arr[j];\n }\n arr[index] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n public int Remove(int index) {\n if (index < 0 || index >= arrSize)\n throw new IndexOutOfRangeException(\"\u7d22\u5f15\u8d8a\u754c\");\n int num = arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (int j = index; j < arrSize - 1; j++) {\n arr[j] = arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n arrSize--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n public void ExtendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a arrCapacity * extendRatio \u7684\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n Array.Resize(ref arr, arrCapacity * extendRatio);\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n arrCapacity = arr.Length;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n public int[] ToArray() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n int[] arr = new int[arrSize];\n for (int i = 0; i < arrSize; i++) {\n arr[i] = Get(i);\n }\n return arr;\n }\n}\n
my_list.go/* \u5217\u8868\u7c7b */\ntype myList struct {\n arrCapacity int\n arr []int\n arrSize int\n extendRatio int\n}\n\n/* \u6784\u9020\u51fd\u6570 */\nfunc newMyList() *myList {\n return &myList{\n arrCapacity: 10, // \u5217\u8868\u5bb9\u91cf\n arr: make([]int, 10), // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n arrSize: 0, // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n extendRatio: 2, // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n }\n}\n\n/* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09 */\nfunc (l *myList) size() int {\n return l.arrSize\n}\n\n/* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\nfunc (l *myList) capacity() int {\n return l.arrCapacity\n}\n\n/* \u8bbf\u95ee\u5143\u7d20 */\nfunc (l *myList) get(index int) int {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if index < 0 || index >= l.arrSize {\n panic(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n return l.arr[index]\n}\n\n/* \u66f4\u65b0\u5143\u7d20 */\nfunc (l *myList) set(num, index int) {\n if index < 0 || index >= l.arrSize {\n panic(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n l.arr[index] = num\n}\n\n/* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\nfunc (l *myList) add(num int) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if l.arrSize == l.arrCapacity {\n l.extendCapacity()\n }\n l.arr[l.arrSize] = num\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n l.arrSize++\n}\n\n/* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\nfunc (l *myList) insert(num, index int) {\n if index < 0 || index >= l.arrSize {\n panic(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if l.arrSize == l.arrCapacity {\n l.extendCapacity()\n }\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for j := l.arrSize - 1; j >= index; j-- {\n l.arr[j+1] = l.arr[j]\n }\n l.arr[index] = num\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n l.arrSize++\n}\n\n/* \u5220\u9664\u5143\u7d20 */\nfunc (l *myList) remove(index int) int {\n if index < 0 || index >= l.arrSize {\n panic(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n num := l.arr[index]\n // \u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for j := index; j < l.arrSize-1; j++ {\n l.arr[j] = l.arr[j+1]\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n l.arrSize--\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num\n}\n\n/* \u5217\u8868\u6269\u5bb9 */\nfunc (l *myList) extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extendRatio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n l.arr = append(l.arr, make([]int, l.arrCapacity*(l.extendRatio-1))...)\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n l.arrCapacity = len(l.arr)\n}\n\n/* \u8fd4\u56de\u6709\u6548\u957f\u5ea6\u7684\u5217\u8868 */\nfunc (l *myList) toArray() []int {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n return l.arr[:l.arrSize]\n}\n
my_list.swift/* \u5217\u8868\u7c7b */\nclass MyList {\n private var arr: [Int] // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n private var _capacity = 10 // \u5217\u8868\u5bb9\u91cf\n private var _size = 0 // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n private let extendRatio = 2 // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n init() {\n arr = Array(repeating: 0, count: _capacity)\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n func size() -> Int {\n _size\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n func capacity() -> Int {\n _capacity\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n func get(index: Int) -> Int {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\u5219\u629b\u51fa\u9519\u8bef\uff0c\u4e0b\u540c\n if index < 0 || index >= _size {\n fatalError(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n return arr[index]\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n func set(index: Int, num: Int) {\n if index < 0 || index >= _size {\n fatalError(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n arr[index] = num\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n func add(num: Int) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if _size == _capacity {\n extendCapacity()\n }\n arr[_size] = num\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size += 1\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n func insert(index: Int, num: Int) {\n if index < 0 || index >= _size {\n fatalError(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if _size == _capacity {\n extendCapacity()\n }\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for j in sequence(first: _size - 1, next: { $0 >= index + 1 ? $0 - 1 : nil }) {\n arr[j + 1] = arr[j]\n }\n arr[index] = num\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size += 1\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n @discardableResult\n func remove(index: Int) -> Int {\n if index < 0 || index >= _size {\n fatalError(\"\u7d22\u5f15\u8d8a\u754c\")\n }\n let num = arr[index]\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for j in index ..< (_size - 1) {\n arr[j] = arr[j + 1]\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size -= 1\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n func extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extendRatio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n arr = arr + Array(repeating: 0, count: _capacity * (extendRatio - 1))\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n _capacity = arr.count\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n func toArray() -> [Int] {\n var arr = Array(repeating: 0, count: _size)\n for i in 0 ..< _size {\n arr[i] = get(index: i)\n }\n return arr\n }\n}\n
my_list.js/* \u5217\u8868\u7c7b */\nclass MyList {\n #arr = new Array(); // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n #capacity = 10; // \u5217\u8868\u5bb9\u91cf\n #size = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n #extendRatio = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n constructor() {\n this.#arr = new Array(this.#capacity);\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n size() {\n return this.#size;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n capacity() {\n return this.#capacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n get(index) {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 || index >= this.#size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n return this.#arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n set(index, num) {\n if (index < 0 || index >= this.#size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n this.#arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n add(num) {\n // \u5982\u679c\u957f\u5ea6\u7b49\u4e8e\u5bb9\u91cf\uff0c\u5219\u9700\u8981\u6269\u5bb9\n if (this.#size === this.#capacity) {\n this.extendCapacity();\n }\n // \u5c06\u65b0\u5143\u7d20\u6dfb\u52a0\u5230\u5217\u8868\u5c3e\u90e8\n this.#arr[this.#size] = num;\n this.#size++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n insert(index, num) {\n if (index < 0 || index >= this.#size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (this.#size === this.#capacity) {\n this.extendCapacity();\n }\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (let j = this.#size - 1; j >= index; j--) {\n this.#arr[j + 1] = this.#arr[j];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n this.#arr[index] = num;\n this.#size++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n remove(index) {\n if (index < 0 || index >= this.#size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n let num = this.#arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (let j = index; j < this.#size - 1; j++) {\n this.#arr[j] = this.#arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n this.#size--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extendRatio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n this.#arr = this.#arr.concat(\n new Array(this.capacity() * (this.#extendRatio - 1))\n );\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n this.#capacity = this.#arr.length;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n toArray() {\n let size = this.size();\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n const arr = new Array(size);\n for (let i = 0; i < size; i++) {\n arr[i] = this.get(i);\n }\n return arr;\n }\n}\n
my_list.ts/* \u5217\u8868\u7c7b */\nclass MyList {\n private arr: Array<number>; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n private _capacity: number = 10; // \u5217\u8868\u5bb9\u91cf\n private _size: number = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n private extendRatio: number = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n constructor() {\n this.arr = new Array(this._capacity);\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n public size(): number {\n return this._size;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n public capacity(): number {\n return this._capacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n public get(index: number): number {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 || index >= this._size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n return this.arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n public set(index: number, num: number): void {\n if (index < 0 || index >= this._size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n this.arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n public add(num: number): void {\n // \u5982\u679c\u957f\u5ea6\u7b49\u4e8e\u5bb9\u91cf\uff0c\u5219\u9700\u8981\u6269\u5bb9\n if (this._size === this._capacity) this.extendCapacity();\n // \u5c06\u65b0\u5143\u7d20\u6dfb\u52a0\u5230\u5217\u8868\u5c3e\u90e8\n this.arr[this._size] = num;\n this._size++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n public insert(index: number, num: number): void {\n if (index < 0 || index >= this._size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (this._size === this._capacity) {\n this.extendCapacity();\n }\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (let j = this._size - 1; j >= index; j--) {\n this.arr[j + 1] = this.arr[j];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n this.arr[index] = num;\n this._size++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n public remove(index: number): number {\n if (index < 0 || index >= this._size) throw new Error('\u7d22\u5f15\u8d8a\u754c');\n let num = this.arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (let j = index; j < this._size - 1; j++) {\n this.arr[j] = this.arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n this._size--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n public extendCapacity(): void {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a size \u7684\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n this.arr = this.arr.concat(\n new Array(this.capacity() * (this.extendRatio - 1))\n );\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n this._capacity = this.arr.length;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n public toArray(): number[] {\n let size = this.size();\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n const arr = new Array(size);\n for (let i = 0; i < size; i++) {\n arr[i] = this.get(i);\n }\n return arr;\n }\n}\n
my_list.dart/* \u5217\u8868\u7c7b */\nclass MyList {\n late List<int> _arr; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n int _capacity = 10; // \u5217\u8868\u5bb9\u91cf\n int _size = 0; // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n int _extendRatio = 2; // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n\n /* \u6784\u9020\u65b9\u6cd5 */\n MyList() {\n _arr = List.filled(_capacity, 0);\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n int size() => _size;\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n int capacity() => _capacity;\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n int get(int index) {\n if (index >= _size) throw RangeError('\u7d22\u5f15\u8d8a\u754c');\n return _arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n void set(int index, int _num) {\n if (index >= _size) throw RangeError('\u7d22\u5f15\u8d8a\u754c');\n _arr[index] = _num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n void add(int _num) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (_size == _capacity) extendCapacity();\n _arr[_size] = _num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size++;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n void insert(int index, int _num) {\n if (index >= _size) throw RangeError('\u7d22\u5f15\u8d8a\u754c');\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (_size == _capacity) extendCapacity();\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for (var j = _size - 1; j >= index; j--) {\n _arr[j + 1] = _arr[j];\n }\n _arr[index] = _num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size++;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n int remove(int index) {\n if (index >= _size) throw RangeError('\u7d22\u5f15\u8d8a\u754c');\n int _num = _arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for (var j = index; j < _size - 1; j++) {\n _arr[j] = _arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n _size--;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return _num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n void extendCapacity() {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 _extendRatio \u500d\u7684\u65b0\u6570\u7ec4\n final _newNums = List.filled(_capacity * _extendRatio, 0);\n // \u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n List.copyRange(_newNums, 0, _arr);\n // \u66f4\u65b0 _arr \u7684\u5f15\u7528\n _arr = _newNums;\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n _capacity = _arr.length;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n List<int> toArray() {\n List<int> arr = [];\n for (var i = 0; i < _size; i++) {\n arr.add(get(i));\n }\n return arr;\n }\n}\n
my_list.rs/* \u5217\u8868\u7c7b */\n#[allow(dead_code)]\nstruct MyList {\n arr: Vec<i32>, // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n capacity: usize, // \u5217\u8868\u5bb9\u91cf\n size: usize, // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n extend_ratio: usize, // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n}\n\n#[allow(unused,unused_comparisons)]\nimpl MyList {\n /* \u6784\u9020\u65b9\u6cd5 */\n pub fn new(capacity: usize) -> Self {\n let mut vec = Vec::new(); \n vec.resize(capacity, 0);\n Self {\n arr: vec,\n capacity,\n size: 0,\n extend_ratio: 2,\n }\n }\n\n /* \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09*/\n pub fn size(&self) -> usize {\n return self.size;\n }\n\n /* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\n pub fn capacity(&self) -> usize {\n return self.capacity;\n }\n\n /* \u8bbf\u95ee\u5143\u7d20 */\n pub fn get(&self, index: usize) -> i32 {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if index >= self.size {panic!(\"\u7d22\u5f15\u8d8a\u754c\")};\n return self.arr[index];\n }\n\n /* \u66f4\u65b0\u5143\u7d20 */\n pub fn set(&mut self, index: usize, num: i32) {\n if index >= self.size {panic!(\"\u7d22\u5f15\u8d8a\u754c\")};\n self.arr[index] = num;\n }\n\n /* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\n pub fn add(&mut self, num: i32) {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if self.size == self.capacity() {\n self.extend_capacity();\n }\n self.arr[self.size] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.size += 1;\n }\n\n /* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\n pub fn insert(&mut self, index: usize, num: i32) {\n if index >= self.size() {panic!(\"\u7d22\u5f15\u8d8a\u754c\")};\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if self.size == self.capacity() {\n self.extend_capacity();\n }\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n for j in (index..self.size).rev() {\n self.arr[j + 1] = self.arr[j];\n }\n self.arr[index] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.size += 1;\n }\n\n /* \u5220\u9664\u5143\u7d20 */\n pub fn remove(&mut self, index: usize) -> i32 {\n if index >= self.size() {panic!(\"\u7d22\u5f15\u8d8a\u754c\")};\n let num = self.arr[index];\n // \u5c06\u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n for j in (index..self.size - 1) {\n self.arr[j] = self.arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.size -= 1;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n /* \u5217\u8868\u6269\u5bb9 */\n pub fn extend_capacity(&mut self) {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a\u539f\u6570\u7ec4 extend_ratio \u500d\u7684\u65b0\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n let new_capacity = self.capacity * self.extend_ratio;\n self.arr.resize(new_capacity, 0);\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n self.capacity = new_capacity;\n }\n\n /* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4 */\n pub fn to_array(&mut self) -> Vec<i32> {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n let mut arr = Vec::new();\n for i in 0..self.size {\n arr.push(self.get(i));\n }\n arr\n }\n}\n
my_list.c/* \u5217\u8868\u7c7b */\ntypedef struct {\n int *arr; // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n int capacity; // \u5217\u8868\u5bb9\u91cf\n int size; // \u5217\u8868\u5927\u5c0f\n int extendRatio; // \u5217\u8868\u6bcf\u6b21\u6269\u5bb9\u7684\u500d\u6570\n} MyList;\n\n/* \u6784\u9020\u51fd\u6570 */\nMyList *newMyList() {\n MyList *nums = malloc(sizeof(MyList));\n nums->capacity = 10;\n nums->arr = malloc(sizeof(int) * nums->capacity);\n nums->size = 0;\n nums->extendRatio = 2;\n return nums;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delMyList(MyList *nums) {\n free(nums->arr);\n free(nums);\n}\n\n/* \u83b7\u53d6\u5217\u8868\u957f\u5ea6 */\nint size(MyList *nums) {\n return nums->size;\n}\n\n/* \u83b7\u53d6\u5217\u8868\u5bb9\u91cf */\nint capacity(MyList *nums) {\n return nums->capacity;\n}\n\n/* \u8bbf\u95ee\u5143\u7d20 */\nint get(MyList *nums, int index) {\n assert(index >= 0 && index < nums->size);\n return nums->arr[index];\n}\n\n/* \u66f4\u65b0\u5143\u7d20 */\nvoid set(MyList *nums, int index, int num) {\n assert(index >= 0 && index < nums->size);\n nums->arr[index] = num;\n}\n\n/* \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20 */\nvoid add(MyList *nums, int num) {\n if (size(nums) == capacity(nums)) {\n extendCapacity(nums); // \u6269\u5bb9\n }\n nums->arr[size(nums)] = num;\n nums->size++;\n}\n\n/* \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20 */\nvoid insert(MyList *nums, int index, int num) {\n assert(index >= 0 && index < size(nums));\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (size(nums) == capacity(nums)) {\n extendCapacity(nums); // \u6269\u5bb9\n }\n for (int i = size(nums); i > index; --i) {\n nums->arr[i] = nums->arr[i - 1];\n }\n nums->arr[index] = num;\n nums->size++;\n}\n\n/* \u5220\u9664\u5143\u7d20 */\n// \u6ce8\u610f\uff1astdio.h \u5360\u7528\u4e86 remove \u5173\u952e\u8bcd\nint removeItem(MyList *nums, int index) {\n assert(index >= 0 && index < size(nums));\n int num = nums->arr[index];\n for (int i = index; i < size(nums) - 1; i++) {\n nums->arr[i] = nums->arr[i + 1];\n }\n nums->size--;\n return num;\n}\n\n/* \u5217\u8868\u6269\u5bb9 */\nvoid extendCapacity(MyList *nums) {\n // \u5148\u5206\u914d\u7a7a\u95f4\n int newCapacity = capacity(nums) * nums->extendRatio;\n int *extend = (int *)malloc(sizeof(int) * newCapacity);\n int *temp = nums->arr;\n\n // \u62f7\u8d1d\u65e7\u6570\u636e\u5230\u65b0\u6570\u636e\n for (int i = 0; i < size(nums); i++)\n extend[i] = nums->arr[i];\n\n // \u91ca\u653e\u65e7\u6570\u636e\n free(temp);\n\n // \u66f4\u65b0\u65b0\u6570\u636e\n nums->arr = extend;\n nums->capacity = newCapacity;\n}\n\n/* \u5c06\u5217\u8868\u8f6c\u6362\u4e3a Array \u7528\u4e8e\u6253\u5370 */\nint *toArray(MyList *nums) {\n return nums->arr;\n}\n
my_list.zig// \u5217\u8868\u7c7b\nfn MyList(comptime T: type) type {\n return struct {\n const Self = @This();\n\n arr: []T = undefined, // \u6570\u7ec4\uff08\u5b58\u50a8\u5217\u8868\u5143\u7d20\uff09\n arrCapacity: usize = 10, // \u5217\u8868\u5bb9\u91cf\n numSize: usize = 0, // \u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n extendRatio: usize = 2, // \u6bcf\u6b21\u5217\u8868\u6269\u5bb9\u7684\u500d\u6570\n mem_arena: ?std.heap.ArenaAllocator = null,\n mem_allocator: std.mem.Allocator = undefined, // \u5185\u5b58\u5206\u914d\u5668\n\n // \u6784\u9020\u51fd\u6570\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u5217\u8868\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator) !void {\n if (self.mem_arena == null) {\n self.mem_arena = std.heap.ArenaAllocator.init(allocator);\n self.mem_allocator = self.mem_arena.?.allocator();\n }\n self.arr = try self.mem_allocator.alloc(T, self.arrCapacity);\n @memset(self.arr, @as(T, 0));\n }\n\n // \u6790\u6784\u51fd\u6570\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.mem_arena == null) return;\n self.mem_arena.?.deinit();\n }\n\n // \u83b7\u53d6\u5217\u8868\u957f\u5ea6\uff08\u5f53\u524d\u5143\u7d20\u6570\u91cf\uff09\n pub fn size(self: *Self) usize {\n return self.numSize;\n }\n\n // \u83b7\u53d6\u5217\u8868\u5bb9\u91cf\n pub fn capacity(self: *Self) usize {\n return self.arrCapacity;\n }\n\n // \u8bbf\u95ee\u5143\u7d20\n pub fn get(self: *Self, index: usize) T {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 or index >= self.size()) @panic(\"\u7d22\u5f15\u8d8a\u754c\");\n return self.arr[index];\n } \n\n // \u66f4\u65b0\u5143\u7d20\n pub fn set(self: *Self, index: usize, num: T) void {\n // \u7d22\u5f15\u5982\u679c\u8d8a\u754c\uff0c\u5219\u629b\u51fa\u5f02\u5e38\uff0c\u4e0b\u540c\n if (index < 0 or index >= self.size()) @panic(\"\u7d22\u5f15\u8d8a\u754c\");\n self.arr[index] = num;\n } \n\n // \u5728\u5c3e\u90e8\u6dfb\u52a0\u5143\u7d20\n pub fn add(self: *Self, num: T) !void {\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (self.size() == self.capacity()) try self.extendCapacity();\n self.arr[self.size()] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.numSize += 1;\n } \n\n // \u5728\u4e2d\u95f4\u63d2\u5165\u5143\u7d20\n pub fn insert(self: *Self, index: usize, num: T) !void {\n if (index < 0 or index >= self.size()) @panic(\"\u7d22\u5f15\u8d8a\u754c\");\n // \u5143\u7d20\u6570\u91cf\u8d85\u51fa\u5bb9\u91cf\u65f6\uff0c\u89e6\u53d1\u6269\u5bb9\u673a\u5236\n if (self.size() == self.capacity()) try self.extendCapacity();\n // \u5c06\u7d22\u5f15 index \u4ee5\u53ca\u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\n var j = self.size() - 1;\n while (j >= index) : (j -= 1) {\n self.arr[j + 1] = self.arr[j];\n }\n self.arr[index] = num;\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.numSize += 1;\n }\n\n // \u5220\u9664\u5143\u7d20\n pub fn remove(self: *Self, index: usize) T {\n if (index < 0 or index >= self.size()) @panic(\"\u7d22\u5f15\u8d8a\u754c\");\n var num = self.arr[index];\n // \u5c06\u7d22\u5f15 index \u4e4b\u540e\u7684\u5143\u7d20\u90fd\u5411\u524d\u79fb\u52a8\u4e00\u4f4d\n var j = index;\n while (j < self.size() - 1) : (j += 1) {\n self.arr[j] = self.arr[j + 1];\n }\n // \u66f4\u65b0\u5143\u7d20\u6570\u91cf\n self.numSize -= 1;\n // \u8fd4\u56de\u88ab\u5220\u9664\u7684\u5143\u7d20\n return num;\n }\n\n // \u5217\u8868\u6269\u5bb9\n pub fn extendCapacity(self: *Self) !void {\n // \u65b0\u5efa\u4e00\u4e2a\u957f\u5ea6\u4e3a size * extendRatio \u7684\u6570\u7ec4\uff0c\u5e76\u5c06\u539f\u6570\u7ec4\u590d\u5236\u5230\u65b0\u6570\u7ec4\n var newCapacity = self.capacity() * self.extendRatio;\n var extend = try self.mem_allocator.alloc(T, newCapacity);\n @memset(extend, @as(T, 0));\n // \u5c06\u539f\u6570\u7ec4\u4e2d\u7684\u6240\u6709\u5143\u7d20\u590d\u5236\u5230\u65b0\u6570\u7ec4\n std.mem.copy(T, extend, self.arr);\n self.arr = extend;\n // \u66f4\u65b0\u5217\u8868\u5bb9\u91cf\n self.arrCapacity = newCapacity;\n }\n\n // \u5c06\u5217\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4\n pub fn toArray(self: *Self) ![]T {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n var arr = try self.mem_allocator.alloc(T, self.size());\n @memset(arr, @as(T, 0));\n for (arr, 0..) |*num, i| {\n num.* = self.get(i);\n }\n return arr;\n }\n };\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_array_and_linkedlist/ram_and_cache/","title":"4.4 \u00a0 Memory and Cache *","text":"In the first two sections of this chapter, we explored arrays and linked lists, two fundamental and important data structures, representing \"continuous storage\" and \"dispersed storage\" respectively.
In fact, the physical structure largely determines the efficiency of a program's use of memory and cache, which in turn affects the overall performance of the algorithm.
"},{"location":"chapter_array_and_linkedlist/ram_and_cache/#441-computer-storage-devices","title":"4.4.1 \u00a0 Computer Storage Devices","text":"There are three types of storage devices in computers: \"hard disk,\" \"random-access memory (RAM),\" and \"cache memory.\" The following table shows their different roles and performance characteristics in computer systems.
Table 4-2 \u00a0 Computer Storage Devices
Hard Disk Memory Cache Usage Long-term storage of data, including OS, programs, files, etc. Temporary storage of currently running programs and data being processed Stores frequently accessed data and instructions, reducing the number of CPU accesses to memory Volatility Data is not lost after power off Data is lost after power off Data is lost after power off Capacity Larger, TB level Smaller, GB level Very small, MB level Speed Slower, several hundred to thousands MB/s Faster, several tens of GB/s Very fast, several tens to hundreds of GB/s Price Cheaper, several cents to yuan / GB More expensive, tens to hundreds of yuan / GB Very expensive, priced with CPUWe can imagine the computer storage system as a pyramid structure shown in the Figure 4-9 . The storage devices closer to the top of the pyramid are faster, have smaller capacity, and are more costly. This multi-level design is not accidental, but the result of careful consideration by computer scientists and engineers.
Figure 4-9 \u00a0 Computer Storage System
Note
The storage hierarchy of computers reflects a delicate balance between speed, capacity, and cost. In fact, this kind of trade-off is common in all industrial fields, requiring us to find the best balance between different advantages and limitations.
Overall, hard disks are used for long-term storage of large amounts of data, memory is used for temporary storage of data being processed during program execution, and cache is used to store frequently accessed data and instructions to improve program execution efficiency. Together, they ensure the efficient operation of computer systems.
As shown in the Figure 4-10 , during program execution, data is read from the hard disk into memory for CPU computation. The cache can be considered a part of the CPU, smartly loading data from memory to provide fast data access to the CPU, significantly enhancing program execution efficiency and reducing reliance on slower memory.
Figure 4-10 \u00a0 Data Flow Between Hard Disk, Memory, and Cache
"},{"location":"chapter_array_and_linkedlist/ram_and_cache/#442-memory-efficiency-of-data-structures","title":"4.4.2 \u00a0 Memory Efficiency of Data Structures","text":"In terms of memory space utilization, arrays and linked lists have their advantages and limitations.
On one hand, memory is limited and cannot be shared by multiple programs, so we hope that data structures can use space as efficiently as possible. The elements of an array are tightly packed without extra space for storing references (pointers) between linked list nodes, making them more space-efficient. However, arrays require allocating sufficient continuous memory space at once, which may lead to memory waste, and array expansion also requires additional time and space costs. In contrast, linked lists allocate and reclaim memory dynamically on a per-node basis, providing greater flexibility.
On the other hand, during program execution, as memory is repeatedly allocated and released, the degree of fragmentation of free memory becomes higher, leading to reduced memory utilization efficiency. Arrays, due to their continuous storage method, are relatively less likely to cause memory fragmentation. In contrast, the elements of a linked list are dispersedly stored, and frequent insertion and deletion operations make memory fragmentation more likely.
"},{"location":"chapter_array_and_linkedlist/ram_and_cache/#443-cache-efficiency-of-data-structures","title":"4.4.3 \u00a0 Cache Efficiency of Data Structures","text":"Although caches are much smaller in space capacity than memory, they are much faster and play a crucial role in program execution speed. Since the cache's capacity is limited and can only store a small part of frequently accessed data, when the CPU tries to access data not in the cache, a \"cache miss\" occurs, forcing the CPU to load the needed data from slower memory.
Clearly, the fewer the cache misses, the higher the CPU's data read-write efficiency, and the better the program performance. The proportion of successful data retrieval from the cache by the CPU is called the \"cache hit rate,\" a metric often used to measure cache efficiency.
To achieve higher efficiency, caches adopt the following data loading mechanisms.
In fact, arrays and linked lists have different cache utilization efficiencies, mainly reflected in the following aspects.
Overall, arrays have a higher cache hit rate and are generally more efficient in operation than linked lists. This makes data structures based on arrays more popular in solving algorithmic problems.
It should be noted that high cache efficiency does not mean that arrays are always better than linked lists. Which data structure to choose in actual applications should be based on specific requirements. For example, both arrays and linked lists can implement the \"stack\" data structure (which will be detailed in the next chapter), but they are suitable for different scenarios.
Q: Does storing arrays on the stack versus the heap affect time and space efficiency?
Arrays stored on both the stack and heap are stored in contiguous memory spaces, and data operation efficiency is essentially the same. However, stacks and heaps have their own characteristics, leading to the following differences.
Q: Why do arrays require elements of the same type, while linked lists do not emphasize same-type elements?
Linked lists consist of nodes connected by references (pointers), and each node can store data of different types, such as int, double, string, object, etc.
In contrast, array elements must be of the same type, allowing the calculation of offsets to access the corresponding element positions. For example, an array containing both int and long types, with single elements occupying 4 bytes and 8 bytes respectively, cannot use the following formula to calculate offsets, as the array contains elements of two different lengths.
# Element memory address = Array memory address + Element length * Element index\n
Q: After deleting a node, is it necessary to set P.next
to None
?
Not modifying P.next
is also acceptable. From the perspective of the linked list, traversing from the head node to the tail node will no longer encounter P
. This means that node P
has been effectively removed from the list, and where P
points no longer affects the list.
From a garbage collection perspective, for languages with automatic garbage collection mechanisms like Java, Python, and Go, whether node P
is collected depends on whether there are still references pointing to it, not on the value of P.next
. In languages like C and C++, we need to manually free the node's memory.
Q: In linked lists, the time complexity for insertion and deletion operations is O(1)
. But searching for the element before insertion or deletion takes O(n)
time, so why isn't the time complexity O(n)
?
If an element is searched first and then deleted, the time complexity is indeed O(n)
. However, the O(1)
advantage of linked lists in insertion and deletion can be realized in other applications. For example, in the implementation of double-ended queues using linked lists, we maintain pointers always pointing to the head and tail nodes, making each insertion and deletion operation O(1)
.
Q: In the image \"Linked List Definition and Storage Method\", do the light blue storage nodes occupy a single memory address, or do they share half with the node value?
The diagram is just a qualitative representation; quantitative analysis depends on specific situations.
Q: Is adding elements to the end of a list always O(1)
?
If adding an element exceeds the list length, the list needs to be expanded first. The system will request a new memory block and move all elements of the original list over, in which case the time complexity becomes O(n)
.
Q: The statement \"The emergence of lists greatly improves the practicality of arrays, but may lead to some memory space wastage\" - does this refer to the memory occupied by additional variables like capacity, length, and expansion multiplier?
The space wastage here mainly refers to two aspects: on the one hand, lists are set with an initial length, which we may not always need; on the other hand, to prevent frequent expansion, expansion usually multiplies by a coefficient, such as \\(\\times 1.5\\). This results in many empty slots, which we typically cannot fully fill.
Q: In Python, after initializing n = [1, 2, 3]
, the addresses of these 3 elements are contiguous, but initializing m = [2, 1, 3]
shows that each element's id
is not consecutive but identical to those in n
. If the addresses of these elements are not contiguous, is m
still an array?
If we replace list elements with linked list nodes n = [n1, n2, n3, n4, n5]
, these 5 node objects are also typically dispersed throughout memory. However, given a list index, we can still access the node's memory address in O(1)
time, thereby accessing the corresponding node. This is because the array stores references to the nodes, not the nodes themselves.
Unlike many languages, in Python, numbers are also wrapped as objects, and lists store references to these numbers, not the numbers themselves. Therefore, we find that the same number in two arrays has the same id
, and these numbers' memory addresses need not be contiguous.
Q: The std::list
in C++ STL has already implemented a doubly linked list, but it seems that some algorithm books don't directly use it. Is there any limitation?
On the one hand, we often prefer to use arrays to implement algorithms, only using linked lists when necessary, mainly for two reasons.
std::list
usually occupies more space than std::vector
.std::list
has a lower cache utilization rate. Generally, std::vector
performs better.On the other hand, linked lists are primarily necessary for binary trees and graphs. Stacks and queues are often implemented using the programming language's stack
and queue
classes, rather than linked lists.
Q: Does initializing a list res = [0] * self.size()
result in each element of res
referencing the same address?
No. However, this issue arises with two-dimensional arrays, for example, initializing a two-dimensional list res = [[0] * self.size()]
would reference the same list [0]
multiple times.
Q: In deleting a node, is it necessary to break the reference to its successor node?
From the perspective of data structures and algorithms (problem-solving), it's okay not to break the link, as long as the program's logic is correct. From the perspective of standard libraries, breaking the link is safer and more logically clear. If the link is not broken, and the deleted node is not properly recycled, it could affect the recycling of the successor node's memory.
"},{"location":"chapter_computational_complexity/","title":"Chapter 2. \u00a0 Complexity Analysis","text":"Abstract
Complexity analysis is like a space-time navigator in the vast universe of algorithms.
It guides us in exploring deeper within the the dimensions of time and space, seeking more elegant solutions.
"},{"location":"chapter_computational_complexity/#chapter-contents","title":"Chapter Contents","text":"In algorithms, repeatedly performing a task is common and closely related to complexity analysis. Therefore, before introducing time complexity and space complexity, let's first understand how to implement task repetition in programs, focusing on two basic programming control structures: iteration and recursion.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#221-iteration","title":"2.2.1 \u00a0 Iteration","text":"\"Iteration\" is a control structure for repeatedly performing a task. In iteration, a program repeats a block of code as long as a certain condition is met, until this condition is no longer satisfied.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#1-for-loop","title":"1. \u00a0 for Loop","text":"The for
loop is one of the most common forms of iteration, suitable for use when the number of iterations is known in advance.
The following function implements the sum \\(1 + 2 + \\dots + n\\) using a for
loop, with the sum result recorded in the variable res
. Note that in Python, range(a, b)
corresponds to a \"left-closed, right-open\" interval, covering \\(a, a + 1, \\dots, b-1\\):
def for_loop(n: int) -> int:\n \"\"\"for \u5faa\u73af\"\"\"\n res = 0\n # \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for i in range(1, n + 1):\n res += i\n return res\n
iteration.cpp/* for \u5faa\u73af */\nint forLoop(int n) {\n int res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (int i = 1; i <= n; ++i) {\n res += i;\n }\n return res;\n}\n
iteration.java/* for \u5faa\u73af */\nint forLoop(int n) {\n int res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.cs/* for \u5faa\u73af */\nint ForLoop(int n) {\n int res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.go/* for \u5faa\u73af */\nfunc forLoop(n int) int {\n res := 0\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for i := 1; i <= n; i++ {\n res += i\n }\n return res\n}\n
iteration.swift/* for \u5faa\u73af */\nfunc forLoop(n: Int) -> Int {\n var res = 0\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for i in 1 ... n {\n res += i\n }\n return res\n}\n
iteration.js/* for \u5faa\u73af */\nfunction forLoop(n) {\n let res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (let i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.ts/* for \u5faa\u73af */\nfunction forLoop(n: number): number {\n let res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (let i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.dart/* for \u5faa\u73af */\nint forLoop(int n) {\n int res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.rs/* for \u5faa\u73af */\nfn for_loop(n: i32) -> i32 {\n let mut res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for i in 1..=n {\n res += i;\n }\n res\n} \n
iteration.c/* for \u5faa\u73af */\nint forLoop(int n) {\n int res = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n res += i;\n }\n return res;\n}\n
iteration.zig// for \u5faa\u73af\nfn forLoop(n: usize) i32 {\n var res: i32 = 0;\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for (1..n+1) |i| {\n res = res + @as(i32, @intCast(i));\n }\n return res;\n} \n
Visualizing Code Full Screen >
The flowchart below represents this sum function.
Figure 2-1 \u00a0 Flowchart of the Sum Function
The number of operations in this sum function is proportional to the input data size \\(n\\), or in other words, it has a \"linear relationship\". This is actually what time complexity describes. This topic will be detailed in the next section.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#2-while-loop","title":"2. \u00a0 while Loop","text":"Similar to the for
loop, the while
loop is another method to implement iteration. In a while
loop, the program checks the condition in each round; if the condition is true, it continues, otherwise, the loop ends.
Below we use a while
loop to implement the sum \\(1 + 2 + \\dots + n\\):
def while_loop(n: int) -> int:\n \"\"\"while \u5faa\u73af\"\"\"\n res = 0\n i = 1 # \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n # \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while i <= n:\n res += i\n i += 1 # \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n return res\n
iteration.cpp/* while \u5faa\u73af */\nint whileLoop(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.java/* while \u5faa\u73af */\nint whileLoop(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.cs/* while \u5faa\u73af */\nint WhileLoop(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i += 1; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.go/* while \u5faa\u73af */\nfunc whileLoop(n int) int {\n res := 0\n // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n i := 1\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n for i <= n {\n res += i\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++\n }\n return res\n}\n
iteration.swift/* while \u5faa\u73af */\nfunc whileLoop(n: Int) -> Int {\n var res = 0\n var i = 1 // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while i <= n {\n res += i\n i += 1 // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res\n}\n
iteration.js/* while \u5faa\u73af */\nfunction whileLoop(n) {\n let res = 0;\n let i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.ts/* while \u5faa\u73af */\nfunction whileLoop(n: number): number {\n let res = 0;\n let i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.dart/* while \u5faa\u73af */\nint whileLoop(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.rs/* while \u5faa\u73af */\nfn while_loop(n: i32) -> i32 {\n let mut res = 0;\n let mut i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while i <= n {\n res += i;\n i += 1; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n res\n}\n
iteration.c/* while \u5faa\u73af */\nint whileLoop(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += i;\n i++; // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n }\n return res;\n}\n
iteration.zig// while \u5faa\u73af\nfn whileLoop(n: i32) i32 {\n var res: i32 = 0;\n var i: i32 = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 2, ..., n-1, n\n while (i <= n) {\n res += @intCast(i);\n i += 1;\n }\n return res;\n}\n
Visualizing Code Full Screen >
The while
loop is more flexible than the for
loop. In a while
loop, we can freely design the initialization and update steps of the condition variable.
For example, in the following code, the condition variable \\(i\\) is updated twice in each round, which would be inconvenient to implement with a for
loop:
def while_loop_ii(n: int) -> int:\n \"\"\"while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09\"\"\"\n res = 0\n i = 1 # \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n # \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while i <= n:\n res += i\n # \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i += 1\n i *= 2\n return res\n
iteration.cpp/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nint whileLoopII(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.java/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nint whileLoopII(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.cs/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nint WhileLoopII(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i += 1; \n i *= 2;\n }\n return res;\n}\n
iteration.go/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nfunc whileLoopII(n int) int {\n res := 0\n // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n i := 1\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n for i <= n {\n res += i\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++\n i *= 2\n }\n return res\n}\n
iteration.swift/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nfunc whileLoopII(n: Int) -> Int {\n var res = 0\n var i = 1 // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while i <= n {\n res += i\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i += 1\n i *= 2\n }\n return res\n}\n
iteration.js/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nfunction whileLoopII(n) {\n let res = 0;\n let i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.ts/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nfunction whileLoopII(n: number): number {\n let res = 0;\n let i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.dart/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nint whileLoopII(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.rs/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nfn while_loop_ii(n: i32) -> i32 {\n let mut res = 0;\n let mut i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while i <= n {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i += 1;\n i *= 2;\n }\n res\n}\n
iteration.c/* while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09 */\nint whileLoopII(int n) {\n int res = 0;\n int i = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += i;\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i++;\n i *= 2;\n }\n return res;\n}\n
iteration.zig// while \u5faa\u73af\uff08\u4e24\u6b21\u66f4\u65b0\uff09\nfn whileLoopII(n: i32) i32 {\n var res: i32 = 0;\n var i: i32 = 1; // \u521d\u59cb\u5316\u6761\u4ef6\u53d8\u91cf\n // \u5faa\u73af\u6c42\u548c 1, 4, 10, ...\n while (i <= n) {\n res += @intCast(i);\n // \u66f4\u65b0\u6761\u4ef6\u53d8\u91cf\n i += 1;\n i *= 2;\n }\n return res;\n}\n
Visualizing Code Full Screen >
Overall, for
loops are more concise, while while
loops are more flexible. Both can implement iterative structures. Which one to use should be determined based on the specific requirements of the problem.
We can nest one loop structure within another. Below is an example using for
loops:
def nested_for_loop(n: int) -> str:\n \"\"\"\u53cc\u5c42 for \u5faa\u73af\"\"\"\n res = \"\"\n # \u5faa\u73af i = 1, 2, ..., n-1, n\n for i in range(1, n + 1):\n # \u5faa\u73af j = 1, 2, ..., n-1, n\n for j in range(1, n + 1):\n res += f\"({i}, {j}), \"\n return res\n
iteration.cpp/* \u53cc\u5c42 for \u5faa\u73af */\nstring nestedForLoop(int n) {\n ostringstream res;\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (int i = 1; i <= n; ++i) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (int j = 1; j <= n; ++j) {\n res << \"(\" << i << \", \" << j << \"), \";\n }\n }\n return res.str();\n}\n
iteration.java/* \u53cc\u5c42 for \u5faa\u73af */\nString nestedForLoop(int n) {\n StringBuilder res = new StringBuilder();\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (int j = 1; j <= n; j++) {\n res.append(\"(\" + i + \", \" + j + \"), \");\n }\n }\n return res.toString();\n}\n
iteration.cs/* \u53cc\u5c42 for \u5faa\u73af */\nstring NestedForLoop(int n) {\n StringBuilder res = new();\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (int j = 1; j <= n; j++) {\n res.Append($\"({i}, {j}), \");\n }\n }\n return res.ToString();\n}\n
iteration.go/* \u53cc\u5c42 for \u5faa\u73af */\nfunc nestedForLoop(n int) string {\n res := \"\"\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for i := 1; i <= n; i++ {\n for j := 1; j <= n; j++ {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n res += fmt.Sprintf(\"(%d, %d), \", i, j)\n }\n }\n return res\n}\n
iteration.swift/* \u53cc\u5c42 for \u5faa\u73af */\nfunc nestedForLoop(n: Int) -> String {\n var res = \"\"\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for i in 1 ... n {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for j in 1 ... n {\n res.append(\"(\\(i), \\(j)), \")\n }\n }\n return res\n}\n
iteration.js/* \u53cc\u5c42 for \u5faa\u73af */\nfunction nestedForLoop(n) {\n let res = '';\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (let i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (let j = 1; j <= n; j++) {\n res += `(${i}, ${j}), `;\n }\n }\n return res;\n}\n
iteration.ts/* \u53cc\u5c42 for \u5faa\u73af */\nfunction nestedForLoop(n: number): string {\n let res = '';\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (let i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (let j = 1; j <= n; j++) {\n res += `(${i}, ${j}), `;\n }\n }\n return res;\n}\n
iteration.dart/* \u53cc\u5c42 for \u5faa\u73af */\nString nestedForLoop(int n) {\n String res = \"\";\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (int j = 1; j <= n; j++) {\n res += \"($i, $j), \";\n }\n }\n return res;\n}\n
iteration.rs/* \u53cc\u5c42 for \u5faa\u73af */\nfn nested_for_loop(n: i32) -> String {\n let mut res = vec![];\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for i in 1..=n {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for j in 1..=n {\n res.push(format!(\"({}, {}), \", i, j));\n }\n }\n res.join(\"\")\n}\n
iteration.c/* \u53cc\u5c42 for \u5faa\u73af */\nchar *nestedForLoop(int n) {\n // n * n \u4e3a\u5bf9\u5e94\u70b9\u6570\u91cf\uff0c\"(i, j), \" \u5bf9\u5e94\u5b57\u7b26\u4e32\u957f\u6700\u5927\u4e3a 6+10*2\uff0c\u52a0\u4e0a\u6700\u540e\u4e00\u4e2a\u7a7a\u5b57\u7b26 \\0 \u7684\u989d\u5916\u7a7a\u95f4\n int size = n * n * 26 + 1;\n char *res = malloc(size * sizeof(char));\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (int i = 1; i <= n; i++) {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (int j = 1; j <= n; j++) {\n char tmp[26];\n snprintf(tmp, sizeof(tmp), \"(%d, %d), \", i, j);\n strncat(res, tmp, size - strlen(res) - 1);\n }\n }\n return res;\n}\n
iteration.zig// \u53cc\u5c42 for \u5faa\u73af\nfn nestedForLoop(allocator: Allocator, n: usize) ![]const u8 {\n var res = std.ArrayList(u8).init(allocator);\n defer res.deinit();\n var buffer: [20]u8 = undefined;\n // \u5faa\u73af i = 1, 2, ..., n-1, n\n for (1..n+1) |i| {\n // \u5faa\u73af j = 1, 2, ..., n-1, n\n for (1..n+1) |j| {\n var _str = try std.fmt.bufPrint(&buffer, \"({d}, {d}), \", .{i, j});\n try res.appendSlice(_str);\n }\n }\n return res.toOwnedSlice();\n}\n
Visualizing Code Full Screen >
The flowchart below represents this nested loop.
Figure 2-2 \u00a0 Flowchart of the Nested Loop
In this case, the number of operations in the function is proportional to \\(n^2\\), or the algorithm's running time and the input data size \\(n\\) have a \"quadratic relationship\".
We can continue adding nested loops, each nesting is a \"dimensional escalation,\" which will increase the time complexity to \"cubic,\" \"quartic,\" and so on.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#222-recursion","title":"2.2.2 \u00a0 Recursion","text":"\"Recursion\" is an algorithmic strategy that solves problems by having a function call itself. It mainly consists of two phases.
From an implementation perspective, recursive code mainly includes three elements.
Observe the following code, where calling the function recur(n)
completes the computation of \\(1 + 2 + \\dots + n\\):
def recur(n: int) -> int:\n \"\"\"\u9012\u5f52\"\"\"\n # \u7ec8\u6b62\u6761\u4ef6\n if n == 1:\n return 1\n # \u9012\uff1a\u9012\u5f52\u8c03\u7528\n res = recur(n - 1)\n # \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res\n
recursion.cpp/* \u9012\u5f52 */\nint recur(int n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1)\n return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n int res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.java/* \u9012\u5f52 */\nint recur(int n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1)\n return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n int res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.cs/* \u9012\u5f52 */\nint Recur(int n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1)\n return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n int res = Recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.go/* \u9012\u5f52 */\nfunc recur(n int) int {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 1 {\n return 1\n }\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n res := recur(n - 1)\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res\n}\n
recursion.swift/* \u9012\u5f52 */\nfunc recur(n: Int) -> Int {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 1 {\n return 1\n }\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n let res = recur(n: n - 1)\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res\n}\n
recursion.js/* \u9012\u5f52 */\nfunction recur(n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n === 1) return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n const res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.ts/* \u9012\u5f52 */\nfunction recur(n: number): number {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n === 1) return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n const res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.dart/* \u9012\u5f52 */\nint recur(int n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1) return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n int res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.rs/* \u9012\u5f52 */\nfn recur(n: i32) -> i32 {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 1 {\n return 1;\n }\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n let res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n n + res\n}\n
recursion.c/* \u9012\u5f52 */\nint recur(int n) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1)\n return 1;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n int res = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
recursion.zig// \u9012\u5f52\u51fd\u6570\nfn recur(n: i32) i32 {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 1) {\n return 1;\n }\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n var res: i32 = recur(n - 1);\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n return n + res;\n}\n
Visualizing Code Full Screen >
The Figure 2-3 shows the recursive process of this function.
Figure 2-3 \u00a0 Recursive Process of the Sum Function
Although iteration and recursion can achieve the same results from a computational standpoint, they represent two entirely different paradigms of thinking and solving problems.
Taking the sum function as an example, let's define the problem as \\(f(n) = 1 + 2 + \\dots + n\\).
Each time a recursive function calls itself, the system allocates memory for the newly initiated function to store local variables, call addresses, and other information. This leads to two main consequences.
As shown in the Figure 2-4 , there are \\(n\\) unreturned recursive functions before triggering the termination condition, indicating a recursion depth of \\(n\\).
Figure 2-4 \u00a0 Recursion Call Depth
In practice, the depth of recursion allowed by programming languages is usually limited, and excessively deep recursion can lead to stack overflow errors.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#2-tail-recursion","title":"2. \u00a0 Tail Recursion","text":"Interestingly, if a function makes its recursive call as the last step before returning, it can be optimized by compilers or interpreters to be as space-efficient as iteration. This scenario is known as \"tail recursion\".
For example, in calculating \\(1 + 2 + \\dots + n\\), we can make the result variable res
a parameter of the function, thereby achieving tail recursion:
def tail_recur(n, res):\n \"\"\"\u5c3e\u9012\u5f52\"\"\"\n # \u7ec8\u6b62\u6761\u4ef6\n if n == 0:\n return res\n # \u5c3e\u9012\u5f52\u8c03\u7528\n return tail_recur(n - 1, res + n)\n
recursion.cpp/* \u5c3e\u9012\u5f52 */\nint tailRecur(int n, int res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0)\n return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.java/* \u5c3e\u9012\u5f52 */\nint tailRecur(int n, int res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0)\n return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.cs/* \u5c3e\u9012\u5f52 */\nint TailRecur(int n, int res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0)\n return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return TailRecur(n - 1, res + n);\n}\n
recursion.go/* \u5c3e\u9012\u5f52 */\nfunc tailRecur(n int, res int) int {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 0 {\n return res\n }\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n-1, res+n)\n}\n
recursion.swift/* \u5c3e\u9012\u5f52 */\nfunc tailRecur(n: Int, res: Int) -> Int {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 0 {\n return res\n }\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n: n - 1, res: res + n)\n}\n
recursion.js/* \u5c3e\u9012\u5f52 */\nfunction tailRecur(n, res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n === 0) return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.ts/* \u5c3e\u9012\u5f52 */\nfunction tailRecur(n: number, res: number): number {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n === 0) return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.dart/* \u5c3e\u9012\u5f52 */\nint tailRecur(int n, int res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0) return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.rs/* \u5c3e\u9012\u5f52 */\nfn tail_recur(n: i32, res: i32) -> i32 {\n // \u7ec8\u6b62\u6761\u4ef6\n if n == 0 {\n return res;\n }\n // \u5c3e\u9012\u5f52\u8c03\u7528\n tail_recur(n - 1, res + n)\n}\n
recursion.c/* \u5c3e\u9012\u5f52 */\nint tailRecur(int n, int res) {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0)\n return res;\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
recursion.zig// \u5c3e\u9012\u5f52\u51fd\u6570\nfn tailRecur(n: i32, res: i32) i32 {\n // \u7ec8\u6b62\u6761\u4ef6\n if (n == 0) {\n return res;\n }\n // \u5c3e\u9012\u5f52\u8c03\u7528\n return tailRecur(n - 1, res + n);\n}\n
Visualizing Code Full Screen >
The execution process of tail recursion is shown in the following figure. Comparing regular recursion and tail recursion, the point of the summation operation is different.
Figure 2-5 \u00a0 Tail Recursion Process
Tip
Note that many compilers or interpreters do not support tail recursion optimization. For example, Python does not support tail recursion optimization by default, so even if the function is in the form of tail recursion, it may still encounter stack overflow issues.
"},{"location":"chapter_computational_complexity/iteration_and_recursion/#3-recursion-tree","title":"3. \u00a0 Recursion Tree","text":"When dealing with algorithms related to \"divide and conquer\", recursion often offers a more intuitive approach and more readable code than iteration. Take the \"Fibonacci sequence\" as an example.
Question
Given a Fibonacci sequence \\(0, 1, 1, 2, 3, 5, 8, 13, \\dots\\), find the \\(n\\)th number in the sequence.
Let the \\(n\\)th number of the Fibonacci sequence be \\(f(n)\\), it's easy to deduce two conclusions:
Using the recursive relation, and considering the first two numbers as termination conditions, we can write the recursive code. Calling fib(n)
will yield the \\(n\\)th number of the Fibonacci sequence:
def fib(n: int) -> int:\n \"\"\"\u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52\"\"\"\n # \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if n == 1 or n == 2:\n return n - 1\n # \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n res = fib(n - 1) + fib(n - 2)\n # \u8fd4\u56de\u7ed3\u679c f(n)\n return res\n
recursion.cpp/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nint fib(int n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 || n == 2)\n return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n int res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.java/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nint fib(int n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 || n == 2)\n return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n int res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.cs/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nint Fib(int n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 || n == 2)\n return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n int res = Fib(n - 1) + Fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.go/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nfunc fib(n int) int {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if n == 1 || n == 2 {\n return n - 1\n }\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n res := fib(n-1) + fib(n-2)\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res\n}\n
recursion.swift/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nfunc fib(n: Int) -> Int {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if n == 1 || n == 2 {\n return n - 1\n }\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n let res = fib(n: n - 1) + fib(n: n - 2)\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res\n}\n
recursion.js/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nfunction fib(n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n === 1 || n === 2) return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n const res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.ts/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nfunction fib(n: number): number {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n === 1 || n === 2) return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n const res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.dart/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nint fib(int n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 || n == 2) return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n int res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.rs/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nfn fib(n: i32) -> i32 {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if n == 1 || n == 2 {\n return n - 1;\n }\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n let res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c\n res\n}\n
recursion.c/* \u6590\u6ce2\u90a3\u5951\u6570\u5217\uff1a\u9012\u5f52 */\nint fib(int n) {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 || n == 2)\n return n - 1;\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n int res = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
recursion.zig// \u6590\u6ce2\u90a3\u5951\u6570\u5217\nfn fib(n: i32) i32 {\n // \u7ec8\u6b62\u6761\u4ef6 f(1) = 0, f(2) = 1\n if (n == 1 or n == 2) {\n return n - 1;\n }\n // \u9012\u5f52\u8c03\u7528 f(n) = f(n-1) + f(n-2)\n var res: i32 = fib(n - 1) + fib(n - 2);\n // \u8fd4\u56de\u7ed3\u679c f(n)\n return res;\n}\n
Visualizing Code Full Screen >
Observing the above code, we see that it recursively calls two functions within itself, meaning that one call generates two branching calls. As illustrated below, this continuous recursive calling eventually creates a \"recursion tree\" with a depth of \\(n\\).
Figure 2-6 \u00a0 Fibonacci Sequence Recursion Tree
Fundamentally, recursion embodies the paradigm of \"breaking down a problem into smaller sub-problems.\" This divide-and-conquer strategy is crucial.
Summarizing the above content, the following table shows the differences between iteration and recursion in terms of implementation, performance, and applicability.
Table: Comparison of Iteration and Recursion Characteristics
Iteration Recursion Approach Loop structure Function calls itself Time Efficiency Generally higher efficiency, no function call overhead Each function call generates overhead Memory Usage Typically uses a fixed size of memory space Accumulative function calls can use a substantial amount of stack frame space Suitable Problems Suitable for simple loop tasks, intuitive and readable code Suitable for problem decomposition, like trees, graphs, divide-and-conquer, backtracking, etc., concise and clear code structureTip
If you find the following content difficult to understand, consider revisiting it after reading the \"Stack\" chapter.
So, what is the intrinsic connection between iteration and recursion? Taking the above recursive function as an example, the summation operation occurs during the recursion's \"return\" phase. This means that the initially called function is actually the last to complete its summation operation, mirroring the \"last in, first out\" principle of a stack.
In fact, recursive terms like \"call stack\" and \"stack frame space\" hint at the close relationship between recursion and stacks.
Therefore, we can use an explicit stack to simulate the behavior of the call stack, thus transforming recursion into an iterative form:
PythonC++JavaC#GoSwiftJSTSDartRustCZig recursion.pydef for_loop_recur(n: int) -> int:\n \"\"\"\u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52\"\"\"\n # \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n stack = []\n res = 0\n # \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for i in range(n, 0, -1):\n # \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.append(i)\n # \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while stack:\n # \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.pop()\n # res = 1+2+3+...+n\n return res\n
recursion.cpp/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nint forLoopRecur(int n) {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n stack<int> stack;\n int res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (int i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (!stack.empty()) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.top();\n stack.pop();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.java/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nint forLoopRecur(int n) {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n Stack<Integer> stack = new Stack<>();\n int res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (int i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (!stack.isEmpty()) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.pop();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.cs/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nint ForLoopRecur(int n) {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n Stack<int> stack = new();\n int res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (int i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.Push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (stack.Count > 0) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.Pop();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.go/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nfunc forLoopRecur(n int) int {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n stack := list.New()\n res := 0\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for i := n; i > 0; i-- {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.PushBack(i)\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n for stack.Len() != 0 {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.Back().Value.(int)\n stack.Remove(stack.Back())\n }\n // res = 1+2+3+...+n\n return res\n}\n
recursion.swift/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nfunc forLoopRecur(n: Int) -> Int {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n var stack: [Int] = []\n var res = 0\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for i in stride(from: n, to: 0, by: -1) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.append(i)\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while !stack.isEmpty {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.removeLast()\n }\n // res = 1+2+3+...+n\n return res\n}\n
recursion.js/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nfunction forLoopRecur(n) {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n const stack = [];\n let res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (let i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (stack.length) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.pop();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.ts/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nfunction forLoopRecur(n: number): number {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808 \n const stack: number[] = [];\n let res: number = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (let i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (stack.length) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.pop();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.dart/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nint forLoopRecur(int n) {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n List<int> stack = [];\n int res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (int i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.add(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (!stack.isEmpty) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.removeLast();\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.rs/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nfn for_loop_recur(n: i32) -> i32 {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n let mut stack = Vec::new();\n let mut res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for i in (1..=n).rev() {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack.push(i);\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while !stack.is_empty() {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack.pop().unwrap();\n }\n // res = 1+2+3+...+n\n res\n}\n
recursion.c/* \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52 */\nint forLoopRecur(int n) {\n int stack[1000]; // \u501f\u52a9\u4e00\u4e2a\u5927\u6570\u7ec4\u6765\u6a21\u62df\u6808\n int top = -1; // \u6808\u9876\u7d22\u5f15\n int res = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n for (int i = n; i > 0; i--) {\n // \u901a\u8fc7\u201c\u5165\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u9012\u201d\n stack[1 + top++] = i;\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n while (top >= 0) {\n // \u901a\u8fc7\u201c\u51fa\u6808\u64cd\u4f5c\u201d\u6a21\u62df\u201c\u5f52\u201d\n res += stack[top--];\n }\n // res = 1+2+3+...+n\n return res;\n}\n
recursion.zig// \u4f7f\u7528\u8fed\u4ee3\u6a21\u62df\u9012\u5f52\nfn forLoopRecur(comptime n: i32) i32 {\n // \u4f7f\u7528\u4e00\u4e2a\u663e\u5f0f\u7684\u6808\u6765\u6a21\u62df\u7cfb\u7edf\u8c03\u7528\u6808\n var stack: [n]i32 = undefined;\n var res: i32 = 0;\n // \u9012\uff1a\u9012\u5f52\u8c03\u7528\n var i: usize = n;\n while (i > 0) {\n stack[i - 1] = @intCast(i);\n i -= 1;\n }\n // \u5f52\uff1a\u8fd4\u56de\u7ed3\u679c\n var index: usize = n;\n while (index > 0) {\n index -= 1;\n res += stack[index];\n }\n // res = 1+2+3+...+n\n return res;\n}\n
Visualizing Code Full Screen >
Observing the above code, when recursion is transformed into iteration, the code becomes more complex. Although iteration and recursion can often be transformed into each other, it's not always advisable to do so for two reasons:
In summary, choosing between iteration and recursion depends on the nature of the specific problem. In programming practice, weighing the pros and cons of each and choosing the appropriate method for the situation is essential.
"},{"location":"chapter_computational_complexity/performance_evaluation/","title":"2.1 \u00a0 Algorithm Efficiency Assessment","text":"In algorithm design, we pursue the following two objectives in sequence.
In other words, under the premise of being able to solve the problem, algorithm efficiency has become the main criterion for evaluating the merits of an algorithm, which includes the following two dimensions.
In short, our goal is to design data structures and algorithms that are both fast and memory-efficient. Effectively assessing algorithm efficiency is crucial because only then can we compare various algorithms and guide the process of algorithm design and optimization.
There are mainly two methods of efficiency assessment: actual testing and theoretical estimation.
"},{"location":"chapter_computational_complexity/performance_evaluation/#211-actual-testing","title":"2.1.1 \u00a0 Actual Testing","text":"Suppose we have algorithms A
and B
, both capable of solving the same problem, and we need to compare their efficiencies. The most direct method is to use a computer to run these two algorithms and monitor and record their runtime and memory usage. This assessment method reflects the actual situation but has significant limitations.
On one hand, it's difficult to eliminate interference from the testing environment. Hardware configurations can affect algorithm performance. For example, algorithm A
might run faster than B
on one computer, but the opposite result may occur on another computer with different configurations. This means we would need to test on a variety of machines to calculate average efficiency, which is impractical.
On the other hand, conducting a full test is very resource-intensive. As the volume of input data changes, the efficiency of the algorithms may vary. For example, with smaller data volumes, algorithm A
might run faster than B
, but the opposite might be true with larger data volumes. Therefore, to draw convincing conclusions, we need to test a wide range of input data sizes, which requires significant computational resources.
Due to the significant limitations of actual testing, we can consider evaluating algorithm efficiency solely through calculations. This estimation method is known as \"asymptotic complexity analysis,\" or simply \"complexity analysis.\"
Complexity analysis reflects the relationship between the time and space resources required for algorithm execution and the size of the input data. It describes the trend of growth in the time and space required by the algorithm as the size of the input data increases. This definition might sound complex, but we can break it down into three key points to understand it better.
Complexity analysis overcomes the disadvantages of actual testing methods, reflected in the following aspects:
Tip
If you're still confused about the concept of complexity, don't worry. We will introduce it in detail in subsequent chapters.
Complexity analysis provides us with a \"ruler\" to measure the time and space resources needed to execute an algorithm and compare the efficiency between different algorithms.
Complexity is a mathematical concept and may be abstract and challenging for beginners. From this perspective, complexity analysis might not be the best content to introduce first. However, when discussing the characteristics of a particular data structure or algorithm, it's hard to avoid analyzing its speed and space usage.
In summary, it's recommended that you establish a preliminary understanding of complexity analysis before diving deep into data structures and algorithms, so that you can carry out simple complexity analyses of algorithms.
"},{"location":"chapter_computational_complexity/space_complexity/","title":"2.4 \u00a0 Space Complexity","text":"\"Space complexity\" is used to measure the growth trend of the memory space occupied by an algorithm as the amount of data increases. This concept is very similar to time complexity, except that \"running time\" is replaced with \"occupied memory space\".
"},{"location":"chapter_computational_complexity/space_complexity/#241-space-related-to-algorithms","title":"2.4.1 \u00a0 Space Related to Algorithms","text":"The memory space used by an algorithm during its execution mainly includes the following types.
Generally, the scope of space complexity statistics includes both \"Temporary Space\" and \"Output Space\".
Temporary space can be further divided into three parts.
When analyzing the space complexity of a program, we typically count the Temporary Data, Stack Frame Space, and Output Data, as shown in the Figure 2-15 .
Figure 2-15 \u00a0 Space Types Used in Algorithms
The relevant code is as follows:
PythonC++JavaC#GoSwiftJSTSDartRustCZigclass Node:\n \"\"\"Classes\"\"\"\"\n def __init__(self, x: int):\n self.val: int = x # node value\n self.next: Node | None = None # reference to the next node\n\ndef function() -> int:\n \"\"\"\"Functions\"\"\"\"\"\n # Perform certain operations...\n return 0\n\ndef algorithm(n) -> int: # input data\n A = 0 # temporary data (constant, usually in uppercase)\n b = 0 # temporary data (variable)\n node = Node(0) # temporary data (object)\n c = function() # Stack frame space (call function)\n return A + b + c # output data\n
/* Structures */\nstruct Node {\n int val;\n Node *next;\n Node(int x) : val(x), next(nullptr) {}\n};\n\n/* Functions */\nint func() {\n // Perform certain operations...\n return 0;\n}\n\nint algorithm(int n) { // input data\n const int a = 0; // temporary data (constant)\n int b = 0; // temporary data (variable)\n Node* node = new Node(0); // temporary data (object)\n int c = func(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
/* Classes */\nclass Node {\n int val;\n Node next;\n Node(int x) { val = x; }\n}\n\n/* Functions */\nint function() {\n // Perform certain operations...\n return 0;\n}\n\nint algorithm(int n) { // input data\n final int a = 0; // temporary data (constant)\n int b = 0; // temporary data (variable)\n Node node = new Node(0); // temporary data (object)\n int c = function(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
/* Classes */\nclass Node {\n int val;\n Node next;\n Node(int x) { val = x; }\n}\n\n/* Functions */\nint Function() {\n // Perform certain operations...\n return 0;\n}\n\nint Algorithm(int n) { // input data\n const int a = 0; // temporary data (constant)\n int b = 0; // temporary data (variable)\n Node node = new(0); // temporary data (object)\n int c = Function(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
/* Structures */\ntype node struct {\n val int\n next *node\n}\n\n/* Create node structure */\nfunc newNode(val int) *node {\n return &node{val: val}\n}\n\n/* Functions */\nfunc function() int {\n // Perform certain operations...\n return 0\n}\n\nfunc algorithm(n int) int { // input data\n const a = 0 // temporary data (constant)\n b := 0 // temporary storage of data (variable)\n newNode(0) // temporary data (object)\n c := function() // stack frame space (call function)\n return a + b + c // output data\n}\n
/* Classes */\nclass Node {\n var val: Int\n var next: Node?\n\n init(x: Int) {\n val = x\n }\n}\n\n/* Functions */\nfunc function() -> Int {\n // Perform certain operations...\n return 0\n}\n\nfunc algorithm(n: Int) -> Int { // input data\n let a = 0 // temporary data (constant)\n var b = 0 // temporary data (variable)\n let node = Node(x: 0) // temporary data (object)\n let c = function() // stack frame space (call function)\n return a + b + c // output data\n}\n
/* Classes */\nclass Node {\n val;\n next;\n constructor(val) {\n this.val = val === undefined ? 0 : val; // node value\n this.next = null; // reference to the next node\n }\n}\n\n/* Functions */\nfunction constFunc() {\n // Perform certain operations\n return 0;\n}\n\nfunction algorithm(n) { // input data\n const a = 0; // temporary data (constant)\n let b = 0; // temporary data (variable)\n const node = new Node(0); // temporary data (object)\n const c = constFunc(); // Stack frame space (calling function)\n return a + b + c; // output data\n}\n
/* Classes */\nclass Node {\n val: number;\n next: Node | null;\n constructor(val?: number) {\n this.val = val === undefined ? 0 : val; // node value\n this.next = null; // reference to the next node\n }\n}\n\n/* Functions */\nfunction constFunc(): number {\n // Perform certain operations\n return 0;\n}\n\nfunction algorithm(n: number): number { // input data\n const a = 0; // temporary data (constant)\n let b = 0; // temporary data (variable)\n const node = new Node(0); // temporary data (object)\n const c = constFunc(); // Stack frame space (calling function)\n return a + b + c; // output data\n}\n
/* Classes */\nclass Node {\n int val;\n Node next;\n Node(this.val, [this.next]);\n}\n\n/* Functions */\nint function() {\n // Perform certain operations...\n return 0;\n}\n\nint algorithm(int n) { // input data\n const int a = 0; // temporary data (constant)\n int b = 0; // temporary data (variable)\n Node node = Node(0); // temporary data (object)\n int c = function(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
use std::rc::Rc;\nuse std::cell::RefCell;\n\n/* Structures */\nstruct Node {\n val: i32,\n next: Option<Rc<RefCell<Node>>>,\n}\n\n/* Creating a Node structure */\nimpl Node {\n fn new(val: i32) -> Self {\n Self { val: val, next: None }\n }\n}\n\n/* Functions */\nfn function() -> i32 { \n // Perform certain operations...\n return 0;\n}\n\nfn algorithm(n: i32) -> i32 { // input data\n const a: i32 = 0; // temporary data (constant)\n let mut b = 0; // temporary data (variable)\n let node = Node::new(0); // temporary data (object)\n let c = function(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
/* Functions */\nint func() {\n // Perform certain operations...\n return 0;\n}\n\nint algorithm(int n) { // input data\n const int a = 0; // temporary data (constant)\n int b = 0; // temporary data (variable)\n int c = func(); // stack frame space (call function)\n return a + b + c; // output data\n}\n
\n
"},{"location":"chapter_computational_complexity/space_complexity/#242-calculation-method","title":"2.4.2 \u00a0 Calculation Method","text":"The method for calculating space complexity is roughly similar to that of time complexity, with the only change being the shift of the statistical object from \"number of operations\" to \"size of used space\".
However, unlike time complexity, we usually only focus on the worst-case space complexity. This is because memory space is a hard requirement, and we must ensure that there is enough memory space reserved under all input data.
Consider the following code, the term \"worst-case\" in worst-case space complexity has two meanings.
nums
occupies \\(O(n)\\) space, thus the worst-case space complexity is \\(O(n)\\).nums
, the program occupies \\(O(n)\\) space, hence the worst-case space complexity is \\(O(n)\\).def algorithm(n: int):\n a = 0 # O(1)\n b = [0] * 10000 # O(1)\n if n > 10:\n nums = [0] * n # O(n)\n
void algorithm(int n) {\n int a = 0; // O(1)\n vector<int> b(10000); // O(1)\n if (n > 10)\n vector<int> nums(n); // O(n)\n}\n
void algorithm(int n) {\n int a = 0; // O(1)\n int[] b = new int[10000]; // O(1)\n if (n > 10)\n int[] nums = new int[n]; // O(n)\n}\n
void Algorithm(int n) {\n int a = 0; // O(1)\n int[] b = new int[10000]; // O(1)\n if (n > 10) {\n int[] nums = new int[n]; // O(n)\n }\n}\n
func algorithm(n int) {\n a := 0 // O(1)\n b := make([]int, 10000) // O(1)\n var nums []int\n if n > 10 {\n nums := make([]int, n) // O(n)\n }\n fmt.Println(a, b, nums)\n}\n
func algorithm(n: Int) {\n let a = 0 // O(1)\n let b = Array(repeating: 0, count: 10000) // O(1)\n if n > 10 {\n let nums = Array(repeating: 0, count: n) // O(n)\n }\n}\n
function algorithm(n) {\n const a = 0; // O(1)\n const b = new Array(10000); // O(1)\n if (n > 10) {\n const nums = new Array(n); // O(n)\n }\n}\n
function algorithm(n: number): void {\n const a = 0; // O(1)\n const b = new Array(10000); // O(1)\n if (n > 10) {\n const nums = new Array(n); // O(n)\n }\n}\n
void algorithm(int n) {\n int a = 0; // O(1)\n List<int> b = List.filled(10000, 0); // O(1)\n if (n > 10) {\n List<int> nums = List.filled(n, 0); // O(n)\n }\n}\n
fn algorithm(n: i32) {\n let a = 0; // O(1)\n let b = [0; 10000]; // O(1)\n if n > 10 {\n let nums = vec![0; n as usize]; // O(n)\n }\n}\n
void algorithm(int n) {\n int a = 0; // O(1)\n int b[10000]; // O(1)\n if (n > 10)\n int nums[n] = {0}; // O(n)\n}\n
\n
In recursive functions, stack frame space must be taken into count. Consider the following code:
PythonC++JavaC#GoSwiftJSTSDartRustCZigdef function() -> int:\n # Perform certain operations\n return 0\n\ndef loop(n: int):\n \"\"\"Loop O(1)\"\"\"\"\"\n for _ in range(n):\n function()\n\ndef recur(n: int) -> int:\n \"\"\"Recursion O(n)\"\"\"\"\"\n if n == 1: return\n return recur(n - 1)\n
int func() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nvoid loop(int n) {\n for (int i = 0; i < n; i++) {\n func();\n }\n}\n/* Recursion O(n) */\nvoid recur(int n) {\n if (n == 1) return;\n return recur(n - 1);\n}\n
int function() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nvoid loop(int n) {\n for (int i = 0; i < n; i++) {\n function();\n }\n}\n/* Recursion O(n) */\nvoid recur(int n) {\n if (n == 1) return;\n return recur(n - 1);\n}\n
int Function() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nvoid Loop(int n) {\n for (int i = 0; i < n; i++) {\n Function();\n }\n}\n/* Recursion O(n) */\nint Recur(int n) {\n if (n == 1) return 1;\n return Recur(n - 1);\n}\n
func function() int {\n // Perform certain operations\n return 0\n}\n\n/* Cycle O(1) */\nfunc loop(n int) {\n for i := 0; i < n; i++ {\n function()\n }\n}\n\n/* Recursion O(n) */\nfunc recur(n int) {\n if n == 1 {\n return\n }\n recur(n - 1)\n}\n
@discardableResult\nfunc function() -> Int {\n // Perform certain operations\n return 0\n}\n\n/* Cycle O(1) */\nfunc loop(n: Int) {\n for _ in 0 ..< n {\n function()\n }\n}\n\n/* Recursion O(n) */\nfunc recur(n: Int) {\n if n == 1 {\n return\n }\n recur(n: n - 1)\n}\n
function constFunc() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nfunction loop(n) {\n for (let i = 0; i < n; i++) {\n constFunc();\n }\n}\n/* Recursion O(n) */\nfunction recur(n) {\n if (n === 1) return;\n return recur(n - 1);\n}\n
function constFunc(): number {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nfunction loop(n: number): void {\n for (let i = 0; i < n; i++) {\n constFunc();\n }\n}\n/* Recursion O(n) */\nfunction recur(n: number): void {\n if (n === 1) return;\n return recur(n - 1);\n}\n
int function() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nvoid loop(int n) {\n for (int i = 0; i < n; i++) {\n function();\n }\n}\n/* Recursion O(n) */\nvoid recur(int n) {\n if (n == 1) return;\n return recur(n - 1);\n}\n
fn function() -> i32 {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nfn loop(n: i32) {\n for i in 0..n {\n function();\n }\n}\n/* Recursion O(n) */\nvoid recur(n: i32) {\n if n == 1 {\n return;\n }\n recur(n - 1);\n}\n
int func() {\n // Perform certain operations\n return 0;\n}\n/* Cycle O(1) */\nvoid loop(int n) {\n for (int i = 0; i < n; i++) {\n func();\n }\n}\n/* Recursion O(n) */\nvoid recur(int n) {\n if (n == 1) return;\n return recur(n - 1);\n}\n
\n
The time complexity of both loop()
and recur()
functions is \\(O(n)\\), but their space complexities differ.
loop()
function calls function()
\\(n\\) times in a loop, where each iteration's function()
returns and releases its stack frame space, so the space complexity remains \\(O(1)\\).recur()
will have \\(n\\) instances of unreturned recur()
existing simultaneously during its execution, thus occupying \\(O(n)\\) stack frame space.Let the size of the input data be \\(n\\), the following chart displays common types of space complexities (arranged from low to high).
\\[ \\begin{aligned} O(1) < O(\\log n) < O(n) < O(n^2) < O(2^n) \\newline \\text{Constant Order} < \\text{Logarithmic Order} < \\text{Linear Order} < \\text{Quadratic Order} < \\text{Exponential Order} \\end{aligned} \\]Figure 2-16 \u00a0 Common Types of Space Complexity
"},{"location":"chapter_computational_complexity/space_complexity/#1-constant-order-o1","title":"1. \u00a0 Constant Order \\(O(1)\\)","text":"Constant order is common in constants, variables, objects that are independent of the size of input data \\(n\\).
Note that memory occupied by initializing variables or calling functions in a loop, which is released upon entering the next cycle, does not accumulate over space, thus the space complexity remains \\(O(1)\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig space_complexity.pydef function() -> int:\n \"\"\"\u51fd\u6570\"\"\"\n # \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0\n\ndef constant(n: int):\n \"\"\"\u5e38\u6570\u9636\"\"\"\n # \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n a = 0\n nums = [0] * 10000\n node = ListNode(0)\n # \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for _ in range(n):\n c = 0\n # \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for _ in range(n):\n function()\n
space_complexity.cpp/* \u51fd\u6570 */\nint func() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nvoid constant(int n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const int a = 0;\n int b = 0;\n vector<int> nums(10000);\n ListNode node(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n int c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n func();\n }\n}\n
space_complexity.java/* \u51fd\u6570 */\nint function() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nvoid constant(int n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n final int a = 0;\n int b = 0;\n int[] nums = new int[10000];\n ListNode node = new ListNode(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n int c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n function();\n }\n}\n
space_complexity.cs/* \u51fd\u6570 */\nint Function() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nvoid Constant(int n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n int a = 0;\n int b = 0;\n int[] nums = new int[10000];\n ListNode node = new(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n int c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n Function();\n }\n}\n
space_complexity.go/* \u51fd\u6570 */\nfunc function() int {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c...\n return 0\n}\n\n/* \u5e38\u6570\u9636 */\nfunc spaceConstant(n int) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const a = 0\n b := 0\n nums := make([]int, 10000)\n node := newNode(0)\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n var c int\n for i := 0; i < n; i++ {\n c = 0\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for i := 0; i < n; i++ {\n function()\n }\n b += 0\n c += 0\n nums[0] = 0\n node.val = 0\n}\n
space_complexity.swift/* \u51fd\u6570 */\n@discardableResult\nfunc function() -> Int {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0\n}\n\n/* \u5e38\u6570\u9636 */\nfunc constant(n: Int) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n let a = 0\n var b = 0\n let nums = Array(repeating: 0, count: 10000)\n let node = ListNode(x: 0)\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for _ in 0 ..< n {\n let c = 0\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for _ in 0 ..< n {\n function()\n }\n}\n
space_complexity.js/* \u51fd\u6570 */\nfunction constFunc() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nfunction constant(n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const a = 0;\n const b = 0;\n const nums = new Array(10000);\n const node = new ListNode(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (let i = 0; i < n; i++) {\n const c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (let i = 0; i < n; i++) {\n constFunc();\n }\n}\n
space_complexity.ts/* \u51fd\u6570 */\nfunction constFunc(): number {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nfunction constant(n: number): void {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const a = 0;\n const b = 0;\n const nums = new Array(10000);\n const node = new ListNode(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (let i = 0; i < n; i++) {\n const c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (let i = 0; i < n; i++) {\n constFunc();\n }\n}\n
space_complexity.dart/* \u51fd\u6570 */\nint function() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nvoid constant(int n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n final int a = 0;\n int b = 0;\n List<int> nums = List.filled(10000, 0);\n ListNode node = ListNode(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (var i = 0; i < n; i++) {\n int c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (var i = 0; i < n; i++) {\n function();\n }\n}\n
space_complexity.rs/* \u51fd\u6570 */\nfn function() ->i32 {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\n#[allow(unused)]\nfn constant(n: i32) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const A: i32 = 0;\n let b = 0;\n let nums = vec![0; 10000];\n let node = ListNode::new(0);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for i in 0..n {\n let c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for i in 0..n {\n function();\n }\n}\n
space_complexity.c/* \u51fd\u6570 */\nint func() {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n/* \u5e38\u6570\u9636 */\nvoid constant(int n) {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const int a = 0;\n int b = 0;\n int nums[1000];\n ListNode *node = newListNode(0);\n free(node);\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n int c = 0;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n for (int i = 0; i < n; i++) {\n func();\n }\n}\n
space_complexity.zig// \u51fd\u6570\nfn function() i32 {\n // \u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\n return 0;\n}\n\n// \u5e38\u6570\u9636\nfn constant(n: i32) void {\n // \u5e38\u91cf\u3001\u53d8\u91cf\u3001\u5bf9\u8c61\u5360\u7528 O(1) \u7a7a\u95f4\n const a: i32 = 0;\n var b: i32 = 0;\n var nums = [_]i32{0}**10000;\n var node = inc.ListNode(i32){.val = 0};\n var i: i32 = 0;\n // \u5faa\u73af\u4e2d\u7684\u53d8\u91cf\u5360\u7528 O(1) \u7a7a\u95f4\n while (i < n) : (i += 1) {\n var c: i32 = 0;\n _ = c;\n }\n // \u5faa\u73af\u4e2d\u7684\u51fd\u6570\u5360\u7528 O(1) \u7a7a\u95f4\n i = 0;\n while (i < n) : (i += 1) {\n _ = function();\n }\n _ = a;\n _ = b;\n _ = nums;\n _ = node;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_computational_complexity/space_complexity/#2-linear-order-on","title":"2. \u00a0 Linear Order \\(O(n)\\)","text":"Linear order is common in arrays, linked lists, stacks, queues, etc., where the number of elements is proportional to \\(n\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig space_complexity.pydef linear(n: int):\n \"\"\"\u7ebf\u6027\u9636\"\"\"\n # \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n nums = [0] * n\n # \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n hmap = dict[int, str]()\n for i in range(n):\n hmap[i] = str(i)\n
space_complexity.cpp/* \u7ebf\u6027\u9636 */\nvoid linear(int n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n vector<int> nums(n);\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n vector<ListNode> nodes;\n for (int i = 0; i < n; i++) {\n nodes.push_back(ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n unordered_map<int, string> map;\n for (int i = 0; i < n; i++) {\n map[i] = to_string(i);\n }\n}\n
space_complexity.java/* \u7ebf\u6027\u9636 */\nvoid linear(int n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n int[] nums = new int[n];\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n List<ListNode> nodes = new ArrayList<>();\n for (int i = 0; i < n; i++) {\n nodes.add(new ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n Map<Integer, String> map = new HashMap<>();\n for (int i = 0; i < n; i++) {\n map.put(i, String.valueOf(i));\n }\n}\n
space_complexity.cs/* \u7ebf\u6027\u9636 */\nvoid Linear(int n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n int[] nums = new int[n];\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n List<ListNode> nodes = [];\n for (int i = 0; i < n; i++) {\n nodes.Add(new ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n Dictionary<int, string> map = [];\n for (int i = 0; i < n; i++) {\n map.Add(i, i.ToString());\n }\n}\n
space_complexity.go/* \u7ebf\u6027\u9636 */\nfunc spaceLinear(n int) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n _ = make([]int, n)\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n var nodes []*node\n for i := 0; i < n; i++ {\n nodes = append(nodes, newNode(i))\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n m := make(map[int]string, n)\n for i := 0; i < n; i++ {\n m[i] = strconv.Itoa(i)\n }\n}\n
space_complexity.swift/* \u7ebf\u6027\u9636 */\nfunc linear(n: Int) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n let nums = Array(repeating: 0, count: n)\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n let nodes = (0 ..< n).map { ListNode(x: $0) }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n let map = Dictionary(uniqueKeysWithValues: (0 ..< n).map { ($0, \"\\($0)\") })\n}\n
space_complexity.js/* \u7ebf\u6027\u9636 */\nfunction linear(n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n const nums = new Array(n);\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n const nodes = [];\n for (let i = 0; i < n; i++) {\n nodes.push(new ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n const map = new Map();\n for (let i = 0; i < n; i++) {\n map.set(i, i.toString());\n }\n}\n
space_complexity.ts/* \u7ebf\u6027\u9636 */\nfunction linear(n: number): void {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n const nums = new Array(n);\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n const nodes: ListNode[] = [];\n for (let i = 0; i < n; i++) {\n nodes.push(new ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n const map = new Map();\n for (let i = 0; i < n; i++) {\n map.set(i, i.toString());\n }\n}\n
space_complexity.dart/* \u7ebf\u6027\u9636 */\nvoid linear(int n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n List<int> nums = List.filled(n, 0);\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n List<ListNode> nodes = [];\n for (var i = 0; i < n; i++) {\n nodes.add(ListNode(i));\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n Map<int, String> map = HashMap();\n for (var i = 0; i < n; i++) {\n map.putIfAbsent(i, () => i.toString());\n }\n}\n
space_complexity.rs/* \u7ebf\u6027\u9636 */\n#[allow(unused)]\nfn linear(n: i32) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n let mut nums = vec![0; n as usize];\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n let mut nodes = Vec::new();\n for i in 0..n {\n nodes.push(ListNode::new(i))\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n let mut map = HashMap::new();\n for i in 0..n {\n map.insert(i, i.to_string());\n }\n}\n
space_complexity.c/* \u54c8\u5e0c\u8868 */\ntypedef struct {\n int key;\n int val;\n UT_hash_handle hh; // \u57fa\u4e8e uthash.h \u5b9e\u73b0\n} HashTable;\n\n/* \u7ebf\u6027\u9636 */\nvoid linear(int n) {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n int *nums = malloc(sizeof(int) * n);\n free(nums);\n\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n ListNode **nodes = malloc(sizeof(ListNode *) * n);\n for (int i = 0; i < n; i++) {\n nodes[i] = newListNode(i);\n }\n // \u5185\u5b58\u91ca\u653e\n for (int i = 0; i < n; i++) {\n free(nodes[i]);\n }\n free(nodes);\n\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n HashTable *h = NULL;\n for (int i = 0; i < n; i++) {\n HashTable *tmp = malloc(sizeof(HashTable));\n tmp->key = i;\n tmp->val = i;\n HASH_ADD_INT(h, key, tmp);\n }\n\n // \u5185\u5b58\u91ca\u653e\n HashTable *curr, *tmp;\n HASH_ITER(hh, h, curr, tmp) {\n HASH_DEL(h, curr);\n free(curr);\n }\n}\n
space_complexity.zig// \u7ebf\u6027\u9636\nfn linear(comptime n: i32) !void {\n // \u957f\u5ea6\u4e3a n \u7684\u6570\u7ec4\u5360\u7528 O(n) \u7a7a\u95f4\n var nums = [_]i32{0}**n;\n // \u957f\u5ea6\u4e3a n \u7684\u5217\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n var nodes = std.ArrayList(i32).init(std.heap.page_allocator);\n defer nodes.deinit();\n var i: i32 = 0;\n while (i < n) : (i += 1) {\n try nodes.append(i);\n }\n // \u957f\u5ea6\u4e3a n \u7684\u54c8\u5e0c\u8868\u5360\u7528 O(n) \u7a7a\u95f4\n var map = std.AutoArrayHashMap(i32, []const u8).init(std.heap.page_allocator);\n defer map.deinit();\n var j: i32 = 0;\n while (j < n) : (j += 1) {\n const string = try std.fmt.allocPrint(std.heap.page_allocator, \"{d}\", .{j});\n defer std.heap.page_allocator.free(string);\n try map.put(i, string);\n }\n _ = nums;\n}\n
Visualizing Code Full Screen >
As shown below, this function's recursive depth is \\(n\\), meaning there are \\(n\\) instances of unreturned linear_recur()
function, using \\(O(n)\\) size of stack frame space:
def linear_recur(n: int):\n \"\"\"\u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\"\"\"\n print(\"\u9012\u5f52 n =\", n)\n if n == 1:\n return\n linear_recur(n - 1)\n
space_complexity.cpp/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nvoid linearRecur(int n) {\n cout << \"\u9012\u5f52 n = \" << n << endl;\n if (n == 1)\n return;\n linearRecur(n - 1);\n}\n
space_complexity.java/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nvoid linearRecur(int n) {\n System.out.println(\"\u9012\u5f52 n = \" + n);\n if (n == 1)\n return;\n linearRecur(n - 1);\n}\n
space_complexity.cs/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nvoid LinearRecur(int n) {\n Console.WriteLine(\"\u9012\u5f52 n = \" + n);\n if (n == 1) return;\n LinearRecur(n - 1);\n}\n
space_complexity.go/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc spaceLinearRecur(n int) {\n fmt.Println(\"\u9012\u5f52 n =\", n)\n if n == 1 {\n return\n }\n spaceLinearRecur(n - 1)\n}\n
space_complexity.swift/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc linearRecur(n: Int) {\n print(\"\u9012\u5f52 n = \\(n)\")\n if n == 1 {\n return\n }\n linearRecur(n: n - 1)\n}\n
space_complexity.js/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction linearRecur(n) {\n console.log(`\u9012\u5f52 n = ${n}`);\n if (n === 1) return;\n linearRecur(n - 1);\n}\n
space_complexity.ts/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction linearRecur(n: number): void {\n console.log(`\u9012\u5f52 n = ${n}`);\n if (n === 1) return;\n linearRecur(n - 1);\n}\n
space_complexity.dart/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nvoid linearRecur(int n) {\n print('\u9012\u5f52 n = $n');\n if (n == 1) return;\n linearRecur(n - 1);\n}\n
space_complexity.rs/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfn linear_recur(n: i32) {\n println!(\"\u9012\u5f52 n = {}\", n);\n if n == 1 {return};\n linear_recur(n - 1);\n}\n
space_complexity.c/* \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nvoid linearRecur(int n) {\n printf(\"\u9012\u5f52 n = %d\\r\\n\", n);\n if (n == 1)\n return;\n linearRecur(n - 1);\n}\n
space_complexity.zig// \u7ebf\u6027\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\nfn linearRecur(comptime n: i32) void {\n std.debug.print(\"\u9012\u5f52 n = {}\\n\", .{n});\n if (n == 1) return;\n linearRecur(n - 1);\n}\n
Visualizing Code Full Screen >
Figure 2-17 \u00a0 Recursive Function Generating Linear Order Space Complexity
"},{"location":"chapter_computational_complexity/space_complexity/#3-quadratic-order-on2","title":"3. \u00a0 Quadratic Order \\(O(n^2)\\)","text":"Quadratic order is common in matrices and graphs, where the number of elements is quadratic to \\(n\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig space_complexity.pydef quadratic(n: int):\n \"\"\"\u5e73\u65b9\u9636\"\"\"\n # \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n num_matrix = [[0] * n for _ in range(n)]\n
space_complexity.cpp/* \u5e73\u65b9\u9636 */\nvoid quadratic(int n) {\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n vector<vector<int>> numMatrix;\n for (int i = 0; i < n; i++) {\n vector<int> tmp;\n for (int j = 0; j < n; j++) {\n tmp.push_back(0);\n }\n numMatrix.push_back(tmp);\n }\n}\n
space_complexity.java/* \u5e73\u65b9\u9636 */\nvoid quadratic(int n) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n int[][] numMatrix = new int[n][n];\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n List<List<Integer>> numList = new ArrayList<>();\n for (int i = 0; i < n; i++) {\n List<Integer> tmp = new ArrayList<>();\n for (int j = 0; j < n; j++) {\n tmp.add(0);\n }\n numList.add(tmp);\n }\n}\n
space_complexity.cs/* \u5e73\u65b9\u9636 */\nvoid Quadratic(int n) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n int[,] numMatrix = new int[n, n];\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n List<List<int>> numList = [];\n for (int i = 0; i < n; i++) {\n List<int> tmp = [];\n for (int j = 0; j < n; j++) {\n tmp.Add(0);\n }\n numList.Add(tmp);\n }\n}\n
space_complexity.go/* \u5e73\u65b9\u9636 */\nfunc spaceQuadratic(n int) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n numMatrix := make([][]int, n)\n for i := 0; i < n; i++ {\n numMatrix[i] = make([]int, n)\n }\n}\n
space_complexity.swift/* \u5e73\u65b9\u9636 */\nfunc quadratic(n: Int) {\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n let numList = Array(repeating: Array(repeating: 0, count: n), count: n)\n}\n
space_complexity.js/* \u5e73\u65b9\u9636 */\nfunction quadratic(n) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n const numMatrix = Array(n)\n .fill(null)\n .map(() => Array(n).fill(null));\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n const numList = [];\n for (let i = 0; i < n; i++) {\n const tmp = [];\n for (let j = 0; j < n; j++) {\n tmp.push(0);\n }\n numList.push(tmp);\n }\n}\n
space_complexity.ts/* \u5e73\u65b9\u9636 */\nfunction quadratic(n: number): void {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n const numMatrix = Array(n)\n .fill(null)\n .map(() => Array(n).fill(null));\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n const numList = [];\n for (let i = 0; i < n; i++) {\n const tmp = [];\n for (let j = 0; j < n; j++) {\n tmp.push(0);\n }\n numList.push(tmp);\n }\n}\n
space_complexity.dart/* \u5e73\u65b9\u9636 */\nvoid quadratic(int n) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n List<List<int>> numMatrix = List.generate(n, (_) => List.filled(n, 0));\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n List<List<int>> numList = [];\n for (var i = 0; i < n; i++) {\n List<int> tmp = [];\n for (int j = 0; j < n; j++) {\n tmp.add(0);\n }\n numList.add(tmp);\n }\n}\n
space_complexity.rs/* \u5e73\u65b9\u9636 */\n#[allow(unused)]\nfn quadratic(n: i32) {\n // \u77e9\u9635\u5360\u7528 O(n^2) \u7a7a\u95f4\n let num_matrix = vec![vec![0; n as usize]; n as usize];\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n let mut num_list = Vec::new();\n for i in 0..n {\n let mut tmp = Vec::new();\n for j in 0..n {\n tmp.push(0);\n }\n num_list.push(tmp);\n }\n}\n
space_complexity.c/* \u5e73\u65b9\u9636 */\nvoid quadratic(int n) {\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n int **numMatrix = malloc(sizeof(int *) * n);\n for (int i = 0; i < n; i++) {\n int *tmp = malloc(sizeof(int) * n);\n for (int j = 0; j < n; j++) {\n tmp[j] = 0;\n }\n numMatrix[i] = tmp;\n }\n\n // \u5185\u5b58\u91ca\u653e\n for (int i = 0; i < n; i++) {\n free(numMatrix[i]);\n }\n free(numMatrix);\n}\n
space_complexity.zig// \u5e73\u65b9\u9636\nfn quadratic(n: i32) !void {\n // \u4e8c\u7ef4\u5217\u8868\u5360\u7528 O(n^2) \u7a7a\u95f4\n var nodes = std.ArrayList(std.ArrayList(i32)).init(std.heap.page_allocator);\n defer nodes.deinit();\n var i: i32 = 0;\n while (i < n) : (i += 1) {\n var tmp = std.ArrayList(i32).init(std.heap.page_allocator);\n defer tmp.deinit();\n var j: i32 = 0;\n while (j < n) : (j += 1) {\n try tmp.append(0);\n }\n try nodes.append(tmp);\n }\n}\n
Visualizing Code Full Screen >
As shown below, the recursive depth of this function is \\(n\\), and in each recursive call, an array is initialized with lengths \\(n\\), \\(n-1\\), \\(\\dots\\), \\(2\\), \\(1\\), averaging \\(n/2\\), thus overall occupying \\(O(n^2)\\) space:
PythonC++JavaC#GoSwiftJSTSDartRustCZig space_complexity.pydef quadratic_recur(n: int) -> int:\n \"\"\"\u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\"\"\"\n if n <= 0:\n return 0\n # \u6570\u7ec4 nums \u957f\u5ea6\u4e3a n, n-1, ..., 2, 1\n nums = [0] * n\n return quadratic_recur(n - 1)\n
space_complexity.cpp/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint quadraticRecur(int n) {\n if (n <= 0)\n return 0;\n vector<int> nums(n);\n cout << \"\u9012\u5f52 n = \" << n << \" \u4e2d\u7684 nums \u957f\u5ea6 = \" << nums.size() << endl;\n return quadraticRecur(n - 1);\n}\n
space_complexity.java/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint quadraticRecur(int n) {\n if (n <= 0)\n return 0;\n // \u6570\u7ec4 nums \u957f\u5ea6\u4e3a n, n-1, ..., 2, 1\n int[] nums = new int[n];\n System.out.println(\"\u9012\u5f52 n = \" + n + \" \u4e2d\u7684 nums \u957f\u5ea6 = \" + nums.length);\n return quadraticRecur(n - 1);\n}\n
space_complexity.cs/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint QuadraticRecur(int n) {\n if (n <= 0) return 0;\n int[] nums = new int[n];\n Console.WriteLine(\"\u9012\u5f52 n = \" + n + \" \u4e2d\u7684 nums \u957f\u5ea6 = \" + nums.Length);\n return QuadraticRecur(n - 1);\n}\n
space_complexity.go/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc spaceQuadraticRecur(n int) int {\n if n <= 0 {\n return 0\n }\n nums := make([]int, n)\n fmt.Printf(\"\u9012\u5f52 n = %d \u4e2d\u7684 nums \u957f\u5ea6 = %d \\n\", n, len(nums))\n return spaceQuadraticRecur(n - 1)\n}\n
space_complexity.swift/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\n@discardableResult\nfunc quadraticRecur(n: Int) -> Int {\n if n <= 0 {\n return 0\n }\n // \u6570\u7ec4 nums \u957f\u5ea6\u4e3a n, n-1, ..., 2, 1\n let nums = Array(repeating: 0, count: n)\n print(\"\u9012\u5f52 n = \\(n) \u4e2d\u7684 nums \u957f\u5ea6 = \\(nums.count)\")\n return quadraticRecur(n: n - 1)\n}\n
space_complexity.js/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction quadraticRecur(n) {\n if (n <= 0) return 0;\n const nums = new Array(n);\n console.log(`\u9012\u5f52 n = ${n} \u4e2d\u7684 nums \u957f\u5ea6 = ${nums.length}`);\n return quadraticRecur(n - 1);\n}\n
space_complexity.ts/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction quadraticRecur(n: number): number {\n if (n <= 0) return 0;\n const nums = new Array(n);\n console.log(`\u9012\u5f52 n = ${n} \u4e2d\u7684 nums \u957f\u5ea6 = ${nums.length}`);\n return quadraticRecur(n - 1);\n}\n
space_complexity.dart/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint quadraticRecur(int n) {\n if (n <= 0) return 0;\n List<int> nums = List.filled(n, 0);\n print('\u9012\u5f52 n = $n \u4e2d\u7684 nums \u957f\u5ea6 = ${nums.length}');\n return quadraticRecur(n - 1);\n}\n
space_complexity.rs/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfn quadratic_recur(n: i32) -> i32 {\n if n <= 0 {return 0};\n // \u6570\u7ec4 nums \u957f\u5ea6\u4e3a n, n-1, ..., 2, 1\n let nums = vec![0; n as usize];\n println!(\"\u9012\u5f52 n = {} \u4e2d\u7684 nums \u957f\u5ea6 = {}\", n, nums.len());\n return quadratic_recur(n - 1);\n}\n
space_complexity.c/* \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint quadraticRecur(int n) {\n if (n <= 0)\n return 0;\n int *nums = malloc(sizeof(int) * n);\n printf(\"\u9012\u5f52 n = %d \u4e2d\u7684 nums \u957f\u5ea6 = %d\\r\\n\", n, n);\n int res = quadraticRecur(n - 1);\n free(nums);\n return res;\n}\n
space_complexity.zig// \u5e73\u65b9\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\nfn quadraticRecur(comptime n: i32) i32 {\n if (n <= 0) return 0;\n var nums = [_]i32{0}**n;\n std.debug.print(\"\u9012\u5f52 n = {} \u4e2d\u7684 nums \u957f\u5ea6 = {}\\n\", .{n, nums.len});\n return quadraticRecur(n - 1);\n}\n
Visualizing Code Full Screen >
Figure 2-18 \u00a0 Recursive Function Generating Quadratic Order Space Complexity
"},{"location":"chapter_computational_complexity/space_complexity/#4-exponential-order-o2n","title":"4. \u00a0 Exponential Order \\(O(2^n)\\)","text":"Exponential order is common in binary trees. Observe the below image, a \"full binary tree\" with \\(n\\) levels has \\(2^n - 1\\) nodes, occupying \\(O(2^n)\\) space:
PythonC++JavaC#GoSwiftJSTSDartRustCZig space_complexity.pydef build_tree(n: int) -> TreeNode | None:\n \"\"\"\u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09\"\"\"\n if n == 0:\n return None\n root = TreeNode(0)\n root.left = build_tree(n - 1)\n root.right = build_tree(n - 1)\n return root\n
space_complexity.cpp/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nTreeNode *buildTree(int n) {\n if (n == 0)\n return nullptr;\n TreeNode *root = new TreeNode(0);\n root->left = buildTree(n - 1);\n root->right = buildTree(n - 1);\n return root;\n}\n
space_complexity.java/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nTreeNode buildTree(int n) {\n if (n == 0)\n return null;\n TreeNode root = new TreeNode(0);\n root.left = buildTree(n - 1);\n root.right = buildTree(n - 1);\n return root;\n}\n
space_complexity.cs/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nTreeNode? BuildTree(int n) {\n if (n == 0) return null;\n TreeNode root = new(0) {\n left = BuildTree(n - 1),\n right = BuildTree(n - 1)\n };\n return root;\n}\n
space_complexity.go/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nfunc buildTree(n int) *TreeNode {\n if n == 0 {\n return nil\n }\n root := NewTreeNode(0)\n root.Left = buildTree(n - 1)\n root.Right = buildTree(n - 1)\n return root\n}\n
space_complexity.swift/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nfunc buildTree(n: Int) -> TreeNode? {\n if n == 0 {\n return nil\n }\n let root = TreeNode(x: 0)\n root.left = buildTree(n: n - 1)\n root.right = buildTree(n: n - 1)\n return root\n}\n
space_complexity.js/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nfunction buildTree(n) {\n if (n === 0) return null;\n const root = new TreeNode(0);\n root.left = buildTree(n - 1);\n root.right = buildTree(n - 1);\n return root;\n}\n
space_complexity.ts/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nfunction buildTree(n: number): TreeNode | null {\n if (n === 0) return null;\n const root = new TreeNode(0);\n root.left = buildTree(n - 1);\n root.right = buildTree(n - 1);\n return root;\n}\n
space_complexity.dart/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nTreeNode? buildTree(int n) {\n if (n == 0) return null;\n TreeNode root = TreeNode(0);\n root.left = buildTree(n - 1);\n root.right = buildTree(n - 1);\n return root;\n}\n
space_complexity.rs/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nfn build_tree(n: i32) -> Option<Rc<RefCell<TreeNode>>> {\n if n == 0 {return None};\n let root = TreeNode::new(0);\n root.borrow_mut().left = build_tree(n - 1);\n root.borrow_mut().right = build_tree(n - 1);\n return Some(root);\n}\n
space_complexity.c/* \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09 */\nTreeNode *buildTree(int n) {\n if (n == 0)\n return NULL;\n TreeNode *root = newTreeNode(0);\n root->left = buildTree(n - 1);\n root->right = buildTree(n - 1);\n return root;\n}\n
space_complexity.zig// \u6307\u6570\u9636\uff08\u5efa\u7acb\u6ee1\u4e8c\u53c9\u6811\uff09\nfn buildTree(mem_allocator: std.mem.Allocator, n: i32) !?*inc.TreeNode(i32) {\n if (n == 0) return null;\n const root = try mem_allocator.create(inc.TreeNode(i32));\n root.init(0);\n root.left = try buildTree(mem_allocator, n - 1);\n root.right = try buildTree(mem_allocator, n - 1);\n return root;\n}\n
Visualizing Code Full Screen >
Figure 2-19 \u00a0 Full Binary Tree Generating Exponential Order Space Complexity
"},{"location":"chapter_computational_complexity/space_complexity/#5-logarithmic-order-olog-n","title":"5. \u00a0 Logarithmic Order \\(O(\\log n)\\)","text":"Logarithmic order is common in divide-and-conquer algorithms. For example, in merge sort, an array of length \\(n\\) is recursively divided in half each round, forming a recursion tree of height \\(\\log n\\), using \\(O(\\log n)\\) stack frame space.
Another example is converting a number to a string. Given a positive integer \\(n\\), its number of digits is \\(\\log_{10} n + 1\\), corresponding to the length of the string, thus the space complexity is \\(O(\\log_{10} n + 1) = O(\\log n)\\).
"},{"location":"chapter_computational_complexity/space_complexity/#244-balancing-time-and-space","title":"2.4.4 \u00a0 Balancing Time and Space","text":"Ideally, we aim for both time complexity and space complexity to be optimal. However, in practice, optimizing both simultaneously is often difficult.
Lowering time complexity usually comes at the cost of increased space complexity, and vice versa. The approach of sacrificing memory space to improve algorithm speed is known as \"space-time tradeoff\"; the reverse is known as \"time-space tradeoff\".
The choice depends on which aspect we value more. In most cases, time is more precious than space, so \"space-time tradeoff\" is often the more common strategy. Of course, controlling space complexity is also very important when dealing with large volumes of data.
"},{"location":"chapter_computational_complexity/summary/","title":"2.5 \u00a0 Summary","text":""},{"location":"chapter_computational_complexity/summary/#1-key-review","title":"1. \u00a0 Key Review","text":"Algorithm Efficiency Assessment
Time Complexity
Space Complexity
Q: Is the space complexity of tail recursion \\(O(1)\\)?
Theoretically, the space complexity of a tail-recursive function can be optimized to \\(O(1)\\). However, most programming languages (such as Java, Python, C++, Go, C#) do not support automatic optimization of tail recursion, so it's generally considered to have a space complexity of \\(O(n)\\).
Q: What is the difference between the terms \"function\" and \"method\"?
A \"function\" can be executed independently, with all parameters passed explicitly. A \"method\" is associated with an object and is implicitly passed to the object calling it, able to operate on the data contained within an instance of a class.
Here are some examples from common programming languages:
Q: Does the \"Common Types of Space Complexity\" figure reflect the absolute size of occupied space?
No, the figure shows space complexities, which reflect growth trends, not the absolute size of the occupied space.
If you take \\(n = 8\\), you might find that the values of each curve don't correspond to their functions. This is because each curve includes a constant term, intended to compress the value range into a visually comfortable range.
In practice, since we usually don't know the \"constant term\" complexity of each method, it's generally not possible to choose the best solution for \\(n = 8\\) based solely on complexity. However, for \\(n = 8^5\\), it's much easier to choose, as the growth trend becomes dominant.
"},{"location":"chapter_computational_complexity/time_complexity/","title":"2.3 \u00a0 Time Complexity","text":"Time complexity is a concept used to measure how the run time of an algorithm increases with the size of the input data. Understanding time complexity is crucial for accurately assessing the efficiency of an algorithm.
+
might take 1 ns, a multiplication operation *
might take 10 ns, a print operation print()
might take 5 ns, etc.For example, consider the following code with an input size of \\(n\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig# Under an operating platform\ndef algorithm(n: int):\n a = 2 # 1 ns\n a = a + 1 # 1 ns\n a = a * 2 # 10 ns\n # Cycle n times\n for _ in range(n): # 1 ns\n print(0) # 5 ns\n
// Under a particular operating platform\nvoid algorithm(int n) {\n int a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for (int i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n cout << 0 << endl; // 5 ns\n }\n}\n
// Under a particular operating platform\nvoid algorithm(int n) {\n int a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for (int i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n System.out.println(0); // 5 ns\n }\n}\n
// Under a particular operating platform\nvoid Algorithm(int n) {\n int a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for (int i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n Console.WriteLine(0); // 5 ns\n }\n}\n
// Under a particular operating platform\nfunc algorithm(n int) {\n a := 2 // 1 ns\n a = a + 1 // 1 ns\n a = a * 2 // 10 ns\n // Loop n times\n for i := 0; i < n; i++ { // 1 ns\n fmt.Println(a) // 5 ns\n }\n}\n
// Under a particular operating platform\nfunc algorithm(n: Int) {\n var a = 2 // 1 ns\n a = a + 1 // 1 ns\n a = a * 2 // 10 ns\n // Loop n times\n for _ in 0 ..< n { // 1 ns\n print(0) // 5 ns\n }\n}\n
// Under a particular operating platform\nfunction algorithm(n) {\n var a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for(let i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n console.log(0); // 5 ns\n }\n}\n
// Under a particular operating platform\nfunction algorithm(n: number): void {\n var a: number = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for(let i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n console.log(0); // 5 ns\n }\n}\n
// Under a particular operating platform\nvoid algorithm(int n) {\n int a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for (int i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n print(0); // 5 ns\n }\n}\n
// Under a particular operating platform\nfn algorithm(n: i32) {\n let mut a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for _ in 0..n { // 1 ns for each round i++\n println!(\"{}\", 0); // 5 ns\n }\n}\n
// Under a particular operating platform\nvoid algorithm(int n) {\n int a = 2; // 1 ns\n a = a + 1; // 1 ns\n a = a * 2; // 10 ns\n // Loop n times\n for (int i = 0; i < n; i++) { // 1 ns , every round i++ is executed\n printf(\"%d\", 0); // 5 ns\n }\n}\n
// Under a particular operating platform\nfn algorithm(n: usize) void {\n var a: i32 = 2; // 1 ns\n a += 1; // 1 ns\n a *= 2; // 10 ns\n // Loop n times\n for (0..n) |_| { // 1 ns\n std.debug.print(\"{}\\n\", .{0}); // 5 ns\n }\n}\n
Using the above method, the run time of the algorithm can be calculated as \\((6n + 12)\\) ns:
\\[ 1 + 1 + 10 + (1 + 5) \\times n = 6n + 12 \\]However, in practice, counting the run time of an algorithm is neither practical nor reasonable. First, we don't want to tie the estimated time to the running platform, as algorithms need to run on various platforms. Second, it's challenging to know the run time for each type of operation, making the estimation process difficult.
"},{"location":"chapter_computational_complexity/time_complexity/#231-assessing-time-growth-trend","title":"2.3.1 \u00a0 Assessing Time Growth Trend","text":"Time complexity analysis does not count the algorithm's run time, but rather the growth trend of the run time as the data volume increases.
Let's understand this concept of \"time growth trend\" with an example. Assume the input data size is \\(n\\), and consider three algorithms A
, B
, and C
:
# Time complexity of algorithm A: constant order\ndef algorithm_A(n: int):\n print(0)\n# Time complexity of algorithm B: linear order\ndef algorithm_B(n: int):\n for _ in range(n):\n print(0)\n# Time complexity of algorithm C: constant order\ndef algorithm_C(n: int):\n for _ in range(1000000):\n print(0)\n
// Time complexity of algorithm A: constant order\nvoid algorithm_A(int n) {\n cout << 0 << endl;\n}\n// Time complexity of algorithm B: linear order\nvoid algorithm_B(int n) {\n for (int i = 0; i < n; i++) {\n cout << 0 << endl;\n }\n}\n// Time complexity of algorithm C: constant order\nvoid algorithm_C(int n) {\n for (int i = 0; i < 1000000; i++) {\n cout << 0 << endl;\n }\n}\n
// Time complexity of algorithm A: constant order\nvoid algorithm_A(int n) {\n System.out.println(0);\n}\n// Time complexity of algorithm B: linear order\nvoid algorithm_B(int n) {\n for (int i = 0; i < n; i++) {\n System.out.println(0);\n }\n}\n// Time complexity of algorithm C: constant order\nvoid algorithm_C(int n) {\n for (int i = 0; i < 1000000; i++) {\n System.out.println(0);\n }\n}\n
// Time complexity of algorithm A: constant order\nvoid AlgorithmA(int n) {\n Console.WriteLine(0);\n}\n// Time complexity of algorithm B: linear order\nvoid AlgorithmB(int n) {\n for (int i = 0; i < n; i++) {\n Console.WriteLine(0);\n }\n}\n// Time complexity of algorithm C: constant order\nvoid AlgorithmC(int n) {\n for (int i = 0; i < 1000000; i++) {\n Console.WriteLine(0);\n }\n}\n
// Time complexity of algorithm A: constant order\nfunc algorithm_A(n int) {\n fmt.Println(0)\n}\n// Time complexity of algorithm B: linear order\nfunc algorithm_B(n int) {\n for i := 0; i < n; i++ {\n fmt.Println(0)\n }\n}\n// Time complexity of algorithm C: constant order\nfunc algorithm_C(n int) {\n for i := 0; i < 1000000; i++ {\n fmt.Println(0)\n }\n}\n
// Time complexity of algorithm A: constant order\nfunc algorithmA(n: Int) {\n print(0)\n}\n\n// Time complexity of algorithm B: linear order\nfunc algorithmB(n: Int) {\n for _ in 0 ..< n {\n print(0)\n }\n}\n\n// Time complexity of algorithm C: constant order\nfunc algorithmC(n: Int) {\n for _ in 0 ..< 1000000 {\n print(0)\n }\n}\n
// Time complexity of algorithm A: constant order\nfunction algorithm_A(n) {\n console.log(0);\n}\n// Time complexity of algorithm B: linear order\nfunction algorithm_B(n) {\n for (let i = 0; i < n; i++) {\n console.log(0);\n }\n}\n// Time complexity of algorithm C: constant order\nfunction algorithm_C(n) {\n for (let i = 0; i < 1000000; i++) {\n console.log(0);\n }\n}\n
// Time complexity of algorithm A: constant order\nfunction algorithm_A(n: number): void {\n console.log(0);\n}\n// Time complexity of algorithm B: linear order\nfunction algorithm_B(n: number): void {\n for (let i = 0; i < n; i++) {\n console.log(0);\n }\n}\n// Time complexity of algorithm C: constant order\nfunction algorithm_C(n: number): void {\n for (let i = 0; i < 1000000; i++) {\n console.log(0);\n }\n}\n
// Time complexity of algorithm A: constant order\nvoid algorithmA(int n) {\n print(0);\n}\n// Time complexity of algorithm B: linear order\nvoid algorithmB(int n) {\n for (int i = 0; i < n; i++) {\n print(0);\n }\n}\n// Time complexity of algorithm C: constant order\nvoid algorithmC(int n) {\n for (int i = 0; i < 1000000; i++) {\n print(0);\n }\n}\n
// Time complexity of algorithm A: constant order\nfn algorithm_A(n: i32) {\n println!(\"{}\", 0);\n}\n// Time complexity of algorithm B: linear order\nfn algorithm_B(n: i32) {\n for _ in 0..n {\n println!(\"{}\", 0);\n }\n}\n// Time complexity of algorithm C: constant order\nfn algorithm_C(n: i32) {\n for _ in 0..1000000 {\n println!(\"{}\", 0);\n }\n}\n
// Time complexity of algorithm A: constant order\nvoid algorithm_A(int n) {\n printf(\"%d\", 0);\n}\n// Time complexity of algorithm B: linear order\nvoid algorithm_B(int n) {\n for (int i = 0; i < n; i++) {\n printf(\"%d\", 0);\n }\n}\n// Time complexity of algorithm C: constant order\nvoid algorithm_C(int n) {\n for (int i = 0; i < 1000000; i++) {\n printf(\"%d\", 0);\n }\n}\n
// Time complexity of algorithm A: constant order\nfn algorithm_A(n: usize) void {\n _ = n;\n std.debug.print(\"{}\\n\", .{0});\n}\n// Time complexity of algorithm B: linear order\nfn algorithm_B(n: i32) void {\n for (0..n) |_| {\n std.debug.print(\"{}\\n\", .{0});\n }\n}\n// Time complexity of algorithm C: constant order\nfn algorithm_C(n: i32) void {\n _ = n;\n for (0..1000000) |_| {\n std.debug.print(\"{}\\n\", .{0});\n }\n}\n
The following figure shows the time complexities of these three algorithms.
A
has just one print operation, and its run time does not grow with \\(n\\). Its time complexity is considered \"constant order.\"B
involves a print operation looping \\(n\\) times, and its run time grows linearly with \\(n\\). Its time complexity is \"linear order.\"C
has a print operation looping 1,000,000 times. Although it takes a long time, it is independent of the input data size \\(n\\). Therefore, the time complexity of C
is the same as A
, which is \"constant order.\"Figure 2-7 \u00a0 Time Growth Trend of Algorithms A, B, and C
Compared to directly counting the run time of an algorithm, what are the characteristics of time complexity analysis?
B
has linearly growing run time, which is slower than algorithm A
when \\(n > 1\\) and slower than C
when \\(n > 1,000,000\\). In fact, as long as the input data size \\(n\\) is sufficiently large, a \"constant order\" complexity algorithm will always be better than a \"linear order\" one, demonstrating the essence of time growth trend.A
and C
have the same time complexity, their actual run times can be quite different. Similarly, even though algorithm B
has a higher time complexity than C
, it is clearly superior when the input data size \\(n\\) is small. In these cases, it's difficult to judge the efficiency of algorithms based solely on time complexity. Nonetheless, despite these issues, complexity analysis remains the most effective and commonly used method for evaluating algorithm efficiency.Consider a function with an input size of \\(n\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZigdef algorithm(n: int):\n a = 1 # +1\n a = a + 1 # +1\n a = a * 2 # +1\n # Cycle n times\n for i in range(n): # +1\n print(0) # +1\n
void algorithm(int n) {\n int a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n // Loop n times\n for (int i = 0; i < n; i++) { // +1 (execute i ++ every round)\n cout << 0 << endl; // +1\n }\n}\n
void algorithm(int n) {\n int a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n // Loop n times\n for (int i = 0; i < n; i++) { // +1 (execute i ++ every round)\n System.out.println(0); // +1\n }\n}\n
void Algorithm(int n) {\n int a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n // Loop n times\n for (int i = 0; i < n; i++) { // +1 (execute i ++ every round)\n Console.WriteLine(0); // +1\n }\n}\n
func algorithm(n int) {\n a := 1 // +1\n a = a + 1 // +1\n a = a * 2 // +1\n // Loop n times\n for i := 0; i < n; i++ { // +1\n fmt.Println(a) // +1\n }\n}\n
func algorithm(n: Int) {\n var a = 1 // +1\n a = a + 1 // +1\n a = a * 2 // +1\n // Loop n times\n for _ in 0 ..< n { // +1\n print(0) // +1\n }\n}\n
function algorithm(n) {\n var a = 1; // +1\n a += 1; // +1\n a *= 2; // +1\n // Loop n times\n for(let i = 0; i < n; i++){ // +1 (execute i ++ every round)\n console.log(0); // +1\n }\n}\n
function algorithm(n: number): void{\n var a: number = 1; // +1\n a += 1; // +1\n a *= 2; // +1\n // Loop n times\n for(let i = 0; i < n; i++){ // +1 (execute i ++ every round)\n console.log(0); // +1\n }\n}\n
void algorithm(int n) {\n int a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n // Loop n times\n for (int i = 0; i < n; i++) { // +1 (execute i ++ every round)\n print(0); // +1\n }\n}\n
fn algorithm(n: i32) {\n let mut a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n\n // Loop n times\n for _ in 0..n { // +1 (execute i ++ every round)\n println!(\"{}\", 0); // +1\n }\n}\n
void algorithm(int n) {\n int a = 1; // +1\n a = a + 1; // +1\n a = a * 2; // +1\n // Loop n times\n for (int i = 0; i < n; i++) { // +1 (execute i ++ every round)\n printf(\"%d\", 0); // +1\n }\n} \n
fn algorithm(n: usize) void {\n var a: i32 = 1; // +1\n a += 1; // +1\n a *= 2; // +1\n // Loop n times\n for (0..n) |_| { // +1 (execute i ++ every round)\n std.debug.print(\"{}\\n\", .{0}); // +1\n }\n}\n
Given a function that represents the number of operations of an algorithm as a function of the input size \\(n\\), denoted as \\(T(n)\\), consider the following example:
\\[ T(n) = 3 + 2n \\]Since \\(T(n)\\) is a linear function, its growth trend is linear, and therefore, its time complexity is of linear order, denoted as \\(O(n)\\). This mathematical notation, known as \"big-O notation,\" represents the \"asymptotic upper bound\" of the function \\(T(n)\\).
In essence, time complexity analysis is about finding the asymptotic upper bound of the \"number of operations \\(T(n)\\)\". It has a precise mathematical definition.
Asymptotic Upper Bound
If there exist positive real numbers \\(c\\) and \\(n_0\\) such that for all \\(n > n_0\\), \\(T(n) \\leq c \\cdot f(n)\\), then \\(f(n)\\) is considered an asymptotic upper bound of \\(T(n)\\), denoted as \\(T(n) = O(f(n))\\).
As illustrated below, calculating the asymptotic upper bound involves finding a function \\(f(n)\\) such that, as \\(n\\) approaches infinity, \\(T(n)\\) and \\(f(n)\\) have the same growth order, differing only by a constant factor \\(c\\).
Figure 2-8 \u00a0 Asymptotic Upper Bound of a Function
"},{"location":"chapter_computational_complexity/time_complexity/#233-calculation-method","title":"2.3.3 \u00a0 Calculation Method","text":"While the concept of asymptotic upper bound might seem mathematically dense, you don't need to fully grasp it right away. Let's first understand the method of calculation, which can be practiced and comprehended over time.
Once \\(f(n)\\) is determined, we obtain the time complexity \\(O(f(n))\\). But how do we determine the asymptotic upper bound \\(f(n)\\)? This process generally involves two steps: counting the number of operations and determining the asymptotic upper bound.
"},{"location":"chapter_computational_complexity/time_complexity/#1-step-1-counting-the-number-of-operations","title":"1. \u00a0 Step 1: Counting the Number of Operations","text":"This step involves going through the code line by line. However, due to the presence of the constant \\(c\\) in \\(c \\cdot f(n)\\), all coefficients and constant terms in \\(T(n)\\) can be ignored. This principle allows for simplification techniques in counting operations.
Given a function, we can use these techniques to count operations:
PythonC++JavaC#GoSwiftJSTSDartRustCZigdef algorithm(n: int):\n a = 1 # +0 (trick 1)\n a = a + n # +0 (trick 1)\n # +n (technique 2)\n for i in range(5 * n + 1):\n print(0)\n # +n*n (technique 3)\n for i in range(2 * n):\n for j in range(n + 1):\n print(0)\n
void algorithm(int n) {\n int a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (int i = 0; i < 5 * n + 1; i++) {\n cout << 0 << endl;\n }\n // +n*n (technique 3)\n for (int i = 0; i < 2 * n; i++) {\n for (int j = 0; j < n + 1; j++) {\n cout << 0 << endl;\n }\n }\n}\n
void algorithm(int n) {\n int a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (int i = 0; i < 5 * n + 1; i++) {\n System.out.println(0);\n }\n // +n*n (technique 3)\n for (int i = 0; i < 2 * n; i++) {\n for (int j = 0; j < n + 1; j++) {\n System.out.println(0);\n }\n }\n}\n
void Algorithm(int n) {\n int a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (int i = 0; i < 5 * n + 1; i++) {\n Console.WriteLine(0);\n }\n // +n*n (technique 3)\n for (int i = 0; i < 2 * n; i++) {\n for (int j = 0; j < n + 1; j++) {\n Console.WriteLine(0);\n }\n }\n}\n
func algorithm(n int) {\n a := 1 // +0 (trick 1)\n a = a + n // +0 (trick 1)\n // +n (technique 2)\n for i := 0; i < 5 * n + 1; i++ {\n fmt.Println(0)\n }\n // +n*n (technique 3)\n for i := 0; i < 2 * n; i++ {\n for j := 0; j < n + 1; j++ {\n fmt.Println(0)\n }\n }\n}\n
func algorithm(n: Int) {\n var a = 1 // +0 (trick 1)\n a = a + n // +0 (trick 1)\n // +n (technique 2)\n for _ in 0 ..< (5 * n + 1) {\n print(0)\n }\n // +n*n (technique 3)\n for _ in 0 ..< (2 * n) {\n for _ in 0 ..< (n + 1) {\n print(0)\n }\n }\n}\n
function algorithm(n) {\n let a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (let i = 0; i < 5 * n + 1; i++) {\n console.log(0);\n }\n // +n*n (technique 3)\n for (let i = 0; i < 2 * n; i++) {\n for (let j = 0; j < n + 1; j++) {\n console.log(0);\n }\n }\n}\n
function algorithm(n: number): void {\n let a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (let i = 0; i < 5 * n + 1; i++) {\n console.log(0);\n }\n // +n*n (technique 3)\n for (let i = 0; i < 2 * n; i++) {\n for (let j = 0; j < n + 1; j++) {\n console.log(0);\n }\n }\n}\n
void algorithm(int n) {\n int a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (int i = 0; i < 5 * n + 1; i++) {\n print(0);\n }\n // +n*n (technique 3)\n for (int i = 0; i < 2 * n; i++) {\n for (int j = 0; j < n + 1; j++) {\n print(0);\n }\n }\n}\n
fn algorithm(n: i32) {\n let mut a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n\n // +n (technique 2)\n for i in 0..(5 * n + 1) {\n println!(\"{}\", 0);\n }\n\n // +n*n (technique 3)\n for i in 0..(2 * n) {\n for j in 0..(n + 1) {\n println!(\"{}\", 0);\n }\n }\n}\n
void algorithm(int n) {\n int a = 1; // +0 (trick 1)\n a = a + n; // +0 (trick 1)\n // +n (technique 2)\n for (int i = 0; i < 5 * n + 1; i++) {\n printf(\"%d\", 0);\n }\n // +n*n (technique 3)\n for (int i = 0; i < 2 * n; i++) {\n for (int j = 0; j < n + 1; j++) {\n printf(\"%d\", 0);\n }\n }\n}\n
fn algorithm(n: usize) void {\n var a: i32 = 1; // +0 (trick 1)\n a = a + @as(i32, @intCast(n)); // +0 (trick 1)\n\n // +n (technique 2)\n for(0..(5 * n + 1)) |_| {\n std.debug.print(\"{}\\n\", .{0});\n }\n\n // +n*n (technique 3)\n for(0..(2 * n)) |_| {\n for(0..(n + 1)) |_| {\n std.debug.print(\"{}\\n\", .{0});\n }\n }\n}\n
The formula below shows the counting results before and after simplification, both leading to a time complexity of \\(O(n^2)\\):
\\[ \\begin{aligned} T(n) & = 2n(n + 1) + (5n + 1) + 2 & \\text{Complete Count (-.-|||)} \\newline & = 2n^2 + 7n + 3 \\newline T(n) & = n^2 + n & \\text{Simplified Count (o.O)} \\end{aligned} \\]"},{"location":"chapter_computational_complexity/time_complexity/#2-step-2-determining-the-asymptotic-upper-bound","title":"2. \u00a0 Step 2: Determining the Asymptotic Upper Bound","text":"The time complexity is determined by the highest order term in \\(T(n)\\). This is because, as \\(n\\) approaches infinity, the highest order term dominates, rendering the influence of other terms negligible.
The following table illustrates examples of different operation counts and their corresponding time complexities. Some exaggerated values are used to emphasize that coefficients cannot alter the order of growth. When \\(n\\) becomes very large, these constants become insignificant.
Table: Time Complexity for Different Operation Counts
Operation Count \\(T(n)\\) Time Complexity \\(O(f(n))\\) \\(100000\\) \\(O(1)\\) \\(3n + 2\\) \\(O(n)\\) \\(2n^2 + 3n + 2\\) \\(O(n^2)\\) \\(n^3 + 10000n^2\\) \\(O(n^3)\\) \\(2^n + 10000n^{10000}\\) \\(O(2^n)\\)"},{"location":"chapter_computational_complexity/time_complexity/#234-common-types-of-time-complexity","title":"2.3.4 \u00a0 Common Types of Time Complexity","text":"Let's consider the input data size as \\(n\\). The common types of time complexities are illustrated below, arranged from lowest to highest:
\\[ \\begin{aligned} O(1) < O(\\log n) < O(n) < O(n \\log n) < O(n^2) < O(2^n) < O(n!) \\newline \\text{Constant Order} < \\text{Logarithmic Order} < \\text{Linear Order} < \\text{Linear-Logarithmic Order} < \\text{Quadratic Order} < \\text{Exponential Order} < \\text{Factorial Order} \\end{aligned} \\]Figure 2-9 \u00a0 Common Types of Time Complexity
"},{"location":"chapter_computational_complexity/time_complexity/#1-constant-order-o1","title":"1. \u00a0 Constant Order \\(O(1)\\)","text":"Constant order means the number of operations is independent of the input data size \\(n\\). In the following function, although the number of operations size
might be large, the time complexity remains \\(O(1)\\) as it's unrelated to \\(n\\):
def constant(n: int) -> int:\n \"\"\"\u5e38\u6570\u9636\"\"\"\n count = 0\n size = 100000\n for _ in range(size):\n count += 1\n return count\n
time_complexity.cpp/* \u5e38\u6570\u9636 */\nint constant(int n) {\n int count = 0;\n int size = 100000;\n for (int i = 0; i < size; i++)\n count++;\n return count;\n}\n
time_complexity.java/* \u5e38\u6570\u9636 */\nint constant(int n) {\n int count = 0;\n int size = 100000;\n for (int i = 0; i < size; i++)\n count++;\n return count;\n}\n
time_complexity.cs/* \u5e38\u6570\u9636 */\nint Constant(int n) {\n int count = 0;\n int size = 100000;\n for (int i = 0; i < size; i++)\n count++;\n return count;\n}\n
time_complexity.go/* \u5e38\u6570\u9636 */\nfunc constant(n int) int {\n count := 0\n size := 100000\n for i := 0; i < size; i++ {\n count++\n }\n return count\n}\n
time_complexity.swift/* \u5e38\u6570\u9636 */\nfunc constant(n: Int) -> Int {\n var count = 0\n let size = 100_000\n for _ in 0 ..< size {\n count += 1\n }\n return count\n}\n
time_complexity.js/* \u5e38\u6570\u9636 */\nfunction constant(n) {\n let count = 0;\n const size = 100000;\n for (let i = 0; i < size; i++) count++;\n return count;\n}\n
time_complexity.ts/* \u5e38\u6570\u9636 */\nfunction constant(n: number): number {\n let count = 0;\n const size = 100000;\n for (let i = 0; i < size; i++) count++;\n return count;\n}\n
time_complexity.dart/* \u5e38\u6570\u9636 */\nint constant(int n) {\n int count = 0;\n int size = 100000;\n for (var i = 0; i < size; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.rs/* \u5e38\u6570\u9636 */\nfn constant(n: i32) -> i32 {\n _ = n;\n let mut count = 0;\n let size = 100_000;\n for _ in 0..size {\n count += 1;\n }\n count\n}\n
time_complexity.c/* \u5e38\u6570\u9636 */\nint constant(int n) {\n int count = 0;\n int size = 100000;\n int i = 0;\n for (int i = 0; i < size; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.zig// \u5e38\u6570\u9636\nfn constant(n: i32) i32 {\n _ = n;\n var count: i32 = 0;\n const size: i32 = 100_000;\n var i: i32 = 0;\n while(i<size) : (i += 1) {\n count += 1;\n }\n return count;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_computational_complexity/time_complexity/#2-linear-order-on","title":"2. \u00a0 Linear Order \\(O(n)\\)","text":"Linear order indicates the number of operations grows linearly with the input data size \\(n\\). Linear order commonly appears in single-loop structures:
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef linear(n: int) -> int:\n \"\"\"\u7ebf\u6027\u9636\"\"\"\n count = 0\n for _ in range(n):\n count += 1\n return count\n
time_complexity.cpp/* \u7ebf\u6027\u9636 */\nint linear(int n) {\n int count = 0;\n for (int i = 0; i < n; i++)\n count++;\n return count;\n}\n
time_complexity.java/* \u7ebf\u6027\u9636 */\nint linear(int n) {\n int count = 0;\n for (int i = 0; i < n; i++)\n count++;\n return count;\n}\n
time_complexity.cs/* \u7ebf\u6027\u9636 */\nint Linear(int n) {\n int count = 0;\n for (int i = 0; i < n; i++)\n count++;\n return count;\n}\n
time_complexity.go/* \u7ebf\u6027\u9636 */\nfunc linear(n int) int {\n count := 0\n for i := 0; i < n; i++ {\n count++\n }\n return count\n}\n
time_complexity.swift/* \u7ebf\u6027\u9636 */\nfunc linear(n: Int) -> Int {\n var count = 0\n for _ in 0 ..< n {\n count += 1\n }\n return count\n}\n
time_complexity.js/* \u7ebf\u6027\u9636 */\nfunction linear(n) {\n let count = 0;\n for (let i = 0; i < n; i++) count++;\n return count;\n}\n
time_complexity.ts/* \u7ebf\u6027\u9636 */\nfunction linear(n: number): number {\n let count = 0;\n for (let i = 0; i < n; i++) count++;\n return count;\n}\n
time_complexity.dart/* \u7ebf\u6027\u9636 */\nint linear(int n) {\n int count = 0;\n for (var i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.rs/* \u7ebf\u6027\u9636 */\nfn linear(n: i32) -> i32 {\n let mut count = 0;\n for _ in 0..n {\n count += 1;\n }\n count\n}\n
time_complexity.c/* \u7ebf\u6027\u9636 */\nint linear(int n) {\n int count = 0;\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.zig// \u7ebf\u6027\u9636\nfn linear(n: i32) i32 {\n var count: i32 = 0;\n var i: i32 = 0;\n while (i < n) : (i += 1) {\n count += 1;\n }\n return count;\n}\n
Visualizing Code Full Screen >
Operations like array traversal and linked list traversal have a time complexity of \\(O(n)\\), where \\(n\\) is the length of the array or list:
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef array_traversal(nums: list[int]) -> int:\n \"\"\"\u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09\"\"\"\n count = 0\n # \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for num in nums:\n count += 1\n return count\n
time_complexity.cpp/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nint arrayTraversal(vector<int> &nums) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (int num : nums) {\n count++;\n }\n return count;\n}\n
time_complexity.java/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nint arrayTraversal(int[] nums) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (int num : nums) {\n count++;\n }\n return count;\n}\n
time_complexity.cs/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nint ArrayTraversal(int[] nums) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n foreach (int num in nums) {\n count++;\n }\n return count;\n}\n
time_complexity.go/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nfunc arrayTraversal(nums []int) int {\n count := 0\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for range nums {\n count++\n }\n return count\n}\n
time_complexity.swift/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nfunc arrayTraversal(nums: [Int]) -> Int {\n var count = 0\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for _ in nums {\n count += 1\n }\n return count\n}\n
time_complexity.js/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nfunction arrayTraversal(nums) {\n let count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (let i = 0; i < nums.length; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.ts/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nfunction arrayTraversal(nums: number[]): number {\n let count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (let i = 0; i < nums.length; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.dart/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nint arrayTraversal(List<int> nums) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (var _num in nums) {\n count++;\n }\n return count;\n}\n
time_complexity.rs/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nfn array_traversal(nums: &[i32]) -> i32 {\n let mut count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for _ in nums {\n count += 1;\n }\n count\n}\n
time_complexity.c/* \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09 */\nint arrayTraversal(int *nums, int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.zig// \u7ebf\u6027\u9636\uff08\u904d\u5386\u6570\u7ec4\uff09\nfn arrayTraversal(nums: []i32) i32 {\n var count: i32 = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u6b63\u6bd4\n for (nums) |_| {\n count += 1;\n }\n return count;\n}\n
Visualizing Code Full Screen >
It's important to note that the input data size \\(n\\) should be determined based on the type of input data. For example, in the first example, \\(n\\) represents the input data size, while in the second example, the length of the array \\(n\\) is the data size.
"},{"location":"chapter_computational_complexity/time_complexity/#3-quadratic-order-on2","title":"3. \u00a0 Quadratic Order \\(O(n^2)\\)","text":"Quadratic order means the number of operations grows quadratically with the input data size \\(n\\). Quadratic order typically appears in nested loops, where both the outer and inner loops have a time complexity of \\(O(n)\\), resulting in an overall complexity of \\(O(n^2)\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef quadratic(n: int) -> int:\n \"\"\"\u5e73\u65b9\u9636\"\"\"\n count = 0\n # \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for i in range(n):\n for j in range(n):\n count += 1\n return count\n
time_complexity.cpp/* \u5e73\u65b9\u9636 */\nint quadratic(int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.java/* \u5e73\u65b9\u9636 */\nint quadratic(int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.cs/* \u5e73\u65b9\u9636 */\nint Quadratic(int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.go/* \u5e73\u65b9\u9636 */\nfunc quadratic(n int) int {\n count := 0\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for i := 0; i < n; i++ {\n for j := 0; j < n; j++ {\n count++\n }\n }\n return count\n}\n
time_complexity.swift/* \u5e73\u65b9\u9636 */\nfunc quadratic(n: Int) -> Int {\n var count = 0\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for _ in 0 ..< n {\n for _ in 0 ..< n {\n count += 1\n }\n }\n return count\n}\n
time_complexity.js/* \u5e73\u65b9\u9636 */\nfunction quadratic(n) {\n let count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.ts/* \u5e73\u65b9\u9636 */\nfunction quadratic(n: number): number {\n let count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.dart/* \u5e73\u65b9\u9636 */\nint quadratic(int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.rs/* \u5e73\u65b9\u9636 */\nfn quadratic(n: i32) -> i32 {\n let mut count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for _ in 0..n {\n for _ in 0..n {\n count += 1;\n }\n }\n count\n}\n
time_complexity.c/* \u5e73\u65b9\u9636 */\nint quadratic(int n) {\n int count = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < n; j++) {\n count++;\n }\n }\n return count;\n}\n
time_complexity.zig// \u5e73\u65b9\u9636\nfn quadratic(n: i32) i32 {\n var count: i32 = 0;\n var i: i32 = 0;\n // \u5faa\u73af\u6b21\u6570\u4e0e\u6570\u7ec4\u957f\u5ea6\u6210\u5e73\u65b9\u5173\u7cfb\n while (i < n) : (i += 1) {\n var j: i32 = 0;\n while (j < n) : (j += 1) {\n count += 1;\n }\n }\n return count;\n}\n
Visualizing Code Full Screen >
The following image compares constant order, linear order, and quadratic order time complexities.
Figure 2-10 \u00a0 Constant, Linear, and Quadratic Order Time Complexities
For instance, in bubble sort, the outer loop runs \\(n - 1\\) times, and the inner loop runs \\(n-1\\), \\(n-2\\), ..., \\(2\\), \\(1\\) times, averaging \\(n / 2\\) times, resulting in a time complexity of \\(O((n - 1) n / 2) = O(n^2)\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef bubble_sort(nums: list[int]) -> int:\n \"\"\"\u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09\"\"\"\n count = 0 # \u8ba1\u6570\u5668\n # \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for i in range(len(nums) - 1, 0, -1):\n # \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for j in range(i):\n if nums[j] > nums[j + 1]:\n # \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n tmp: int = nums[j]\n nums[j] = nums[j + 1]\n nums[j + 1] = tmp\n count += 3 # \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n return count\n
time_complexity.cpp/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nint bubbleSort(vector<int> &nums) {\n int count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (int i = nums.size() - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (int j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n int tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.java/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nint bubbleSort(int[] nums) {\n int count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (int i = nums.length - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (int j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n int tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.cs/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nint BubbleSort(int[] nums) {\n int count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (int i = nums.Length - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef \n for (int j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]);\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.go/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nfunc bubbleSort(nums []int) int {\n count := 0 // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for i := len(nums) - 1; i > 0; i-- {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for j := 0; j < i; j++ {\n if nums[j] > nums[j+1] {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n tmp := nums[j]\n nums[j] = nums[j+1]\n nums[j+1] = tmp\n count += 3 // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count\n}\n
time_complexity.swift/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nfunc bubbleSort(nums: inout [Int]) -> Int {\n var count = 0 // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for i in stride(from: nums.count - 1, to: 0, by: -1) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef \n for j in 0 ..< i {\n if nums[j] > nums[j + 1] {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n let tmp = nums[j]\n nums[j] = nums[j + 1]\n nums[j + 1] = tmp\n count += 3 // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count\n}\n
time_complexity.js/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nfunction bubbleSort(nums) {\n let count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (let i = nums.length - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (let j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n let tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.ts/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nfunction bubbleSort(nums: number[]): number {\n let count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (let i = nums.length - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (let j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n let tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.dart/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nint bubbleSort(List<int> nums) {\n int count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (var i = nums.length - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (var j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n int tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.rs/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nfn bubble_sort(nums: &mut [i32]) -> i32 {\n let mut count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for i in (1..nums.len()).rev() {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef \n for j in 0..i {\n if nums[j] > nums[j + 1] {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n let tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n count\n}\n
time_complexity.c/* \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09 */\nint bubbleSort(int *nums, int n) {\n int count = 0; // \u8ba1\u6570\u5668\n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n for (int i = n - 1; i > 0; i--) {\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef\n for (int j = 0; j < i; j++) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n int tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
time_complexity.zig// \u5e73\u65b9\u9636\uff08\u5192\u6ce1\u6392\u5e8f\uff09\nfn bubbleSort(nums: []i32) i32 {\n var count: i32 = 0; // \u8ba1\u6570\u5668 \n // \u5916\u5faa\u73af\uff1a\u672a\u6392\u5e8f\u533a\u95f4\u4e3a [0, i]\n var i: i32 = @as(i32, @intCast(nums.len)) - 1;\n while (i > 0) : (i -= 1) {\n var j: usize = 0;\n // \u5185\u5faa\u73af\uff1a\u5c06\u672a\u6392\u5e8f\u533a\u95f4 [0, i] \u4e2d\u7684\u6700\u5927\u5143\u7d20\u4ea4\u6362\u81f3\u8be5\u533a\u95f4\u7684\u6700\u53f3\u7aef \n while (j < i) : (j += 1) {\n if (nums[j] > nums[j + 1]) {\n // \u4ea4\u6362 nums[j] \u4e0e nums[j + 1]\n var tmp = nums[j];\n nums[j] = nums[j + 1];\n nums[j + 1] = tmp;\n count += 3; // \u5143\u7d20\u4ea4\u6362\u5305\u542b 3 \u4e2a\u5355\u5143\u64cd\u4f5c\n }\n }\n }\n return count;\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_computational_complexity/time_complexity/#4-exponential-order-o2n","title":"4. \u00a0 Exponential Order \\(O(2^n)\\)","text":"Biological \"cell division\" is a classic example of exponential order growth: starting with one cell, it becomes two after one division, four after two divisions, and so on, resulting in \\(2^n\\) cells after \\(n\\) divisions.
The following image and code simulate the cell division process, with a time complexity of \\(O(2^n)\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef exponential(n: int) -> int:\n \"\"\"\u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09\"\"\"\n count = 0\n base = 1\n # \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for _ in range(n):\n for _ in range(base):\n count += 1\n base *= 2\n # count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count\n
time_complexity.cpp/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint exponential(int n) {\n int count = 0, base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < base; j++) {\n count++;\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.java/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint exponential(int n) {\n int count = 0, base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < base; j++) {\n count++;\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.cs/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint Exponential(int n) {\n int count = 0, bas = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < bas; j++) {\n count++;\n }\n bas *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.go/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09*/\nfunc exponential(n int) int {\n count, base := 0, 1\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for i := 0; i < n; i++ {\n for j := 0; j < base; j++ {\n count++\n }\n base *= 2\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count\n}\n
time_complexity.swift/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunc exponential(n: Int) -> Int {\n var count = 0\n var base = 1\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for _ in 0 ..< n {\n for _ in 0 ..< base {\n count += 1\n }\n base *= 2\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count\n}\n
time_complexity.js/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunction exponential(n) {\n let count = 0,\n base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < base; j++) {\n count++;\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.ts/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunction exponential(n: number): number {\n let count = 0,\n base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < base; j++) {\n count++;\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.dart/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint exponential(int n) {\n int count = 0, base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (var i = 0; i < n; i++) {\n for (var j = 0; j < base; j++) {\n count++;\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.rs/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfn exponential(n: i32) -> i32 {\n let mut count = 0;\n let mut base = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for _ in 0..n {\n for _ in 0..base {\n count += 1\n }\n base *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n count\n}\n
time_complexity.c/* \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint exponential(int n) {\n int count = 0;\n int bas = 1;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n for (int i = 0; i < n; i++) {\n for (int j = 0; j < bas; j++) {\n count++;\n }\n bas *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
time_complexity.zig// \u6307\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09\nfn exponential(n: i32) i32 {\n var count: i32 = 0;\n var bas: i32 = 1;\n var i: i32 = 0;\n // \u7ec6\u80de\u6bcf\u8f6e\u4e00\u5206\u4e3a\u4e8c\uff0c\u5f62\u6210\u6570\u5217 1, 2, 4, 8, ..., 2^(n-1)\n while (i < n) : (i += 1) {\n var j: i32 = 0;\n while (j < bas) : (j += 1) {\n count += 1;\n }\n bas *= 2;\n }\n // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1\n return count;\n}\n
Visualizing Code Full Screen >
Figure 2-11 \u00a0 Exponential Order Time Complexity
In practice, exponential order often appears in recursive functions. For example, in the code below, it recursively splits into two halves, stopping after \\(n\\) divisions:
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef exp_recur(n: int) -> int:\n \"\"\"\u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\"\"\"\n if n == 1:\n return 1\n return exp_recur(n - 1) + exp_recur(n - 1) + 1\n
time_complexity.cpp/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint expRecur(int n) {\n if (n == 1)\n return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.java/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint expRecur(int n) {\n if (n == 1)\n return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.cs/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint ExpRecur(int n) {\n if (n == 1) return 1;\n return ExpRecur(n - 1) + ExpRecur(n - 1) + 1;\n}\n
time_complexity.go/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09*/\nfunc expRecur(n int) int {\n if n == 1 {\n return 1\n }\n return expRecur(n-1) + expRecur(n-1) + 1\n}\n
time_complexity.swift/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc expRecur(n: Int) -> Int {\n if n == 1 {\n return 1\n }\n return expRecur(n: n - 1) + expRecur(n: n - 1) + 1\n}\n
time_complexity.js/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction expRecur(n) {\n if (n === 1) return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.ts/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction expRecur(n: number): number {\n if (n === 1) return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.dart/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint expRecur(int n) {\n if (n == 1) return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.rs/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfn exp_recur(n: i32) -> i32 {\n if n == 1 {\n return 1;\n }\n exp_recur(n - 1) + exp_recur(n - 1) + 1\n}\n
time_complexity.c/* \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint expRecur(int n) {\n if (n == 1)\n return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
time_complexity.zig// \u6307\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\nfn expRecur(n: i32) i32 {\n if (n == 1) return 1;\n return expRecur(n - 1) + expRecur(n - 1) + 1;\n}\n
Visualizing Code Full Screen >
Exponential order growth is extremely rapid and is commonly seen in exhaustive search methods (brute force, backtracking, etc.). For large-scale problems, exponential order is unacceptable, often requiring dynamic programming or greedy algorithms as solutions.
"},{"location":"chapter_computational_complexity/time_complexity/#5-logarithmic-order-olog-n","title":"5. \u00a0 Logarithmic Order \\(O(\\log n)\\)","text":"In contrast to exponential order, logarithmic order reflects situations where \"the size is halved each round.\" Given an input data size \\(n\\), since the size is halved each round, the number of iterations is \\(\\log_2 n\\), the inverse function of \\(2^n\\).
The following image and code simulate the \"halving each round\" process, with a time complexity of \\(O(\\log_2 n)\\), commonly abbreviated as \\(O(\\log n)\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef logarithmic(n: float) -> int:\n \"\"\"\u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09\"\"\"\n count = 0\n while n > 1:\n n = n / 2\n count += 1\n return count\n
time_complexity.cpp/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint logarithmic(float n) {\n int count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.java/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint logarithmic(float n) {\n int count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.cs/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint Logarithmic(float n) {\n int count = 0;\n while (n > 1) {\n n /= 2;\n count++;\n }\n return count;\n}\n
time_complexity.go/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09*/\nfunc logarithmic(n float64) int {\n count := 0\n for n > 1 {\n n = n / 2\n count++\n }\n return count\n}\n
time_complexity.swift/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunc logarithmic(n: Double) -> Int {\n var count = 0\n var n = n\n while n > 1 {\n n = n / 2\n count += 1\n }\n return count\n}\n
time_complexity.js/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunction logarithmic(n) {\n let count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.ts/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfunction logarithmic(n: number): number {\n let count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.dart/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint logarithmic(num n) {\n int count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.rs/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nfn logarithmic(mut n: f32) -> i32 {\n let mut count = 0;\n while n > 1.0 {\n n = n / 2.0;\n count += 1;\n }\n count\n}\n
time_complexity.c/* \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09 */\nint logarithmic(float n) {\n int count = 0;\n while (n > 1) {\n n = n / 2;\n count++;\n }\n return count;\n}\n
time_complexity.zig// \u5bf9\u6570\u9636\uff08\u5faa\u73af\u5b9e\u73b0\uff09\nfn logarithmic(n: f32) i32 {\n var count: i32 = 0;\n var n_var = n;\n while (n_var > 1)\n {\n n_var = n_var / 2;\n count +=1;\n }\n return count;\n}\n
Visualizing Code Full Screen >
Figure 2-12 \u00a0 Logarithmic Order Time Complexity
Like exponential order, logarithmic order also frequently appears in recursive functions. The code below forms a recursive tree of height \\(\\log_2 n\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef log_recur(n: float) -> int:\n \"\"\"\u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\"\"\"\n if n <= 1:\n return 0\n return log_recur(n / 2) + 1\n
time_complexity.cpp/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint logRecur(float n) {\n if (n <= 1)\n return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.java/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint logRecur(float n) {\n if (n <= 1)\n return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.cs/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint LogRecur(float n) {\n if (n <= 1) return 0;\n return LogRecur(n / 2) + 1;\n}\n
time_complexity.go/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09*/\nfunc logRecur(n float64) int {\n if n <= 1 {\n return 0\n }\n return logRecur(n/2) + 1\n}\n
time_complexity.swift/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc logRecur(n: Double) -> Int {\n if n <= 1 {\n return 0\n }\n return logRecur(n: n / 2) + 1\n}\n
time_complexity.js/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction logRecur(n) {\n if (n <= 1) return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.ts/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction logRecur(n: number): number {\n if (n <= 1) return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.dart/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint logRecur(num n) {\n if (n <= 1) return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.rs/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfn log_recur(n: f32) -> i32 {\n if n <= 1.0 {\n return 0;\n }\n log_recur(n / 2.0) + 1\n}\n
time_complexity.c/* \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint logRecur(float n) {\n if (n <= 1)\n return 0;\n return logRecur(n / 2) + 1;\n}\n
time_complexity.zig// \u5bf9\u6570\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\nfn logRecur(n: f32) i32 {\n if (n <= 1) return 0;\n return logRecur(n / 2) + 1;\n}\n
Visualizing Code Full Screen >
Logarithmic order is typical in algorithms based on the divide-and-conquer strategy, embodying the \"split into many\" and \"simplify complex problems\" approach. It's slow-growing and is the most ideal time complexity after constant order.
What is the base of \\(O(\\log n)\\)?
Technically, \"splitting into \\(m\\)\" corresponds to a time complexity of \\(O(\\log_m n)\\). Using the logarithm base change formula, we can equate different logarithmic complexities:
\\[ O(\\log_m n) = O(\\log_k n / \\log_k m) = O(\\log_k n) \\]This means the base \\(m\\) can be changed without affecting the complexity. Therefore, we often omit the base \\(m\\) and simply denote logarithmic order as \\(O(\\log n)\\).
"},{"location":"chapter_computational_complexity/time_complexity/#6-linear-logarithmic-order-on-log-n","title":"6. \u00a0 Linear-Logarithmic Order \\(O(n \\log n)\\)","text":"Linear-logarithmic order often appears in nested loops, with the complexities of the two loops being \\(O(\\log n)\\) and \\(O(n)\\) respectively. The related code is as follows:
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef linear_log_recur(n: float) -> int:\n \"\"\"\u7ebf\u6027\u5bf9\u6570\u9636\"\"\"\n if n <= 1:\n return 1\n count: int = linear_log_recur(n // 2) + linear_log_recur(n // 2)\n for _ in range(n):\n count += 1\n return count\n
time_complexity.cpp/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nint linearLogRecur(float n) {\n if (n <= 1)\n return 1;\n int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.java/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nint linearLogRecur(float n) {\n if (n <= 1)\n return 1;\n int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.cs/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nint LinearLogRecur(float n) {\n if (n <= 1) return 1;\n int count = LinearLogRecur(n / 2) + LinearLogRecur(n / 2);\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.go/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nfunc linearLogRecur(n float64) int {\n if n <= 1 {\n return 1\n }\n count := linearLogRecur(n/2) + linearLogRecur(n/2)\n for i := 0.0; i < n; i++ {\n count++\n }\n return count\n}\n
time_complexity.swift/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nfunc linearLogRecur(n: Double) -> Int {\n if n <= 1 {\n return 1\n }\n var count = linearLogRecur(n: n / 2) + linearLogRecur(n: n / 2)\n for _ in stride(from: 0, to: n, by: 1) {\n count += 1\n }\n return count\n}\n
time_complexity.js/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nfunction linearLogRecur(n) {\n if (n <= 1) return 1;\n let count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (let i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.ts/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nfunction linearLogRecur(n: number): number {\n if (n <= 1) return 1;\n let count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (let i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.dart/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nint linearLogRecur(num n) {\n if (n <= 1) return 1;\n int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (var i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.rs/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nfn linear_log_recur(n: f32) -> i32 {\n if n <= 1.0 {\n return 1;\n }\n let mut count = linear_log_recur(n / 2.0) + linear_log_recur(n / 2.0);\n for _ in 0 ..n as i32 {\n count += 1;\n }\n return count\n}\n
time_complexity.c/* \u7ebf\u6027\u5bf9\u6570\u9636 */\nint linearLogRecur(float n) {\n if (n <= 1)\n return 1;\n int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n for (int i = 0; i < n; i++) {\n count++;\n }\n return count;\n}\n
time_complexity.zig// \u7ebf\u6027\u5bf9\u6570\u9636\nfn linearLogRecur(n: f32) i32 {\n if (n <= 1) return 1;\n var count: i32 = linearLogRecur(n / 2) + linearLogRecur(n / 2);\n var i: f32 = 0;\n while (i < n) : (i += 1) {\n count += 1;\n }\n return count;\n}\n
Visualizing Code Full Screen >
The image below demonstrates how linear-logarithmic order is generated. Each level of a binary tree has \\(n\\) operations, and the tree has \\(\\log_2 n + 1\\) levels, resulting in a time complexity of \\(O(n \\log n)\\).
Figure 2-13 \u00a0 Linear-Logarithmic Order Time Complexity
Mainstream sorting algorithms typically have a time complexity of \\(O(n \\log n)\\), such as quicksort, mergesort, and heapsort.
"},{"location":"chapter_computational_complexity/time_complexity/#7-factorial-order-on","title":"7. \u00a0 Factorial Order \\(O(n!)\\)","text":"Factorial order corresponds to the mathematical problem of \"full permutation.\" Given \\(n\\) distinct elements, the total number of possible permutations is:
\\[ n! = n \\times (n - 1) \\times (n - 2) \\times \\dots \\times 2 \\times 1 \\]Factorials are typically implemented using recursion. As shown in the image and code below, the first level splits into \\(n\\) branches, the second level into \\(n - 1\\) branches, and so on, stopping after the \\(n\\)th level:
PythonC++JavaC#GoSwiftJSTSDartRustCZig time_complexity.pydef factorial_recur(n: int) -> int:\n \"\"\"\u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\"\"\"\n if n == 0:\n return 1\n count = 0\n # \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for _ in range(n):\n count += factorial_recur(n - 1)\n return count\n
time_complexity.cpp/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint factorialRecur(int n) {\n if (n == 0)\n return 1;\n int count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (int i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.java/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint factorialRecur(int n) {\n if (n == 0)\n return 1;\n int count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (int i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.cs/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint FactorialRecur(int n) {\n if (n == 0) return 1;\n int count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (int i = 0; i < n; i++) {\n count += FactorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.go/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc factorialRecur(n int) int {\n if n == 0 {\n return 1\n }\n count := 0\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for i := 0; i < n; i++ {\n count += factorialRecur(n - 1)\n }\n return count\n}\n
time_complexity.swift/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunc factorialRecur(n: Int) -> Int {\n if n == 0 {\n return 1\n }\n var count = 0\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for _ in 0 ..< n {\n count += factorialRecur(n: n - 1)\n }\n return count\n}\n
time_complexity.js/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction factorialRecur(n) {\n if (n === 0) return 1;\n let count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (let i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.ts/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfunction factorialRecur(n: number): number {\n if (n === 0) return 1;\n let count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (let i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.dart/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint factorialRecur(int n) {\n if (n == 0) return 1;\n int count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for (var i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.rs/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nfn factorial_recur(n: i32) -> i32 {\n if n == 0 {\n return 1;\n }\n let mut count = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n for _ in 0..n {\n count += factorial_recur(n - 1);\n }\n count\n}\n
time_complexity.c/* \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09 */\nint factorialRecur(int n) {\n if (n == 0)\n return 1;\n int count = 0;\n for (int i = 0; i < n; i++) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
time_complexity.zig// \u9636\u4e58\u9636\uff08\u9012\u5f52\u5b9e\u73b0\uff09\nfn factorialRecur(n: i32) i32 {\n if (n == 0) return 1;\n var count: i32 = 0;\n var i: i32 = 0;\n // \u4ece 1 \u4e2a\u5206\u88c2\u51fa n \u4e2a\n while (i < n) : (i += 1) {\n count += factorialRecur(n - 1);\n }\n return count;\n}\n
Visualizing Code Full Screen >
Figure 2-14 \u00a0 Factorial Order Time Complexity
Note that factorial order grows even faster than exponential order; it's unacceptable for larger \\(n\\) values.
"},{"location":"chapter_computational_complexity/time_complexity/#235-worst-best-and-average-time-complexities","title":"2.3.5 \u00a0 Worst, Best, and Average Time Complexities","text":"The time efficiency of an algorithm is often not fixed but depends on the distribution of the input data. Assume we have an array nums
of length \\(n\\), consisting of numbers from \\(1\\) to \\(n\\), each appearing only once, but in a randomly shuffled order. The task is to return the index of the element \\(1\\). We can draw the following conclusions:
nums = [?, ?, ..., 1]
, that is, when the last element is \\(1\\), it requires a complete traversal of the array, achieving the worst-case time complexity of \\(O(n)\\).nums = [1, ?, ?, ...]
, that is, when the first element is \\(1\\), no matter the length of the array, no further traversal is needed, achieving the best-case time complexity of \\(\\Omega(1)\\).The \"worst-case time complexity\" corresponds to the asymptotic upper bound, denoted by the big \\(O\\) notation. Correspondingly, the \"best-case time complexity\" corresponds to the asymptotic lower bound, denoted by \\(\\Omega\\):
PythonC++JavaC#GoSwiftJSTSDartRustCZig worst_best_time_complexity.pydef random_numbers(n: int) -> list[int]:\n \"\"\"\u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a: 1, 2, ..., n \uff0c\u987a\u5e8f\u88ab\u6253\u4e71\"\"\"\n # \u751f\u6210\u6570\u7ec4 nums =: 1, 2, 3, ..., n\n nums = [i for i in range(1, n + 1)]\n # \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n random.shuffle(nums)\n return nums\n\ndef find_one(nums: list[int]) -> int:\n \"\"\"\u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15\"\"\"\n for i in range(len(nums)):\n # \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n # \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if nums[i] == 1:\n return i\n return -1\n
worst_best_time_complexity.cpp/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nvector<int> randomNumbers(int n) {\n vector<int> nums(n);\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (int i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u4f7f\u7528\u7cfb\u7edf\u65f6\u95f4\u751f\u6210\u968f\u673a\u79cd\u5b50\n unsigned seed = chrono::system_clock::now().time_since_epoch().count();\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n shuffle(nums.begin(), nums.end(), default_random_engine(seed));\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nint findOne(vector<int> &nums) {\n for (int i = 0; i < nums.size(); i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] == 1)\n return i;\n }\n return -1;\n}\n
worst_best_time_complexity.java/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nint[] randomNumbers(int n) {\n Integer[] nums = new Integer[n];\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (int i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n Collections.shuffle(Arrays.asList(nums));\n // Integer[] -> int[]\n int[] res = new int[n];\n for (int i = 0; i < n; i++) {\n res[i] = nums[i];\n }\n return res;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nint findOne(int[] nums) {\n for (int i = 0; i < nums.length; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] == 1)\n return i;\n }\n return -1;\n}\n
worst_best_time_complexity.cs/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nint[] RandomNumbers(int n) {\n int[] nums = new int[n];\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (int i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n for (int i = 0; i < nums.Length; i++) {\n int index = new Random().Next(i, nums.Length);\n (nums[i], nums[index]) = (nums[index], nums[i]);\n }\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nint FindOne(int[] nums) {\n for (int i = 0; i < nums.Length; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] == 1)\n return i;\n }\n return -1;\n}\n
worst_best_time_complexity.go/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nfunc randomNumbers(n int) []int {\n nums := make([]int, n)\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for i := 0; i < n; i++ {\n nums[i] = i + 1\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n rand.Shuffle(len(nums), func(i, j int) {\n nums[i], nums[j] = nums[j], nums[i]\n })\n return nums\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nfunc findOne(nums []int) int {\n for i := 0; i < len(nums); i++ {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if nums[i] == 1 {\n return i\n }\n }\n return -1\n}\n
worst_best_time_complexity.swift/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nfunc randomNumbers(n: Int) -> [Int] {\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n var nums = Array(1 ... n)\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n nums.shuffle()\n return nums\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nfunc findOne(nums: [Int]) -> Int {\n for i in nums.indices {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if nums[i] == 1 {\n return i\n }\n }\n return -1\n}\n
worst_best_time_complexity.js/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nfunction randomNumbers(n) {\n const nums = Array(n);\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (let i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n for (let i = 0; i < n; i++) {\n const r = Math.floor(Math.random() * (i + 1));\n const temp = nums[i];\n nums[i] = nums[r];\n nums[r] = temp;\n }\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nfunction findOne(nums) {\n for (let i = 0; i < nums.length; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] === 1) {\n return i;\n }\n }\n return -1;\n}\n
worst_best_time_complexity.ts/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nfunction randomNumbers(n: number): number[] {\n const nums = Array(n);\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (let i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n for (let i = 0; i < n; i++) {\n const r = Math.floor(Math.random() * (i + 1));\n const temp = nums[i];\n nums[i] = nums[r];\n nums[r] = temp;\n }\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nfunction findOne(nums: number[]): number {\n for (let i = 0; i < nums.length; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] === 1) {\n return i;\n }\n }\n return -1;\n}\n
worst_best_time_complexity.dart/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nList<int> randomNumbers(int n) {\n final nums = List.filled(n, 0);\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (var i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n nums.shuffle();\n\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nint findOne(List<int> nums) {\n for (var i = 0; i < nums.length; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] == 1) return i;\n }\n\n return -1;\n}\n
worst_best_time_complexity.rs/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nfn random_numbers(n: i32) -> Vec<i32> {\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n let mut nums = (1..=n).collect::<Vec<i32>>();\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n nums.shuffle(&mut thread_rng());\n nums\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nfn find_one(nums: &[i32]) -> Option<usize> {\n for i in 0..nums.len() {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if nums[i] == 1 {\n return Some(i);\n }\n }\n None\n}\n
worst_best_time_complexity.c/* \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71 */\nint *randomNumbers(int n) {\n // \u5206\u914d\u5806\u533a\u5185\u5b58\uff08\u521b\u5efa\u4e00\u7ef4\u53ef\u53d8\u957f\u6570\u7ec4\uff1a\u6570\u7ec4\u4e2d\u5143\u7d20\u6570\u91cf\u4e3a n \uff0c\u5143\u7d20\u7c7b\u578b\u4e3a int \uff09\n int *nums = (int *)malloc(n * sizeof(int));\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (int i = 0; i < n; i++) {\n nums[i] = i + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n for (int i = n - 1; i > 0; i--) {\n int j = rand() % (i + 1);\n int temp = nums[i];\n nums[i] = nums[j];\n nums[j] = temp;\n }\n return nums;\n}\n\n/* \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15 */\nint findOne(int *nums, int n) {\n for (int i = 0; i < n; i++) {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (nums[i] == 1)\n return i;\n }\n return -1;\n}\n
worst_best_time_complexity.zig// \u751f\u6210\u4e00\u4e2a\u6570\u7ec4\uff0c\u5143\u7d20\u4e3a { 1, 2, ..., n }\uff0c\u987a\u5e8f\u88ab\u6253\u4e71\nfn randomNumbers(comptime n: usize) [n]i32 {\n var nums: [n]i32 = undefined;\n // \u751f\u6210\u6570\u7ec4 nums = { 1, 2, 3, ..., n }\n for (&nums, 0..) |*num, i| {\n num.* = @as(i32, @intCast(i)) + 1;\n }\n // \u968f\u673a\u6253\u4e71\u6570\u7ec4\u5143\u7d20\n const rand = std.crypto.random;\n rand.shuffle(i32, &nums);\n return nums;\n}\n\n// \u67e5\u627e\u6570\u7ec4 nums \u4e2d\u6570\u5b57 1 \u6240\u5728\u7d22\u5f15\nfn findOne(nums: []i32) i32 {\n for (nums, 0..) |num, i| {\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5934\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u4f73\u65f6\u95f4\u590d\u6742\u5ea6 O(1)\n // \u5f53\u5143\u7d20 1 \u5728\u6570\u7ec4\u5c3e\u90e8\u65f6\uff0c\u8fbe\u5230\u6700\u5dee\u65f6\u95f4\u590d\u6742\u5ea6 O(n)\n if (num == 1) return @intCast(i);\n }\n return -1;\n}\n
Visualizing Code Full Screen >
It's important to note that the best-case time complexity is rarely used in practice, as it is usually only achievable under very low probabilities and might be misleading. The worst-case time complexity is more practical as it provides a safety value for efficiency, allowing us to confidently use the algorithm.
From the above example, it's clear that both the worst-case and best-case time complexities only occur under \"special data distributions,\" which may have a small probability of occurrence and may not accurately reflect the algorithm's run efficiency. In contrast, the average time complexity can reflect the algorithm's efficiency under random input data, denoted by the \\(\\Theta\\) notation.
For some algorithms, we can simply estimate the average case under a random data distribution. For example, in the aforementioned example, since the input array is shuffled, the probability of element \\(1\\) appearing at any index is equal. Therefore, the average number of loops for the algorithm is half the length of the array \\(n / 2\\), giving an average time complexity of \\(\\Theta(n / 2) = \\Theta(n)\\).
However, calculating the average time complexity for more complex algorithms can be quite difficult, as it's challenging to analyze the overall mathematical expectation under the data distribution. In such cases, we usually use the worst-case time complexity as the standard for judging the efficiency of the algorithm.
Why is the \\(\\Theta\\) symbol rarely seen?
Possibly because the \\(O\\) notation is more commonly spoken, it is often used to represent the average time complexity. However, strictly speaking, this practice is not accurate. In this book and other materials, if you encounter statements like \"average time complexity \\(O(n)\\)\", please understand it directly as \\(\\Theta(n)\\).
"},{"location":"chapter_data_structure/","title":"Chapter 3. \u00a0 Data Structures","text":"Abstract
Data structures serve as a robust and diverse framework.
They offer a blueprint for the orderly organization of data, upon which algorithms come to life.
"},{"location":"chapter_data_structure/#chapter-contents","title":"Chapter Contents","text":"When discussing data in computers, various forms like text, images, videos, voice and 3D models comes to mind. Despite their different organizational forms, they are all composed of various basic data types.
Basic data types are those that the CPU can directly operate on and are directly used in algorithms, mainly including the following.
byte
, short
, int
, long
.float
, double
, used to represent decimals.char
, used to represent letters, punctuation, and even emojis in various languages.bool
, used to represent \"yes\" or \"no\" decisions.Basic data types are stored in computers in binary form. One binary digit is 1 bit. In most modern operating systems, 1 byte consists of 8 bits.
The range of values for basic data types depends on the size of the space they occupy. Below, we take Java as an example.
byte
occupies 1 byte = 8 bits and can represent \\(2^8\\) numbers.int
occupies 4 bytes = 32 bits and can represent \\(2^{32}\\) numbers.The following table lists the space occupied, value range, and default values of various basic data types in Java. While memorizing this table isn't necessary, having a general understanding of it and referencing it when required is recommended.
Table 3-1 \u00a0 Space Occupied and Value Range of Basic Data Types
Type Symbol Space Occupied Minimum Value Maximum Value Default Value Integerbyte
1 byte \\(-2^7\\) (\\(-128\\)) \\(2^7 - 1\\) (\\(127\\)) 0 short
2 bytes \\(-2^{15}\\) \\(2^{15} - 1\\) 0 int
4 bytes \\(-2^{31}\\) \\(2^{31} - 1\\) 0 long
8 bytes \\(-2^{63}\\) \\(2^{63} - 1\\) 0 Float float
4 bytes \\(1.175 \\times 10^{-38}\\) \\(3.403 \\times 10^{38}\\) \\(0.0\\text{f}\\) double
8 bytes \\(2.225 \\times 10^{-308}\\) \\(1.798 \\times 10^{308}\\) 0.0 Char char
2 bytes 0 \\(2^{16} - 1\\) 0 Boolean bool
1 byte \\(\\text{false}\\) \\(\\text{true}\\) \\(\\text{false}\\) Please note that the above table is specific to Java's basic data types. Every programming language has its own data type definitions, which might differ in space occupied, value ranges, and default values.
int
can be of any size, limited only by available memory; the floating-point float
is double precision 64-bit; there is no char
type, as a single character is actually a string str
of length 1.char
in C and C++ is 1 byte, while in most programming languages, it depends on the specific character encoding method, as detailed in the \"Character Encoding\" chapter.So, what is the connection between basic data types and data structures? We know that data structures are ways to organize and store data in computers. The focus here is on \"structure\" rather than \"data\".
If we want to represent \"a row of numbers\", we naturally think of using an array. This is because the linear structure of an array can represent the adjacency and the ordering of the numbers, but whether the stored content is an integer int
, a decimal float
, or a character char
, is irrelevant to the \"data structure\".
In other words, basic data types provide the \"content type\" of data, while data structures provide the \"way of organizing\" data. For example, in the following code, we use the same data structure (array) to store and represent different basic data types, including int
, float
, char
, bool
, etc.
# Using various basic data types to initialize arrays\nnumbers: list[int] = [0] * 5\ndecimals: list[float] = [0.0] * 5\n# Python's characters are actually strings of length 1\ncharacters: list[str] = ['0'] * 5\nbools: list[bool] = [False] * 5\n# Python's lists can freely store various basic data types and object references\ndata = [0, 0.0, 'a', False, ListNode(0)]\n
// Using various basic data types to initialize arrays\nint numbers[5];\nfloat decimals[5];\nchar characters[5];\nbool bools[5];\n
// Using various basic data types to initialize arrays\nint[] numbers = new int[5];\nfloat[] decimals = new float[5];\nchar[] characters = new char[5];\nboolean[] bools = new boolean[5];\n
// Using various basic data types to initialize arrays\nint[] numbers = new int[5];\nfloat[] decimals = new float[5];\nchar[] characters = new char[5];\nbool[] bools = new bool[5];\n
// Using various basic data types to initialize arrays\nvar numbers = [5]int{}\nvar decimals = [5]float64{}\nvar characters = [5]byte{}\nvar bools = [5]bool{}\n
// Using various basic data types to initialize arrays\nlet numbers = Array(repeating: Int(), count: 5)\nlet decimals = Array(repeating: Double(), count: 5)\nlet characters = Array(repeating: Character(\"a\"), count: 5)\nlet bools = Array(repeating: Bool(), count: 5)\n
// JavaScript's arrays can freely store various basic data types and objects\nconst array = [0, 0.0, 'a', false];\n
// Using various basic data types to initialize arrays\nconst numbers: number[] = [];\nconst characters: string[] = [];\nconst bools: boolean[] = [];\n
// Using various basic data types to initialize arrays\nList<int> numbers = List.filled(5, 0);\nList<double> decimals = List.filled(5, 0.0);\nList<String> characters = List.filled(5, 'a');\nList<bool> bools = List.filled(5, false);\n
// Using various basic data types to initialize arrays\nlet numbers: Vec<i32> = vec![0; 5];\nlet decimals: Vec<f32> = vec![0.0, 5];\nlet characters: Vec<char> = vec!['0'; 5];\nlet bools: Vec<bool> = vec![false; 5];\n
// Using various basic data types to initialize arrays\nint numbers[10];\nfloat decimals[10];\nchar characters[10];\nbool bools[10];\n
// Using various basic data types to initialize arrays\nvar numbers: [5]i32 = undefined;\nvar decimals: [5]f32 = undefined;\nvar characters: [5]u8 = undefined;\nvar bools: [5]bool = undefined;\n
"},{"location":"chapter_data_structure/character_encoding/","title":"3.4 \u00a0 Character Encoding *","text":"In computers, all data is stored in binary form, and the character char
is no exception. To represent characters, we need to establish a \"character set\" that defines a one-to-one correspondence between each character and binary numbers. With a character set, computers can convert binary numbers to characters by looking up a table.
The \"ASCII code\" is one of the earliest character sets, officially known as the American Standard Code for Information Interchange. It uses 7 binary digits (the lower 7 bits of a byte) to represent a character, allowing for a maximum of 128 different characters. As shown in the Figure 3-6 , ASCII includes uppercase and lowercase English letters, numbers 0 ~ 9, some punctuation marks, and some control characters (such as newline and tab).
Figure 3-6 \u00a0 ASCII Code
However, ASCII can only represent English characters. With the globalization of computers, a character set called \"EASCII\" was developed to represent more languages. It expands on the 7-bit basis of ASCII to 8 bits, enabling the representation of 256 different characters.
Globally, a series of EASCII character sets for different regions emerged. The first 128 characters of these sets are uniformly ASCII, while the remaining 128 characters are defined differently to cater to various language requirements.
"},{"location":"chapter_data_structure/character_encoding/#342-gbk-character-set","title":"3.4.2 \u00a0 GBK Character Set","text":"Later, it was found that EASCII still could not meet the character requirements of many languages. For instance, there are nearly a hundred thousand Chinese characters, with several thousand used in everyday life. In 1980, China's National Standards Bureau released the \"GB2312\" character set, which included 6763 Chinese characters, essentially meeting the computer processing needs for Chinese.
However, GB2312 could not handle some rare and traditional characters. The \"GBK\" character set, an expansion of GB2312, includes a total of 21886 Chinese characters. In the GBK encoding scheme, ASCII characters are represented with one byte, while Chinese characters use two bytes.
"},{"location":"chapter_data_structure/character_encoding/#343-unicode-character-set","title":"3.4.3 \u00a0 Unicode Character Set","text":"With the rapid development of computer technology and a plethora of character sets and encoding standards, numerous problems arose. On one hand, these character sets generally only defined characters for specific languages and could not function properly in multilingual environments. On the other hand, the existence of multiple character set standards for the same language caused garbled text when information was exchanged between computers using different encoding standards.
Researchers of that era thought: What if we introduced a comprehensive character set that included all languages and symbols worldwide, wouldn't that solve the problems of cross-language environments and garbled text? Driven by this idea, the extensive character set, Unicode, was born.
The Chinese name for \"Unicode\" is \"\u7edf\u4e00\u7801\" (Unified Code), theoretically capable of accommodating over a million characters. It aims to incorporate characters from all over the world into a single set, providing a universal character set for processing and displaying various languages and reducing the issues of garbled text due to different encoding standards.
Since its release in 1991, Unicode has continually expanded to include new languages and characters. As of September 2022, Unicode contains 149,186 characters, including characters, symbols, and even emojis from various languages. In the vast Unicode character set, commonly used characters occupy 2 bytes, while some rare characters take up 3 or even 4 bytes.
Unicode is a universal character set that assigns a number (called a \"code point\") to each character, but it does not specify how these character code points should be stored in a computer. One might ask: When Unicode code points of varying lengths appear in a text, how does the system parse the characters? For example, given a 2-byte code, how does the system determine if it represents a single 2-byte character or two 1-byte characters?
A straightforward solution to this problem is to store all characters as equal-length encodings. As shown in the Figure 3-7 , each character in \"Hello\" occupies 1 byte, while each character in \"\u7b97\u6cd5\" (algorithm) occupies 2 bytes. We could encode all characters in \"Hello \u7b97\u6cd5\" as 2 bytes by padding the higher bits with zeros. This way, the system can parse a character every 2 bytes, recovering the content of the phrase.
Figure 3-7 \u00a0 Unicode Encoding Example
However, as ASCII has shown us, encoding English only requires 1 byte. Using the above approach would double the space occupied by English text compared to ASCII encoding, which is a waste of memory space. Therefore, a more efficient Unicode encoding method is needed.
"},{"location":"chapter_data_structure/character_encoding/#344-utf-8-encoding","title":"3.4.4 \u00a0 UTF-8 Encoding","text":"Currently, UTF-8 has become the most widely used Unicode encoding method internationally. It is a variable-length encoding, using 1 to 4 bytes to represent a character, depending on the complexity of the character. ASCII characters need only 1 byte, Latin and Greek letters require 2 bytes, commonly used Chinese characters need 3 bytes, and some other rare characters need 4 bytes.
The encoding rules for UTF-8 are not complex and can be divided into two cases:
The Figure 3-8 shows the UTF-8 encoding for \"Hello\u7b97\u6cd5\". It can be observed that since the highest \\(n\\) bits are set to \\(1\\), the system can determine the length of the character as \\(n\\) by counting the number of highest bits set to \\(1\\).
But why set the highest 2 bits of the remaining bytes to \\(10\\)? Actually, this \\(10\\) serves as a kind of checksum. If the system starts parsing text from an incorrect byte, the \\(10\\) at the beginning of the byte can help the system quickly detect an anomaly.
The reason for using \\(10\\) as a checksum is that, under UTF-8 encoding rules, it's impossible for the highest two bits of a character to be \\(10\\). This can be proven by contradiction: If the highest two bits of a character are \\(10\\), it indicates that the character's length is \\(1\\), corresponding to ASCII. However, the highest bit of an ASCII character should be \\(0\\), contradicting the assumption.
Figure 3-8 \u00a0 UTF-8 Encoding Example
Apart from UTF-8, other common encoding methods include:
From the perspective of storage space, UTF-8 is highly efficient for representing English characters, requiring only 1 byte; UTF-16 might be more efficient for encoding some non-English characters (like Chinese), as it requires only 2 bytes, while UTF-8 might need 3 bytes.
From a compatibility standpoint, UTF-8 is the most versatile, with many tools and libraries supporting UTF-8 as a priority.
"},{"location":"chapter_data_structure/character_encoding/#345-character-encoding-in-programming-languages","title":"3.4.5 \u00a0 Character Encoding in Programming Languages","text":"In many classic programming languages, strings during program execution are encoded using fixed-length encodings like UTF-16 or UTF-32. This allows strings to be treated as arrays, offering several advantages:
The design of character encoding schemes in programming languages is an interesting topic involving various factors:
String
type uses UTF-16 encoding, with each character occupying 2 bytes. This was based on the initial belief that 16 bits were sufficient to represent all possible characters, a judgment later proven incorrect. As the Unicode standard expanded beyond 16 bits, characters in Java may now be represented by a pair of 16-bit values, known as \u201csurrogate pairs.\u201dDue to the underestimation of character counts, these languages had to resort to using \"surrogate pairs\" to represent Unicode characters exceeding 16 bits. This approach has its drawbacks: strings containing surrogate pairs may have characters occupying 2 or 4 bytes, losing the advantage of fixed-length encoding, and handling surrogate pairs adds to the complexity and debugging difficulty of programming.
Owing to these reasons, some programming languages have adopted different encoding schemes:
str
type uses Unicode encoding with a flexible representation where the storage length of characters depends on the largest Unicode code point in the string. If all characters are ASCII, each character occupies 1 byte; if characters exceed ASCII but are within the Basic Multilingual Plane (BMP), each occupies 2 bytes; if characters exceed the BMP, each occupies 4 bytes.string
type internally uses UTF-8 encoding. Go also provides the rune
type for representing individual Unicode code points.str
and String
types use UTF-8 encoding internally. Rust also offers the char
type for individual Unicode code points.It\u2019s important to note that the above discussion pertains to how strings are stored in programming languages, which is a different issue from how strings are stored in files or transmitted over networks. For file storage or network transmission, strings are usually encoded in UTF-8 format for optimal compatibility and space efficiency.
"},{"location":"chapter_data_structure/classification_of_data_structure/","title":"3.1 \u00a0 Classification of Data Structures","text":"Common data structures include arrays, linked lists, stacks, queues, hash tables, trees, heaps, and graphs. They can be classified into \"logical structure\" and \"physical structure\".
"},{"location":"chapter_data_structure/classification_of_data_structure/#311-logical-structure-linear-and-non-linear","title":"3.1.1 \u00a0 Logical Structure: Linear and Non-Linear","text":"The logical structures reveal the logical relationships between data elements. In arrays and linked lists, data are arranged in a specific sequence, demonstrating the linear relationship between data; while in trees, data are arranged hierarchically from the top down, showing the derived relationship between \"ancestors\" and \"descendants\"; and graphs are composed of nodes and edges, reflecting the intricate network relationship.
As shown in the Figure 3-1 , logical structures can be divided into two major categories: \"linear\" and \"non-linear\". Linear structures are more intuitive, indicating data is arranged linearly in logical relationships; non-linear structures, conversely, are arranged non-linearly.
Figure 3-1 \u00a0 Linear and Non-Linear Data Structures
Non-linear data structures can be further divided into tree structures and network structures.
During the execution of an algorithm, the data being processed is stored in memory. The Figure 3-2 shows a computer memory stick where each black square is a physical memory space. We can think of memory as a vast Excel spreadsheet, with each cell capable of storing a certain amount of data.
The system accesses the data at the target location by means of a memory address. As shown in the Figure 3-2 , the computer assigns a unique identifier to each cell in the table according to specific rules, ensuring that each memory space has a unique memory address. With these addresses, the program can access the data stored in memory.
Figure 3-2 \u00a0 Memory Stick, Memory Spaces, Memory Addresses
Tip
It's worth noting that comparing memory to an Excel spreadsheet is a simplified analogy. The actual working mechanism of memory is more complex, involving concepts like address space, memory management, cache mechanisms, virtual memory, and physical memory.
Memory is a shared resource for all programs. When a block of memory is occupied by one program, it cannot be simultaneously used by other programs. Therefore, considering memory resources is crucial in designing data structures and algorithms. For instance, the algorithm's peak memory usage should not exceed the remaining free memory of the system; if there is a lack of contiguous memory blocks, then the data structure chosen must be able to be stored in non-contiguous memory blocks.
As illustrated in the Figure 3-3 , the physical structure reflects the way data is stored in computer memory and it can be divided into contiguous space storage (arrays) and non-contiguous space storage (linked lists). The two types of physical structures exhibit complementary characteristics in terms of time efficiency and space efficiency.
Figure 3-3 \u00a0 Contiguous Space Storage and Dispersed Space Storage
It is worth noting that all data structures are implemented based on arrays, linked lists, or a combination of both. For example, stacks and queues can be implemented using either arrays or linked lists; while implementations of hash tables may involve both arrays and linked lists. - Array-based implementations: Stacks, Queues, Hash Tables, Trees, Heaps, Graphs, Matrices, Tensors (arrays with dimensions \\(\\geq 3\\)). - Linked-list-based implementations: Stacks, Queues, Hash Tables, Trees, Heaps, Graphs, etc.
Data structures implemented based on arrays are also called \u201cStatic Data Structures,\u201d meaning their length cannot be changed after initialization. Conversely, those based on linked lists are called \u201cDynamic Data Structures,\u201d which can still adjust their size during program execution.
Tip
If you find it challenging to comprehend the physical structure, it is recommended that you read the next chapter, \"Arrays and Linked Lists,\" and revisit this section later.
"},{"location":"chapter_data_structure/number_encoding/","title":"3.3 \u00a0 Number Encoding *","text":"Note
In this book, chapters marked with an asterisk '*' are optional readings. If you are short on time or find them challenging, you may skip these initially and return to them after completing the essential chapters.
"},{"location":"chapter_data_structure/number_encoding/#331-integer-encoding","title":"3.3.1 \u00a0 Integer Encoding","text":"In the table from the previous section, we observed that all integer types can represent one more negative number than positive numbers, such as the byte
range of \\([-128, 127]\\). This phenomenon seems counterintuitive, and its underlying reason involves knowledge of sign-magnitude, one's complement, and two's complement encoding.
Firstly, it's important to note that numbers are stored in computers using the two's complement form. Before analyzing why this is the case, let's define these three encoding methods:
The following diagram illustrates the conversions among sign-magnitude, one's complement, and two's complement:
Figure 3-4 \u00a0 Conversions between Sign-Magnitude, One's Complement, and Two's Complement
Although sign-magnitude is the most intuitive, it has limitations. For one, negative numbers in sign-magnitude cannot be directly used in calculations. For example, in sign-magnitude, calculating \\(1 + (-2)\\) results in \\(-3\\), which is incorrect.
\\[ \\begin{aligned} & 1 + (-2) \\newline & \\rightarrow 0000 \\; 0001 + 1000 \\; 0010 \\newline & = 1000 \\; 0011 \\newline & \\rightarrow -3 \\end{aligned} \\]To address this, computers introduced the one's complement. If we convert to one's complement and calculate \\(1 + (-2)\\), then convert the result back to sign-magnitude, we get the correct result of \\(-1\\).
\\[ \\begin{aligned} & 1 + (-2) \\newline & \\rightarrow 0000 \\; 0001 \\; \\text{(Sign-magnitude)} + 1000 \\; 0010 \\; \\text{(Sign-magnitude)} \\newline & = 0000 \\; 0001 \\; \\text{(One's complement)} + 1111 \\; 1101 \\; \\text{(One's complement)} \\newline & = 1111 \\; 1110 \\; \\text{(One's complement)} \\newline & = 1000 \\; 0001 \\; \\text{(Sign-magnitude)} \\newline & \\rightarrow -1 \\end{aligned} \\]Additionally, there are two representations of zero in sign-magnitude: \\(+0\\) and \\(-0\\). This means two different binary encodings for zero, which could lead to ambiguity. For example, in conditional checks, not differentiating between positive and negative zero might result in incorrect outcomes. Addressing this ambiguity would require additional checks, potentially reducing computational efficiency.
\\[ \\begin{aligned} +0 & \\rightarrow 0000 \\; 0000 \\newline -0 & \\rightarrow 1000 \\; 0000 \\end{aligned} \\]Like sign-magnitude, one's complement also suffers from the positive and negative zero ambiguity. Therefore, computers further introduced the two's complement. Let's observe the conversion process for negative zero in sign-magnitude, one's complement, and two's complement:
\\[ \\begin{aligned} -0 \\rightarrow \\; & 1000 \\; 0000 \\; \\text{(Sign-magnitude)} \\newline = \\; & 1111 \\; 1111 \\; \\text{(One's complement)} \\newline = 1 \\; & 0000 \\; 0000 \\; \\text{(Two's complement)} \\newline \\end{aligned} \\]Adding \\(1\\) to the one's complement of negative zero produces a carry, but with byte
length being only 8 bits, the carried-over \\(1\\) to the 9th bit is discarded. Therefore, the two's complement of negative zero is \\(0000 \\; 0000\\), the same as positive zero, thus resolving the ambiguity.
One last puzzle is the \\([-128, 127]\\) range for byte
, with an additional negative number, \\(-128\\). We observe that for the interval \\([-127, +127]\\), all integers have corresponding sign-magnitude, one's complement, and two's complement, allowing for mutual conversion between them.
However, the two's complement \\(1000 \\; 0000\\) is an exception without a corresponding sign-magnitude. According to the conversion method, its sign-magnitude would be \\(0000 \\; 0000\\), indicating zero. This presents a contradiction because its two's complement should represent itself. Computers designate this special two's complement \\(1000 \\; 0000\\) as representing \\(-128\\). In fact, the calculation of \\((-1) + (-127)\\) in two's complement results in \\(-128\\).
\\[ \\begin{aligned} & (-127) + (-1) \\newline & \\rightarrow 1111 \\; 1111 \\; \\text{(Sign-magnitude)} + 1000 \\; 0001 \\; \\text{(Sign-magnitude)} \\newline & = 1000 \\; 0000 \\; \\text{(One's complement)} + 1111 \\; 1110 \\; \\text{(One's complement)} \\newline & = 1000 \\; 0001 \\; \\text{(Two's complement)} + 1111 \\; 1111 \\; \\text{(Two's complement)} \\newline & = 1000 \\; 0000 \\; \\text{(Two's complement)} \\newline & \\rightarrow -128 \\end{aligned} \\]As you might have noticed, all these calculations are additions, hinting at an important fact: computers' internal hardware circuits are primarily designed around addition operations. This is because addition is simpler to implement in hardware compared to other operations like multiplication, division, and subtraction, allowing for easier parallelization and faster computation.
It's important to note that this doesn't mean computers can only perform addition. By combining addition with basic logical operations, computers can execute a variety of other mathematical operations. For example, the subtraction \\(a - b\\) can be translated into \\(a + (-b)\\); multiplication and division can be translated into multiple additions or subtractions.
We can now summarize the reason for using two's complement in computers: with two's complement representation, computers can use the same circuits and operations to handle both positive and negative number addition, eliminating the need for special hardware circuits for subtraction and avoiding the ambiguity of positive and negative zero. This greatly simplifies hardware design and enhances computational efficiency.
The design of two's complement is quite ingenious, and due to space constraints, we'll stop here. Interested readers are encouraged to explore further.
"},{"location":"chapter_data_structure/number_encoding/#332-floating-point-number-encoding","title":"3.3.2 \u00a0 Floating-Point Number Encoding","text":"You might have noticed something intriguing: despite having the same length of 4 bytes, why does a float
have a much larger range of values compared to an int
? This seems counterintuitive, as one would expect the range to shrink for float
since it needs to represent fractions.
In fact, this is due to the different representation method used by floating-point numbers (float
). Let's consider a 32-bit binary number as:
According to the IEEE 754 standard, a 32-bit float
consists of the following three parts:
The value of a binary float
number is calculated as:
Converted to a decimal formula, this becomes:
\\[ \\text{val} = (-1)^{\\mathrm{S}} \\times 2^{\\mathrm{E} - 127} \\times (1 + \\mathrm{N}) \\]The range of each component is:
\\[ \\begin{aligned} \\mathrm{S} \\in & \\{ 0, 1\\}, \\quad \\mathrm{E} \\in \\{ 1, 2, \\dots, 254 \\} \\newline (1 + \\mathrm{N}) = & (1 + \\sum_{i=1}^{23} b_{23-i} \\times 2^{-i}) \\subset [1, 2 - 2^{-23}] \\end{aligned} \\]Figure 3-5 \u00a0 Example Calculation of a float in IEEE 754 Standard
Observing the diagram, given an example data \\(\\mathrm{S} = 0\\), \\(\\mathrm{E} = 124\\), \\(\\mathrm{N} = 2^{-2} + 2^{-3} = 0.375\\), we have:
\\[ \\text{val} = (-1)^0 \\times 2^{124 - 127} \\times (1 + 0.375) = 0.171875 \\]Now we can answer the initial question: The representation of float
includes an exponent bit, leading to a much larger range than int
. Based on the above calculation, the maximum positive number representable by float
is approximately \\(2^{254 - 127} \\times (2 - 2^{-23}) \\approx 3.4 \\times 10^{38}\\), and the minimum negative number is obtained by switching the sign bit.
However, the trade-off for float
's expanded range is a sacrifice in precision. The integer type int
uses all 32 bits to represent the number, with values evenly distributed; but due to the exponent bit, the larger the value of a float
, the greater the difference between adjacent numbers.
As shown in the Table 3-2 , exponent bits \\(E = 0\\) and \\(E = 255\\) have special meanings, used to represent zero, infinity, \\(\\mathrm{NaN}\\), etc.
Table 3-2 \u00a0 Meaning of Exponent Bits
Exponent Bit E Fraction Bit \\(\\mathrm{N} = 0\\) Fraction Bit \\(\\mathrm{N} \\ne 0\\) Calculation Formula \\(0\\) \\(\\pm 0\\) Subnormal Numbers \\((-1)^{\\mathrm{S}} \\times 2^{-126} \\times (0.\\mathrm{N})\\) \\(1, 2, \\dots, 254\\) Normal Numbers Normal Numbers \\((-1)^{\\mathrm{S}} \\times 2^{(\\mathrm{E} -127)} \\times (1.\\mathrm{N})\\) \\(255\\) \\(\\pm \\infty\\) \\(\\mathrm{NaN}\\)It's worth noting that subnormal numbers significantly improve the precision of floating-point numbers. The smallest positive normal number is \\(2^{-126}\\), and the smallest positive subnormal number is \\(2^{-126} \\times 2^{-23}\\).
Double-precision double
also uses a similar representation method to float
, which is not elaborated here for brevity.
byte
, short
, int
, long
), floating-point numbers (float
, double
), characters (char
), and booleans (boolean
). Their range depends on the size of the space occupied and the representation method.Q: Why does a hash table contain both linear and non-linear data structures?
The underlying structure of a hash table is an array. To resolve hash collisions, we may use \"chaining\": each bucket in the array points to a linked list, which, when exceeding a certain threshold, might be transformed into a tree (usually a red-black tree). From a storage perspective, the foundation of a hash table is an array, where each bucket slot might contain a value, a linked list, or a tree. Therefore, hash tables may contain both linear data structures (arrays, linked lists) and non-linear data structures (trees).
Q: Is the length of the char
type 1 byte?
The length of the char
type is determined by the encoding method used by the programming language. For example, Java, JavaScript, TypeScript, and C# all use UTF-16 encoding (to save Unicode code points), so the length of the char type is 2 bytes.
Q: Is there ambiguity in calling data structures based on arrays \"static data structures\"? Because operations like push and pop on stacks are \"dynamic\".
While stacks indeed allow for dynamic data operations, the data structure itself remains \"static\" (with unchangeable length). Even though data structures based on arrays can dynamically add or remove elements, their capacity is fixed. If the data volume exceeds the pre-allocated size, a new, larger array needs to be created, and the contents of the old array copied into it.
Q: When building stacks (queues) without specifying their size, why are they considered \"static data structures\"?
In high-level programming languages, we don't need to manually specify the initial capacity of stacks (queues); this task is automatically handled internally by the class. For example, the initial capacity of Java's ArrayList is usually 10. Furthermore, the expansion operation is also implemented automatically. See the subsequent \"List\" chapter for details.
"},{"location":"chapter_introduction/","title":"Chapter 1. \u00a0 Introduction to Algorithms","text":"Abstract
A graceful maiden dances, intertwined with the data, her skirt swaying to the melody of algorithms.
She invites you to a dance, follow her steps, and enter the world of algorithms full of logic and beauty.
"},{"location":"chapter_introduction/#chapter-contents","title":"Chapter Contents","text":"When we hear the word \"algorithm,\" we naturally think of mathematics. However, many algorithms do not involve complex mathematics but rely more on basic logic, which can be seen everywhere in our daily lives.
Before formally discussing algorithms, there's an interesting fact worth sharing: you have already unconsciously learned many algorithms and have become accustomed to applying them in your daily life. Here, I will give a few specific examples to prove this point.
Example 1: Looking Up a Dictionary. In an English dictionary, words are listed alphabetically. Suppose we're searching for a word that starts with the letter \\(r\\). This is typically done in the following way:
1.
and 2.
until you find the page where the word starts with \\(r\\).Figure 1-1 \u00a0 Process of Looking Up a Dictionary
This essential skill for elementary students, looking up a dictionary, is actually the famous \"Binary Search\" algorithm. From a data structure perspective, we can consider the dictionary as a sorted \"array\"; from an algorithmic perspective, the series of actions taken to look up a word in the dictionary can be viewed as \"Binary Search.\"
Example 2: Organizing Playing Cards. When playing cards, we need to arrange the cards in our hand in ascending order, as shown in the following process.
2.
until all cards are in order.Figure 1-2 \u00a0 Playing Cards Sorting Process
The above method of organizing playing cards is essentially the \"Insertion Sort\" algorithm, which is very efficient for small datasets. Many programming languages' sorting functions include the insertion sort.
Example 3: Making Change. Suppose we buy goods worth \\(69\\) yuan at a supermarket and give the cashier \\(100\\) yuan, then the cashier needs to give us \\(31\\) yuan in change. They would naturally complete the thought process as shown below.
Figure 1-3 \u00a0 Change making process
In the above steps, we make the best choice at each step (using the largest denomination possible), ultimately resulting in a feasible change-making plan. From the perspective of data structures and algorithms, this method is essentially a \"Greedy\" algorithm.
From cooking a meal to interstellar travel, almost all problem-solving involves algorithms. The advent of computers allows us to store data structures in memory and write code to call the CPU and GPU to execute algorithms. In this way, we can transfer real-life problems to computers, solving various complex issues more efficiently.
Tip
If concepts such as data structures, algorithms, arrays, and binary search still seem somewhat obsecure, I encourage you to continue reading. This book will gently guide you into the realm of understanding data structures and algorithms.
"},{"location":"chapter_introduction/summary/","title":"1.3 \u00a0 Summary","text":"An \"algorithm\" is a set of instructions or steps to solve a specific problem within a finite amount of time. It has the following characteristics:
A \"data structure\" is a way of organizing and storing data in a computer, with the following design goals:
Designing data structures is a balancing act, often requiring trade-offs. If you want to improve in one aspect, you often need to compromise in another. Here are two examples:
As shown in the Figure 1-4 , data structures and algorithms are highly related and closely integrated, specifically in the following three aspects:
Figure 1-4 \u00a0 Relationship between data structures and algorithms
Data structures and algorithms can be likened to a set of building blocks, as illustrated in the Figure 1-5 . A building block set includes numerous pieces, accompanied by detailed assembly instructions. Following these instructions step by step allows us to construct an intricate block model.
Figure 1-5 \u00a0 Assembling blocks
The detailed correspondence between the two is shown in the Table 1-1 .
Table 1-1 \u00a0 Comparing Data Structures and Algorithms to Building Blocks
Data Structures and Algorithms Building Blocks Input data Unassembled blocks Data structure Organization of blocks, including shape, size, connections, etc Algorithm A series of steps to assemble the blocks into the desired shape Output data Completed Block modelIt's worth noting that data structures and algorithms are independent of programming languages. For this reason, this book is able to provide implementations in multiple programming languages.
Conventional Abbreviation
In real-life discussions, we often refer to \"Data Structures and Algorithms\" simply as \"Algorithms\". For example, the well-known LeetCode algorithm problems actually test both data structure and algorithm knowledge.
"},{"location":"chapter_preface/","title":"Chapter 0. \u00a0 Preface","text":"Abstract
Algorithms are like a beautiful symphony, with each line of code flowing like a rhythm.
May this book ring softly in your mind, leaving a unique and profound melody.
"},{"location":"chapter_preface/#chapter-contents","title":"Chapter Contents","text":"This open-source project aims to create a free, and beginner-friendly crash course on data structures and algorithms.
If you are new to algorithms with limited exposure, or you have accumulated some experience in algorithms, but you only have a vague understanding of data structures and algorithms, and you are constantly jumping between \"yep\" and \"hmm\", then this book is for you!
If you have already accumulated a certain amount of problem-solving experience, and are familiar with most types of problems, then this book can help you review and organize your algorithm knowledge system. The repository's source code can be used as a \"problem-solving toolkit\" or an \"algorithm cheat sheet\".
If you are an algorithm expert, we look forward to receiving your valuable suggestions, or join us and collaborate.
Prerequisites
You should know how to write and read simple code in at least one programming language.
"},{"location":"chapter_preface/about_the_book/#012-content-structure","title":"0.1.2 \u00a0 Content Structure","text":"The main content of the book is shown in the following figure.
Figure 0-1 \u00a0 Main Content of the Book
"},{"location":"chapter_preface/about_the_book/#013-acknowledgements","title":"0.1.3 \u00a0 Acknowledgements","text":"This book is continuously improved with the joint efforts of many contributors from the open-source community. Thanks to each writer who invested their time and energy, listed in the order generated by GitHub: krahets, codingonion, nuomi1, Gonglja, Reanon, justin-tse, danielsss, hpstory, S-N-O-R-L-A-X, night-cruise, msk397, gvenusleo, RiverTwilight, gyt95, zhuoqinyue, Zuoxun, Xia-Sang, mingXta, FangYuan33, GN-Yu, IsChristina, xBLACKICEx, guowei-gong, Cathay-Chen, mgisr, JoseHung, qualifier1024, pengchzn, Guanngxu, longsizhuo, L-Super, what-is-me, yuan0221, lhxsm, Slone123c, WSL0809, longranger2, theNefelibatas, xiongsp, JeffersonHuang, hongyun-robot, K3v123, yuelinxin, a16su, gaofer, malone6, Wonderdch, xjr7670, DullSword, Horbin-Magician, NI-SW, reeswell, XC-Zero, XiaChuerwu, yd-j, iron-irax, huawuque404, MolDuM, Nigh, KorsChen, foursevenlove, 52coder, bubble9um, youshaoXG, curly210102, gltianwen, fanchenggang, Transmigration-zhou, FloranceYeh, FreddieLi, ShiMaRing, lipusheng, Javesun99, JackYang-hellobobo, shanghai-Jerry, 0130w, Keynman, psychelzh, logan-qiu, ZnYang2018, MwumLi, 1ch0, Phoenix0415, qingpeng9802, Richard-Zhang1019, QiLOL, Suremotoo, Turing-1024-Lee, Evilrabbit520, GaochaoZhu, ZJKung, linzeyan, hezhizhen, ZongYangL, beintentional, czruby, coderlef, dshlstarr, szu17dmy, fbigm, gledfish, hts0000, boloboloda, iStig, jiaxianhua, wenjianmin, keshida, kilikilikid, lclc6, lwbaptx, liuxjerry, lucaswangdev, lyl625760, chadyi, noobcodemaker, selear, siqyka, syd168, 4yDX3906, tao363, wangwang105, weibk, yabo083, yi427, yishangzhang, zhouLion, baagod, ElaBosak233, xb534, luluxia, yanedie, thomasq0, YangXuanyi and th1nk3r-ing.
The code review work for this book was completed by codingonion, Gonglja, gvenusleo, hpstory, justin\u2010tse, krahets, night-cruise, nuomi1, and Reanon (listed in alphabetical order). Thanks to them for their time and effort, ensuring the standardization and uniformity of the code in various languages.
Throughout the creation of this book, numerous individuals provided invaluable assistance, including but not limited to:
Throughout the writing journey, I delved into numerous textbooks and articles on data structures and algorithms. These works served as exemplary models, ensuring the accuracy and quality of this book's content. I extend my gratitude to all who preceded me for their invaluable contributions!
This book advocates a combination of hands-on and minds-on learning, inspired in this regard by \"Dive into Deep Learning\". I highly recommend this excellent book to all readers.
Heartfelt thanks to my parents, whose ongoing support and encouragement have allowed me to do this interesting work.
"},{"location":"chapter_preface/suggestions/","title":"0.2 \u00a0 How to Read","text":"Tip
For the best reading experience, it is recommended that you read through this section.
"},{"location":"chapter_preface/suggestions/#021-writing-conventions","title":"0.2.1 \u00a0 Writing Conventions","text":"\"\"\"Header comments for labeling functions, classes, test samples, etc\"\"\"\"\n\n# Comments for explaining details\n\n\"\"\"\nMultiline\ncomments\n\"\"\"\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
/* Header comments for labeling functions, classes, test samples, etc */\n\n// Comments for explaining details.\n\n/**\n * Multiline\n * comments\n */\n
// Header comments for labeling functions, classes, test samples, etc\n\n// Comments for explaining details.\n\n// Multiline\n// comments\n
"},{"location":"chapter_preface/suggestions/#022-efficient-learning-via-animated-illustrations","title":"0.2.2 \u00a0 Efficient Learning via Animated Illustrations","text":"Compared with text, videos and pictures have a higher density of information and are more structured, making them easier to understand. In this book, key and difficult concepts are mainly presented through animations and illustrations, with text serving as explanations and supplements.
When encountering content with animations or illustrations as shown in the Figure 0-2 , prioritize understanding the figure, with text as supplementary, integrating both for a comprehensive understanding.
Figure 0-2 \u00a0 Animated Illustration Example
"},{"location":"chapter_preface/suggestions/#023-deepen-understanding-through-coding-practice","title":"0.2.3 \u00a0 Deepen Understanding through Coding Practice","text":"The source code of this book is hosted on the GitHub Repository. As shown in the Figure 0-3 , the source code comes with test examples and can be executed with just a single click.
If time permits, it's recommended to type out the code yourself. If pressed for time, at least read and run all the codes.
Compared to just reading code, writing code often yields more learning. Learning by doing is the real way to learn.
Figure 0-3 \u00a0 Running Code Example
Setting up to run the code involves three main steps.
Step 1: Install a local programming environment. Follow the tutorial in the appendix for installation, or skip this step if already installed.
Step 2: Clone or download the code repository. Visit the GitHub Repository.
If Git is installed, use the following command to clone the repository:
git clone https://github.com/krahets/hello-algo.git\n
Alternatively, you can also click the \"Download ZIP\" button at the location shown in the Figure 0-4 to directly download the code as a compressed ZIP file. Then, you can simply extract it locally.
Figure 0-4 \u00a0 Cloning Repository and Downloading Code
Step 3: Run the source code. As shown in the Figure 0-5 , for the code block labeled with the file name at the top, we can find the corresponding source code file in the codes
folder of the repository. These files can be executed with a single click, which will help you save unnecessary debugging time and allow you to focus on learning.
Figure 0-5 \u00a0 Code Block and Corresponding Source Code File
"},{"location":"chapter_preface/suggestions/#024-learning-together-in-discussion","title":"0.2.4 \u00a0 Learning Together in Discussion","text":"While reading this book, please don't skip over the points that you didn't learn. Feel free to post your questions in the comment section. We will be happy to answer them and can usually respond within two days.
As illustrated in the Figure 0-6 , each chapter features a comment section at the bottom. I encourage you to pay attention to these comments. They not only expose you to others' encountered problems, aiding in identifying knowledge gaps and sparking deeper contemplation, but also invite you to generously contribute by answering fellow readers' inquiries, sharing insights, and fostering mutual improvement.
Figure 0-6 \u00a0 Comment Section Example
"},{"location":"chapter_preface/suggestions/#025-algorithm-learning-path","title":"0.2.5 \u00a0 Algorithm Learning Path","text":"Overall, the journey of mastering data structures and algorithms can be divided into three stages:
As shown in the Figure 0-7 , this book mainly covers \u201cStage 1,\u201d aiming to help you more efficiently embark on Stages 2 and 3.
Figure 0-7 \u00a0 Algorithm Learning Path
"},{"location":"chapter_preface/summary/","title":"0.3 \u00a0 Summary","text":"Abstract
Stacks are like stacking cats, while queues are like cats lining up.
They respectively represent the logical relationships of Last-In-First-Out (LIFO) and First-In-First-Out (FIFO).
"},{"location":"chapter_stack_and_queue/#chapter-contents","title":"Chapter Contents","text":"In a regular queue, we can only delete elements from the head or add elements to the tail. As shown in the Figure 5-7 , a \"double-ended queue (deque)\" offers more flexibility, allowing the addition or removal of elements at both the head and the tail.
Figure 5-7 \u00a0 Operations in Double-Ended Queue
"},{"location":"chapter_stack_and_queue/deque/#531-common-operations-in-double-ended-queue","title":"5.3.1 \u00a0 Common Operations in Double-Ended Queue","text":"The common operations in a double-ended queue are listed below, and the specific method names depend on the programming language used.
Table 5-3 \u00a0 Efficiency of Double-Ended Queue Operations
Method Name Description Time ComplexitypushFirst()
Add an element to the front \\(O(1)\\) pushLast()
Add an element to the rear \\(O(1)\\) popFirst()
Remove the front element \\(O(1)\\) popLast()
Remove the rear element \\(O(1)\\) peekFirst()
Access the front element \\(O(1)\\) peekLast()
Access the rear element \\(O(1)\\) Similarly, we can directly use the double-ended queue classes implemented in programming languages:
PythonC++JavaC#GoSwiftJSTSDartRustCZig deque.pyfrom collections import deque\n\n# Initialize the deque\ndeque: deque[int] = deque()\n\n# Enqueue elements\ndeque.append(2) # Add to the rear\ndeque.append(5)\ndeque.append(4)\ndeque.appendleft(3) # Add to the front\ndeque.appendleft(1)\n\n# Access elements\nfront: int = deque[0] # Front element\nrear: int = deque[-1] # Rear element\n\n# Dequeue elements\npop_front: int = deque.popleft() # Front element dequeued\npop_rear: int = deque.pop() # Rear element dequeued\n\n# Get the length of the deque\nsize: int = len(deque)\n\n# Check if the deque is empty\nis_empty: bool = len(deque) == 0\n
deque.cpp/* Initialize the deque */\ndeque<int> deque;\n\n/* Enqueue elements */\ndeque.push_back(2); // Add to the rear\ndeque.push_back(5);\ndeque.push_back(4);\ndeque.push_front(3); // Add to the front\ndeque.push_front(1);\n\n/* Access elements */\nint front = deque.front(); // Front element\nint back = deque.back(); // Rear element\n\n/* Dequeue elements */\ndeque.pop_front(); // Front element dequeued\ndeque.pop_back(); // Rear element dequeued\n\n/* Get the length of the deque */\nint size = deque.size();\n\n/* Check if the deque is empty */\nbool empty = deque.empty();\n
deque.java/* Initialize the deque */\nDeque<Integer> deque = new LinkedList<>();\n\n/* Enqueue elements */\ndeque.offerLast(2); // Add to the rear\ndeque.offerLast(5);\ndeque.offerLast(4);\ndeque.offerFirst(3); // Add to the front\ndeque.offerFirst(1);\n\n/* Access elements */\nint peekFirst = deque.peekFirst(); // Front element\nint peekLast = deque.peekLast(); // Rear element\n\n/* Dequeue elements */\nint popFirst = deque.pollFirst(); // Front element dequeued\nint popLast = deque.pollLast(); // Rear element dequeued\n\n/* Get the length of the deque */\nint size = deque.size();\n\n/* Check if the deque is empty */\nboolean isEmpty = deque.isEmpty();\n
deque.cs/* Initialize the deque */\n// In C#, LinkedList is used as a deque\nLinkedList<int> deque = new();\n\n/* Enqueue elements */\ndeque.AddLast(2); // Add to the rear\ndeque.AddLast(5);\ndeque.AddLast(4);\ndeque.AddFirst(3); // Add to the front\ndeque.AddFirst(1);\n\n/* Access elements */\nint peekFirst = deque.First.Value; // Front element\nint peekLast = deque.Last.Value; // Rear element\n\n/* Dequeue elements */\ndeque.RemoveFirst(); // Front element dequeued\ndeque.RemoveLast(); // Rear element dequeued\n\n/* Get the length of the deque */\nint size = deque.Count;\n\n/* Check if the deque is empty */\nbool isEmpty = deque.Count == 0;\n
deque_test.go/* Initialize the deque */\n// In Go, use list as a deque\ndeque := list.New()\n\n/* Enqueue elements */\ndeque.PushBack(2) // Add to the rear\ndeque.PushBack(5)\ndeque.PushBack(4)\ndeque.PushFront(3) // Add to the front\ndeque.PushFront(1)\n\n/* Access elements */\nfront := deque.Front() // Front element\nrear := deque.Back() // Rear element\n\n/* Dequeue elements */\ndeque.Remove(front) // Front element dequeued\ndeque.Remove(rear) // Rear element dequeued\n\n/* Get the length of the deque */\nsize := deque.Len()\n\n/* Check if the deque is empty */\nisEmpty := deque.Len() == 0\n
deque.swift/* Initialize the deque */\n// Swift does not have a built-in deque class, so Array can be used as a deque\nvar deque: [Int] = []\n\n/* Enqueue elements */\ndeque.append(2) // Add to the rear\ndeque.append(5)\ndeque.append(4)\ndeque.insert(3, at: 0) // Add to the front\ndeque.insert(1, at: 0)\n\n/* Access elements */\nlet peekFirst = deque.first! // Front element\nlet peekLast = deque.last! // Rear element\n\n/* Dequeue elements */\n// Using Array, popFirst has a complexity of O(n)\nlet popFirst = deque.removeFirst() // Front element dequeued\nlet popLast = deque.removeLast() // Rear element dequeued\n\n/* Get the length of the deque */\nlet size = deque.count\n\n/* Check if the deque is empty */\nlet isEmpty = deque.isEmpty\n
deque.js/* Initialize the deque */\n// JavaScript does not have a built-in deque, so Array is used as a deque\nconst deque = [];\n\n/* Enqueue elements */\ndeque.push(2);\ndeque.push(5);\ndeque.push(4);\n// Note that unshift() has a time complexity of O(n) as it's an array\ndeque.unshift(3);\ndeque.unshift(1);\n\n/* Access elements */\nconst peekFirst = deque[0]; // Front element\nconst peekLast = deque[deque.length - 1]; // Rear element\n\n/* Dequeue elements */\n// Note that shift() has a time complexity of O(n) as it's an array\nconst popFront = deque.shift(); // Front element dequeued\nconst popBack = deque.pop(); // Rear element dequeued\n\n/* Get the length of the deque */\nconst size = deque.length;\n\n/* Check if the deque is empty */\nconst isEmpty = size === 0;\n
deque.ts/* Initialize the deque */\n// TypeScript does not have a built-in deque, so Array is used as a deque\nconst deque: number[] = [];\n\n/* Enqueue elements */\ndeque.push(2);\ndeque.push(5);\ndeque.push(4);\n// Note that unshift() has a time complexity of O(n) as it's an array\ndeque.unshift(3);\ndeque.unshift(1);\n\n/* Access elements */\nconst peekFirst: number = deque[0]; // Front element\nconst peekLast: number = deque[deque.length - 1]; // Rear element\n\n/* Dequeue elements */\n// Note that shift() has a time complexity of O(n) as it's an array\nconst popFront: number = deque.shift() as number; // Front element dequeued\nconst popBack: number = deque.pop() as number; // Rear element dequeued\n\n/* Get the length of the deque */\nconst size: number = deque.length;\n\n/* Check if the deque is empty */\nconst isEmpty: boolean = size === 0;\n
deque.dart/* Initialize the deque */\n// In Dart, Queue is defined as a deque\nQueue<int> deque = Queue<int>();\n\n/* Enqueue elements */\ndeque.addLast(2); // Add to the rear\ndeque.addLast(5);\ndeque.addLast(4);\ndeque.addFirst(3); // Add to the front\ndeque.addFirst(1);\n\n/* Access elements */\nint peekFirst = deque.first; // Front element\nint peekLast = deque.last; // Rear element\n\n/* Dequeue elements */\nint popFirst = deque.removeFirst(); // Front element dequeued\nint popLast = deque.removeLast(); // Rear element dequeued\n\n/* Get the length of the deque */\nint size = deque.length;\n\n/* Check if the deque is empty */\nbool isEmpty = deque.isEmpty;\n
deque.rs/* Initialize the deque */\nlet mut deque: VecDeque<u32> = VecDeque::new();\n\n/* Enqueue elements */\ndeque.push_back(2); // Add to the rear\ndeque.push_back(5);\ndeque.push_back(4);\ndeque.push_front(3); // Add to the front\ndeque.push_front(1);\n\n/* Access elements */\nif let Some(front) = deque.front() { // Front element\n}\nif let Some(rear) = deque.back() { // Rear element\n}\n\n/* Dequeue elements */\nif let Some(pop_front) = deque.pop_front() { // Front element dequeued\n}\nif let Some(pop_rear) = deque.pop_back() { // Rear element dequeued\n}\n\n/* Get the length of the deque */\nlet size = deque.len();\n\n/* Check if the deque is empty */\nlet is_empty = deque.is_empty();\n
deque.c// C does not provide a built-in deque\n
deque.zig\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/deque/#532-implementing-a-double-ended-queue","title":"5.3.2 \u00a0 Implementing a Double-Ended Queue *","text":"The implementation of a double-ended queue is similar to that of a regular queue, with the choice of either linked lists or arrays as the underlying data structure.
"},{"location":"chapter_stack_and_queue/deque/#1-implementation-based-on-doubly-linked-list","title":"1. \u00a0 Implementation Based on Doubly Linked List","text":"Recall from the previous section that we used a regular singly linked list to implement a queue, as it conveniently allows for deleting the head node (corresponding to dequeue operation) and adding new nodes after the tail node (corresponding to enqueue operation).
For a double-ended queue, both the head and the tail can perform enqueue and dequeue operations. In other words, a double-ended queue needs to implement another symmetric direction of operations. For this, we use a \"doubly linked list\" as the underlying data structure of the double-ended queue.
As shown in the Figure 5-8 , we treat the head and tail nodes of the doubly linked list as the front and rear of the double-ended queue, respectively, and implement the functionality to add and remove nodes at both ends.
LinkedListDequepushLast()pushFirst()popLast()popFirst()Figure 5-8 \u00a0 Implementing Double-Ended Queue with Doubly Linked List for Enqueue and Dequeue Operations
The implementation code is as follows:
PythonC++JavaC#GoSwiftJSTSDartRustCZig linkedlist_deque.pyclass ListNode:\n \"\"\"\u53cc\u5411\u94fe\u8868\u8282\u70b9\"\"\"\n\n def __init__(self, val: int):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self.val: int = val\n self.next: ListNode | None = None # \u540e\u7ee7\u8282\u70b9\u5f15\u7528\n self.prev: ListNode | None = None # \u524d\u9a71\u8282\u70b9\u5f15\u7528\n\nclass LinkedListDeque:\n \"\"\"\u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217\"\"\"\n\n def __init__(self):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._front: ListNode | None = None # \u5934\u8282\u70b9 front\n self._rear: ListNode | None = None # \u5c3e\u8282\u70b9 rear\n self._size: int = 0 # \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\"\"\"\n return self._size\n\n def is_empty(self) -> bool:\n \"\"\"\u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a\"\"\"\n return self.size() == 0\n\n def push(self, num: int, is_front: bool):\n \"\"\"\u5165\u961f\u64cd\u4f5c\"\"\"\n node = ListNode(num)\n # \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if self.is_empty():\n self._front = self._rear = node\n # \u961f\u9996\u5165\u961f\u64cd\u4f5c\n elif is_front:\n # \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n self._front.prev = node\n node.next = self._front\n self._front = node # \u66f4\u65b0\u5934\u8282\u70b9\n # \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n else:\n # \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n self._rear.next = node\n node.prev = self._rear\n self._rear = node # \u66f4\u65b0\u5c3e\u8282\u70b9\n self._size += 1 # \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n\n def push_first(self, num: int):\n \"\"\"\u961f\u9996\u5165\u961f\"\"\"\n self.push(num, True)\n\n def push_last(self, num: int):\n \"\"\"\u961f\u5c3e\u5165\u961f\"\"\"\n self.push(num, False)\n\n def pop(self, is_front: bool) -> int:\n \"\"\"\u51fa\u961f\u64cd\u4f5c\"\"\"\n if self.is_empty():\n raise IndexError(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\")\n # \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if is_front:\n val: int = self._front.val # \u6682\u5b58\u5934\u8282\u70b9\u503c\n # \u5220\u9664\u5934\u8282\u70b9\n fnext: ListNode | None = self._front.next\n if fnext != None:\n fnext.prev = None\n self._front.next = None\n self._front = fnext # \u66f4\u65b0\u5934\u8282\u70b9\n # \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n else:\n val: int = self._rear.val # \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n # \u5220\u9664\u5c3e\u8282\u70b9\n rprev: ListNode | None = self._rear.prev\n if rprev != None:\n rprev.next = None\n self._rear.prev = None\n self._rear = rprev # \u66f4\u65b0\u5c3e\u8282\u70b9\n self._size -= 1 # \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val\n\n def pop_first(self) -> int:\n \"\"\"\u961f\u9996\u51fa\u961f\"\"\"\n return self.pop(True)\n\n def pop_last(self) -> int:\n \"\"\"\u961f\u5c3e\u51fa\u961f\"\"\"\n return self.pop(False)\n\n def peek_first(self) -> int:\n \"\"\"\u8bbf\u95ee\u961f\u9996\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\")\n return self._front.val\n\n def peek_last(self) -> int:\n \"\"\"\u8bbf\u95ee\u961f\u5c3e\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\")\n return self._rear.val\n\n def to_array(self) -> list[int]:\n \"\"\"\u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370\"\"\"\n node = self._front\n res = [0] * self.size()\n for i in range(self.size()):\n res[i] = node.val\n node = node.next\n return res\n
linkedlist_deque.cpp/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nstruct DoublyListNode {\n int val; // \u8282\u70b9\u503c\n DoublyListNode *next; // \u540e\u7ee7\u8282\u70b9\u6307\u9488\n DoublyListNode *prev; // \u524d\u9a71\u8282\u70b9\u6307\u9488\n DoublyListNode(int val) : val(val), prev(nullptr), next(nullptr) {\n }\n};\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n private:\n DoublyListNode *front, *rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear\n int queSize = 0; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n public:\n /* \u6784\u9020\u65b9\u6cd5 */\n LinkedListDeque() : front(nullptr), rear(nullptr) {\n }\n\n /* \u6790\u6784\u65b9\u6cd5 */\n ~LinkedListDeque() {\n // \u904d\u5386\u94fe\u8868\u5220\u9664\u8282\u70b9\uff0c\u91ca\u653e\u5185\u5b58\n DoublyListNode *pre, *cur = front;\n while (cur != nullptr) {\n pre = cur;\n cur = cur->next;\n delete pre;\n }\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n void push(int num, bool isFront) {\n DoublyListNode *node = new DoublyListNode(num);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (isEmpty())\n front = rear = node;\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n else if (isFront) {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n front->prev = node;\n node->next = front;\n front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n rear->next = node;\n node->prev = rear;\n rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize++; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n void pushFirst(int num) {\n push(num, true);\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n void pushLast(int num) {\n push(num, false);\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n int pop(bool isFront) {\n if (isEmpty())\n throw out_of_range(\"\u961f\u5217\u4e3a\u7a7a\");\n int val;\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if (isFront) {\n val = front->val; // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n DoublyListNode *fNext = front->next;\n if (fNext != nullptr) {\n fNext->prev = nullptr;\n front->next = nullptr;\n delete front;\n }\n front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n } else {\n val = rear->val; // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n DoublyListNode *rPrev = rear->prev;\n if (rPrev != nullptr) {\n rPrev->next = nullptr;\n rear->prev = nullptr;\n delete rear;\n }\n rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize--; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n }\n\n /* \u961f\u9996\u51fa\u961f */\n int popFirst() {\n return pop(true);\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n int popLast() {\n return pop(false);\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int peekFirst() {\n if (isEmpty())\n throw out_of_range(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\");\n return front->val;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n int peekLast() {\n if (isEmpty())\n throw out_of_range(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\");\n return rear->val;\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n vector<int> toVector() {\n DoublyListNode *node = front;\n vector<int> res(size());\n for (int i = 0; i < res.size(); i++) {\n res[i] = node->val;\n node = node->next;\n }\n return res;\n }\n};\n
linkedlist_deque.java/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode {\n int val; // \u8282\u70b9\u503c\n ListNode next; // \u540e\u7ee7\u8282\u70b9\u5f15\u7528\n ListNode prev; // \u524d\u9a71\u8282\u70b9\u5f15\u7528\n\n ListNode(int val) {\n this.val = val;\n prev = next = null;\n }\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n private ListNode front, rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear\n private int queSize = 0; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n public LinkedListDeque() {\n front = rear = null;\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n public int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public boolean isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n private void push(int num, boolean isFront) {\n ListNode node = new ListNode(num);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (isEmpty())\n front = rear = node;\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n else if (isFront) {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n front.prev = node;\n node.next = front;\n front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n rear.next = node;\n node.prev = rear;\n rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize++; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n public void pushFirst(int num) {\n push(num, true);\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n public void pushLast(int num) {\n push(num, false);\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n private int pop(boolean isFront) {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n int val;\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if (isFront) {\n val = front.val; // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n ListNode fNext = front.next;\n if (fNext != null) {\n fNext.prev = null;\n front.next = null;\n }\n front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n } else {\n val = rear.val; // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n ListNode rPrev = rear.prev;\n if (rPrev != null) {\n rPrev.next = null;\n rear.prev = null;\n }\n rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize--; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n }\n\n /* \u961f\u9996\u51fa\u961f */\n public int popFirst() {\n return pop(true);\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n public int popLast() {\n return pop(false);\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int peekFirst() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return front.val;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n public int peekLast() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return rear.val;\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n public int[] toArray() {\n ListNode node = front;\n int[] res = new int[size()];\n for (int i = 0; i < res.length; i++) {\n res[i] = node.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_deque.cs/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode(int val) {\n public int val = val; // \u8282\u70b9\u503c\n public ListNode? next = null; // \u540e\u7ee7\u8282\u70b9\u5f15\u7528\n public ListNode? prev = null; // \u524d\u9a71\u8282\u70b9\u5f15\u7528\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n ListNode? front, rear; // \u5934\u8282\u70b9 front, \u5c3e\u8282\u70b9 rear\n int queSize = 0; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n public LinkedListDeque() {\n front = null;\n rear = null;\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n public int Size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public bool IsEmpty() {\n return Size() == 0;\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n void Push(int num, bool isFront) {\n ListNode node = new(num);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (IsEmpty()) {\n front = node;\n rear = node;\n }\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n else if (isFront) {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n front!.prev = node;\n node.next = front;\n front = node; // \u66f4\u65b0\u5934\u8282\u70b9 \n }\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n rear!.next = node;\n node.prev = rear;\n rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n\n queSize++; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n public void PushFirst(int num) {\n Push(num, true);\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n public void PushLast(int num) {\n Push(num, false);\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n int? Pop(bool isFront) {\n if (IsEmpty())\n throw new Exception();\n int? val;\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if (isFront) {\n val = front?.val; // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n ListNode? fNext = front?.next;\n if (fNext != null) {\n fNext.prev = null;\n front!.next = null;\n }\n front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n }\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n else {\n val = rear?.val; // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n ListNode? rPrev = rear?.prev;\n if (rPrev != null) {\n rPrev.next = null;\n rear!.prev = null;\n }\n rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n\n queSize--; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n }\n\n /* \u961f\u9996\u51fa\u961f */\n public int? PopFirst() {\n return Pop(true);\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n public int? PopLast() {\n return Pop(false);\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int? PeekFirst() {\n if (IsEmpty())\n throw new Exception();\n return front?.val;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n public int? PeekLast() {\n if (IsEmpty())\n throw new Exception();\n return rear?.val;\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n public int?[] ToArray() {\n ListNode? node = front;\n int?[] res = new int?[Size()];\n for (int i = 0; i < res.Length; i++) {\n res[i] = node?.val;\n node = node?.next;\n }\n\n return res;\n }\n}\n
linkedlist_deque.go/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\ntype linkedListDeque struct {\n // \u4f7f\u7528\u5185\u7f6e\u5305 list\n data *list.List\n}\n\n/* \u521d\u59cb\u5316\u53cc\u7aef\u961f\u5217 */\nfunc newLinkedListDeque() *linkedListDeque {\n return &linkedListDeque{\n data: list.New(),\n }\n}\n\n/* \u961f\u9996\u5143\u7d20\u5165\u961f */\nfunc (s *linkedListDeque) pushFirst(value any) {\n s.data.PushFront(value)\n}\n\n/* \u961f\u5c3e\u5143\u7d20\u5165\u961f */\nfunc (s *linkedListDeque) pushLast(value any) {\n s.data.PushBack(value)\n}\n\n/* \u961f\u9996\u5143\u7d20\u51fa\u961f */\nfunc (s *linkedListDeque) popFirst() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Front()\n s.data.Remove(e)\n return e.Value\n}\n\n/* \u961f\u5c3e\u5143\u7d20\u51fa\u961f */\nfunc (s *linkedListDeque) popLast() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Back()\n s.data.Remove(e)\n return e.Value\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nfunc (s *linkedListDeque) peekFirst() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Front()\n return e.Value\n}\n\n/* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\nfunc (s *linkedListDeque) peekLast() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Back()\n return e.Value\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nfunc (s *linkedListDeque) size() int {\n return s.data.Len()\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nfunc (s *linkedListDeque) isEmpty() bool {\n return s.data.Len() == 0\n}\n\n/* \u83b7\u53d6 List \u7528\u4e8e\u6253\u5370 */\nfunc (s *linkedListDeque) toList() *list.List {\n return s.data\n}\n
linkedlist_deque.swift/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode {\n var val: Int // \u8282\u70b9\u503c\n var next: ListNode? // \u540e\u7ee7\u8282\u70b9\u5f15\u7528\n weak var prev: ListNode? // \u524d\u9a71\u8282\u70b9\u5f15\u7528\n\n init(val: Int) {\n self.val = val\n }\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n private var front: ListNode? // \u5934\u8282\u70b9 front\n private var rear: ListNode? // \u5c3e\u8282\u70b9 rear\n private var queSize: Int // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n init() {\n queSize = 0\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n func size() -> Int {\n queSize\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n func isEmpty() -> Bool {\n size() == 0\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n private func push(num: Int, isFront: Bool) {\n let node = ListNode(val: num)\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if isEmpty() {\n front = node\n rear = node\n }\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n else if isFront {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n front?.prev = node\n node.next = front\n front = node // \u66f4\u65b0\u5934\u8282\u70b9\n }\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n rear?.next = node\n node.prev = rear\n rear = node // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize += 1 // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n func pushFirst(num: Int) {\n push(num: num, isFront: true)\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n func pushLast(num: Int) {\n push(num: num, isFront: false)\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n private func pop(isFront: Bool) -> Int {\n if isEmpty() {\n fatalError(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\")\n }\n let val: Int\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if isFront {\n val = front!.val // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n let fNext = front?.next\n if fNext != nil {\n fNext?.prev = nil\n front?.next = nil\n }\n front = fNext // \u66f4\u65b0\u5934\u8282\u70b9\n }\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n else {\n val = rear!.val // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n let rPrev = rear?.prev\n if rPrev != nil {\n rPrev?.next = nil\n rear?.prev = nil\n }\n rear = rPrev // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n queSize -= 1 // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val\n }\n\n /* \u961f\u9996\u51fa\u961f */\n func popFirst() -> Int {\n pop(isFront: true)\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n func popLast() -> Int {\n pop(isFront: false)\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n func peekFirst() -> Int? {\n isEmpty() ? nil : front?.val\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n func peekLast() -> Int? {\n isEmpty() ? nil : rear?.val\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n func toArray() -> [Int] {\n var node = front\n var res = Array(repeating: 0, count: size())\n for i in res.indices {\n res[i] = node!.val\n node = node?.next\n }\n return res\n }\n}\n
linkedlist_deque.js/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode {\n prev; // \u524d\u9a71\u8282\u70b9\u5f15\u7528 (\u6307\u9488)\n next; // \u540e\u7ee7\u8282\u70b9\u5f15\u7528 (\u6307\u9488)\n val; // \u8282\u70b9\u503c\n\n constructor(val) {\n this.val = val;\n this.next = null;\n this.prev = null;\n }\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n #front; // \u5934\u8282\u70b9 front\n #rear; // \u5c3e\u8282\u70b9 rear\n #queSize; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n constructor() {\n this.#front = null;\n this.#rear = null;\n this.#queSize = 0;\n }\n\n /* \u961f\u5c3e\u5165\u961f\u64cd\u4f5c */\n pushLast(val) {\n const node = new ListNode(val);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (this.#queSize === 0) {\n this.#front = node;\n this.#rear = node;\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n this.#rear.next = node;\n node.prev = this.#rear;\n this.#rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n this.#queSize++;\n }\n\n /* \u961f\u9996\u5165\u961f\u64cd\u4f5c */\n pushFirst(val) {\n const node = new ListNode(val);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (this.#queSize === 0) {\n this.#front = node;\n this.#rear = node;\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n this.#front.prev = node;\n node.next = this.#front;\n this.#front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n }\n this.#queSize++;\n }\n\n /* \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c */\n popLast() {\n if (this.#queSize === 0) {\n return null;\n }\n const value = this.#rear.val; // \u5b58\u50a8\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n let temp = this.#rear.prev;\n if (temp !== null) {\n temp.next = null;\n this.#rear.prev = null;\n }\n this.#rear = temp; // \u66f4\u65b0\u5c3e\u8282\u70b9\n this.#queSize--;\n return value;\n }\n\n /* \u961f\u9996\u51fa\u961f\u64cd\u4f5c */\n popFirst() {\n if (this.#queSize === 0) {\n return null;\n }\n const value = this.#front.val; // \u5b58\u50a8\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n let temp = this.#front.next;\n if (temp !== null) {\n temp.prev = null;\n this.#front.next = null;\n }\n this.#front = temp; // \u66f4\u65b0\u5934\u8282\u70b9\n this.#queSize--;\n return value;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n peekLast() {\n return this.#queSize === 0 ? null : this.#rear.val;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peekFirst() {\n return this.#queSize === 0 ? null : this.#front.val;\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n size() {\n return this.#queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty() {\n return this.#queSize === 0;\n }\n\n /* \u6253\u5370\u53cc\u5411\u961f\u5217 */\n print() {\n const arr = [];\n let temp = this.#front;\n while (temp !== null) {\n arr.push(temp.val);\n temp = temp.next;\n }\n console.log('[' + arr.join(', ') + ']');\n }\n}\n
linkedlist_deque.ts/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode {\n prev: ListNode; // \u524d\u9a71\u8282\u70b9\u5f15\u7528 (\u6307\u9488)\n next: ListNode; // \u540e\u7ee7\u8282\u70b9\u5f15\u7528 (\u6307\u9488)\n val: number; // \u8282\u70b9\u503c\n\n constructor(val: number) {\n this.val = val;\n this.next = null;\n this.prev = null;\n }\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\nclass LinkedListDeque {\n private front: ListNode; // \u5934\u8282\u70b9 front\n private rear: ListNode; // \u5c3e\u8282\u70b9 rear\n private queSize: number; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n constructor() {\n this.front = null;\n this.rear = null;\n this.queSize = 0;\n }\n\n /* \u961f\u5c3e\u5165\u961f\u64cd\u4f5c */\n pushLast(val: number): void {\n const node: ListNode = new ListNode(val);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (this.queSize === 0) {\n this.front = node;\n this.rear = node;\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n this.rear.next = node;\n node.prev = this.rear;\n this.rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n this.queSize++;\n }\n\n /* \u961f\u9996\u5165\u961f\u64cd\u4f5c */\n pushFirst(val: number): void {\n const node: ListNode = new ListNode(val);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (this.queSize === 0) {\n this.front = node;\n this.rear = node;\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n this.front.prev = node;\n node.next = this.front;\n this.front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n }\n this.queSize++;\n }\n\n /* \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c */\n popLast(): number {\n if (this.queSize === 0) {\n return null;\n }\n const value: number = this.rear.val; // \u5b58\u50a8\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n let temp: ListNode = this.rear.prev;\n if (temp !== null) {\n temp.next = null;\n this.rear.prev = null;\n }\n this.rear = temp; // \u66f4\u65b0\u5c3e\u8282\u70b9\n this.queSize--;\n return value;\n }\n\n /* \u961f\u9996\u51fa\u961f\u64cd\u4f5c */\n popFirst(): number {\n if (this.queSize === 0) {\n return null;\n }\n const value: number = this.front.val; // \u5b58\u50a8\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n let temp: ListNode = this.front.next;\n if (temp !== null) {\n temp.prev = null;\n this.front.next = null;\n }\n this.front = temp; // \u66f4\u65b0\u5934\u8282\u70b9\n this.queSize--;\n return value;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n peekLast(): number {\n return this.queSize === 0 ? null : this.rear.val;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peekFirst(): number {\n return this.queSize === 0 ? null : this.front.val;\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n size(): number {\n return this.queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty(): boolean {\n return this.queSize === 0;\n }\n\n /* \u6253\u5370\u53cc\u5411\u961f\u5217 */\n print(): void {\n const arr: number[] = [];\n let temp: ListNode = this.front;\n while (temp !== null) {\n arr.push(temp.val);\n temp = temp.next;\n }\n console.log('[' + arr.join(', ') + ']');\n }\n}\n
linkedlist_deque.dart/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\nclass ListNode {\n int val; // \u8282\u70b9\u503c\n ListNode? next; // \u540e\u7ee7\u8282\u70b9\u5f15\u7528\n ListNode? prev; // \u524d\u9a71\u8282\u70b9\u5f15\u7528\n\n ListNode(this.val, {this.next, this.prev});\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u5bf9\u5217 */\nclass LinkedListDeque {\n late ListNode? _front; // \u5934\u8282\u70b9 _front\n late ListNode? _rear; // \u5c3e\u8282\u70b9 _rear\n int _queSize = 0; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n\n LinkedListDeque() {\n this._front = null;\n this._rear = null;\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u957f\u5ea6 */\n int size() {\n return this._queSize;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n void push(int _num, bool isFront) {\n final ListNode node = ListNode(_num);\n if (isEmpty()) {\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 _front \u548c _rear \u90fd\u6307\u5411 node\n _front = _rear = node;\n } else if (isFront) {\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n _front!.prev = node;\n node.next = _front;\n _front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n } else {\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n _rear!.next = node;\n node.prev = _rear;\n _rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n _queSize++; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n void pushFirst(int _num) {\n push(_num, true);\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n void pushLast(int _num) {\n push(_num, false);\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n int? pop(bool isFront) {\n // \u82e5\u961f\u5217\u4e3a\u7a7a\uff0c\u76f4\u63a5\u8fd4\u56de null\n if (isEmpty()) {\n return null;\n }\n final int val;\n if (isFront) {\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n val = _front!.val; // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n ListNode? fNext = _front!.next;\n if (fNext != null) {\n fNext.prev = null;\n _front!.next = null;\n }\n _front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n } else {\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n val = _rear!.val; // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n ListNode? rPrev = _rear!.prev;\n if (rPrev != null) {\n rPrev.next = null;\n _rear!.prev = null;\n }\n _rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n _queSize--; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n }\n\n /* \u961f\u9996\u51fa\u961f */\n int? popFirst() {\n return pop(true);\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n int? popLast() {\n return pop(false);\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int? peekFirst() {\n return _front?.val;\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n int? peekLast() {\n return _rear?.val;\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n List<int> toArray() {\n ListNode? node = _front;\n final List<int> res = [];\n for (int i = 0; i < _queSize; i++) {\n res.add(node!.val);\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_deque.rs/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\npub struct ListNode<T> {\n pub val: T, // \u8282\u70b9\u503c\n pub next: Option<Rc<RefCell<ListNode<T>>>>, // \u540e\u7ee7\u8282\u70b9\u6307\u9488\n pub prev: Option<Rc<RefCell<ListNode<T>>>>, // \u524d\u9a71\u8282\u70b9\u6307\u9488\n}\n\nimpl<T> ListNode<T> {\n pub fn new(val: T) -> Rc<RefCell<ListNode<T>>> {\n Rc::new(RefCell::new(ListNode {\n val,\n next: None,\n prev: None,\n }))\n }\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\n#[allow(dead_code)]\npub struct LinkedListDeque<T> {\n front: Option<Rc<RefCell<ListNode<T>>>>, // \u5934\u8282\u70b9 front\n rear: Option<Rc<RefCell<ListNode<T>>>>, // \u5c3e\u8282\u70b9 rear \n que_size: usize, // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n}\n\nimpl<T: Copy> LinkedListDeque<T> {\n pub fn new() -> Self {\n Self {\n front: None,\n rear: None,\n que_size: 0, \n }\n }\n\n /* \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6 */\n pub fn size(&self) -> usize {\n return self.que_size;\n }\n\n /* \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n pub fn is_empty(&self) -> bool {\n return self.size() == 0;\n }\n\n /* \u5165\u961f\u64cd\u4f5c */\n pub fn push(&mut self, num: T, is_front: bool) {\n let node = ListNode::new(num);\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n if is_front {\n match self.front.take() {\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n None => {\n self.rear = Some(node.clone());\n self.front = Some(node);\n }\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n Some(old_front) => {\n old_front.borrow_mut().prev = Some(node.clone());\n node.borrow_mut().next = Some(old_front);\n self.front = Some(node); // \u66f4\u65b0\u5934\u8282\u70b9\n }\n }\n } \n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n else {\n match self.rear.take() {\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n None => {\n self.front = Some(node.clone());\n self.rear = Some(node);\n }\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n Some(old_rear) => {\n old_rear.borrow_mut().next = Some(node.clone());\n node.borrow_mut().prev = Some(old_rear);\n self.rear = Some(node); // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n }\n }\n self.que_size += 1; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n }\n\n /* \u961f\u9996\u5165\u961f */\n pub fn push_first(&mut self, num: T) {\n self.push(num, true);\n }\n\n /* \u961f\u5c3e\u5165\u961f */\n pub fn push_last(&mut self, num: T) {\n self.push(num, false);\n }\n\n /* \u51fa\u961f\u64cd\u4f5c */\n pub fn pop(&mut self, is_front: bool) -> Option<T> {\n // \u82e5\u961f\u5217\u4e3a\u7a7a\uff0c\u76f4\u63a5\u8fd4\u56de None\n if self.is_empty() { \n return None \n };\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if is_front {\n self.front.take().map(|old_front| {\n match old_front.borrow_mut().next.take() {\n Some(new_front) => {\n new_front.borrow_mut().prev.take();\n self.front = Some(new_front); // \u66f4\u65b0\u5934\u8282\u70b9\n }\n None => {\n self.rear.take();\n }\n }\n self.que_size -= 1; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n Rc::try_unwrap(old_front).ok().unwrap().into_inner().val\n })\n\n } \n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n else {\n self.rear.take().map(|old_rear| {\n match old_rear.borrow_mut().prev.take() {\n Some(new_rear) => {\n new_rear.borrow_mut().next.take();\n self.rear = Some(new_rear); // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n None => {\n self.front.take();\n }\n }\n self.que_size -= 1; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n Rc::try_unwrap(old_rear).ok().unwrap().into_inner().val\n })\n }\n }\n\n /* \u961f\u9996\u51fa\u961f */\n pub fn pop_first(&mut self) -> Option<T> {\n return self.pop(true);\n }\n\n /* \u961f\u5c3e\u51fa\u961f */\n pub fn pop_last(&mut self) -> Option<T> {\n return self.pop(false);\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n pub fn peek_first(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {\n self.front.as_ref()\n }\n\n /* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\n pub fn peek_last(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {\n self.rear.as_ref()\n }\n\n /* \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370 */\n pub fn to_array(&self, head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {\n if let Some(node) = head {\n let mut nums = self.to_array(node.borrow().next.as_ref());\n nums.insert(0, node.borrow().val);\n return nums;\n }\n return Vec::new();\n }\n}\n
linkedlist_deque.c/* \u53cc\u5411\u94fe\u8868\u8282\u70b9 */\ntypedef struct DoublyListNode {\n int val; // \u8282\u70b9\u503c\n struct DoublyListNode *next; // \u540e\u7ee7\u8282\u70b9\n struct DoublyListNode *prev; // \u524d\u9a71\u8282\u70b9\n} DoublyListNode;\n\n/* \u6784\u9020\u51fd\u6570 */\nDoublyListNode *newDoublyListNode(int num) {\n DoublyListNode *new = (DoublyListNode *)malloc(sizeof(DoublyListNode));\n new->val = num;\n new->next = NULL;\n new->prev = NULL;\n return new;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delDoublyListNode(DoublyListNode *node) {\n free(node);\n}\n\n/* \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217 */\ntypedef struct {\n DoublyListNode *front, *rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear\n int queSize; // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n} LinkedListDeque;\n\n/* \u6784\u9020\u51fd\u6570 */\nLinkedListDeque *newLinkedListDeque() {\n LinkedListDeque *deque = (LinkedListDeque *)malloc(sizeof(LinkedListDeque));\n deque->front = NULL;\n deque->rear = NULL;\n deque->queSize = 0;\n return deque;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delLinkedListdeque(LinkedListDeque *deque) {\n // \u91ca\u653e\u6240\u6709\u8282\u70b9\n for (int i = 0; i < deque->queSize && deque->front != NULL; i++) {\n DoublyListNode *tmp = deque->front;\n deque->front = deque->front->next;\n free(tmp);\n }\n // \u91ca\u653e deque \u7ed3\u6784\u4f53\n free(deque);\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nint size(LinkedListDeque *deque) {\n return deque->queSize;\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nbool empty(LinkedListDeque *deque) {\n return (size(deque) == 0);\n}\n\n/* \u5165\u961f */\nvoid push(LinkedListDeque *deque, int num, bool isFront) {\n DoublyListNode *node = newDoublyListNode(num);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411node\n if (empty(deque)) {\n deque->front = deque->rear = node;\n }\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n else if (isFront) {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n deque->front->prev = node;\n node->next = deque->front;\n deque->front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n }\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n deque->rear->next = node;\n node->prev = deque->rear;\n deque->rear = node;\n }\n deque->queSize++; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n}\n\n/* \u961f\u9996\u5165\u961f */\nvoid pushFirst(LinkedListDeque *deque, int num) {\n push(deque, num, true);\n}\n\n/* \u961f\u5c3e\u5165\u961f */\nvoid pushLast(LinkedListDeque *deque, int num) {\n push(deque, num, false);\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nint peekFirst(LinkedListDeque *deque) {\n assert(size(deque) && deque->front);\n return deque->front->val;\n}\n\n/* \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20 */\nint peekLast(LinkedListDeque *deque) {\n assert(size(deque) && deque->rear);\n return deque->rear->val;\n}\n\n/* \u51fa\u961f */\nint pop(LinkedListDeque *deque, bool isFront) {\n if (empty(deque))\n return -1;\n int val;\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if (isFront) {\n val = peekFirst(deque); // \u6682\u5b58\u5934\u8282\u70b9\u503c\n DoublyListNode *fNext = deque->front->next;\n if (fNext) {\n fNext->prev = NULL;\n deque->front->next = NULL;\n delDoublyListNode(deque->front);\n }\n deque->front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n }\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n else {\n val = peekLast(deque); // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n DoublyListNode *rPrev = deque->rear->prev;\n if (rPrev) {\n rPrev->next = NULL;\n deque->rear->prev = NULL;\n delDoublyListNode(deque->rear);\n }\n deque->rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n deque->queSize--; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n}\n\n/* \u961f\u9996\u51fa\u961f */\nint popFirst(LinkedListDeque *deque) {\n return pop(deque, true);\n}\n\n/* \u961f\u5c3e\u51fa\u961f */\nint popLast(LinkedListDeque *deque) {\n return pop(deque, false);\n}\n\n/* \u6253\u5370\u961f\u5217 */\nvoid printLinkedListDeque(LinkedListDeque *deque) {\n int *arr = malloc(sizeof(int) * deque->queSize);\n // \u62f7\u8d1d\u94fe\u8868\u4e2d\u7684\u6570\u636e\u5230\u6570\u7ec4\n int i;\n DoublyListNode *node;\n for (i = 0, node = deque->front; i < deque->queSize; i++) {\n arr[i] = node->val;\n node = node->next;\n }\n printArray(arr, deque->queSize);\n free(arr);\n}\n
linkedlist_deque.zig// \u53cc\u5411\u94fe\u8868\u8282\u70b9\nfn ListNode(comptime T: type) type {\n return struct {\n const Self = @This();\n\n val: T = undefined, // \u8282\u70b9\u503c\n next: ?*Self = null, // \u540e\u7ee7\u8282\u70b9\u6307\u9488\n prev: ?*Self = null, // \u524d\u9a71\u8282\u70b9\u6307\u9488\n\n // Initialize a list node with specific value\n pub fn init(self: *Self, x: i32) void {\n self.val = x;\n self.next = null;\n self.prev = null;\n }\n };\n}\n\n// \u57fa\u4e8e\u53cc\u5411\u94fe\u8868\u5b9e\u73b0\u7684\u53cc\u5411\u961f\u5217\nfn LinkedListDeque(comptime T: type) type {\n return struct {\n const Self = @This();\n\n front: ?*ListNode(T) = null, // \u5934\u8282\u70b9 front\n rear: ?*ListNode(T) = null, // \u5c3e\u8282\u70b9 rear\n que_size: usize = 0, // \u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n mem_arena: ?std.heap.ArenaAllocator = null,\n mem_allocator: std.mem.Allocator = undefined, // \u5185\u5b58\u5206\u914d\u5668\n\n // \u6784\u9020\u51fd\u6570\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u961f\u5217\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator) !void {\n if (self.mem_arena == null) {\n self.mem_arena = std.heap.ArenaAllocator.init(allocator);\n self.mem_allocator = self.mem_arena.?.allocator();\n }\n self.front = null;\n self.rear = null;\n self.que_size = 0;\n }\n\n // \u6790\u6784\u51fd\u6570\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.mem_arena == null) return;\n self.mem_arena.?.deinit();\n }\n\n // \u83b7\u53d6\u53cc\u5411\u961f\u5217\u7684\u957f\u5ea6\n pub fn size(self: *Self) usize {\n return self.que_size;\n }\n\n // \u5224\u65ad\u53cc\u5411\u961f\u5217\u662f\u5426\u4e3a\u7a7a\n pub fn isEmpty(self: *Self) bool {\n return self.size() == 0;\n }\n\n // \u5165\u961f\u64cd\u4f5c\n pub fn push(self: *Self, num: T, is_front: bool) !void {\n var node = try self.mem_allocator.create(ListNode(T));\n node.init(num);\n // \u82e5\u94fe\u8868\u4e3a\u7a7a\uff0c\u5219\u4ee4 front \u548c rear \u90fd\u6307\u5411 node\n if (self.isEmpty()) {\n self.front = node;\n self.rear = node;\n // \u961f\u9996\u5165\u961f\u64cd\u4f5c\n } else if (is_front) {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5934\u90e8\n self.front.?.prev = node;\n node.next = self.front;\n self.front = node; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u5165\u961f\u64cd\u4f5c\n } else {\n // \u5c06 node \u6dfb\u52a0\u81f3\u94fe\u8868\u5c3e\u90e8\n self.rear.?.next = node;\n node.prev = self.rear;\n self.rear = node; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n self.que_size += 1; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n } \n\n // \u961f\u9996\u5165\u961f\n pub fn pushFirst(self: *Self, num: T) !void {\n try self.push(num, true);\n } \n\n // \u961f\u5c3e\u5165\u961f\n pub fn pushLast(self: *Self, num: T) !void {\n try self.push(num, false);\n } \n\n // \u51fa\u961f\u64cd\u4f5c\n pub fn pop(self: *Self, is_front: bool) T {\n if (self.isEmpty()) @panic(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\");\n var val: T = undefined;\n // \u961f\u9996\u51fa\u961f\u64cd\u4f5c\n if (is_front) {\n val = self.front.?.val; // \u6682\u5b58\u5934\u8282\u70b9\u503c\n // \u5220\u9664\u5934\u8282\u70b9\n var fNext = self.front.?.next;\n if (fNext != null) {\n fNext.?.prev = null;\n self.front.?.next = null;\n }\n self.front = fNext; // \u66f4\u65b0\u5934\u8282\u70b9\n // \u961f\u5c3e\u51fa\u961f\u64cd\u4f5c\n } else {\n val = self.rear.?.val; // \u6682\u5b58\u5c3e\u8282\u70b9\u503c\n // \u5220\u9664\u5c3e\u8282\u70b9\n var rPrev = self.rear.?.prev;\n if (rPrev != null) {\n rPrev.?.next = null;\n self.rear.?.prev = null;\n }\n self.rear = rPrev; // \u66f4\u65b0\u5c3e\u8282\u70b9\n }\n self.que_size -= 1; // \u66f4\u65b0\u961f\u5217\u957f\u5ea6\n return val;\n } \n\n // \u961f\u9996\u51fa\u961f\n pub fn popFirst(self: *Self) T {\n return self.pop(true);\n } \n\n // \u961f\u5c3e\u51fa\u961f\n pub fn popLast(self: *Self) T {\n return self.pop(false);\n } \n\n // \u8bbf\u95ee\u961f\u9996\u5143\u7d20\n pub fn peekFirst(self: *Self) T {\n if (self.isEmpty()) @panic(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\");\n return self.front.?.val;\n } \n\n // \u8bbf\u95ee\u961f\u5c3e\u5143\u7d20\n pub fn peekLast(self: *Self) T {\n if (self.isEmpty()) @panic(\"\u53cc\u5411\u961f\u5217\u4e3a\u7a7a\");\n return self.rear.?.val;\n }\n\n // \u8fd4\u56de\u6570\u7ec4\u7528\u4e8e\u6253\u5370\n pub fn toArray(self: *Self) ![]T {\n var node = self.front;\n var res = try self.mem_allocator.alloc(T, self.size());\n @memset(res, @as(T, 0));\n var i: usize = 0;\n while (i < res.len) : (i += 1) {\n res[i] = node.?.val;\n node = node.?.next;\n }\n return res;\n }\n };\n}\n
"},{"location":"chapter_stack_and_queue/deque/#2-implementation-based-on-array","title":"2. \u00a0 Implementation Based on Array","text":"As shown in the Figure 5-9 , similar to implementing a queue with an array, we can also use a circular array to implement a double-ended queue.
ArrayDequepushLast()pushFirst()popLast()popFirst()Figure 5-9 \u00a0 Implementing Double-Ended Queue with Array for Enqueue and Dequeue Operations
The implementation only needs to add methods for \"front enqueue\" and \"rear dequeue\":
[file]{array_deque}-[func]{}\n
"},{"location":"chapter_stack_and_queue/deque/#533-applications-of-double-ended-queue","title":"5.3.3 \u00a0 Applications of Double-Ended Queue","text":"The double-ended queue combines the logic of both stacks and queues, thus it can implement all the application scenarios of these two, while offering greater flexibility.
We know that the \"undo\" feature in software is typically implemented using a stack: the system pushes
each change operation onto the stack, and then pops
to implement undoing. However, considering the limitations of system resources, software often restricts the number of undo steps (for example, only allowing the last 50 steps). When the length of the stack exceeds 50, the software needs to perform a deletion operation at the bottom of the stack (the front of the queue). But a regular stack cannot perform this function, which is where a double-ended queue becomes necessary. Note that the core logic of \"undo\" still follows the Last-In-First-Out principle of a stack, but a double-ended queue can more flexibly implement some additional logic.
\"Queue\" is a linear data structure that follows the First-In-First-Out (FIFO) rule. As the name suggests, a queue simulates the phenomenon of lining up, where newcomers join the back of the queue, and people at the front of the queue leave one by one.
As shown in the Figure 5-4 , we call the front of the queue the \"head\" and the back the \"tail.\" The operation of adding elements to the tail of the queue is termed \"enqueue,\" and the operation of removing elements from the head is termed \"dequeue.\"
Figure 5-4 \u00a0 Queue's First-In-First-Out Rule
"},{"location":"chapter_stack_and_queue/queue/#521-common-operations-on-queue","title":"5.2.1 \u00a0 Common Operations on Queue","text":"The common operations on a queue are shown in the Table 5-2 . Note that method names may vary across different programming languages. Here, we adopt the same naming convention as used for stacks.
Table 5-2 \u00a0 Efficiency of Queue Operations
Method Name Description Time Complexitypush()
Enqueue an element, add it to the tail \\(O(1)\\) pop()
Dequeue the head element \\(O(1)\\) peek()
Access the head element \\(O(1)\\) We can directly use the ready-made queue classes in programming languages:
PythonC++JavaC#GoSwiftJSTSDartRustCZig queue.pyfrom collections import deque\n\n# Initialize the queue\n# In Python, we generally use the deque class as a queue\n# Although queue.Queue() is a pure queue class, it's not very user-friendly, so it's not recommended\nque: deque[int] = deque()\n\n# Enqueue elements\nque.append(1)\nque.append(3)\nque.append(2)\nque.append(5)\nque.append(4)\n\n# Access the front element\nfront: int = que[0]\n\n# Dequeue an element\npop: int = que.popleft()\n\n# Get the length of the queue\nsize: int = len(que)\n\n# Check if the queue is empty\nis_empty: bool = len(que) == 0\n
queue.cpp/* Initialize the queue */\nqueue<int> queue;\n\n/* Enqueue elements */\nqueue.push(1);\nqueue.push(3);\nqueue.push(2);\nqueue.push(5);\nqueue.push(4);\n\n/* Access the front element */\nint front = queue.front();\n\n/* Dequeue an element */\nqueue.pop();\n\n/* Get the length of the queue */\nint size = queue.size();\n\n/* Check if the queue is empty */\nbool empty = queue.empty();\n
queue.java/* Initialize the queue */\nQueue<Integer> queue = new LinkedList<>();\n\n/* Enqueue elements */\nqueue.offer(1);\nqueue.offer(3);\nqueue.offer(2);\nqueue.offer(5);\nqueue.offer(4);\n\n/* Access the front element */\nint peek = queue.peek();\n\n/* Dequeue an element */\nint pop = queue.poll();\n\n/* Get the length of the queue */\nint size = queue.size();\n\n/* Check if the queue is empty */\nboolean isEmpty = queue.isEmpty();\n
queue.cs/* Initialize the queue */\nQueue<int> queue = new();\n\n/* Enqueue elements */\nqueue.Enqueue(1);\nqueue.Enqueue(3);\nqueue.Enqueue(2);\nqueue.Enqueue(5);\nqueue.Enqueue(4);\n\n/* Access the front element */\nint peek = queue.Peek();\n\n/* Dequeue an element */\nint pop = queue.Dequeue();\n\n/* Get the length of the queue */\nint size = queue.Count;\n\n/* Check if the queue is empty */\nbool isEmpty = queue.Count == 0;\n
queue_test.go/* Initialize the queue */\n// In Go, use list as a queue\nqueue := list.New()\n\n/* Enqueue elements */\nqueue.PushBack(1)\nqueue.PushBack(3)\nqueue.PushBack(2)\nqueue.PushBack(5)\nqueue.PushBack(4)\n\n/* Access the front element */\npeek := queue.Front()\n\n/* Dequeue an element */\npop := queue.Front()\nqueue.Remove(pop)\n\n/* Get the length of the queue */\nsize := queue.Len()\n\n/* Check if the queue is empty */\nisEmpty := queue.Len() == 0\n
queue.swift/* Initialize the queue */\n// Swift does not have a built-in queue class, so Array can be used as a queue\nvar queue: [Int] = []\n\n/* Enqueue elements */\nqueue.append(1)\nqueue.append(3)\nqueue.append(2)\nqueue.append(5)\nqueue.append(4)\n\n/* Access the front element */\nlet peek = queue.first!\n\n/* Dequeue an element */\n// Since it's an array, removeFirst has a complexity of O(n)\nlet pool = queue.removeFirst()\n\n/* Get the length of the queue */\nlet size = queue.count\n\n/* Check if the queue is empty */\nlet isEmpty = queue.isEmpty\n
queue.js/* Initialize the queue */\n// JavaScript does not have a built-in queue, so Array can be used as a queue\nconst queue = [];\n\n/* Enqueue elements */\nqueue.push(1);\nqueue.push(3);\nqueue.push(2);\nqueue.push(5);\nqueue.push(4);\n\n/* Access the front element */\nconst peek = queue[0];\n\n/* Dequeue an element */\n// Since the underlying structure is an array, shift() method has a time complexity of O(n)\nconst pop = queue.shift();\n\n/* Get the length of the queue */\nconst size = queue.length;\n\n/* Check if the queue is empty */\nconst empty = queue.length === 0;\n
queue.ts/* Initialize the queue */\n// TypeScript does not have a built-in queue, so Array can be used as a queue \nconst queue: number[] = [];\n\n/* Enqueue elements */\nqueue.push(1);\nqueue.push(3);\nqueue.push(2);\nqueue.push(5);\nqueue.push(4);\n\n/* Access the front element */\nconst peek = queue[0];\n\n/* Dequeue an element */\n// Since the underlying structure is an array, shift() method has a time complexity of O(n)\nconst pop = queue.shift();\n\n/* Get the length of the queue */\nconst size = queue.length;\n\n/* Check if the queue is empty */\nconst empty = queue.length === 0;\n
queue.dart/* Initialize the queue */\n// In Dart, the Queue class is a double-ended queue but can be used as a queue\nQueue<int> queue = Queue();\n\n/* Enqueue elements */\nqueue.add(1);\nqueue.add(3);\nqueue.add(2);\nqueue.add(5);\nqueue.add(4);\n\n/* Access the front element */\nint peek = queue.first;\n\n/* Dequeue an element */\nint pop = queue.removeFirst();\n\n/* Get the length of the queue */\nint size = queue.length;\n\n/* Check if the queue is empty */\nbool isEmpty = queue.isEmpty;\n
queue.rs/* Initialize the double-ended queue */\n// In Rust, use a double-ended queue as a regular queue\nlet mut deque: VecDeque<u32> = VecDeque::new();\n\n/* Enqueue elements */\ndeque.push_back(1);\ndeque.push_back(3);\ndeque.push_back(2);\ndeque.push_back(5);\ndeque.push_back(4);\n\n/* Access the front element */\nif let Some(front) = deque.front() {\n}\n\n/* Dequeue an element */\nif let Some(pop) = deque.pop_front() {\n}\n\n/* Get the length of the queue */\nlet size = deque.len();\n\n/* Check if the queue is empty */\nlet is_empty = deque.is_empty();\n
queue.c// C does not provide a built-in queue\n
queue.zig\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/queue/#522-implementing-a-queue","title":"5.2.2 \u00a0 Implementing a Queue","text":"To implement a queue, we need a data structure that allows adding elements at one end and removing them at the other. Both linked lists and arrays meet this requirement.
"},{"location":"chapter_stack_and_queue/queue/#1-implementation-based-on-linked-list","title":"1. \u00a0 Implementation Based on Linked List","text":"As shown in the Figure 5-5 , we can consider the \"head node\" and \"tail node\" of a linked list as the \"head\" and \"tail\" of the queue, respectively. We restrict the operations so that nodes can only be added at the tail and removed at the head.
LinkedListQueuepush()pop()Figure 5-5 \u00a0 Implementing Queue with Linked List for Enqueue and Dequeue Operations
Below is the code for implementing a queue using a linked list:
PythonC++JavaC#GoSwiftJSTSDartRustCZig linkedlist_queue.pyclass LinkedListQueue:\n \"\"\"\u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217\"\"\"\n\n def __init__(self):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._front: ListNode | None = None # \u5934\u8282\u70b9 front\n self._rear: ListNode | None = None # \u5c3e\u8282\u70b9 rear\n self._size: int = 0\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6\"\"\"\n return self._size\n\n def is_empty(self) -> bool:\n \"\"\"\u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a\"\"\"\n return not self._front\n\n def push(self, num: int):\n \"\"\"\u5165\u961f\"\"\"\n # \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n node = ListNode(num)\n # \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if self._front is None:\n self._front = node\n self._rear = node\n # \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n else:\n self._rear.next = node\n self._rear = node\n self._size += 1\n\n def pop(self) -> int:\n \"\"\"\u51fa\u961f\"\"\"\n num = self.peek()\n # \u5220\u9664\u5934\u8282\u70b9\n self._front = self._front.next\n self._size -= 1\n return num\n\n def peek(self) -> int:\n \"\"\"\u8bbf\u95ee\u961f\u9996\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u961f\u5217\u4e3a\u7a7a\")\n return self._front.val\n\n def to_list(self) -> list[int]:\n \"\"\"\u8f6c\u5316\u4e3a\u5217\u8868\u7528\u4e8e\u6253\u5370\"\"\"\n queue = []\n temp = self._front\n while temp:\n queue.append(temp.val)\n temp = temp.next\n return queue\n
linkedlist_queue.cpp/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n private:\n ListNode *front, *rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear\n int queSize;\n\n public:\n LinkedListQueue() {\n front = nullptr;\n rear = nullptr;\n queSize = 0;\n }\n\n ~LinkedListQueue() {\n // \u904d\u5386\u94fe\u8868\u5220\u9664\u8282\u70b9\uff0c\u91ca\u653e\u5185\u5b58\n freeMemoryLinkedList(front);\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return queSize == 0;\n }\n\n /* \u5165\u961f */\n void push(int num) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n ListNode *node = new ListNode(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (front == nullptr) {\n front = node;\n rear = node;\n }\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n else {\n rear->next = node;\n rear = node;\n }\n queSize++;\n }\n\n /* \u51fa\u961f */\n int pop() {\n int num = peek();\n // \u5220\u9664\u5934\u8282\u70b9\n ListNode *tmp = front;\n front = front->next;\n // \u91ca\u653e\u5185\u5b58\n delete tmp;\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int peek() {\n if (size() == 0)\n throw out_of_range(\"\u961f\u5217\u4e3a\u7a7a\");\n return front->val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Vector \u5e76\u8fd4\u56de */\n vector<int> toVector() {\n ListNode *node = front;\n vector<int> res(size());\n for (int i = 0; i < res.size(); i++) {\n res[i] = node->val;\n node = node->next;\n }\n return res;\n }\n};\n
linkedlist_queue.java/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n private ListNode front, rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear\n private int queSize = 0;\n\n public LinkedListQueue() {\n front = null;\n rear = null;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n public int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public boolean isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u961f */\n public void push(int num) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n ListNode node = new ListNode(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (front == null) {\n front = node;\n rear = node;\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n } else {\n rear.next = node;\n rear = node;\n }\n queSize++;\n }\n\n /* \u51fa\u961f */\n public int pop() {\n int num = peek();\n // \u5220\u9664\u5934\u8282\u70b9\n front = front.next;\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int peek() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return front.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public int[] toArray() {\n ListNode node = front;\n int[] res = new int[size()];\n for (int i = 0; i < res.length; i++) {\n res[i] = node.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_queue.cs/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n ListNode? front, rear; // \u5934\u8282\u70b9 front \uff0c\u5c3e\u8282\u70b9 rear \n int queSize = 0;\n\n public LinkedListQueue() {\n front = null;\n rear = null;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n public int Size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public bool IsEmpty() {\n return Size() == 0;\n }\n\n /* \u5165\u961f */\n public void Push(int num) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n ListNode node = new(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (front == null) {\n front = node;\n rear = node;\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n } else if (rear != null) {\n rear.next = node;\n rear = node;\n }\n queSize++;\n }\n\n /* \u51fa\u961f */\n public int Pop() {\n int num = Peek();\n // \u5220\u9664\u5934\u8282\u70b9\n front = front?.next;\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int Peek() {\n if (IsEmpty())\n throw new Exception();\n return front!.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public int[] ToArray() {\n if (front == null)\n return [];\n\n ListNode? node = front;\n int[] res = new int[Size()];\n for (int i = 0; i < res.Length; i++) {\n res[i] = node!.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_queue.go/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\ntype linkedListQueue struct {\n // \u4f7f\u7528\u5185\u7f6e\u5305 list \u6765\u5b9e\u73b0\u961f\u5217\n data *list.List\n}\n\n/* \u521d\u59cb\u5316\u961f\u5217 */\nfunc newLinkedListQueue() *linkedListQueue {\n return &linkedListQueue{\n data: list.New(),\n }\n}\n\n/* \u5165\u961f */\nfunc (s *linkedListQueue) push(value any) {\n s.data.PushBack(value)\n}\n\n/* \u51fa\u961f */\nfunc (s *linkedListQueue) pop() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Front()\n s.data.Remove(e)\n return e.Value\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nfunc (s *linkedListQueue) peek() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Front()\n return e.Value\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nfunc (s *linkedListQueue) size() int {\n return s.data.Len()\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nfunc (s *linkedListQueue) isEmpty() bool {\n return s.data.Len() == 0\n}\n\n/* \u83b7\u53d6 List \u7528\u4e8e\u6253\u5370 */\nfunc (s *linkedListQueue) toList() *list.List {\n return s.data\n}\n
linkedlist_queue.swift/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n private var front: ListNode? // \u5934\u8282\u70b9\n private var rear: ListNode? // \u5c3e\u8282\u70b9\n private var _size = 0\n\n init() {}\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n func size() -> Int {\n _size\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n func isEmpty() -> Bool {\n size() == 0\n }\n\n /* \u5165\u961f */\n func push(num: Int) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n let node = ListNode(x: num)\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if front == nil {\n front = node\n rear = node\n }\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n else {\n rear?.next = node\n rear = node\n }\n _size += 1\n }\n\n /* \u51fa\u961f */\n @discardableResult\n func pop() -> Int {\n let num = peek()\n // \u5220\u9664\u5934\u8282\u70b9\n front = front?.next\n _size -= 1\n return num\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n func peek() -> Int {\n if isEmpty() {\n fatalError(\"\u961f\u5217\u4e3a\u7a7a\")\n }\n return front!.val\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n func toArray() -> [Int] {\n var node = front\n var res = Array(repeating: 0, count: size())\n for i in res.indices {\n res[i] = node!.val\n node = node?.next\n }\n return res\n }\n}\n
linkedlist_queue.js/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n #front; // \u5934\u8282\u70b9 #front\n #rear; // \u5c3e\u8282\u70b9 #rear\n #queSize = 0;\n\n constructor() {\n this.#front = null;\n this.#rear = null;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n get size() {\n return this.#queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty() {\n return this.size === 0;\n }\n\n /* \u5165\u961f */\n push(num) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n const node = new ListNode(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (!this.#front) {\n this.#front = node;\n this.#rear = node;\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n } else {\n this.#rear.next = node;\n this.#rear = node;\n }\n this.#queSize++;\n }\n\n /* \u51fa\u961f */\n pop() {\n const num = this.peek();\n // \u5220\u9664\u5934\u8282\u70b9\n this.#front = this.#front.next;\n this.#queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peek() {\n if (this.size === 0) throw new Error('\u961f\u5217\u4e3a\u7a7a');\n return this.#front.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n toArray() {\n let node = this.#front;\n const res = new Array(this.size);\n for (let i = 0; i < res.length; i++) {\n res[i] = node.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_queue.ts/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n private front: ListNode | null; // \u5934\u8282\u70b9 front\n private rear: ListNode | null; // \u5c3e\u8282\u70b9 rear\n private queSize: number = 0;\n\n constructor() {\n this.front = null;\n this.rear = null;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n get size(): number {\n return this.queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty(): boolean {\n return this.size === 0;\n }\n\n /* \u5165\u961f */\n push(num: number): void {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n const node = new ListNode(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (!this.front) {\n this.front = node;\n this.rear = node;\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n } else {\n this.rear!.next = node;\n this.rear = node;\n }\n this.queSize++;\n }\n\n /* \u51fa\u961f */\n pop(): number {\n const num = this.peek();\n if (!this.front) throw new Error('\u961f\u5217\u4e3a\u7a7a');\n // \u5220\u9664\u5934\u8282\u70b9\n this.front = this.front.next;\n this.queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peek(): number {\n if (this.size === 0) throw new Error('\u961f\u5217\u4e3a\u7a7a');\n return this.front!.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n toArray(): number[] {\n let node = this.front;\n const res = new Array<number>(this.size);\n for (let i = 0; i < res.length; i++) {\n res[i] = node!.val;\n node = node!.next;\n }\n return res;\n }\n}\n
linkedlist_queue.dart/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\nclass LinkedListQueue {\n ListNode? _front; // \u5934\u8282\u70b9 _front\n ListNode? _rear; // \u5c3e\u8282\u70b9 _rear\n int _queSize = 0; // \u961f\u5217\u957f\u5ea6\n\n LinkedListQueue() {\n _front = null;\n _rear = null;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n int size() {\n return _queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return _queSize == 0;\n }\n\n /* \u5165\u961f */\n void push(int _num) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 _num\n final node = ListNode(_num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (_front == null) {\n _front = node;\n _rear = node;\n } else {\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n _rear!.next = node;\n _rear = node;\n }\n _queSize++;\n }\n\n /* \u51fa\u961f */\n int pop() {\n final int _num = peek();\n // \u5220\u9664\u5934\u8282\u70b9\n _front = _front!.next;\n _queSize--;\n return _num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int peek() {\n if (_queSize == 0) {\n throw Exception('\u961f\u5217\u4e3a\u7a7a');\n }\n return _front!.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n List<int> toArray() {\n ListNode? node = _front;\n final List<int> queue = [];\n while (node != null) {\n queue.add(node.val);\n node = node.next;\n }\n return queue;\n }\n}\n
linkedlist_queue.rs/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\n#[allow(dead_code)]\npub struct LinkedListQueue<T> {\n front: Option<Rc<RefCell<ListNode<T>>>>, // \u5934\u8282\u70b9 front\n rear: Option<Rc<RefCell<ListNode<T>>>>, // \u5c3e\u8282\u70b9 rear \n que_size: usize, // \u961f\u5217\u7684\u957f\u5ea6\n}\n\nimpl<T: Copy> LinkedListQueue<T> {\n pub fn new() -> Self {\n Self {\n front: None,\n rear: None,\n que_size: 0, \n }\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n pub fn size(&self) -> usize {\n return self.que_size;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n pub fn is_empty(&self) -> bool {\n return self.size() == 0;\n }\n\n /* \u5165\u961f */\n pub fn push(&mut self, num: T) {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n let new_rear = ListNode::new(num);\n match self.rear.take() {\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n Some(old_rear) => {\n old_rear.borrow_mut().next = Some(new_rear.clone());\n self.rear = Some(new_rear);\n }\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n None => {\n self.front = Some(new_rear.clone());\n self.rear = Some(new_rear);\n }\n }\n self.que_size += 1;\n }\n\n /* \u51fa\u961f */\n pub fn pop(&mut self) -> Option<T> {\n self.front.take().map(|old_front| {\n match old_front.borrow_mut().next.take() {\n Some(new_front) => {\n self.front = Some(new_front);\n }\n None => {\n self.rear.take();\n }\n }\n self.que_size -= 1;\n Rc::try_unwrap(old_front).ok().unwrap().into_inner().val\n })\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n pub fn peek(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {\n self.front.as_ref()\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n pub fn to_array(&self, head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {\n if let Some(node) = head {\n let mut nums = self.to_array(node.borrow().next.as_ref());\n nums.insert(0, node.borrow().val);\n return nums;\n }\n return Vec::new();\n }\n}\n
linkedlist_queue.c/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217 */\ntypedef struct {\n ListNode *front, *rear;\n int queSize;\n} LinkedListQueue;\n\n/* \u6784\u9020\u51fd\u6570 */\nLinkedListQueue *newLinkedListQueue() {\n LinkedListQueue *queue = (LinkedListQueue *)malloc(sizeof(LinkedListQueue));\n queue->front = NULL;\n queue->rear = NULL;\n queue->queSize = 0;\n return queue;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delLinkedListQueue(LinkedListQueue *queue) {\n // \u91ca\u653e\u6240\u6709\u8282\u70b9\n for (int i = 0; i < queue->queSize && queue->front != NULL; i++) {\n ListNode *tmp = queue->front;\n queue->front = queue->front->next;\n free(tmp);\n }\n // \u91ca\u653e queue \u7ed3\u6784\u4f53\n free(queue);\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nint size(LinkedListQueue *queue) {\n return queue->queSize;\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nbool empty(LinkedListQueue *queue) {\n return (size(queue) == 0);\n}\n\n/* \u5165\u961f */\nvoid push(LinkedListQueue *queue, int num) {\n // \u5c3e\u8282\u70b9\u5904\u6dfb\u52a0 node\n ListNode *node = newListNode(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (queue->front == NULL) {\n queue->front = node;\n queue->rear = node;\n }\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n else {\n queue->rear->next = node;\n queue->rear = node;\n }\n queue->queSize++;\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nint peek(LinkedListQueue *queue) {\n assert(size(queue) && queue->front);\n return queue->front->val;\n}\n\n/* \u51fa\u961f */\nint pop(LinkedListQueue *queue) {\n int num = peek(queue);\n ListNode *tmp = queue->front;\n queue->front = queue->front->next;\n free(tmp);\n queue->queSize--;\n return num;\n}\n\n/* \u6253\u5370\u961f\u5217 */\nvoid printLinkedListQueue(LinkedListQueue *queue) {\n int *arr = malloc(sizeof(int) * queue->queSize);\n // \u62f7\u8d1d\u94fe\u8868\u4e2d\u7684\u6570\u636e\u5230\u6570\u7ec4\n int i;\n ListNode *node;\n for (i = 0, node = queue->front; i < queue->queSize; i++) {\n arr[i] = node->val;\n node = node->next;\n }\n printArray(arr, queue->queSize);\n free(arr);\n}\n
linkedlist_queue.zig// \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u961f\u5217\nfn LinkedListQueue(comptime T: type) type {\n return struct {\n const Self = @This();\n\n front: ?*inc.ListNode(T) = null, // \u5934\u8282\u70b9 front\n rear: ?*inc.ListNode(T) = null, // \u5c3e\u8282\u70b9 rear\n que_size: usize = 0, // \u961f\u5217\u7684\u957f\u5ea6\n mem_arena: ?std.heap.ArenaAllocator = null,\n mem_allocator: std.mem.Allocator = undefined, // \u5185\u5b58\u5206\u914d\u5668\n\n // \u6784\u9020\u51fd\u6570\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u961f\u5217\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator) !void {\n if (self.mem_arena == null) {\n self.mem_arena = std.heap.ArenaAllocator.init(allocator);\n self.mem_allocator = self.mem_arena.?.allocator();\n }\n self.front = null;\n self.rear = null;\n self.que_size = 0;\n }\n\n // \u6790\u6784\u51fd\u6570\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.mem_arena == null) return;\n self.mem_arena.?.deinit();\n }\n\n // \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6\n pub fn size(self: *Self) usize {\n return self.que_size;\n }\n\n // \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a\n pub fn isEmpty(self: *Self) bool {\n return self.size() == 0;\n }\n\n // \u8bbf\u95ee\u961f\u9996\u5143\u7d20\n pub fn peek(self: *Self) T {\n if (self.size() == 0) @panic(\"\u961f\u5217\u4e3a\u7a7a\");\n return self.front.?.val;\n } \n\n // \u5165\u961f\n pub fn push(self: *Self, num: T) !void {\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n var node = try self.mem_allocator.create(inc.ListNode(T));\n node.init(num);\n // \u5982\u679c\u961f\u5217\u4e3a\u7a7a\uff0c\u5219\u4ee4\u5934\u3001\u5c3e\u8282\u70b9\u90fd\u6307\u5411\u8be5\u8282\u70b9\n if (self.front == null) {\n self.front = node;\n self.rear = node;\n // \u5982\u679c\u961f\u5217\u4e0d\u4e3a\u7a7a\uff0c\u5219\u5c06\u8be5\u8282\u70b9\u6dfb\u52a0\u5230\u5c3e\u8282\u70b9\u540e\n } else {\n self.rear.?.next = node;\n self.rear = node;\n }\n self.que_size += 1;\n } \n\n // \u51fa\u961f\n pub fn pop(self: *Self) T {\n var num = self.peek();\n // \u5220\u9664\u5934\u8282\u70b9\n self.front = self.front.?.next;\n self.que_size -= 1;\n return num;\n } \n\n // \u5c06\u94fe\u8868\u8f6c\u6362\u4e3a\u6570\u7ec4\n pub fn toArray(self: *Self) ![]T {\n var node = self.front;\n var res = try self.mem_allocator.alloc(T, self.size());\n @memset(res, @as(T, 0));\n var i: usize = 0;\n while (i < res.len) : (i += 1) {\n res[i] = node.?.val;\n node = node.?.next;\n }\n return res;\n }\n };\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/queue/#2-implementation-based-on-array","title":"2. \u00a0 Implementation Based on Array","text":"Deleting the first element in an array has a time complexity of \\(O(n)\\), which would make the dequeue operation inefficient. However, this problem can be cleverly avoided as follows.
We can use a variable front
to point to the index of the head element and maintain a size
variable to record the length of the queue. Define rear = front + size
, which points to the position right after the tail element.
With this design, the effective interval of elements in the array is [front, rear - 1]
. The implementation methods for various operations are shown in the Figure 5-6 .
rear
index and increase size
by 1.front
by 1 and decrease size
by 1.Both enqueue and dequeue operations only require a single operation, each with a time complexity of \\(O(1)\\).
ArrayQueuepush()pop()Figure 5-6 \u00a0 Implementing Queue with Array for Enqueue and Dequeue Operations
You might notice a problem: as enqueue and dequeue operations are continuously performed, both front
and rear
move to the right and will eventually reach the end of the array and can't move further. To resolve this issue, we can treat the array as a \"circular array.\"
For a circular array, front
or rear
needs to loop back to the start of the array upon reaching the end. This cyclical pattern can be achieved with a \"modulo operation,\" as shown in the code below:
class ArrayQueue:\n \"\"\"\u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217\"\"\"\n\n def __init__(self, size: int):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._nums: list[int] = [0] * size # \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n self._front: int = 0 # \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n self._size: int = 0 # \u961f\u5217\u957f\u5ea6\n\n def capacity(self) -> int:\n \"\"\"\u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf\"\"\"\n return len(self._nums)\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6\"\"\"\n return self._size\n\n def is_empty(self) -> bool:\n \"\"\"\u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a\"\"\"\n return self._size == 0\n\n def push(self, num: int):\n \"\"\"\u5165\u961f\"\"\"\n if self._size == self.capacity():\n raise IndexError(\"\u961f\u5217\u5df2\u6ee1\")\n # \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n # \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n rear: int = (self._front + self._size) % self.capacity()\n # \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n self._nums[rear] = num\n self._size += 1\n\n def pop(self) -> int:\n \"\"\"\u51fa\u961f\"\"\"\n num: int = self.peek()\n # \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n self._front = (self._front + 1) % self.capacity()\n self._size -= 1\n return num\n\n def peek(self) -> int:\n \"\"\"\u8bbf\u95ee\u961f\u9996\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u961f\u5217\u4e3a\u7a7a\")\n return self._nums[self._front]\n\n def to_list(self) -> list[int]:\n \"\"\"\u8fd4\u56de\u5217\u8868\u7528\u4e8e\u6253\u5370\"\"\"\n res = [0] * self.size()\n j: int = self._front\n for i in range(self.size()):\n res[i] = self._nums[(j % self.capacity())]\n j += 1\n return res\n
array_queue.cpp/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n private:\n int *nums; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n int front; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n int queSize; // \u961f\u5217\u957f\u5ea6\n int queCapacity; // \u961f\u5217\u5bb9\u91cf\n\n public:\n ArrayQueue(int capacity) {\n // \u521d\u59cb\u5316\u6570\u7ec4\n nums = new int[capacity];\n queCapacity = capacity;\n front = queSize = 0;\n }\n\n ~ArrayQueue() {\n delete[] nums;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n int capacity() {\n return queCapacity;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u961f */\n void push(int num) {\n if (queSize == queCapacity) {\n cout << \"\u961f\u5217\u5df2\u6ee1\" << endl;\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n int rear = (front + queSize) % queCapacity;\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n nums[rear] = num;\n queSize++;\n }\n\n /* \u51fa\u961f */\n int pop() {\n int num = peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n front = (front + 1) % queCapacity;\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int peek() {\n if (isEmpty())\n throw out_of_range(\"\u961f\u5217\u4e3a\u7a7a\");\n return nums[front];\n }\n\n /* \u5c06\u6570\u7ec4\u8f6c\u5316\u4e3a Vector \u5e76\u8fd4\u56de */\n vector<int> toVector() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n vector<int> arr(queSize);\n for (int i = 0, j = front; i < queSize; i++, j++) {\n arr[i] = nums[j % queCapacity];\n }\n return arr;\n }\n};\n
array_queue.java/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n private int[] nums; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n private int front; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n private int queSize; // \u961f\u5217\u957f\u5ea6\n\n public ArrayQueue(int capacity) {\n nums = new int[capacity];\n front = queSize = 0;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n public int capacity() {\n return nums.length;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n public int size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public boolean isEmpty() {\n return queSize == 0;\n }\n\n /* \u5165\u961f */\n public void push(int num) {\n if (queSize == capacity()) {\n System.out.println(\"\u961f\u5217\u5df2\u6ee1\");\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n int rear = (front + queSize) % capacity();\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n nums[rear] = num;\n queSize++;\n }\n\n /* \u51fa\u961f */\n public int pop() {\n int num = peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n front = (front + 1) % capacity();\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int peek() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return nums[front];\n }\n\n /* \u8fd4\u56de\u6570\u7ec4 */\n public int[] toArray() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n int[] res = new int[queSize];\n for (int i = 0, j = front; i < queSize; i++, j++) {\n res[i] = nums[j % capacity()];\n }\n return res;\n }\n}\n
array_queue.cs/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n int[] nums; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n int front; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n int queSize; // \u961f\u5217\u957f\u5ea6\n\n public ArrayQueue(int capacity) {\n nums = new int[capacity];\n front = queSize = 0;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n int Capacity() {\n return nums.Length;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n public int Size() {\n return queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n public bool IsEmpty() {\n return queSize == 0;\n }\n\n /* \u5165\u961f */\n public void Push(int num) {\n if (queSize == Capacity()) {\n Console.WriteLine(\"\u961f\u5217\u5df2\u6ee1\");\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n int rear = (front + queSize) % Capacity();\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n nums[rear] = num;\n queSize++;\n }\n\n /* \u51fa\u961f */\n public int Pop() {\n int num = Peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n front = (front + 1) % Capacity();\n queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n public int Peek() {\n if (IsEmpty())\n throw new Exception();\n return nums[front];\n }\n\n /* \u8fd4\u56de\u6570\u7ec4 */\n public int[] ToArray() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n int[] res = new int[queSize];\n for (int i = 0, j = front; i < queSize; i++, j++) {\n res[i] = nums[j % this.Capacity()];\n }\n return res;\n }\n}\n
array_queue.go/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\ntype arrayQueue struct {\n nums []int // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n front int // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n queSize int // \u961f\u5217\u957f\u5ea6\n queCapacity int // \u961f\u5217\u5bb9\u91cf\uff08\u5373\u6700\u5927\u5bb9\u7eb3\u5143\u7d20\u6570\u91cf\uff09\n}\n\n/* \u521d\u59cb\u5316\u961f\u5217 */\nfunc newArrayQueue(queCapacity int) *arrayQueue {\n return &arrayQueue{\n nums: make([]int, queCapacity),\n queCapacity: queCapacity,\n front: 0,\n queSize: 0,\n }\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nfunc (q *arrayQueue) size() int {\n return q.queSize\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nfunc (q *arrayQueue) isEmpty() bool {\n return q.queSize == 0\n}\n\n/* \u5165\u961f */\nfunc (q *arrayQueue) push(num int) {\n // \u5f53 rear == queCapacity \u8868\u793a\u961f\u5217\u5df2\u6ee1\n if q.queSize == q.queCapacity {\n return\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n rear := (q.front + q.queSize) % q.queCapacity\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n q.nums[rear] = num\n q.queSize++\n}\n\n/* \u51fa\u961f */\nfunc (q *arrayQueue) pop() any {\n num := q.peek()\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n q.front = (q.front + 1) % q.queCapacity\n q.queSize--\n return num\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nfunc (q *arrayQueue) peek() any {\n if q.isEmpty() {\n return nil\n }\n return q.nums[q.front]\n}\n\n/* \u83b7\u53d6 Slice \u7528\u4e8e\u6253\u5370 */\nfunc (q *arrayQueue) toSlice() []int {\n rear := (q.front + q.queSize)\n if rear >= q.queCapacity {\n rear %= q.queCapacity\n return append(q.nums[q.front:], q.nums[:rear]...)\n }\n return q.nums[q.front:rear]\n}\n
array_queue.swift/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n private var nums: [Int] // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n private var front = 0 // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n private var queSize = 0 // \u961f\u5217\u957f\u5ea6\n\n init(capacity: Int) {\n // \u521d\u59cb\u5316\u6570\u7ec4\n nums = Array(repeating: 0, count: capacity)\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n func capacity() -> Int {\n nums.count\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n func size() -> Int {\n queSize\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n func isEmpty() -> Bool {\n queSize == 0\n }\n\n /* \u5165\u961f */\n func push(num: Int) {\n if size() == capacity() {\n print(\"\u961f\u5217\u5df2\u6ee1\")\n return\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n let rear = (front + queSize) % capacity()\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n nums[rear] = num\n queSize += 1\n }\n\n /* \u51fa\u961f */\n @discardableResult\n func pop() -> Int {\n let num = peek()\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n front = (front + 1) % capacity()\n queSize -= 1\n return num\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n func peek() -> Int {\n if isEmpty() {\n fatalError(\"\u961f\u5217\u4e3a\u7a7a\")\n }\n return nums[front]\n }\n\n /* \u8fd4\u56de\u6570\u7ec4 */\n func toArray() -> [Int] {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n var res = Array(repeating: 0, count: queSize)\n for (i, j) in sequence(first: (0, front), next: { $0 < self.queSize - 1 ? ($0 + 1, $1 + 1) : nil }) {\n res[i] = nums[j % capacity()]\n }\n return res\n }\n}\n
array_queue.js/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n #nums; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n #front = 0; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n #queSize = 0; // \u961f\u5217\u957f\u5ea6\n\n constructor(capacity) {\n this.#nums = new Array(capacity);\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n get capacity() {\n return this.#nums.length;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n get size() {\n return this.#queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty() {\n return this.#queSize === 0;\n }\n\n /* \u5165\u961f */\n push(num) {\n if (this.size === this.capacity) {\n console.log('\u961f\u5217\u5df2\u6ee1');\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n const rear = (this.#front + this.size) % this.capacity;\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n this.#nums[rear] = num;\n this.#queSize++;\n }\n\n /* \u51fa\u961f */\n pop() {\n const num = this.peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n this.#front = (this.#front + 1) % this.capacity;\n this.#queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peek() {\n if (this.isEmpty()) throw new Error('\u961f\u5217\u4e3a\u7a7a');\n return this.#nums[this.#front];\n }\n\n /* \u8fd4\u56de Array */\n toArray() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n const arr = new Array(this.size);\n for (let i = 0, j = this.#front; i < this.size; i++, j++) {\n arr[i] = this.#nums[j % this.capacity];\n }\n return arr;\n }\n}\n
array_queue.ts/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n private nums: number[]; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n private front: number; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n private queSize: number; // \u961f\u5217\u957f\u5ea6\n\n constructor(capacity: number) {\n this.nums = new Array(capacity);\n this.front = this.queSize = 0;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n get capacity(): number {\n return this.nums.length;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n get size(): number {\n return this.queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n isEmpty(): boolean {\n return this.queSize === 0;\n }\n\n /* \u5165\u961f */\n push(num: number): void {\n if (this.size === this.capacity) {\n console.log('\u961f\u5217\u5df2\u6ee1');\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n const rear = (this.front + this.queSize) % this.capacity;\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n this.nums[rear] = num;\n this.queSize++;\n }\n\n /* \u51fa\u961f */\n pop(): number {\n const num = this.peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n this.front = (this.front + 1) % this.capacity;\n this.queSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n peek(): number {\n if (this.isEmpty()) throw new Error('\u961f\u5217\u4e3a\u7a7a');\n return this.nums[this.front];\n }\n\n /* \u8fd4\u56de Array */\n toArray(): number[] {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n const arr = new Array(this.size);\n for (let i = 0, j = this.front; i < this.size; i++, j++) {\n arr[i] = this.nums[j % this.capacity];\n }\n return arr;\n }\n}\n
array_queue.dart/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nclass ArrayQueue {\n late List<int> _nums; // \u7528\u4e8e\u50a8\u5b58\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n late int _front; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n late int _queSize; // \u961f\u5217\u957f\u5ea6\n\n ArrayQueue(int capacity) {\n _nums = List.filled(capacity, 0);\n _front = _queSize = 0;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n int capaCity() {\n return _nums.length;\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n int size() {\n return _queSize;\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return _queSize == 0;\n }\n\n /* \u5165\u961f */\n void push(int _num) {\n if (_queSize == capaCity()) {\n throw Exception(\"\u961f\u5217\u5df2\u6ee1\");\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n int rear = (_front + _queSize) % capaCity();\n // \u5c06 _num \u6dfb\u52a0\u81f3\u961f\u5c3e\n _nums[rear] = _num;\n _queSize++;\n }\n\n /* \u51fa\u961f */\n int pop() {\n int _num = peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n _front = (_front + 1) % capaCity();\n _queSize--;\n return _num;\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n int peek() {\n if (isEmpty()) {\n throw Exception(\"\u961f\u5217\u4e3a\u7a7a\");\n }\n return _nums[_front];\n }\n\n /* \u8fd4\u56de Array */\n List<int> toArray() {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n final List<int> res = List.filled(_queSize, 0);\n for (int i = 0, j = _front; i < _queSize; i++, j++) {\n res[i] = _nums[j % capaCity()];\n }\n return res;\n }\n}\n
array_queue.rs/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\nstruct ArrayQueue {\n nums: Vec<i32>, // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n front: i32, // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n que_size: i32, // \u961f\u5217\u957f\u5ea6\n que_capacity: i32, // \u961f\u5217\u5bb9\u91cf\n}\n\nimpl ArrayQueue {\n /* \u6784\u9020\u65b9\u6cd5 */\n fn new(capacity: i32) -> ArrayQueue {\n ArrayQueue {\n nums: vec![0; capacity as usize],\n front: 0,\n que_size: 0,\n que_capacity: capacity,\n }\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\n fn capacity(&self) -> i32 {\n self.que_capacity\n }\n\n /* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\n fn size(&self) -> i32 {\n self.que_size\n }\n\n /* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\n fn is_empty(&self) -> bool {\n self.que_size == 0\n }\n\n /* \u5165\u961f */\n fn push(&mut self, num: i32) {\n if self.que_size == self.capacity() {\n println!(\"\u961f\u5217\u5df2\u6ee1\");\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n let rear = (self.front + self.que_size) % self.que_capacity;\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n self.nums[rear as usize] = num;\n self.que_size += 1;\n }\n\n /* \u51fa\u961f */\n fn pop(&mut self) -> i32 {\n let num = self.peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n self.front = (self.front + 1) % self.que_capacity;\n self.que_size -= 1;\n num\n }\n\n /* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\n fn peek(&self) -> i32 {\n if self.is_empty() {\n panic!(\"index out of bounds\");\n }\n self.nums[self.front as usize]\n }\n\n /* \u8fd4\u56de\u6570\u7ec4 */\n fn to_vector(&self) -> Vec<i32> {\n let cap = self.que_capacity;\n let mut j = self.front;\n let mut arr = vec![0; self.que_size as usize];\n for i in 0..self.que_size {\n arr[i as usize] = self.nums[(j % cap) as usize];\n j += 1;\n }\n arr\n }\n}\n
array_queue.c/* \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217 */\ntypedef struct {\n int *nums; // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4\n int front; // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n int queSize; // \u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e + 1\n int queCapacity; // \u961f\u5217\u5bb9\u91cf\n} ArrayQueue;\n\n/* \u6784\u9020\u51fd\u6570 */\nArrayQueue *newArrayQueue(int capacity) {\n ArrayQueue *queue = (ArrayQueue *)malloc(sizeof(ArrayQueue));\n // \u521d\u59cb\u5316\u6570\u7ec4\n queue->queCapacity = capacity;\n queue->nums = (int *)malloc(sizeof(int) * queue->queCapacity);\n queue->front = queue->queSize = 0;\n return queue;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delArrayQueue(ArrayQueue *queue) {\n free(queue->nums);\n free(queue);\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf */\nint capacity(ArrayQueue *queue) {\n return queue->queCapacity;\n}\n\n/* \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6 */\nint size(ArrayQueue *queue) {\n return queue->queSize;\n}\n\n/* \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a */\nbool empty(ArrayQueue *queue) {\n return queue->queSize == 0;\n}\n\n/* \u8bbf\u95ee\u961f\u9996\u5143\u7d20 */\nint peek(ArrayQueue *queue) {\n assert(size(queue) != 0);\n return queue->nums[queue->front];\n}\n\n/* \u5165\u961f */\nvoid push(ArrayQueue *queue, int num) {\n if (size(queue) == capacity(queue)) {\n printf(\"\u961f\u5217\u5df2\u6ee1\\r\\n\");\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n int rear = (queue->front + queue->queSize) % queue->queCapacity;\n // \u5c06 num \u6dfb\u52a0\u81f3\u961f\u5c3e\n queue->nums[rear] = num;\n queue->queSize++;\n}\n\n/* \u51fa\u961f */\nint pop(ArrayQueue *queue) {\n int num = peek(queue);\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n queue->front = (queue->front + 1) % queue->queCapacity;\n queue->queSize--;\n return num;\n}\n
array_queue.zig// \u57fa\u4e8e\u73af\u5f62\u6570\u7ec4\u5b9e\u73b0\u7684\u961f\u5217\nfn ArrayQueue(comptime T: type) type {\n return struct {\n const Self = @This();\n\n nums: []T = undefined, // \u7528\u4e8e\u5b58\u50a8\u961f\u5217\u5143\u7d20\u7684\u6570\u7ec4 \n cap: usize = 0, // \u961f\u5217\u5bb9\u91cf\n front: usize = 0, // \u961f\u9996\u6307\u9488\uff0c\u6307\u5411\u961f\u9996\u5143\u7d20\n queSize: usize = 0, // \u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e + 1\n mem_arena: ?std.heap.ArenaAllocator = null,\n mem_allocator: std.mem.Allocator = undefined, // \u5185\u5b58\u5206\u914d\u5668\n\n // \u6784\u9020\u51fd\u6570\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u6570\u7ec4\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator, cap: usize) !void {\n if (self.mem_arena == null) {\n self.mem_arena = std.heap.ArenaAllocator.init(allocator);\n self.mem_allocator = self.mem_arena.?.allocator();\n }\n self.cap = cap;\n self.nums = try self.mem_allocator.alloc(T, self.cap);\n @memset(self.nums, @as(T, 0));\n }\n\n // \u6790\u6784\u51fd\u6570\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.mem_arena == null) return;\n self.mem_arena.?.deinit();\n }\n\n // \u83b7\u53d6\u961f\u5217\u7684\u5bb9\u91cf\n pub fn capacity(self: *Self) usize {\n return self.cap;\n }\n\n // \u83b7\u53d6\u961f\u5217\u7684\u957f\u5ea6\n pub fn size(self: *Self) usize {\n return self.queSize;\n }\n\n // \u5224\u65ad\u961f\u5217\u662f\u5426\u4e3a\u7a7a\n pub fn isEmpty(self: *Self) bool {\n return self.queSize == 0;\n }\n\n // \u5165\u961f\n pub fn push(self: *Self, num: T) !void {\n if (self.size() == self.capacity()) {\n std.debug.print(\"\u961f\u5217\u5df2\u6ee1\\n\", .{});\n return;\n }\n // \u8ba1\u7b97\u961f\u5c3e\u6307\u9488\uff0c\u6307\u5411\u961f\u5c3e\u7d22\u5f15 + 1\n // \u901a\u8fc7\u53d6\u4f59\u64cd\u4f5c\u5b9e\u73b0 rear \u8d8a\u8fc7\u6570\u7ec4\u5c3e\u90e8\u540e\u56de\u5230\u5934\u90e8\n var rear = (self.front + self.queSize) % self.capacity();\n // \u5728\u5c3e\u8282\u70b9\u540e\u6dfb\u52a0 num\n self.nums[rear] = num;\n self.queSize += 1;\n } \n\n // \u51fa\u961f\n pub fn pop(self: *Self) T {\n var num = self.peek();\n // \u961f\u9996\u6307\u9488\u5411\u540e\u79fb\u52a8\u4e00\u4f4d\uff0c\u82e5\u8d8a\u8fc7\u5c3e\u90e8\uff0c\u5219\u8fd4\u56de\u5230\u6570\u7ec4\u5934\u90e8\n self.front = (self.front + 1) % self.capacity();\n self.queSize -= 1;\n return num;\n } \n\n // \u8bbf\u95ee\u961f\u9996\u5143\u7d20\n pub fn peek(self: *Self) T {\n if (self.isEmpty()) @panic(\"\u961f\u5217\u4e3a\u7a7a\");\n return self.nums[self.front];\n } \n\n // \u8fd4\u56de\u6570\u7ec4\n pub fn toArray(self: *Self) ![]T {\n // \u4ec5\u8f6c\u6362\u6709\u6548\u957f\u5ea6\u8303\u56f4\u5185\u7684\u5217\u8868\u5143\u7d20\n var res = try self.mem_allocator.alloc(T, self.size());\n @memset(res, @as(T, 0));\n var i: usize = 0;\n var j: usize = self.front;\n while (i < self.size()) : ({ i += 1; j += 1; }) {\n res[i] = self.nums[j % self.capacity()];\n }\n return res;\n }\n };\n}\n
Visualizing Code Full Screen >
The above implementation of the queue still has limitations: its length is fixed. However, this issue is not difficult to resolve. We can replace the array with a dynamic array to introduce an expansion mechanism. Interested readers can try to implement this themselves.
The comparison of the two implementations is consistent with that of the stack and is not repeated here.
"},{"location":"chapter_stack_and_queue/queue/#523-typical-applications-of-queue","title":"5.2.3 \u00a0 Typical Applications of Queue","text":"\"Stack\" is a linear data structure that follows the principle of Last-In-First-Out (LIFO).
We can compare a stack to a pile of plates on a table. To access the bottom plate, one must remove the plates on top. If we replace the plates with various types of elements (such as integers, characters, objects, etc.), we obtain the data structure known as a stack.
As shown in the following figure, we refer to the top of the pile of elements as the \"top of the stack\" and the bottom as the \"bottom of the stack.\" The operation of adding elements to the top of the stack is called \"push,\" and the operation of removing the top element is called \"pop.\"
Figure 5-1 \u00a0 Stack's Last-In-First-Out Rule
"},{"location":"chapter_stack_and_queue/stack/#511-common-operations-on-stack","title":"5.1.1 \u00a0 Common Operations on Stack","text":"The common operations on a stack are shown in the Table 5-1 . The specific method names depend on the programming language used. Here, we use push()
, pop()
, and peek()
as examples.
Table 5-1 \u00a0 Efficiency of Stack Operations
Method Description Time Complexitypush()
Push an element onto the stack (add to the top) \\(O(1)\\) pop()
Pop the top element from the stack \\(O(1)\\) peek()
Access the top element of the stack \\(O(1)\\) Typically, we can directly use the stack class built into the programming language. However, some languages may not specifically provide a stack class. In these cases, we can use the language's \"array\" or \"linked list\" as a stack and ignore operations that are not related to stack logic in the program.
PythonC++JavaC#GoSwiftJSTSDartRustCZig stack.py# Initialize the stack\n# Python does not have a built-in stack class, so a list can be used as a stack\nstack: list[int] = []\n\n# Push elements onto the stack\nstack.append(1)\nstack.append(3)\nstack.append(2)\nstack.append(5)\nstack.append(4)\n\n# Access the top element of the stack\npeek: int = stack[-1]\n\n# Pop an element from the stack\npop: int = stack.pop()\n\n# Get the length of the stack\nsize: int = len(stack)\n\n# Check if the stack is empty\nis_empty: bool = len(stack) == 0\n
stack.cpp/* Initialize the stack */\nstack<int> stack;\n\n/* Push elements onto the stack */\nstack.push(1);\nstack.push(3);\nstack.push(2);\nstack.push(5);\nstack.push(4);\n\n/* Access the top element of the stack */\nint top = stack.top();\n\n/* Pop an element from the stack */\nstack.pop(); // No return value\n\n/* Get the length of the stack */\nint size = stack.size();\n\n/* Check if the stack is empty */\nbool empty = stack.empty();\n
stack.java/* Initialize the stack */\nStack<Integer> stack = new Stack<>();\n\n/* Push elements onto the stack */\nstack.push(1);\nstack.push(3);\nstack.push(2);\nstack.push(5);\nstack.push(4);\n\n/* Access the top element of the stack */\nint peek = stack.peek();\n\n/* Pop an element from the stack */\nint pop = stack.pop();\n\n/* Get the length of the stack */\nint size = stack.size();\n\n/* Check if the stack is empty */\nboolean isEmpty = stack.isEmpty();\n
stack.cs/* Initialize the stack */\nStack<int> stack = new();\n\n/* Push elements onto the stack */\nstack.Push(1);\nstack.Push(3);\nstack.Push(2);\nstack.Push(5);\nstack.Push(4);\n\n/* Access the top element of the stack */\nint peek = stack.Peek();\n\n/* Pop an element from the stack */\nint pop = stack.Pop();\n\n/* Get the length of the stack */\nint size = stack.Count;\n\n/* Check if the stack is empty */\nbool isEmpty = stack.Count == 0;\n
stack_test.go/* Initialize the stack */\n// In Go, it is recommended to use a Slice as a stack\nvar stack []int\n\n/* Push elements onto the stack */\nstack = append(stack, 1)\nstack = append(stack, 3)\nstack = append(stack, 2)\nstack = append(stack, 5)\nstack = append(stack, 4)\n\n/* Access the top element of the stack */\npeek := stack[len(stack)-1]\n\n/* Pop an element from the stack */\npop := stack[len(stack)-1]\nstack = stack[:len(stack)-1]\n\n/* Get the length of the stack */\nsize := len(stack)\n\n/* Check if the stack is empty */\nisEmpty := len(stack) == 0\n
stack.swift/* Initialize the stack */\n// Swift does not have a built-in stack class, so Array can be used as a stack\nvar stack: [Int] = []\n\n/* Push elements onto the stack */\nstack.append(1)\nstack.append(3)\nstack.append(2)\nstack.append(5)\nstack.append(4)\n\n/* Access the top element of the stack */\nlet peek = stack.last!\n\n/* Pop an element from the stack */\nlet pop = stack.removeLast()\n\n/* Get the length of the stack */\nlet size = stack.count\n\n/* Check if the stack is empty */\nlet isEmpty = stack.isEmpty\n
stack.js/* Initialize the stack */\n// JavaScript does not have a built-in stack class, so Array can be used as a stack\nconst stack = [];\n\n/* Push elements onto the stack */\nstack.push(1);\nstack.push(3);\nstack.push(2);\nstack.push(5);\nstack.push(4);\n\n/* Access the top element of the stack */\nconst peek = stack[stack.length-1];\n\n/* Pop an element from the stack */\nconst pop = stack.pop();\n\n/* Get the length of the stack */\nconst size = stack.length;\n\n/* Check if the stack is empty */\nconst is_empty = stack.length === 0;\n
stack.ts/* Initialize the stack */\n// TypeScript does not have a built-in stack class, so Array can be used as a stack\nconst stack: number[] = [];\n\n/* Push elements onto the stack */\nstack.push(1);\nstack.push(3);\nstack.push(2);\nstack.push(5);\nstack.push(4);\n\n/* Access the top element of the stack */\nconst peek = stack[stack.length - 1];\n\n/* Pop an element from the stack */\nconst pop = stack.pop();\n\n/* Get the length of the stack */\nconst size = stack.length;\n\n/* Check if the stack is empty */\nconst is_empty = stack.length === 0;\n
stack.dart/* Initialize the stack */\n// Dart does not have a built-in stack class, so List can be used as a stack\nList<int> stack = [];\n\n/* Push elements onto the stack */\nstack.add(1);\nstack.add(3);\nstack.add(2);\nstack.add(5);\nstack.add(4);\n\n/* Access the top element of the stack */\nint peek = stack.last;\n\n/* Pop an element from the stack */\nint pop = stack.removeLast();\n\n/* Get the length of the stack */\nint size = stack.length;\n\n/* Check if the stack is empty */\nbool isEmpty = stack.isEmpty;\n
stack.rs/* Initialize the stack */\n// Use Vec as a stack\nlet mut stack: Vec<i32> = Vec::new();\n\n/* Push elements onto the stack */\nstack.push(1);\nstack.push(3);\nstack.push(2);\nstack.push(5);\nstack.push(4);\n\n/* Access the top element of the stack */\nlet top = stack.last().unwrap();\n\n/* Pop an element from the stack */\nlet pop = stack.pop().unwrap();\n\n/* Get the length of the stack */\nlet size = stack.len();\n\n/* Check if the stack is empty */\nlet is_empty = stack.is_empty();\n
stack.c// C does not provide a built-in stack\n
stack.zig\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/stack/#512-implementing-a-stack","title":"5.1.2 \u00a0 Implementing a Stack","text":"To understand the mechanics of a stack more deeply, let's try implementing a stack class ourselves.
A stack follows the principle of Last-In-First-Out, which means we can only add or remove elements at the top of the stack. However, both arrays and linked lists allow adding and removing elements at any position, therefore a stack can be seen as a restricted array or linked list. In other words, we can \"mask\" some unrelated operations of arrays or linked lists to make their logic conform to the characteristics of a stack.
"},{"location":"chapter_stack_and_queue/stack/#1-implementation-based-on-linked-list","title":"1. \u00a0 Implementation Based on Linked List","text":"When implementing a stack using a linked list, we can consider the head node of the list as the top of the stack and the tail node as the bottom of the stack.
As shown in the Figure 5-2 , for the push operation, we simply insert elements at the head of the linked list. This method of node insertion is known as \"head insertion.\" For the pop operation, we just need to remove the head node from the list.
LinkedListStackpush()pop()Figure 5-2 \u00a0 Implementing Stack with Linked List for Push and Pop Operations
Below is an example code for implementing a stack based on a linked list:
PythonC++JavaC#GoSwiftJSTSDartRustCZig linkedlist_stack.pyclass LinkedListStack:\n \"\"\"\u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808\"\"\"\n\n def __init__(self):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._peek: ListNode | None = None\n self._size: int = 0\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u6808\u7684\u957f\u5ea6\"\"\"\n return self._size\n\n def is_empty(self) -> bool:\n \"\"\"\u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a\"\"\"\n return not self._peek\n\n def push(self, val: int):\n \"\"\"\u5165\u6808\"\"\"\n node = ListNode(val)\n node.next = self._peek\n self._peek = node\n self._size += 1\n\n def pop(self) -> int:\n \"\"\"\u51fa\u6808\"\"\"\n num = self.peek()\n self._peek = self._peek.next\n self._size -= 1\n return num\n\n def peek(self) -> int:\n \"\"\"\u8bbf\u95ee\u6808\u9876\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u6808\u4e3a\u7a7a\")\n return self._peek.val\n\n def to_list(self) -> list[int]:\n \"\"\"\u8f6c\u5316\u4e3a\u5217\u8868\u7528\u4e8e\u6253\u5370\"\"\"\n arr = []\n node = self._peek\n while node:\n arr.append(node.val)\n node = node.next\n arr.reverse()\n return arr\n
linkedlist_stack.cpp/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n private:\n ListNode *stackTop; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n int stkSize; // \u6808\u7684\u957f\u5ea6\n\n public:\n LinkedListStack() {\n stackTop = nullptr;\n stkSize = 0;\n }\n\n ~LinkedListStack() {\n // \u904d\u5386\u94fe\u8868\u5220\u9664\u8282\u70b9\uff0c\u91ca\u653e\u5185\u5b58\n freeMemoryLinkedList(stackTop);\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n int size() {\n return stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u6808 */\n void push(int num) {\n ListNode *node = new ListNode(num);\n node->next = stackTop;\n stackTop = node;\n stkSize++;\n }\n\n /* \u51fa\u6808 */\n int pop() {\n int num = top();\n ListNode *tmp = stackTop;\n stackTop = stackTop->next;\n // \u91ca\u653e\u5185\u5b58\n delete tmp;\n stkSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n int top() {\n if (isEmpty())\n throw out_of_range(\"\u6808\u4e3a\u7a7a\");\n return stackTop->val;\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n vector<int> toVector() {\n ListNode *node = stackTop;\n vector<int> res(size());\n for (int i = res.size() - 1; i >= 0; i--) {\n res[i] = node->val;\n node = node->next;\n }\n return res;\n }\n};\n
linkedlist_stack.java/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n private ListNode stackPeek; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n private int stkSize = 0; // \u6808\u7684\u957f\u5ea6\n\n public LinkedListStack() {\n stackPeek = null;\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n public int size() {\n return stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n public boolean isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u6808 */\n public void push(int num) {\n ListNode node = new ListNode(num);\n node.next = stackPeek;\n stackPeek = node;\n stkSize++;\n }\n\n /* \u51fa\u6808 */\n public int pop() {\n int num = peek();\n stackPeek = stackPeek.next;\n stkSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n public int peek() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return stackPeek.val;\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public int[] toArray() {\n ListNode node = stackPeek;\n int[] res = new int[size()];\n for (int i = res.length - 1; i >= 0; i--) {\n res[i] = node.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_stack.cs/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n ListNode? stackPeek; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n int stkSize = 0; // \u6808\u7684\u957f\u5ea6\n\n public LinkedListStack() {\n stackPeek = null;\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n public int Size() {\n return stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n public bool IsEmpty() {\n return Size() == 0;\n }\n\n /* \u5165\u6808 */\n public void Push(int num) {\n ListNode node = new(num) {\n next = stackPeek\n };\n stackPeek = node;\n stkSize++;\n }\n\n /* \u51fa\u6808 */\n public int Pop() {\n int num = Peek();\n stackPeek = stackPeek!.next;\n stkSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n public int Peek() {\n if (IsEmpty())\n throw new Exception();\n return stackPeek!.val;\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public int[] ToArray() {\n if (stackPeek == null)\n return [];\n\n ListNode? node = stackPeek;\n int[] res = new int[Size()];\n for (int i = res.Length - 1; i >= 0; i--) {\n res[i] = node!.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_stack.go/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\ntype linkedListStack struct {\n // \u4f7f\u7528\u5185\u7f6e\u5305 list \u6765\u5b9e\u73b0\u6808\n data *list.List\n}\n\n/* \u521d\u59cb\u5316\u6808 */\nfunc newLinkedListStack() *linkedListStack {\n return &linkedListStack{\n data: list.New(),\n }\n}\n\n/* \u5165\u6808 */\nfunc (s *linkedListStack) push(value int) {\n s.data.PushBack(value)\n}\n\n/* \u51fa\u6808 */\nfunc (s *linkedListStack) pop() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Back()\n s.data.Remove(e)\n return e.Value\n}\n\n/* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\nfunc (s *linkedListStack) peek() any {\n if s.isEmpty() {\n return nil\n }\n e := s.data.Back()\n return e.Value\n}\n\n/* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\nfunc (s *linkedListStack) size() int {\n return s.data.Len()\n}\n\n/* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\nfunc (s *linkedListStack) isEmpty() bool {\n return s.data.Len() == 0\n}\n\n/* \u83b7\u53d6 List \u7528\u4e8e\u6253\u5370 */\nfunc (s *linkedListStack) toList() *list.List {\n return s.data\n}\n
linkedlist_stack.swift/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n private var _peek: ListNode? // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n private var _size = 0 // \u6808\u7684\u957f\u5ea6\n\n init() {}\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n func size() -> Int {\n _size\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n func isEmpty() -> Bool {\n size() == 0\n }\n\n /* \u5165\u6808 */\n func push(num: Int) {\n let node = ListNode(x: num)\n node.next = _peek\n _peek = node\n _size += 1\n }\n\n /* \u51fa\u6808 */\n @discardableResult\n func pop() -> Int {\n let num = peek()\n _peek = _peek?.next\n _size -= 1\n return num\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n func peek() -> Int {\n if isEmpty() {\n fatalError(\"\u6808\u4e3a\u7a7a\")\n }\n return _peek!.val\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n func toArray() -> [Int] {\n var node = _peek\n var res = Array(repeating: 0, count: _size)\n for i in sequence(first: res.count - 1, next: { $0 >= 0 + 1 ? $0 - 1 : nil }) {\n res[i] = node!.val\n node = node?.next\n }\n return res\n }\n}\n
linkedlist_stack.js/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n #stackPeek; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n #stkSize = 0; // \u6808\u7684\u957f\u5ea6\n\n constructor() {\n this.#stackPeek = null;\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n get size() {\n return this.#stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n isEmpty() {\n return this.size === 0;\n }\n\n /* \u5165\u6808 */\n push(num) {\n const node = new ListNode(num);\n node.next = this.#stackPeek;\n this.#stackPeek = node;\n this.#stkSize++;\n }\n\n /* \u51fa\u6808 */\n pop() {\n const num = this.peek();\n this.#stackPeek = this.#stackPeek.next;\n this.#stkSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n peek() {\n if (!this.#stackPeek) throw new Error('\u6808\u4e3a\u7a7a');\n return this.#stackPeek.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n toArray() {\n let node = this.#stackPeek;\n const res = new Array(this.size);\n for (let i = res.length - 1; i >= 0; i--) {\n res[i] = node.val;\n node = node.next;\n }\n return res;\n }\n}\n
linkedlist_stack.ts/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n private stackPeek: ListNode | null; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n private stkSize: number = 0; // \u6808\u7684\u957f\u5ea6\n\n constructor() {\n this.stackPeek = null;\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n get size(): number {\n return this.stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n isEmpty(): boolean {\n return this.size === 0;\n }\n\n /* \u5165\u6808 */\n push(num: number): void {\n const node = new ListNode(num);\n node.next = this.stackPeek;\n this.stackPeek = node;\n this.stkSize++;\n }\n\n /* \u51fa\u6808 */\n pop(): number {\n const num = this.peek();\n if (!this.stackPeek) throw new Error('\u6808\u4e3a\u7a7a');\n this.stackPeek = this.stackPeek.next;\n this.stkSize--;\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n peek(): number {\n if (!this.stackPeek) throw new Error('\u6808\u4e3a\u7a7a');\n return this.stackPeek.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n toArray(): number[] {\n let node = this.stackPeek;\n const res = new Array<number>(this.size);\n for (let i = res.length - 1; i >= 0; i--) {\n res[i] = node!.val;\n node = node!.next;\n }\n return res;\n }\n}\n
linkedlist_stack.dart/* \u57fa\u4e8e\u94fe\u8868\u7c7b\u5b9e\u73b0\u7684\u6808 */\nclass LinkedListStack {\n ListNode? _stackPeek; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n int _stkSize = 0; // \u6808\u7684\u957f\u5ea6\n\n LinkedListStack() {\n _stackPeek = null;\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n int size() {\n return _stkSize;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return _stkSize == 0;\n }\n\n /* \u5165\u6808 */\n void push(int _num) {\n final ListNode node = ListNode(_num);\n node.next = _stackPeek;\n _stackPeek = node;\n _stkSize++;\n }\n\n /* \u51fa\u6808 */\n int pop() {\n final int _num = peek();\n _stackPeek = _stackPeek!.next;\n _stkSize--;\n return _num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n int peek() {\n if (_stackPeek == null) {\n throw Exception(\"\u6808\u4e3a\u7a7a\");\n }\n return _stackPeek!.val;\n }\n\n /* \u5c06\u94fe\u8868\u8f6c\u5316\u4e3a List \u5e76\u8fd4\u56de */\n List<int> toList() {\n ListNode? node = _stackPeek;\n List<int> list = [];\n while (node != null) {\n list.add(node.val);\n node = node.next;\n }\n list = list.reversed.toList();\n return list;\n }\n}\n
linkedlist_stack.rs/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\n#[allow(dead_code)]\npub struct LinkedListStack<T> {\n stack_peek: Option<Rc<RefCell<ListNode<T>>>>, // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n stk_size: usize, // \u6808\u7684\u957f\u5ea6\n}\n\nimpl<T: Copy> LinkedListStack<T> {\n pub fn new() -> Self {\n Self {\n stack_peek: None,\n stk_size: 0,\n }\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n pub fn size(&self) -> usize {\n return self.stk_size;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n pub fn is_empty(&self) -> bool {\n return self.size() == 0;\n }\n\n /* \u5165\u6808 */\n pub fn push(&mut self, num: T) {\n let node = ListNode::new(num);\n node.borrow_mut().next = self.stack_peek.take();\n self.stack_peek = Some(node);\n self.stk_size += 1;\n }\n\n /* \u51fa\u6808 */\n pub fn pop(&mut self) -> Option<T> {\n self.stack_peek.take().map(|old_head| {\n match old_head.borrow_mut().next.take() {\n Some(new_head) => {\n self.stack_peek = Some(new_head);\n }\n None => {\n self.stack_peek = None;\n }\n }\n self.stk_size -= 1;\n Rc::try_unwrap(old_head).ok().unwrap().into_inner().val\n })\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n pub fn peek(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {\n self.stack_peek.as_ref()\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n pub fn to_array(&self, head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {\n if let Some(node) = head {\n let mut nums = self.to_array(node.borrow().next.as_ref());\n nums.push(node.borrow().val);\n return nums;\n }\n return Vec::new();\n }\n}\n
linkedlist_stack.c/* \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808 */\ntypedef struct {\n ListNode *top; // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n int size; // \u6808\u7684\u957f\u5ea6\n} LinkedListStack;\n\n/* \u6784\u9020\u51fd\u6570 */\nLinkedListStack *newLinkedListStack() {\n LinkedListStack *s = malloc(sizeof(LinkedListStack));\n s->top = NULL;\n s->size = 0;\n return s;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delLinkedListStack(LinkedListStack *s) {\n while (s->top) {\n ListNode *n = s->top->next;\n free(s->top);\n s->top = n;\n }\n free(s);\n}\n\n/* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\nint size(LinkedListStack *s) {\n return s->size;\n}\n\n/* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\nbool isEmpty(LinkedListStack *s) {\n return size(s) == 0;\n}\n\n/* \u5165\u6808 */\nvoid push(LinkedListStack *s, int num) {\n ListNode *node = (ListNode *)malloc(sizeof(ListNode));\n node->next = s->top; // \u66f4\u65b0\u65b0\u52a0\u8282\u70b9\u6307\u9488\u57df\n node->val = num; // \u66f4\u65b0\u65b0\u52a0\u8282\u70b9\u6570\u636e\u57df\n s->top = node; // \u66f4\u65b0\u6808\u9876\n s->size++; // \u66f4\u65b0\u6808\u5927\u5c0f\n}\n\n/* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\nint peek(LinkedListStack *s) {\n if (s->size == 0) {\n printf(\"\u6808\u4e3a\u7a7a\\n\");\n return INT_MAX;\n }\n return s->top->val;\n}\n\n/* \u51fa\u6808 */\nint pop(LinkedListStack *s) {\n int val = peek(s);\n ListNode *tmp = s->top;\n s->top = s->top->next;\n // \u91ca\u653e\u5185\u5b58\n free(tmp);\n s->size--;\n return val;\n}\n
linkedlist_stack.zig// \u57fa\u4e8e\u94fe\u8868\u5b9e\u73b0\u7684\u6808\nfn LinkedListStack(comptime T: type) type {\n return struct {\n const Self = @This();\n\n stack_top: ?*inc.ListNode(T) = null, // \u5c06\u5934\u8282\u70b9\u4f5c\u4e3a\u6808\u9876\n stk_size: usize = 0, // \u6808\u7684\u957f\u5ea6\n mem_arena: ?std.heap.ArenaAllocator = null,\n mem_allocator: std.mem.Allocator = undefined, // \u5185\u5b58\u5206\u914d\u5668\n\n // \u6784\u9020\u51fd\u6570\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u6808\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator) !void {\n if (self.mem_arena == null) {\n self.mem_arena = std.heap.ArenaAllocator.init(allocator);\n self.mem_allocator = self.mem_arena.?.allocator();\n }\n self.stack_top = null;\n self.stk_size = 0;\n }\n\n // \u6790\u6784\u51fd\u6570\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.mem_arena == null) return;\n self.mem_arena.?.deinit();\n }\n\n // \u83b7\u53d6\u6808\u7684\u957f\u5ea6\n pub fn size(self: *Self) usize {\n return self.stk_size;\n }\n\n // \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a\n pub fn isEmpty(self: *Self) bool {\n return self.size() == 0;\n }\n\n // \u8bbf\u95ee\u6808\u9876\u5143\u7d20\n pub fn peek(self: *Self) T {\n if (self.size() == 0) @panic(\"\u6808\u4e3a\u7a7a\");\n return self.stack_top.?.val;\n } \n\n // \u5165\u6808\n pub fn push(self: *Self, num: T) !void {\n var node = try self.mem_allocator.create(inc.ListNode(T));\n node.init(num);\n node.next = self.stack_top;\n self.stack_top = node;\n self.stk_size += 1;\n } \n\n // \u51fa\u6808\n pub fn pop(self: *Self) T {\n var num = self.peek();\n self.stack_top = self.stack_top.?.next;\n self.stk_size -= 1;\n return num;\n } \n\n // \u5c06\u6808\u8f6c\u6362\u4e3a\u6570\u7ec4\n pub fn toArray(self: *Self) ![]T {\n var node = self.stack_top;\n var res = try self.mem_allocator.alloc(T, self.size());\n @memset(res, @as(T, 0));\n var i: usize = 0;\n while (i < res.len) : (i += 1) {\n res[res.len - i - 1] = node.?.val;\n node = node.?.next;\n }\n return res;\n }\n };\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/stack/#2-implementation-based-on-array","title":"2. \u00a0 Implementation Based on Array","text":"When implementing a stack using an array, we can consider the end of the array as the top of the stack. As shown in the Figure 5-3 , push and pop operations correspond to adding and removing elements at the end of the array, respectively, both with a time complexity of \\(O(1)\\).
ArrayStackpush()pop()Figure 5-3 \u00a0 Implementing Stack with Array for Push and Pop Operations
Since the elements to be pushed onto the stack may continuously increase, we can use a dynamic array, thus avoiding the need to handle array expansion ourselves. Here is an example code:
PythonC++JavaC#GoSwiftJSTSDartRustCZig array_stack.pyclass ArrayStack:\n \"\"\"\u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808\"\"\"\n\n def __init__(self):\n \"\"\"\u6784\u9020\u65b9\u6cd5\"\"\"\n self._stack: list[int] = []\n\n def size(self) -> int:\n \"\"\"\u83b7\u53d6\u6808\u7684\u957f\u5ea6\"\"\"\n return len(self._stack)\n\n def is_empty(self) -> bool:\n \"\"\"\u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a\"\"\"\n return self._stack == []\n\n def push(self, item: int):\n \"\"\"\u5165\u6808\"\"\"\n self._stack.append(item)\n\n def pop(self) -> int:\n \"\"\"\u51fa\u6808\"\"\"\n if self.is_empty():\n raise IndexError(\"\u6808\u4e3a\u7a7a\")\n return self._stack.pop()\n\n def peek(self) -> int:\n \"\"\"\u8bbf\u95ee\u6808\u9876\u5143\u7d20\"\"\"\n if self.is_empty():\n raise IndexError(\"\u6808\u4e3a\u7a7a\")\n return self._stack[-1]\n\n def to_list(self) -> list[int]:\n \"\"\"\u8fd4\u56de\u5217\u8868\u7528\u4e8e\u6253\u5370\"\"\"\n return self._stack\n
array_stack.cpp/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n private:\n vector<int> stack;\n\n public:\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n int size() {\n return stack.size();\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return stack.size() == 0;\n }\n\n /* \u5165\u6808 */\n void push(int num) {\n stack.push_back(num);\n }\n\n /* \u51fa\u6808 */\n int pop() {\n int num = top();\n stack.pop_back();\n return num;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n int top() {\n if (isEmpty())\n throw out_of_range(\"\u6808\u4e3a\u7a7a\");\n return stack.back();\n }\n\n /* \u8fd4\u56de Vector */\n vector<int> toVector() {\n return stack;\n }\n};\n
array_stack.java/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n private ArrayList<Integer> stack;\n\n public ArrayStack() {\n // \u521d\u59cb\u5316\u5217\u8868\uff08\u52a8\u6001\u6570\u7ec4\uff09\n stack = new ArrayList<>();\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n public int size() {\n return stack.size();\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n public boolean isEmpty() {\n return size() == 0;\n }\n\n /* \u5165\u6808 */\n public void push(int num) {\n stack.add(num);\n }\n\n /* \u51fa\u6808 */\n public int pop() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return stack.remove(size() - 1);\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n public int peek() {\n if (isEmpty())\n throw new IndexOutOfBoundsException();\n return stack.get(size() - 1);\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public Object[] toArray() {\n return stack.toArray();\n }\n}\n
array_stack.cs/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n List<int> stack;\n public ArrayStack() {\n // \u521d\u59cb\u5316\u5217\u8868\uff08\u52a8\u6001\u6570\u7ec4\uff09\n stack = [];\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n public int Size() {\n return stack.Count;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n public bool IsEmpty() {\n return Size() == 0;\n }\n\n /* \u5165\u6808 */\n public void Push(int num) {\n stack.Add(num);\n }\n\n /* \u51fa\u6808 */\n public int Pop() {\n if (IsEmpty())\n throw new Exception();\n var val = Peek();\n stack.RemoveAt(Size() - 1);\n return val;\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n public int Peek() {\n if (IsEmpty())\n throw new Exception();\n return stack[Size() - 1];\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n public int[] ToArray() {\n return [.. stack];\n }\n}\n
array_stack.go/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\ntype arrayStack struct {\n data []int // \u6570\u636e\n}\n\n/* \u521d\u59cb\u5316\u6808 */\nfunc newArrayStack() *arrayStack {\n return &arrayStack{\n // \u8bbe\u7f6e\u6808\u7684\u957f\u5ea6\u4e3a 0\uff0c\u5bb9\u91cf\u4e3a 16\n data: make([]int, 0, 16),\n }\n}\n\n/* \u6808\u7684\u957f\u5ea6 */\nfunc (s *arrayStack) size() int {\n return len(s.data)\n}\n\n/* \u6808\u662f\u5426\u4e3a\u7a7a */\nfunc (s *arrayStack) isEmpty() bool {\n return s.size() == 0\n}\n\n/* \u5165\u6808 */\nfunc (s *arrayStack) push(v int) {\n // \u5207\u7247\u4f1a\u81ea\u52a8\u6269\u5bb9\n s.data = append(s.data, v)\n}\n\n/* \u51fa\u6808 */\nfunc (s *arrayStack) pop() any {\n val := s.peek()\n s.data = s.data[:len(s.data)-1]\n return val\n}\n\n/* \u83b7\u53d6\u6808\u9876\u5143\u7d20 */\nfunc (s *arrayStack) peek() any {\n if s.isEmpty() {\n return nil\n }\n val := s.data[len(s.data)-1]\n return val\n}\n\n/* \u83b7\u53d6 Slice \u7528\u4e8e\u6253\u5370 */\nfunc (s *arrayStack) toSlice() []int {\n return s.data\n}\n
array_stack.swift/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n private var stack: [Int]\n\n init() {\n // \u521d\u59cb\u5316\u5217\u8868\uff08\u52a8\u6001\u6570\u7ec4\uff09\n stack = []\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n func size() -> Int {\n stack.count\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n func isEmpty() -> Bool {\n stack.isEmpty\n }\n\n /* \u5165\u6808 */\n func push(num: Int) {\n stack.append(num)\n }\n\n /* \u51fa\u6808 */\n @discardableResult\n func pop() -> Int {\n if isEmpty() {\n fatalError(\"\u6808\u4e3a\u7a7a\")\n }\n return stack.removeLast()\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n func peek() -> Int {\n if isEmpty() {\n fatalError(\"\u6808\u4e3a\u7a7a\")\n }\n return stack.last!\n }\n\n /* \u5c06 List \u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n func toArray() -> [Int] {\n stack\n }\n}\n
array_stack.js/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n #stack;\n constructor() {\n this.#stack = [];\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n get size() {\n return this.#stack.length;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n isEmpty() {\n return this.#stack.length === 0;\n }\n\n /* \u5165\u6808 */\n push(num) {\n this.#stack.push(num);\n }\n\n /* \u51fa\u6808 */\n pop() {\n if (this.isEmpty()) throw new Error('\u6808\u4e3a\u7a7a');\n return this.#stack.pop();\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n top() {\n if (this.isEmpty()) throw new Error('\u6808\u4e3a\u7a7a');\n return this.#stack[this.#stack.length - 1];\n }\n\n /* \u8fd4\u56de Array */\n toArray() {\n return this.#stack;\n }\n}\n
array_stack.ts/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n private stack: number[];\n constructor() {\n this.stack = [];\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n get size(): number {\n return this.stack.length;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n isEmpty(): boolean {\n return this.stack.length === 0;\n }\n\n /* \u5165\u6808 */\n push(num: number): void {\n this.stack.push(num);\n }\n\n /* \u51fa\u6808 */\n pop(): number | undefined {\n if (this.isEmpty()) throw new Error('\u6808\u4e3a\u7a7a');\n return this.stack.pop();\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n top(): number | undefined {\n if (this.isEmpty()) throw new Error('\u6808\u4e3a\u7a7a');\n return this.stack[this.stack.length - 1];\n }\n\n /* \u8fd4\u56de Array */\n toArray() {\n return this.stack;\n }\n}\n
array_stack.dart/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nclass ArrayStack {\n late List<int> _stack;\n ArrayStack() {\n _stack = [];\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n int size() {\n return _stack.length;\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n bool isEmpty() {\n return _stack.isEmpty;\n }\n\n /* \u5165\u6808 */\n void push(int _num) {\n _stack.add(_num);\n }\n\n /* \u51fa\u6808 */\n int pop() {\n if (isEmpty()) {\n throw Exception(\"\u6808\u4e3a\u7a7a\");\n }\n return _stack.removeLast();\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n int peek() {\n if (isEmpty()) {\n throw Exception(\"\u6808\u4e3a\u7a7a\");\n }\n return _stack.last;\n }\n\n /* \u5c06\u6808\u8f6c\u5316\u4e3a Array \u5e76\u8fd4\u56de */\n List<int> toArray() => _stack;\n}\n
array_stack.rs/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\nstruct ArrayStack<T> {\n stack: Vec<T>,\n}\n\nimpl<T> ArrayStack<T> {\n /* \u521d\u59cb\u5316\u6808 */\n fn new() -> ArrayStack<T> {\n ArrayStack::<T> { stack: Vec::<T>::new() }\n }\n\n /* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\n fn size(&self) -> usize {\n self.stack.len()\n }\n\n /* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\n fn is_empty(&self) -> bool {\n self.size() == 0\n }\n\n /* \u5165\u6808 */\n fn push(&mut self, num: T) {\n self.stack.push(num);\n }\n\n /* \u51fa\u6808 */\n fn pop(&mut self) -> Option<T> {\n match self.stack.pop() {\n Some(num) => Some(num),\n None => None,\n }\n }\n\n /* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\n fn peek(&self) -> Option<&T> {\n if self.is_empty() { panic!(\"\u6808\u4e3a\u7a7a\") };\n self.stack.last()\n }\n\n /* \u8fd4\u56de &Vec */\n fn to_array(&self) -> &Vec<T> {\n &self.stack\n }\n}\n
array_stack.c/* \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808 */\ntypedef struct {\n int *data;\n int size;\n} ArrayStack;\n\n/* \u6784\u9020\u51fd\u6570 */\nArrayStack *newArrayStack() {\n ArrayStack *stack = malloc(sizeof(ArrayStack));\n // \u521d\u59cb\u5316\u4e00\u4e2a\u5927\u5bb9\u91cf\uff0c\u907f\u514d\u6269\u5bb9\n stack->data = malloc(sizeof(int) * MAX_SIZE);\n stack->size = 0;\n return stack;\n}\n\n/* \u6790\u6784\u51fd\u6570 */\nvoid delArrayStack(ArrayStack *stack) {\n free(stack->data);\n free(stack);\n}\n\n/* \u83b7\u53d6\u6808\u7684\u957f\u5ea6 */\nint size(ArrayStack *stack) {\n return stack->size;\n}\n\n/* \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a */\nbool isEmpty(ArrayStack *stack) {\n return stack->size == 0;\n}\n\n/* \u5165\u6808 */\nvoid push(ArrayStack *stack, int num) {\n if (stack->size == MAX_SIZE) {\n printf(\"\u6808\u5df2\u6ee1\\n\");\n return;\n }\n stack->data[stack->size] = num;\n stack->size++;\n}\n\n/* \u8bbf\u95ee\u6808\u9876\u5143\u7d20 */\nint peek(ArrayStack *stack) {\n if (stack->size == 0) {\n printf(\"\u6808\u4e3a\u7a7a\\n\");\n return INT_MAX;\n }\n return stack->data[stack->size - 1];\n}\n\n/* \u51fa\u6808 */\nint pop(ArrayStack *stack) {\n int val = peek(stack);\n stack->size--;\n return val;\n}\n
array_stack.zig// \u57fa\u4e8e\u6570\u7ec4\u5b9e\u73b0\u7684\u6808\nfn ArrayStack(comptime T: type) type {\n return struct {\n const Self = @This();\n\n stack: ?std.ArrayList(T) = null, \n\n // \u6784\u9020\u65b9\u6cd5\uff08\u5206\u914d\u5185\u5b58+\u521d\u59cb\u5316\u6808\uff09\n pub fn init(self: *Self, allocator: std.mem.Allocator) void {\n if (self.stack == null) {\n self.stack = std.ArrayList(T).init(allocator);\n }\n }\n\n // \u6790\u6784\u65b9\u6cd5\uff08\u91ca\u653e\u5185\u5b58\uff09\n pub fn deinit(self: *Self) void {\n if (self.stack == null) return;\n self.stack.?.deinit();\n }\n\n // \u83b7\u53d6\u6808\u7684\u957f\u5ea6\n pub fn size(self: *Self) usize {\n return self.stack.?.items.len;\n }\n\n // \u5224\u65ad\u6808\u662f\u5426\u4e3a\u7a7a\n pub fn isEmpty(self: *Self) bool {\n return self.size() == 0;\n }\n\n // \u8bbf\u95ee\u6808\u9876\u5143\u7d20\n pub fn peek(self: *Self) T {\n if (self.isEmpty()) @panic(\"\u6808\u4e3a\u7a7a\");\n return self.stack.?.items[self.size() - 1];\n } \n\n // \u5165\u6808\n pub fn push(self: *Self, num: T) !void {\n try self.stack.?.append(num);\n } \n\n // \u51fa\u6808\n pub fn pop(self: *Self) T {\n var num = self.stack.?.pop();\n return num;\n } \n\n // \u8fd4\u56de ArrayList\n pub fn toList(self: *Self) std.ArrayList(T) {\n return self.stack.?;\n }\n };\n}\n
Visualizing Code Full Screen >
"},{"location":"chapter_stack_and_queue/stack/#513-comparison-of-the-two-implementations","title":"5.1.3 \u00a0 Comparison of the Two Implementations","text":"Supported Operations
Both implementations support all the operations defined in a stack. The array implementation additionally supports random access, but this is beyond the scope of a stack definition and is generally not used.
Time Efficiency
In the array-based implementation, both push and pop operations occur in pre-allocated continuous memory, which has good cache locality and therefore higher efficiency. However, if the push operation exceeds the array capacity, it triggers a resizing mechanism, making the time complexity of that push operation \\(O(n)\\).
In the linked list implementation, list expansion is very flexible, and there is no efficiency decrease issue as in array expansion. However, the push operation requires initializing a node object and modifying pointers, so its efficiency is relatively lower. If the elements being pushed are already node objects, then the initialization step can be skipped, improving efficiency.
Thus, when the elements for push and pop operations are basic data types like int
or double
, we can draw the following conclusions:
Space Efficiency
When initializing a list, the system allocates an \"initial capacity,\" which might exceed the actual need; moreover, the expansion mechanism usually increases capacity by a specific factor (like doubling), which may also exceed the actual need. Therefore, the array-based stack might waste some space.
However, since linked list nodes require extra space for storing pointers, the space occupied by linked list nodes is relatively larger.
In summary, we cannot simply determine which implementation is more memory-efficient. It requires analysis based on specific circumstances.
"},{"location":"chapter_stack_and_queue/stack/#514-typical-applications-of-stack","title":"5.1.4 \u00a0 Typical Applications of Stack","text":"Q: Is the browser's forward and backward functionality implemented with a doubly linked list?
The forward and backward functionality of a browser fundamentally represents the \"stack\" concept. When a user visits a new page, it is added to the top of the stack; when they click the back button, the page is popped from the top. A double-ended queue can conveniently implement some additional operations, as mentioned in the \"Double-Ended Queue\" section.
Q: After popping from a stack, is it necessary to free the memory of the popped node?
If the popped node will still be used later, it's not necessary to free its memory. In languages like Java and Python that have automatic garbage collection, manual memory release isn't required; in C and C++, manual memory release is necessary if the node will no longer be used.
Q: A double-ended queue seems like two stacks joined together. What are its uses?
A double-ended queue is essentially a combination of a stack and a queue, or like two stacks joined together. It exhibits both stack and queue logic, therefore enabling the implementation of all applications of stacks and queues with added flexibility.
Q: How exactly are undo and redo implemented?
Undo and redo are implemented using two stacks: Stack A for undo and Stack B for redo.
500 幅动画图解、12 种编程语言代码、2000 条社区问答,助你快速入门数据结构与算法
+提供网页版和 PDF 版,兼容 PC、平板或手机,随时随地阅读
+“一本通俗易懂的数据结构与算法入门书,引导读者手脑并用地学习,强烈推荐算法初学者阅读。”
+—— 邓俊辉,清华大学计算机系教授
+“如果我当年学数据结构与算法的时候有《Hello 算法》,学起来应该会简单 10 倍!”
+—— 李沐,亚马逊资深首席科学家
+内容清晰易懂,学习曲线平滑
+"A picture is worth a thousand words."“一图胜千言”
+十余种编程语言,代码可视化运行
+"Talk is cheap. Show me the code."“少吹牛,看代码”
+欢迎讨论与提问,读者间携手共进
+"Knowledge increases by sharing."“知识在分享中得以增长”
+