From cbf4ab0aaaf2d53e02cd9617aa942f00cbba3ba3 Mon Sep 17 00:00:00 2001 From: krahets Date: Fri, 25 Nov 2022 03:59:38 +0800 Subject: [PATCH] Add Python codes for the chapter of array and linked list. --- .../chapter_array_and_linkedlist/my_list.java | 4 +- .../chapter_array_and_linkedlist/array.py | 89 +++++++++++++++ .../linked_list.py | 69 ++++++++++++ .../chapter_array_and_linkedlist/list.py | 54 +++++++++ .../chapter_array_and_linkedlist/my_list.py | 100 +++++++++++++++++ docs/chapter_array_and_linkedlist/array.md | 53 ++++++++- .../linked_list.md | 53 ++++++++- docs/chapter_array_and_linkedlist/list.md | 103 ++++++++++++++++-- 8 files changed, 503 insertions(+), 22 deletions(-) create mode 100644 codes/python/chapter_array_and_linkedlist/array.py diff --git a/codes/java/chapter_array_and_linkedlist/my_list.java b/codes/java/chapter_array_and_linkedlist/my_list.java index 50aa5fd..d4db0a2 100644 --- a/codes/java/chapter_array_and_linkedlist/my_list.java +++ b/codes/java/chapter_array_and_linkedlist/my_list.java @@ -20,12 +20,12 @@ class MyList { nums = new int[initialCapacity]; } - /* 获取列表容量 */ + /* 获取列表长度(即当前元素数量)*/ public int size() { return size; } - /* 获取列表长度(即当前元素数量) */ + /* 获取列表容量 */ public int capacity() { return nums.length; } diff --git a/codes/python/chapter_array_and_linkedlist/array.py b/codes/python/chapter_array_and_linkedlist/array.py new file mode 100644 index 0000000..f505aa4 --- /dev/null +++ b/codes/python/chapter_array_and_linkedlist/array.py @@ -0,0 +1,89 @@ +''' +File: array.py +Created Time: 2022-11-25 +Author: Krahets (krahets@163.com) +''' + +import sys, os.path as osp +sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) +from include import * + +""" 随机访问元素 """ +def randomAccess(nums): + # 在区间 [0, len(nums)) 中随机抽取一个数字 + random_index = random.randint(0, len(nums)) + # 获取并返回随机元素 + random_num = nums[random_index] + return random_num + +""" 扩展数组长度 """ +def extend(nums, enlarge): + # 初始化一个扩展长度后的数组 + res = [0] * (len(nums) + enlarge) + # 将原数组中的所有元素复制到新数组 + for i in range(len(nums)): + res[i] = nums[i] + # 返回扩展后的新数组 + return res + +""" 在数组的索引 index 处插入元素 num """ +def insert(nums, num, index): + # 把索引 index 以及之后的所有元素向后移动一位 + for i in range(len(nums) - 1, index - 1, -1): + nums[i] = nums[i - 1] + # 将 num 赋给 index 处元素 + nums[index] = num + +""" 删除索引 index 处元素 """ +def remove(nums, index): + # 把索引 index 之后的所有元素向前移动一位 + for i in range(index, len(nums) - 1): + nums[i] = nums[i + 1] + +""" 遍历数组 """ +def traverse(nums): + count = 0 + # 通过索引遍历数组 + for i in range(len(nums)): + count += 1 + # 直接遍历数组 + for num in nums: + count += 1 + +""" 在数组中查找指定元素 """ +def find(nums, target): + for i in range(len(nums)): + if nums[i] == target: + return i + return -1 + +""" Driver Code """ +if __name__ == "__main__": + """ 初始化数组 """ + arr = [0] * 5 + print("数组 arr =", arr) + nums = [1, 3, 2, 5, 4] + print("数组 nums =", nums) + + """ 随机访问 """ + random_num = randomAccess(nums) + print("在 nums 中获取随机元素", random_num) + + """ 长度扩展 """ + nums = extend(nums, 3) + print("将数组长度扩展至 8 ,得到 nums =", nums) + + """ 插入元素 """ + insert(nums, 6, 3) + print("在索引 3 处插入数字 6 ,得到 nums =", nums) + + """ 删除元素 """ + remove(nums, 2) + print("删除索引 2 处的元素,得到 nums =", nums) + + """ 遍历数组 """ + traverse(nums) + + """ 查找元素 """ + index = find(nums, 3) + print("在 nums 中查找元素 3 ,得到索引 =", index) diff --git a/codes/python/chapter_array_and_linkedlist/linked_list.py b/codes/python/chapter_array_and_linkedlist/linked_list.py index d95d8af..1f5aaca 100644 --- a/codes/python/chapter_array_and_linkedlist/linked_list.py +++ b/codes/python/chapter_array_and_linkedlist/linked_list.py @@ -8,3 +8,72 @@ import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * +""" 在链表的结点 n0 之后插入结点 P """ +def insert(n0, P): + n1 = n0.next + n0.next = P + P.next = n1 + +""" 删除链表的结点 n0 之后的首个结点 """ +def remove(n0): + if not n0.next: + return + # n0 -> P -> n1 + P = n0.next + n1 = P.next + n0.next = n1 + +""" 访问链表中索引为 index 的结点 """ +def access(head, index): + for _ in range(index): + head = head.next + if not head: + return None + return head + +""" 在链表中查找值为 target 的首个结点 """ +def find(head, target): + index = 0 + while head: + if head.val == target: + return index + head = head.next + index += 1 + return -1 + +""" +Driver Code +""" +if __name__ == "__main__": + """ 初始化链表 """ + # 初始化各个结点 + n0 = ListNode(1) + n1 = ListNode(3) + n2 = ListNode(2) + n3 = ListNode(5) + n4 = ListNode(4) + # 构建引用指向 + n0.next = n1 + n1.next = n2 + n2.next = n3 + n3.next = n4 + print("初始化的链表为") + print_linked_list(n0) + + """ 插入结点 """ + insert(n0, ListNode(0)) + print("插入结点后的链表为") + print_linked_list(n0) + + """ 删除结点 """ + remove(n0) + print("删除结点后的链表为") + print_linked_list(n0) + + """ 访问结点 """ + node = access(n0, 3) + print("链表中索引 3 处的结点的值 = {}".format(node.val)) + + """ 查找结点 """ + index = find(n0, 2) + print("链表中值为 2 的结点的索引 = {}".format(index)) diff --git a/codes/python/chapter_array_and_linkedlist/list.py b/codes/python/chapter_array_and_linkedlist/list.py index 823bfe4..8f45765 100644 --- a/codes/python/chapter_array_and_linkedlist/list.py +++ b/codes/python/chapter_array_and_linkedlist/list.py @@ -8,3 +8,57 @@ import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * +""" +Driver Code +""" +if __name__ == "__main__": + """ 初始化列表 """ + list = [1, 3, 2, 5, 4] + print("列表 list =", list) + + """ 访问元素 """ + num = list[1] + print("访问索引 1 处的元素,得到 num =", num) + + """ 更新元素 """ + list[1] = 0 + print("将索引 1 处的元素更新为 0 ,得到 list =", list) + + """ 清空列表 """ + list.clear() + print("清空列表后 list =", list) + + """ 尾部添加元素 """ + list.append(1) + list.append(3) + list.append(2) + list.append(5) + list.append(4) + print("添加元素后 list = ", list) + + """ 中间插入元素 """ + list.insert(3, 6) + print("在索引 3 处插入数字 6 ,得到 list =", list) + + """ 删除元素 """ + list.pop(3) + print("删除索引 3 处的元素,得到 list =", list) + + """ 通过索引遍历列表 """ + count = 0 + for i in range(len(list)): + count += 1 + + """ 直接遍历列表元素 """ + count = 0 + for n in list: + count += 1 + + """ 拼接两个列表 """ + list1 = [6, 8, 7, 10, 9] + list += list1 + print("将列表 list1 拼接到 list 之后,得到 list =", list) + + """ 排序列表 """ + list.sort() + print("排序列表后 list =", list) diff --git a/codes/python/chapter_array_and_linkedlist/my_list.py b/codes/python/chapter_array_and_linkedlist/my_list.py index 51e060f..e5202ed 100644 --- a/codes/python/chapter_array_and_linkedlist/my_list.py +++ b/codes/python/chapter_array_and_linkedlist/my_list.py @@ -8,3 +8,103 @@ import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * + +""" 列表类简易实现 """ +class MyList: + """ 构造函数 """ + def __init__(self): + self._initial_capacity = 10 # 列表初始容量 + self._nums = [0] * self._initial_capacity # 数组(存储列表元素) + self._size = 0 # 列表长度(即当前元素数量) + self._extend_ratio = 2 # 每次列表扩容的倍数 + + """ 获取列表长度(即当前元素数量) """ + def size(self): + return self._size + + """ 获取列表容量 """ + def capacity(self): + return len(self._nums) + + """ 访问元素 """ + def get(self, index): + # 索引如果越界则抛出异常,下同 + assert index < self._size, "索引越界" + return self._nums[index] + + """ 更新元素 """ + def set(self, num, index): + assert index < self._size, "索引越界" + self._nums[index] = num + + """ 中间插入元素 """ + def add(self, num, index=-1): + assert index < self._size, "索引越界" + if index == -1: + index = self._size + # 元素数量超出容量时,触发扩容机制 + if self._size == self.capacity(): + self.extend_capacity() + # 索引 i 以及之后的元素都向后移动一位 + for j in range(self._size - 1, index - 1, -1): + self._nums[j + 1] = self._nums[j] + self._nums[index] = num + # 更新元素数量 + self._size += 1 + + """ 删除元素 """ + def remove(self, index): + assert index < self._size, "索引越界" + # 索引 i 之后的元素都向前移动一位 + for j in range(index, self._size - 1): + self._nums[j] = self._nums[j + 1] + # 更新元素数量 + self._size -= 1 + + """ 列表扩容 """ + def extend_capacity(self): + # 新建一个长度为 self._size 的数组,并将原数组拷贝到新数组 + self._nums = self._nums + [0] * self.capacity() * (self._extend_ratio - 1) + + """ 返回有效长度的列表 """ + def to_array(self): + return self._nums[:self._size] + + +""" +Driver Code +""" +if __name__ == "__main__": + """ 初始化列表 """ + list = MyList() + """ 尾部添加元素 """ + list.add(1) + list.add(3) + list.add(2) + list.add(5) + list.add(4) + print("列表 list = {} ,容量 = {} ,长度 = {}" + .format(list.to_array(), list.capacity(), list.size())) + + """ 中间插入元素 """ + list.add(num=6, index=3) + print("在索引 3 处插入数字 6 ,得到 list =", list.to_array()) + + """ 删除元素 """ + list.remove(3) + print("删除索引 3 处的元素,得到 list =", list.to_array()) + + """ 访问元素 """ + num = list.get(1) + print("访问索引 1 处的元素,得到 num =", num) + + """ 更新元素 """ + list.set(0, 1) + print("将索引 1 处的元素更新为 0 ,得到 list =", list.to_array()) + + """ 测试扩容机制 """ + for i in range(10): + # 在 i = 5 时,列表长度将超出列表容量,此时触发扩容机制 + list.add(i) + print("扩容后的列表 list = {} ,容量 = {} ,长度 = {}" + .format(list.to_array(), list.capacity(), list.size())) diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 12869a4..14a24ab 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -33,7 +33,9 @@ comments: true === "Python" ```python title="array.py" - + """ 初始化数组 """ + arr = [0] * 5 # [ 0, 0, 0, 0, 0 ] + nums = [1, 3, 2, 5, 4] ``` ## 数组优点 @@ -74,7 +76,13 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Python" ```python title="array.py" - + """ 随机访问元素 """ + def randomAccess(nums): + # 在区间 [0, len(nums)) 中随机抽取一个数字 + random_index = random.randint(0, len(nums)) + # 获取并返回随机元素 + random_num = nums[random_index] + return random_num ``` ## 数组缺点 @@ -106,7 +114,15 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Python" ```python title="array.py" - + """ 扩展数组长度 """ + def extend(nums, enlarge): + # 初始化一个扩展长度后的数组 + res = [0] * (len(nums) + enlarge) + # 将原数组中的所有元素复制到新数组 + for i in range(len(nums)): + res[i] = nums[i] + # 返回扩展后的新数组 + return res ``` **数组中插入或删除元素效率低下。** 假设我们想要在数组中间某位置插入一个元素,由于数组元素在内存中是 “紧挨着的” ,它们之间没有空间再放任何数据。因此,我们不得不将此索引之后的所有元素都向后移动一位,然后再把元素赋值给该索引。删除元素也是类似,需要把此索引之后的元素都向前移动一位。总体看有以下缺点: @@ -150,7 +166,19 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Python" ```python title="array.py" - + """ 在数组的索引 index 处插入元素 num """ + def insert(nums, num, index): + # 把索引 index 以及之后的所有元素向后移动一位 + for i in range(len(nums) - 1, index - 1, -1): + nums[i] = nums[i - 1] + # 将 num 赋给 index 处元素 + nums[index] = num + + """ 删除索引 index 处元素 """ + def remove(nums, index): + # 把索引 index 之后的所有元素向前移动一位 + for i in range(index, len(nums) - 1): + nums[i] = nums[i + 1] ``` ## 数组常用操作 @@ -183,7 +211,15 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Python" ```python title="array.py" - + """ 遍历数组 """ + def traverse(nums): + count = 0 + # 通过索引遍历数组 + for i in range(len(nums)): + count += 1 + # 直接遍历数组 + for num in nums: + count += 1 ``` **数组查找。** 通过遍历数组,查找数组内的指定元素,并输出对应索引。 @@ -210,7 +246,12 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Python" ```python title="array.py" - + """ 在数组中查找指定元素 """ + def find(nums, target): + for i in range(len(nums)): + if nums[i] == target: + return i + return -1 ``` ## 数组典型应用 diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index 514034e..51da061 100644 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -77,7 +77,18 @@ comments: true === "Python" ```python title="" - + """ 初始化链表 1 -> 3 -> 2 -> 5 -> 4 """ + # 初始化各个结点 + n0 = ListNode(1) + n1 = ListNode(3) + n2 = ListNode(2) + n3 = ListNode(5) + n4 = ListNode(4) + # 构建引用指向 + n0.next = n1 + n1.next = n2 + n2.next = n3 + n3.next = n4 ``` ## 链表优点 @@ -118,7 +129,20 @@ comments: true === "Python" ```python title="" - + """ 在链表的结点 n0 之后插入结点 P """ + def insert(n0, P): + n1 = n0.next + n0.next = P + P.next = n1 + + """ 删除链表的结点 n0 之后的首个结点 """ + def remove(n0): + if not n0.next: + return + # n0 -> P -> n1 + P = n0.next + n1 = P.next + n0.next = n1 ``` ## 链表缺点 @@ -148,7 +172,13 @@ comments: true === "Python" ```python title="" - + """ 访问链表中索引为 index 的结点 """ + def access(head, index): + for _ in range(index): + head = head.next + if not head: + return None + return head ``` **链表的内存占用多。** 链表以结点为单位,每个结点除了保存值外,还需额外保存指针(引用)。这意味着同样数据量下,链表比数组需要占用更多内存空间。 @@ -182,7 +212,15 @@ comments: true === "Python" ```python title="" - + """ 在链表中查找值为 target 的首个结点 """ + def find(head, target): + index = 0 + while head: + if head.val == target: + return index + head = head.next + index += 1 + return -1 ``` ## 常见链表类型 @@ -214,7 +252,12 @@ comments: true === "Python" ```python title="" - + """ 双向链表结点类 """ + class ListNode: + def __init__(self, x): + self.val = x # 结点值 + self.next = None # 指向后继结点的指针(引用) + self.prev = None # 指向前驱结点的指针(引用) ``` ![linkedlist_common_types](linked_list.assets/linkedlist_common_types.png) diff --git a/docs/chapter_array_and_linkedlist/list.md b/docs/chapter_array_and_linkedlist/list.md index 4f584db..e630860 100644 --- a/docs/chapter_array_and_linkedlist/list.md +++ b/docs/chapter_array_and_linkedlist/list.md @@ -30,7 +30,8 @@ comments: true === "Python" ```python title="list.py" - + """ 初始化列表 """ + list = [1, 3, 2, 5, 4] ``` **访问与更新元素。** 列表的底层数据结构是数组,因此可以在 $O(1)$ 时间内访问与更新元素,效率很高。 @@ -54,7 +55,11 @@ comments: true === "Python" ```python title="list.py" - + """ 访问元素 """ + num = list[1] # 访问索引 1 处的元素 + + """ 更新元素 """ + list[1] = 0 # 将索引 1 处的元素更新为 0 ``` **在列表中添加、插入、删除元素。** 相对于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但是插入与删除元素的效率仍与数组一样低,时间复杂度为 $O(N)$ 。 @@ -88,7 +93,21 @@ comments: true === "Python" ```python title="list.py" - + """ 清空列表 """ + list.clear() + + """ 尾部添加元素 """ + list.append(1) + list.append(3) + list.append(2) + list.append(5) + list.append(4) + + """ 中间插入元素 """ + list.insert(3, 6) # 在索引 3 处插入数字 6 + + """ 删除元素 """ + list.pop(3) # 删除索引 3 处的元素 ``` **遍历列表。** 与数组一样,列表可以使用索引遍历,也可以使用 `for-each` 直接遍历。 @@ -118,7 +137,15 @@ comments: true === "Python" ```python title="list.py" - + """ 通过索引遍历列表 """ + count = 0 + for i in range(len(list)): + count += 1 + + """ 直接遍历列表元素 """ + count = 0 + for n in list: + count += 1 ``` **拼接两个列表。** 再创建一个新列表 `list1` ,我们可以将其中一个列表拼接到另一个的尾部。 @@ -140,7 +167,9 @@ comments: true === "Python" ```python title="list.py" - + """ 拼接两个列表 """ + list1 = [6, 8, 7, 10, 9] + list += list1 # 将列表 list1 拼接到 list 之后 ``` **排序列表。** 排序也是常用的方法之一,完成列表排序后,我们就可以使用在数组类算法题中经常考察的「二分查找」和「双指针」算法了。 @@ -161,7 +190,8 @@ comments: true === "Python" ```python title="list.py" - + """ 排序列表 """ + list.sort() # 排序后,列表元素从小到大排列 ``` ## 列表简易实现 * @@ -189,12 +219,12 @@ comments: true nums = new int[initialCapacity]; } - /* 获取列表容量 */ + /* 获取列表长度(即当前元素数量)*/ public int size() { return size; } - /* 获取列表长度(即当前元素数量) */ + /* 获取列表容量 */ public int capacity() { return nums.length; } @@ -269,5 +299,60 @@ comments: true === "Python" ```python title="my_list.py" - + """ 列表类简易实现 """ + class MyList: + """ 构造函数 """ + def __init__(self): + self._initial_capacity = 10 # 列表初始容量 + self._nums = [0] * self._initial_capacity # 数组(存储列表元素) + self._size = 0 # 列表长度(即当前元素数量) + self._extend_ratio = 2 # 每次列表扩容的倍数 + + """ 获取列表长度(即当前元素数量) """ + def size(self): + return self._size + + """ 获取列表容量 """ + def capacity(self): + return len(self._nums) + + """ 访问元素 """ + def get(self, index): + # 索引如果越界则抛出异常,下同 + assert index < self._size, "索引越界" + return self._nums[index] + + """ 更新元素 """ + def set(self, num, index): + assert index < self._size, "索引越界" + self._nums[index] = num + + """ 中间插入元素 """ + def add(self, num, index=-1): + assert index < self._size, "索引越界" + if index == -1: + index = self._size + # 元素数量超出容量时,触发扩容机制 + if self._size == self.capacity(): + self.extend_capacity() + # 索引 i 以及之后的元素都向后移动一位 + for j in range(self._size - 1, index - 1, -1): + self._nums[j + 1] = self._nums[j] + self._nums[index] = num + # 更新元素数量 + self._size += 1 + + """ 删除元素 """ + def remove(self, index): + assert index < self._size, "索引越界" + # 索引 i 之后的元素都向前移动一位 + for j in range(index, self._size - 1): + self._nums[j] = self._nums[j + 1] + # 更新元素数量 + self._size -= 1 + + """ 列表扩容 """ + def extend_capacity(self): + # 新建一个长度为 self._size 的数组,并将原数组拷贝到新数组 + self._nums = self._nums + [0] * self.capacity() * (self._extend_ratio - 1) ```