From 121cb10209086d385cc84a3fb992932a106a15bc Mon Sep 17 00:00:00 2001 From: NIngCoder Date: Fri, 25 Nov 2022 15:47:05 +0800 Subject: [PATCH 1/7] =?UTF-8?q?Python=E6=8E=92=E5=BA=8F=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- codes/python/chapter_sorting/bubble_sort.py | 27 +++++++++++++ .../python/chapter_sorting/insertion_sort.py | 16 ++++++++ codes/python/chapter_sorting/merge_sort.py | 38 +++++++++++++++++++ codes/python/chapter_sorting/quick_sort.py | 21 ++++++++++ 4 files changed, 102 insertions(+) diff --git a/codes/python/chapter_sorting/bubble_sort.py b/codes/python/chapter_sorting/bubble_sort.py index 2027047..521dd8a 100644 --- a/codes/python/chapter_sorting/bubble_sort.py +++ b/codes/python/chapter_sorting/bubble_sort.py @@ -8,3 +8,30 @@ import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * +"冒泡排序" +def bubble_sort(nums): + n=len(nums) + # 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for i in range(n-1,-1,-1): + # 内循环:冒泡操作 + for j in range(i): + if nums[j]>nums[j+1]: + nums[j],nums[j+1]=nums[j+1],nums[j] +"冒泡排序(标志优化)" +def bubbleSortWithFlag(nums): + n=len(nums) + # 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for i in range(n-1,-1,-1): + flag=False #初始化标志位 + # 内循环:冒泡操作 + for j in range(i): + if nums[j]>nums[j+1]: + nums[j],nums[j+1]=nums[j+1],nums[j] + flag=True #记录交换元素 + if not flag:break +if __name__=='__main__': + nums=[4,1,3,1,5,2] + bubble_sort(nums) + print("冒泡排序后数组 nums = " ,nums) + bubbleSortWithFlag(nums) + print("冒泡排序后数组 nums = " ,nums) \ No newline at end of file diff --git a/codes/python/chapter_sorting/insertion_sort.py b/codes/python/chapter_sorting/insertion_sort.py index 28e0331..8640abc 100644 --- a/codes/python/chapter_sorting/insertion_sort.py +++ b/codes/python/chapter_sorting/insertion_sort.py @@ -8,3 +8,19 @@ import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * +"直接插入排序" +def insertionSort(nums): + #外循环:base = nums[1], nums[2], ..., nums[n-1] + for i in range(1,len(nums)): + base=nums[i] + j=i-1 + #内循环:将 base 插入到左边的正确位置 + while j>=0 and nums[j]>base: + nums[j+1]=nums[j] #1. 将 nums[j] 向右移动一位 + j-=1 + nums[j+1]=base #2. 将 base 赋值到正确位置 + +if __name__=='__main__': + nums=[4,1,3,1,5,2] + insertionSort(nums) + print("排序后数组 nums = " ,nums) \ No newline at end of file diff --git a/codes/python/chapter_sorting/merge_sort.py b/codes/python/chapter_sorting/merge_sort.py index 6f9b4a6..04c7130 100644 --- a/codes/python/chapter_sorting/merge_sort.py +++ b/codes/python/chapter_sorting/merge_sort.py @@ -8,3 +8,41 @@ import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * + +""" +另一种 思路实现归并排序 +""" +def merge_sort(nums,l,r): + if l>=r:return + mid=l+r>>1 #划分中点 + #进行归并 + merge_sort(nums,l,mid) + merge_sort(nums,mid+1,r) + + k,i,j=0,l,mid+1 #借助辅助数组 完成排序 + while i<=mid and j<=r: + if nums[i]<=nums[j]: #这一步保证了 稳定排序 + help_ls[k]=nums[i] + i+=1 + else: + help_ls[k]=nums[j] + j+=1 + k+=1 + + while i<=mid: #对于左边区域 + help_ls[k]=nums[i] + k,i=k+1,i+1 + while j<=r: #对于右边区域 + help_ls[k]=nums[j] + k,j=k+1,j+1 + + i,j=l,0 + while i<=r: + nums[i]=help_ls[j] + i,j=i+1,j+1 +if __name__=='__main__': + nums=[4,1,3,1,5,2] + n=len(nums) + help_ls=[0 for _ in range(n)] + merge_sort(nums,0,n-1) + print("归并排序完成后 nums = ",nums) \ No newline at end of file diff --git a/codes/python/chapter_sorting/quick_sort.py b/codes/python/chapter_sorting/quick_sort.py index a668096..f66ff5f 100644 --- a/codes/python/chapter_sorting/quick_sort.py +++ b/codes/python/chapter_sorting/quick_sort.py @@ -8,3 +8,24 @@ import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * + +"另一种思维 实现快速排序" +def quick_sort(nums,l,r): + if l>=r: + return + i,j,x=l-1,r+1,nums[l+r>>1] + while i=x:break + while True: + j-=1 + if nums[j]<=x:break + if inums[j+1]: - nums[j],nums[j+1]=nums[j+1],nums[j] -"冒泡排序(标志优化)" -def bubbleSortWithFlag(nums): - n=len(nums) + # 交换 nums[j] 与 nums[j + 1] + if nums[j] > nums[j+1]: + nums[j], nums[j+1] = nums[j+1], nums[j] + + +"""冒泡排序(标志优化)""" +def bubble_sort_with_flag(nums): + n = len(nums) # 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for i in range(n-1,-1,-1): - flag=False #初始化标志位 + for i in range(n-1, -1, -1): + flag = False # 初始化标志位 # 内循环:冒泡操作 for j in range(i): - if nums[j]>nums[j+1]: - nums[j],nums[j+1]=nums[j+1],nums[j] - flag=True #记录交换元素 - if not flag:break -if __name__=='__main__': - nums=[4,1,3,1,5,2] + # 交换 nums[j] 与 nums[j + 1] + if nums[j] > nums[j+1]: + nums[j], nums[j+1] = nums[j+1], nums[j] + flag = True # 记录交换元素 + if not flag: + break # 此轮冒泡未交换任何元素,直接跳出 + + +if __name__ == '__main__': + nums = [4, 1, 3, 1, 5, 2] bubble_sort(nums) - print("冒泡排序后数组 nums = " ,nums) - bubbleSortWithFlag(nums) - print("冒泡排序后数组 nums = " ,nums) \ No newline at end of file + print("排序后数组 nums = ", nums) + + nums1 = [4, 1, 3, 1, 5, 2] + bubble_sort_with_flag(nums1) + print("排序后数组 nums = ", nums1) diff --git a/codes/python/chapter_sorting/insertion_sort.py b/codes/python/chapter_sorting/insertion_sort.py index 8640abc..a3b2b23 100644 --- a/codes/python/chapter_sorting/insertion_sort.py +++ b/codes/python/chapter_sorting/insertion_sort.py @@ -1,26 +1,28 @@ ''' File: insertion_sort.py Created Time: 2022-11-25 -Author: Krahets (krahets@163.com) +Author: timi (xisunyy@163.com) ''' -import sys, os.path as osp -sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * +import sys +import os.path as osp +sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) -"直接插入排序" -def insertionSort(nums): - #外循环:base = nums[1], nums[2], ..., nums[n-1] - for i in range(1,len(nums)): - base=nums[i] - j=i-1 - #内循环:将 base 插入到左边的正确位置 - while j>=0 and nums[j]>base: - nums[j+1]=nums[j] #1. 将 nums[j] 向右移动一位 - j-=1 - nums[j+1]=base #2. 将 base 赋值到正确位置 +"""插入排序""" +def insertion_sort(nums): + # 外循环:base = nums[1], nums[2], ..., nums[n-1] + for i in range(1, len(nums)): + base = nums[i] + j = i-1 + # 内循环:将 base 插入到左边的正确位置 + while j >= 0 and nums[j] > base: + nums[j+1] = nums[j] # 1. 将 nums[j] 向右移动一位 + j -= 1 + nums[j+1] = base # 2. 将 base 赋值到正确位置 -if __name__=='__main__': - nums=[4,1,3,1,5,2] - insertionSort(nums) - print("排序后数组 nums = " ,nums) \ No newline at end of file + +if __name__ == '__main__': + nums = [4, 1, 3, 1, 5, 2] + insertion_sort(nums) + print("排序后数组 nums = ", nums) diff --git a/codes/python/chapter_sorting/merge_sort.py b/codes/python/chapter_sorting/merge_sort.py index 04c7130..f3212ef 100644 --- a/codes/python/chapter_sorting/merge_sort.py +++ b/codes/python/chapter_sorting/merge_sort.py @@ -1,48 +1,60 @@ ''' File: merge_sort.py Created Time: 2022-11-25 -Author: Krahets (krahets@163.com) +Author: timi (xisunyy@163.com) ''' -import sys, os.path as osp -sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) +import copy from include import * +import sys +import os.path as osp +sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) """ -另一种 思路实现归并排序 +合并左子数组和右子数组 +左子数组区间 [left, mid] +右子数组区间 [mid + 1, right] """ -def merge_sort(nums,l,r): - if l>=r:return - mid=l+r>>1 #划分中点 - #进行归并 - merge_sort(nums,l,mid) - merge_sort(nums,mid+1,r) - - k,i,j=0,l,mid+1 #借助辅助数组 完成排序 - while i<=mid and j<=r: - if nums[i]<=nums[j]: #这一步保证了 稳定排序 - help_ls[k]=nums[i] - i+=1 +def merge(nums, left, mid, right): + # 初始化辅助数组 借助 copy模块 + tmp = copy.deepcopy(nums[left:right+1]) + # 左子数组的起始索引和结束索引 + leftStart, leftEnd = left-left, mid - left + # 右子数组的起始索引和结束索引 + rightStart, rightEnd = mid + 1 - left, right - left + # i, j 分别指向左子数组、右子数组的首元素 + i, j = leftStart, rightStart + # 通过覆盖原数组 nums 来合并左子数组和右子数组 + for k in range(left, right+1): + # 若 “左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if i > leftEnd: + nums[k] = tmp[j] + j += 1 + # 否则,若 “右子数组已全部合并完” 或 “左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + elif j > rightEnd or tmp[i] <= tmp[j]: + nums[k] = tmp[i] + i += 1 + # 否则,若 “左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ else: - help_ls[k]=nums[j] - j+=1 - k+=1 - - while i<=mid: #对于左边区域 - help_ls[k]=nums[i] - k,i=k+1,i+1 - while j<=r: #对于右边区域 - help_ls[k]=nums[j] - k,j=k+1,j+1 + nums[k] = tmp[j] + j += 1 - i,j=l,0 - while i<=r: - nums[i]=help_ls[j] - i,j=i+1,j+1 -if __name__=='__main__': - nums=[4,1,3,1,5,2] - n=len(nums) - help_ls=[0 for _ in range(n)] - merge_sort(nums,0,n-1) - print("归并排序完成后 nums = ",nums) \ No newline at end of file + +"""归并排序""" +def merge_sort(nums, left, right): + # 终止条件 + if left >= right: + return # 当子数组长度为 1 时终止递归 + # 划分阶段 + mid = left + right >> 1 # 计算中点 + merge_sort(nums, left, mid) # 递归左子数组 + merge_sort(nums, mid + 1, right) # 递归右子数组 + # 合并阶段 + merge(nums, left, mid, right) + + +if __name__ == '__main__': + nums = [4, 1, 3, 1, 5, 2] + merge_sort(nums, 0, len(nums)-1) + print("归并排序完成后 nums = ", nums) diff --git a/codes/python/chapter_sorting/quick_sort.py b/codes/python/chapter_sorting/quick_sort.py index f66ff5f..50236e4 100644 --- a/codes/python/chapter_sorting/quick_sort.py +++ b/codes/python/chapter_sorting/quick_sort.py @@ -1,31 +1,132 @@ ''' File: quick_sort.py Created Time: 2022-11-25 -Author: Krahets (krahets@163.com) +Author: timi (xisunyy@163.com) ''' -import sys, os.path as osp -sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * +import sys +import os.path as osp +sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) -"另一种思维 实现快速排序" -def quick_sort(nums,l,r): - if l>=r: - return - i,j,x=l-1,r+1,nums[l+r>>1] - while i=x:break - while True: - j-=1 - if nums[j]<=x:break - if i= nums[left]: + j -= 1 # 从右向左找首个小于基准数的元素 + while i < j and nums[i] <= nums[left]: + i += 1 # 从左向右找首个大于基准数的元素 + # 元素交换 + nums[i], nums[j] = nums[j], nums[i] + # 将基准数交换至两子数组的分界线 + nums[i], nums[left] = nums[left], nums[i] + return i # 返回基准数的索引 + + """快速排序""" + def quick_sort(self, nums, left, right): + # 子数组长度为 1 时终止递归 + if left >= right: + return + # 哨兵划分 + pivot = self.partition(nums, left, right) + # 递归左子数组、右子数组 + self.quick_sort(nums, left, pivot-1) + self.quick_sort(nums, pivot+1, right) + + +"""快速排序类(中位基准数优化)""" +class quick_sort_median(): + + # 选取三个元素的中位数 + def median_three(self, nums, left, mid, right): + # 使用了异或操作来简化代码 + # 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if (nums[left] > nums[mid]) ^ (nums[left] > nums[right]): + return left + elif (nums[mid] < nums[left]) ^ (nums[mid] > nums[right]): + return mid + return right + + """哨兵划分(三数取中值)""" + def partition(self, nums, left, right): + # 以 nums[left] 作为基准数 + med = self.median_three(nums, left, (left+right)//2, right) + # 将中位数交换至数组最左端 + nums[left], nums[med] = nums[med], nums[left] + # 以 nums[left] 作为基准数 + i, j = left, right + while i < j: + while i < j and nums[j] >= nums[left]: + j -= 1 # 从右向左找首个小于基准数的元素 + while i < j and nums[i] <= nums[left]: + i += 1 # 从左向右找首个大于基准数的元素 + # 元素交换 + nums[i], nums[j] = nums[j], nums[i] + # 将基准数交换至两子数组的分界线 + nums[i], nums[left] = nums[left], nums[i] + return i # 返回基准数的索引 + + """快速排序""" + def quick_sort(self, nums, left, right): + # 子数组长度为 1 时终止递归 + if left >= right:return + # 哨兵划分 + pivot = self.partition(nums, left, right) + # 递归左子数组、右子数组 + self.quick_sort(nums, left, pivot-1) + self.quick_sort(nums, pivot+1, right) + + +"""快速排序类(尾递归优化)""" +class quick_sort_tail_call(): + + """哨兵划分""" + def partition(self, nums, left, right): + # 以 nums[left] 作为基准数 + i, j = left, right + while i < j: + while i < j and nums[j] >= nums[left]: + j -= 1 # 从右向左找首个小于基准数的元素 + while i < j and nums[i] <= nums[left]: + i += 1 # 从左向右找首个大于基准数的元素 + # 元素交换 + nums[i], nums[j] = nums[j], nums[i] + # 将基准数交换至两子数组的分界线 + nums[i], nums[left] = nums[left], nums[i] + return i # 返回基准数的索引 + + """快速排序(尾递归优化)""" + def quick_sort(self, nums, left, right): + # 子数组长度为 1 时终止 + while left < right: + # 哨兵划分操作 + pivot = self.partition(nums, left, right) + # 对两个子数组中较短的那个执行快排 + if pivot-left < right-pivot: + self.quick_sort(nums, left, pivot-1) # 递归排序左子数组 + left = pivot+1 # 剩余待排序区间为 [pivot + 1, right] + else: + self.quick_sort(nums, pivot+1, right) # 递归排序右子数组 + right = pivot-1 # 剩余待排序区间为 [left, pivot - 1] + +if __name__ == '__main__': + # 快速排序 + nums = [4, 1, 3, 1, 5, 2] + quick_sort().quick_sort(nums, 0, len(nums)-1) + print("快速排序完成后 nums = ", nums) + + # 快速排序(中位基准数优化) + nums1 = [4, 1, 3, 1, 5, 2] + quick_sort_median().quick_sort(nums1, 0, len(nums1)-1) + print("快速排序(中位基准数优化)完成后 nums = ", nums) + + # 快速排序(尾递归优化) + nums2 = [4, 1, 3, 1, 5, 2] + quick_sort_tail_call().quick_sort(nums, 0, len(nums2)-1) + print("快速排序(尾递归优化)完成后 nums = ", nums) From 818fb54efb14b9e8580a4fd5ca427be09ffe3161 Mon Sep 17 00:00:00 2001 From: NIngCoder Date: Sat, 26 Nov 2022 00:46:30 +0800 Subject: [PATCH 3/7] sort --- codes/python/chapter_sorting/bubble_sort.py | 13 ++--- .../python/chapter_sorting/insertion_sort.py | 7 +-- codes/python/chapter_sorting/merge_sort.py | 17 +++--- codes/python/chapter_sorting/quick_sort.py | 53 +++++++++---------- 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/codes/python/chapter_sorting/bubble_sort.py b/codes/python/chapter_sorting/bubble_sort.py index 61f11f5..8bbee89 100644 --- a/codes/python/chapter_sorting/bubble_sort.py +++ b/codes/python/chapter_sorting/bubble_sort.py @@ -7,31 +7,32 @@ Author: timi (xisunyy@163.com) from include import * import sys import os.path as osp + sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) """冒泡排序""" def bubble_sort(nums): n = len(nums) # 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for i in range(n-1, -1, -1): + for i in range(n - 1, -1, -1): # 内循环:冒泡操作 for j in range(i): # 交换 nums[j] 与 nums[j + 1] - if nums[j] > nums[j+1]: - nums[j], nums[j+1] = nums[j+1], nums[j] + if nums[j] > nums[j + 1]: + nums[j], nums[j + 1] = nums[j + 1], nums[j] """冒泡排序(标志优化)""" def bubble_sort_with_flag(nums): n = len(nums) # 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for i in range(n-1, -1, -1): + for i in range(n - 1, -1, -1): flag = False # 初始化标志位 # 内循环:冒泡操作 for j in range(i): # 交换 nums[j] 与 nums[j + 1] - if nums[j] > nums[j+1]: - nums[j], nums[j+1] = nums[j+1], nums[j] + if nums[j] > nums[j + 1]: + nums[j], nums[j + 1] = nums[j + 1], nums[j] flag = True # 记录交换元素 if not flag: break # 此轮冒泡未交换任何元素,直接跳出 diff --git a/codes/python/chapter_sorting/insertion_sort.py b/codes/python/chapter_sorting/insertion_sort.py index a3b2b23..0a46af1 100644 --- a/codes/python/chapter_sorting/insertion_sort.py +++ b/codes/python/chapter_sorting/insertion_sort.py @@ -7,6 +7,7 @@ Author: timi (xisunyy@163.com) from include import * import sys import os.path as osp + sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) """插入排序""" @@ -14,12 +15,12 @@ def insertion_sort(nums): # 外循环:base = nums[1], nums[2], ..., nums[n-1] for i in range(1, len(nums)): base = nums[i] - j = i-1 + j = i - 1 # 内循环:将 base 插入到左边的正确位置 while j >= 0 and nums[j] > base: - nums[j+1] = nums[j] # 1. 将 nums[j] 向右移动一位 + nums[j + 1] = nums[j] # 1. 将 nums[j] 向右移动一位 j -= 1 - nums[j+1] = base # 2. 将 base 赋值到正确位置 + nums[j + 1] = base # 2. 将 base 赋值到正确位置 if __name__ == '__main__': diff --git a/codes/python/chapter_sorting/merge_sort.py b/codes/python/chapter_sorting/merge_sort.py index f3212ef..98a2c5f 100644 --- a/codes/python/chapter_sorting/merge_sort.py +++ b/codes/python/chapter_sorting/merge_sort.py @@ -4,12 +4,11 @@ Created Time: 2022-11-25 Author: timi (xisunyy@163.com) ''' -import copy from include import * import sys import os.path as osp -sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) +sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) """ 合并左子数组和右子数组 @@ -18,15 +17,15 @@ sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) """ def merge(nums, left, mid, right): # 初始化辅助数组 借助 copy模块 - tmp = copy.deepcopy(nums[left:right+1]) + tmp = nums[left:right + 1] # 左子数组的起始索引和结束索引 - leftStart, leftEnd = left-left, mid - left + leftStart, leftEnd = left - left, mid - left # 右子数组的起始索引和结束索引 rightStart, rightEnd = mid + 1 - left, right - left # i, j 分别指向左子数组、右子数组的首元素 i, j = leftStart, rightStart # 通过覆盖原数组 nums 来合并左子数组和右子数组 - for k in range(left, right+1): + for k in range(left, right + 1): # 若 “左子数组已全部合并完”,则选取右子数组元素,并且 j++ if i > leftEnd: nums[k] = tmp[j] @@ -45,16 +44,16 @@ def merge(nums, left, mid, right): def merge_sort(nums, left, right): # 终止条件 if left >= right: - return # 当子数组长度为 1 时终止递归 + return # 当子数组长度为 1 时终止递归 # 划分阶段 mid = left + right >> 1 # 计算中点 - merge_sort(nums, left, mid) # 递归左子数组 - merge_sort(nums, mid + 1, right) # 递归右子数组 + merge_sort(nums, left, mid) # 递归左子数组 + merge_sort(nums, mid + 1, right) # 递归右子数组 # 合并阶段 merge(nums, left, mid, right) if __name__ == '__main__': nums = [4, 1, 3, 1, 5, 2] - merge_sort(nums, 0, len(nums)-1) + merge_sort(nums, 0, len(nums) - 1) print("归并排序完成后 nums = ", nums) diff --git a/codes/python/chapter_sorting/quick_sort.py b/codes/python/chapter_sorting/quick_sort.py index 50236e4..658d6a5 100644 --- a/codes/python/chapter_sorting/quick_sort.py +++ b/codes/python/chapter_sorting/quick_sort.py @@ -7,21 +7,20 @@ Author: timi (xisunyy@163.com) from include import * import sys import os.path as osp -sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) +sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) """快速排序类""" class quick_sort(object): - """哨兵划分""" def partition(self, nums, left, right): # 以 nums[left] 作为基准数 i, j = left, right while i < j: while i < j and nums[j] >= nums[left]: - j -= 1 # 从右向左找首个小于基准数的元素 + j -= 1 # 从右向左找首个小于基准数的元素 while i < j and nums[i] <= nums[left]: - i += 1 # 从左向右找首个大于基准数的元素 + i += 1 # 从左向右找首个大于基准数的元素 # 元素交换 nums[i], nums[j] = nums[j], nums[i] # 将基准数交换至两子数组的分界线 @@ -36,14 +35,14 @@ class quick_sort(object): # 哨兵划分 pivot = self.partition(nums, left, right) # 递归左子数组、右子数组 - self.quick_sort(nums, left, pivot-1) - self.quick_sort(nums, pivot+1, right) + self.quick_sort(nums, left, pivot - 1) + self.quick_sort(nums, pivot + 1, right) """快速排序类(中位基准数优化)""" class quick_sort_median(): - # 选取三个元素的中位数 + """选取三个元素的中位数""" def median_three(self, nums, left, mid, right): # 使用了异或操作来简化代码 # 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 @@ -56,45 +55,44 @@ class quick_sort_median(): """哨兵划分(三数取中值)""" def partition(self, nums, left, right): # 以 nums[left] 作为基准数 - med = self.median_three(nums, left, (left+right)//2, right) + med = self.median_three(nums, left, (left + right) // 2, right) # 将中位数交换至数组最左端 nums[left], nums[med] = nums[med], nums[left] # 以 nums[left] 作为基准数 i, j = left, right while i < j: while i < j and nums[j] >= nums[left]: - j -= 1 # 从右向左找首个小于基准数的元素 + j -= 1 # 从右向左找首个小于基准数的元素 while i < j and nums[i] <= nums[left]: - i += 1 # 从左向右找首个大于基准数的元素 + i += 1 # 从左向右找首个大于基准数的元素 # 元素交换 - nums[i], nums[j] = nums[j], nums[i] - # 将基准数交换至两子数组的分界线 + nums[i], nums[j] = nums[j], nums[i] + # 将基准数交换至两子数组的分界线 nums[i], nums[left] = nums[left], nums[i] - return i # 返回基准数的索引 + return i # 返回基准数的索引 """快速排序""" def quick_sort(self, nums, left, right): # 子数组长度为 1 时终止递归 - if left >= right:return + if left >= right: return # 哨兵划分 pivot = self.partition(nums, left, right) # 递归左子数组、右子数组 - self.quick_sort(nums, left, pivot-1) - self.quick_sort(nums, pivot+1, right) + self.quick_sort(nums, left, pivot - 1) + self.quick_sort(nums, pivot + 1, right) """快速排序类(尾递归优化)""" class quick_sort_tail_call(): - """哨兵划分""" def partition(self, nums, left, right): # 以 nums[left] 作为基准数 i, j = left, right while i < j: while i < j and nums[j] >= nums[left]: - j -= 1 # 从右向左找首个小于基准数的元素 + j -= 1 # 从右向左找首个小于基准数的元素 while i < j and nums[i] <= nums[left]: - i += 1 # 从左向右找首个大于基准数的元素 + i += 1 # 从左向右找首个大于基准数的元素 # 元素交换 nums[i], nums[j] = nums[j], nums[i] # 将基准数交换至两子数组的分界线 @@ -108,25 +106,26 @@ class quick_sort_tail_call(): # 哨兵划分操作 pivot = self.partition(nums, left, right) # 对两个子数组中较短的那个执行快排 - if pivot-left < right-pivot: - self.quick_sort(nums, left, pivot-1) # 递归排序左子数组 - left = pivot+1 # 剩余待排序区间为 [pivot + 1, right] + if pivot - left < right - pivot: + self.quick_sort(nums, left, pivot - 1) # 递归排序左子数组 + left = pivot + 1 # 剩余待排序区间为 [pivot + 1, right] else: - self.quick_sort(nums, pivot+1, right) # 递归排序右子数组 - right = pivot-1 # 剩余待排序区间为 [left, pivot - 1] + self.quick_sort(nums, pivot + 1, right) # 递归排序右子数组 + right = pivot - 1 # 剩余待排序区间为 [left, pivot - 1] + if __name__ == '__main__': # 快速排序 nums = [4, 1, 3, 1, 5, 2] - quick_sort().quick_sort(nums, 0, len(nums)-1) + quick_sort().quick_sort(nums, 0, len(nums) - 1) print("快速排序完成后 nums = ", nums) # 快速排序(中位基准数优化) nums1 = [4, 1, 3, 1, 5, 2] - quick_sort_median().quick_sort(nums1, 0, len(nums1)-1) + quick_sort_median().quick_sort(nums1, 0, len(nums1) - 1) print("快速排序(中位基准数优化)完成后 nums = ", nums) # 快速排序(尾递归优化) nums2 = [4, 1, 3, 1, 5, 2] - quick_sort_tail_call().quick_sort(nums, 0, len(nums2)-1) + quick_sort_tail_call().quick_sort(nums, 0, len(nums2) - 1) print("快速排序(尾递归优化)完成后 nums = ", nums) From db9faf98e1160b947ef21cce2d9629f3130376c3 Mon Sep 17 00:00:00 2001 From: NIngCoder Date: Sat, 26 Nov 2022 00:58:39 +0800 Subject: [PATCH 4/7] sort --- codes/python/chapter_sorting/bubble_sort.py | 4 +-- .../python/chapter_sorting/insertion_sort.py | 2 +- codes/python/chapter_sorting/merge_sort.py | 15 ++++----- codes/python/chapter_sorting/quick_sort.py | 32 +++++++++---------- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/codes/python/chapter_sorting/bubble_sort.py b/codes/python/chapter_sorting/bubble_sort.py index 8bbee89..938ee83 100644 --- a/codes/python/chapter_sorting/bubble_sort.py +++ b/codes/python/chapter_sorting/bubble_sort.py @@ -10,7 +10,7 @@ import os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) -"""冒泡排序""" +""" 冒泡排序 """ def bubble_sort(nums): n = len(nums) # 外循环:待排序元素数量为 n-1, n-2, ..., 1 @@ -22,7 +22,7 @@ def bubble_sort(nums): nums[j], nums[j + 1] = nums[j + 1], nums[j] -"""冒泡排序(标志优化)""" +""" 冒泡排序(标志优化) """ def bubble_sort_with_flag(nums): n = len(nums) # 外循环:待排序元素数量为 n-1, n-2, ..., 1 diff --git a/codes/python/chapter_sorting/insertion_sort.py b/codes/python/chapter_sorting/insertion_sort.py index 0a46af1..37f6833 100644 --- a/codes/python/chapter_sorting/insertion_sort.py +++ b/codes/python/chapter_sorting/insertion_sort.py @@ -10,7 +10,7 @@ import os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) -"""插入排序""" +""" 插入排序 """ def insertion_sort(nums): # 外循环:base = nums[1], nums[2], ..., nums[n-1] for i in range(1, len(nums)): diff --git a/codes/python/chapter_sorting/merge_sort.py b/codes/python/chapter_sorting/merge_sort.py index 98a2c5f..312d205 100644 --- a/codes/python/chapter_sorting/merge_sort.py +++ b/codes/python/chapter_sorting/merge_sort.py @@ -19,19 +19,19 @@ def merge(nums, left, mid, right): # 初始化辅助数组 借助 copy模块 tmp = nums[left:right + 1] # 左子数组的起始索引和结束索引 - leftStart, leftEnd = left - left, mid - left + left_start, left_end = left - left, mid - left # 右子数组的起始索引和结束索引 - rightStart, rightEnd = mid + 1 - left, right - left + right_start, right_end = mid + 1 - left, right - left # i, j 分别指向左子数组、右子数组的首元素 - i, j = leftStart, rightStart + i, j = left_start, right_start # 通过覆盖原数组 nums 来合并左子数组和右子数组 for k in range(left, right + 1): # 若 “左子数组已全部合并完”,则选取右子数组元素,并且 j++ - if i > leftEnd: + if i > left_end: nums[k] = tmp[j] j += 1 # 否则,若 “右子数组已全部合并完” 或 “左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ - elif j > rightEnd or tmp[i] <= tmp[j]: + elif j > right_end or tmp[i] <= tmp[j]: nums[k] = tmp[i] i += 1 # 否则,若 “左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ @@ -39,14 +39,13 @@ def merge(nums, left, mid, right): nums[k] = tmp[j] j += 1 - -"""归并排序""" +""" 归并排序 """ def merge_sort(nums, left, right): # 终止条件 if left >= right: return # 当子数组长度为 1 时终止递归 # 划分阶段 - mid = left + right >> 1 # 计算中点 + mid = (left + right) // 2 # 计算中点 merge_sort(nums, left, mid) # 递归左子数组 merge_sort(nums, mid + 1, right) # 递归右子数组 # 合并阶段 diff --git a/codes/python/chapter_sorting/quick_sort.py b/codes/python/chapter_sorting/quick_sort.py index 658d6a5..dec5726 100644 --- a/codes/python/chapter_sorting/quick_sort.py +++ b/codes/python/chapter_sorting/quick_sort.py @@ -10,9 +10,9 @@ import os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) -"""快速排序类""" -class quick_sort(object): - """哨兵划分""" +""" 快速排序类 """ +class QuickSort(object): + """ 哨兵划分 """ def partition(self, nums, left, right): # 以 nums[left] 作为基准数 i, j = left, right @@ -27,7 +27,7 @@ class quick_sort(object): nums[i], nums[left] = nums[left], nums[i] return i # 返回基准数的索引 - """快速排序""" + """ 快速排序 """ def quick_sort(self, nums, left, right): # 子数组长度为 1 时终止递归 if left >= right: @@ -39,10 +39,10 @@ class quick_sort(object): self.quick_sort(nums, pivot + 1, right) -"""快速排序类(中位基准数优化)""" -class quick_sort_median(): +""" 快速排序类(中位基准数优化)""" +class QuickSortMedian(): - """选取三个元素的中位数""" + """ 选取三个元素的中位数 """ def median_three(self, nums, left, mid, right): # 使用了异或操作来简化代码 # 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 @@ -52,7 +52,7 @@ class quick_sort_median(): return mid return right - """哨兵划分(三数取中值)""" + """ 哨兵划分(三数取中值) """ def partition(self, nums, left, right): # 以 nums[left] 作为基准数 med = self.median_three(nums, left, (left + right) // 2, right) @@ -71,7 +71,7 @@ class quick_sort_median(): nums[i], nums[left] = nums[left], nums[i] return i # 返回基准数的索引 - """快速排序""" + """ 快速排序 """ def quick_sort(self, nums, left, right): # 子数组长度为 1 时终止递归 if left >= right: return @@ -82,9 +82,9 @@ class quick_sort_median(): self.quick_sort(nums, pivot + 1, right) -"""快速排序类(尾递归优化)""" -class quick_sort_tail_call(): - """哨兵划分""" +""" 快速排序类(尾递归优化) """ +class QuickSortTailCall(): + """ 哨兵划分 """ def partition(self, nums, left, right): # 以 nums[left] 作为基准数 i, j = left, right @@ -99,7 +99,7 @@ class quick_sort_tail_call(): nums[i], nums[left] = nums[left], nums[i] return i # 返回基准数的索引 - """快速排序(尾递归优化)""" + """ 快速排序(尾递归优化) """ def quick_sort(self, nums, left, right): # 子数组长度为 1 时终止 while left < right: @@ -117,15 +117,15 @@ class quick_sort_tail_call(): if __name__ == '__main__': # 快速排序 nums = [4, 1, 3, 1, 5, 2] - quick_sort().quick_sort(nums, 0, len(nums) - 1) + QuickSort().quick_sort(nums, 0, len(nums) - 1) print("快速排序完成后 nums = ", nums) # 快速排序(中位基准数优化) nums1 = [4, 1, 3, 1, 5, 2] - quick_sort_median().quick_sort(nums1, 0, len(nums1) - 1) + QuickSortMedian().quick_sort(nums1, 0, len(nums1) - 1) print("快速排序(中位基准数优化)完成后 nums = ", nums) # 快速排序(尾递归优化) nums2 = [4, 1, 3, 1, 5, 2] - quick_sort_tail_call().quick_sort(nums, 0, len(nums2) - 1) + QuickSortTailCall().quick_sort(nums, 0, len(nums2) - 1) print("快速排序(尾递归优化)完成后 nums = ", nums) From 4be25e52733cdcc38ba715d5f7cf5e01a4b8eb8c Mon Sep 17 00:00:00 2001 From: timi <63904151+Boy-timi@users.noreply.github.com> Date: Sat, 26 Nov 2022 01:01:34 +0800 Subject: [PATCH 5/7] Update quick_sort.py --- codes/python/chapter_sorting/quick_sort.py | 1 + 1 file changed, 1 insertion(+) diff --git a/codes/python/chapter_sorting/quick_sort.py b/codes/python/chapter_sorting/quick_sort.py index dec5726..ae932b6 100644 --- a/codes/python/chapter_sorting/quick_sort.py +++ b/codes/python/chapter_sorting/quick_sort.py @@ -12,6 +12,7 @@ sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) """ 快速排序类 """ class QuickSort(object): + """ 哨兵划分 """ def partition(self, nums, left, right): # 以 nums[left] 作为基准数 From 0585f20970570b1cdb3a7ba95934b5e3b53dc313 Mon Sep 17 00:00:00 2001 From: timi <63904151+Boy-timi@users.noreply.github.com> Date: Sat, 26 Nov 2022 01:02:08 +0800 Subject: [PATCH 6/7] Update quick_sort.py --- codes/python/chapter_sorting/quick_sort.py | 1 + 1 file changed, 1 insertion(+) diff --git a/codes/python/chapter_sorting/quick_sort.py b/codes/python/chapter_sorting/quick_sort.py index ae932b6..8f764c7 100644 --- a/codes/python/chapter_sorting/quick_sort.py +++ b/codes/python/chapter_sorting/quick_sort.py @@ -85,6 +85,7 @@ class QuickSortMedian(): """ 快速排序类(尾递归优化) """ class QuickSortTailCall(): + """ 哨兵划分 """ def partition(self, nums, left, right): # 以 nums[left] 作为基准数 From 9f883d58888a19617f4252cb3f104cc9725538d1 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Sat, 26 Nov 2022 01:40:49 +0800 Subject: [PATCH 7/7] 1. Fix the import error. 2. Some codes fine tuning. --- codes/python/chapter_sorting/bubble_sort.py | 18 ++--- .../python/chapter_sorting/insertion_sort.py | 9 +-- codes/python/chapter_sorting/merge_sort.py | 15 ++-- codes/python/chapter_sorting/quick_sort.py | 32 ++++---- .../space_time_tradeoff.md | 4 + docs/chapter_sorting/bubble_sort.md | 36 ++++++++- docs/chapter_sorting/insertion_sort.md | 16 ++++ docs/chapter_sorting/merge_sort.md | 46 +++++++++++- docs/chapter_sorting/quick_sort.md | 75 +++++++++++++++++++ 9 files changed, 206 insertions(+), 45 deletions(-) diff --git a/codes/python/chapter_sorting/bubble_sort.py b/codes/python/chapter_sorting/bubble_sort.py index 938ee83..54260dd 100644 --- a/codes/python/chapter_sorting/bubble_sort.py +++ b/codes/python/chapter_sorting/bubble_sort.py @@ -4,11 +4,9 @@ Created Time: 2022-11-25 Author: timi (xisunyy@163.com) ''' -from include import * -import sys -import os.path as osp - +import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) +from include import * """ 冒泡排序 """ def bubble_sort(nums): @@ -17,11 +15,10 @@ def bubble_sort(nums): for i in range(n - 1, -1, -1): # 内循环:冒泡操作 for j in range(i): - # 交换 nums[j] 与 nums[j + 1] if nums[j] > nums[j + 1]: + # 交换 nums[j] 与 nums[j + 1] nums[j], nums[j + 1] = nums[j + 1], nums[j] - """ 冒泡排序(标志优化) """ def bubble_sort_with_flag(nums): n = len(nums) @@ -30,19 +27,20 @@ def bubble_sort_with_flag(nums): flag = False # 初始化标志位 # 内循环:冒泡操作 for j in range(i): - # 交换 nums[j] 与 nums[j + 1] if nums[j] > nums[j + 1]: + # 交换 nums[j] 与 nums[j + 1] nums[j], nums[j + 1] = nums[j + 1], nums[j] flag = True # 记录交换元素 if not flag: - break # 此轮冒泡未交换任何元素,直接跳出 + break # 此轮冒泡未交换任何元素,直接跳出 +""" Driver Code """ if __name__ == '__main__': nums = [4, 1, 3, 1, 5, 2] bubble_sort(nums) - print("排序后数组 nums = ", nums) + print("排序后数组 nums =", nums) nums1 = [4, 1, 3, 1, 5, 2] bubble_sort_with_flag(nums1) - print("排序后数组 nums = ", nums1) + print("排序后数组 nums =", nums1) diff --git a/codes/python/chapter_sorting/insertion_sort.py b/codes/python/chapter_sorting/insertion_sort.py index 37f6833..9f32673 100644 --- a/codes/python/chapter_sorting/insertion_sort.py +++ b/codes/python/chapter_sorting/insertion_sort.py @@ -4,11 +4,9 @@ Created Time: 2022-11-25 Author: timi (xisunyy@163.com) ''' -from include import * -import sys -import os.path as osp - +import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) +from include import * """ 插入排序 """ def insertion_sort(nums): @@ -20,9 +18,10 @@ def insertion_sort(nums): while j >= 0 and nums[j] > base: nums[j + 1] = nums[j] # 1. 将 nums[j] 向右移动一位 j -= 1 - nums[j + 1] = base # 2. 将 base 赋值到正确位置 + nums[j + 1] = base # 2. 将 base 赋值到正确位置 +""" Driver Code """ if __name__ == '__main__': nums = [4, 1, 3, 1, 5, 2] insertion_sort(nums) diff --git a/codes/python/chapter_sorting/merge_sort.py b/codes/python/chapter_sorting/merge_sort.py index 312d205..e4a1b10 100644 --- a/codes/python/chapter_sorting/merge_sort.py +++ b/codes/python/chapter_sorting/merge_sort.py @@ -4,11 +4,9 @@ Created Time: 2022-11-25 Author: timi (xisunyy@163.com) ''' -from include import * -import sys -import os.path as osp - +import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) +from include import * """ 合并左子数组和右子数组 @@ -43,16 +41,17 @@ def merge(nums, left, mid, right): def merge_sort(nums, left, right): # 终止条件 if left >= right: - return # 当子数组长度为 1 时终止递归 + return # 当子数组长度为 1 时终止递归 # 划分阶段 - mid = (left + right) // 2 # 计算中点 - merge_sort(nums, left, mid) # 递归左子数组 + mid = (left + right) // 2 # 计算中点 + merge_sort(nums, left, mid) # 递归左子数组 merge_sort(nums, mid + 1, right) # 递归右子数组 # 合并阶段 merge(nums, left, mid, right) +""" Driver Code """ if __name__ == '__main__': nums = [4, 1, 3, 1, 5, 2] merge_sort(nums, 0, len(nums) - 1) - print("归并排序完成后 nums = ", nums) + print("归并排序完成后 nums =", nums) diff --git a/codes/python/chapter_sorting/quick_sort.py b/codes/python/chapter_sorting/quick_sort.py index 8f764c7..5ba826b 100644 --- a/codes/python/chapter_sorting/quick_sort.py +++ b/codes/python/chapter_sorting/quick_sort.py @@ -4,15 +4,12 @@ Created Time: 2022-11-25 Author: timi (xisunyy@163.com) ''' -from include import * -import sys -import os.path as osp - +import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) +from include import * """ 快速排序类 """ -class QuickSort(object): - +class QuickSort: """ 哨兵划分 """ def partition(self, nums, left, right): # 以 nums[left] 作为基准数 @@ -39,10 +36,8 @@ class QuickSort(object): self.quick_sort(nums, left, pivot - 1) self.quick_sort(nums, pivot + 1, right) - """ 快速排序类(中位基准数优化)""" -class QuickSortMedian(): - +class QuickSortMedian: """ 选取三个元素的中位数 """ def median_three(self, nums, left, mid, right): # 使用了异或操作来简化代码 @@ -68,7 +63,7 @@ class QuickSortMedian(): i += 1 # 从左向右找首个大于基准数的元素 # 元素交换 nums[i], nums[j] = nums[j], nums[i] - # 将基准数交换至两子数组的分界线 + # 将基准数交换至两子数组的分界线 nums[i], nums[left] = nums[left], nums[i] return i # 返回基准数的索引 @@ -82,10 +77,8 @@ class QuickSortMedian(): self.quick_sort(nums, left, pivot - 1) self.quick_sort(nums, pivot + 1, right) - """ 快速排序类(尾递归优化) """ -class QuickSortTailCall(): - +class QuickSortTailCall: """ 哨兵划分 """ def partition(self, nums, left, right): # 以 nums[left] 作为基准数 @@ -99,7 +92,7 @@ class QuickSortTailCall(): nums[i], nums[j] = nums[j], nums[i] # 将基准数交换至两子数组的分界线 nums[i], nums[left] = nums[left], nums[i] - return i # 返回基准数的索引 + return i # 返回基准数的索引 """ 快速排序(尾递归优化) """ def quick_sort(self, nums, left, right): @@ -110,24 +103,25 @@ class QuickSortTailCall(): # 对两个子数组中较短的那个执行快排 if pivot - left < right - pivot: self.quick_sort(nums, left, pivot - 1) # 递归排序左子数组 - left = pivot + 1 # 剩余待排序区间为 [pivot + 1, right] + left = pivot + 1 # 剩余待排序区间为 [pivot + 1, right] else: self.quick_sort(nums, pivot + 1, right) # 递归排序右子数组 - right = pivot - 1 # 剩余待排序区间为 [left, pivot - 1] + right = pivot - 1 # 剩余待排序区间为 [left, pivot - 1] +""" Driver Code """ if __name__ == '__main__': # 快速排序 nums = [4, 1, 3, 1, 5, 2] QuickSort().quick_sort(nums, 0, len(nums) - 1) - print("快速排序完成后 nums = ", nums) + print("快速排序完成后 nums =", nums) # 快速排序(中位基准数优化) nums1 = [4, 1, 3, 1, 5, 2] QuickSortMedian().quick_sort(nums1, 0, len(nums1) - 1) - print("快速排序(中位基准数优化)完成后 nums = ", nums) + print("快速排序(中位基准数优化)完成后 nums =", nums) # 快速排序(尾递归优化) nums2 = [4, 1, 3, 1, 5, 2] QuickSortTailCall().quick_sort(nums, 0, len(nums2) - 1) - print("快速排序(尾递归优化)完成后 nums = ", nums) + print("快速排序(尾递归优化)完成后 nums =", nums) diff --git a/docs/chapter_computational_complexity/space_time_tradeoff.md b/docs/chapter_computational_complexity/space_time_tradeoff.md index 07975a5..7bc827c 100644 --- a/docs/chapter_computational_complexity/space_time_tradeoff.md +++ b/docs/chapter_computational_complexity/space_time_tradeoff.md @@ -1,3 +1,7 @@ +--- +comments: true +--- + # 权衡时间与空间 理想情况下,我们希望算法的时间复杂度和空间复杂度都能够达到最优,而实际上,同时优化时间复杂度和空间复杂度是非常困难的。 diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index 9e16895..c3ac3ba 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -47,9 +47,7 @@ comments: true ## 算法流程 1. 设数组长度为 $n$ ,完成第一轮「冒泡」后,数组最大元素已在正确位置,接下来只需排序剩余 $n - 1$ 个元素。 - 2. 同理,对剩余 $n - 1$ 个元素执行「冒泡」,可将第二大元素交换至正确位置,因而待排序元素只剩 $n - 2$ 个。 - 3. 以此类推…… **循环 $n - 1$ 轮「冒泡」,即可完成整个数组的排序**。 ![bubble_sort](bubble_sort.assets/bubble_sort.png) @@ -76,6 +74,21 @@ comments: true } ``` +=== "Python" + + ```python + """ 冒泡排序 """ + def bubble_sort(nums): + n = len(nums) + # 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for i in range(n - 1, -1, -1): + # 内循环:冒泡操作 + for j in range(i): + if nums[j] > nums[j + 1]: + # 交换 nums[j] 与 nums[j + 1] + nums[j], nums[j + 1] = nums[j + 1], nums[j] + ``` + ## 算法特性 **时间复杂度 $O(n^2)$ :** 各轮「冒泡」遍历的数组长度为 $n - 1$ , $n - 2$ , $\cdots$ , $2$ , $1$ 次,求和为 $\frac{(n - 1) n}{2}$ ,因此使用 $O(n^2)$ 时间。 @@ -116,3 +129,22 @@ comments: true } } ``` + +=== "Python" + + ```python + """ 冒泡排序(标志优化) """ + def bubble_sort_with_flag(nums): + n = len(nums) + # 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for i in range(n - 1, -1, -1): + flag = False # 初始化标志位 + # 内循环:冒泡操作 + for j in range(i): + if nums[j] > nums[j + 1]: + # 交换 nums[j] 与 nums[j + 1] + nums[j], nums[j + 1] = nums[j + 1], nums[j] + flag = True # 记录交换元素 + if not flag: + break # 此轮冒泡未交换任何元素,直接跳出 + ``` diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index 9b987c9..06efb08 100644 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -44,6 +44,22 @@ comments: true } ``` +=== "Python" + + ```python + """ 插入排序 """ + def insertion_sort(nums): + # 外循环:base = nums[1], nums[2], ..., nums[n-1] + for i in range(1, len(nums)): + base = nums[i] + j = i - 1 + # 内循环:将 base 插入到左边的正确位置 + while j >= 0 and nums[j] > base: + nums[j + 1] = nums[j] # 1. 将 nums[j] 向右移动一位 + j -= 1 + nums[j + 1] = base # 2. 将 base 赋值到正确位置 + ``` + ## 算法特性 **时间复杂度 $O(n^2)$ :** 最差情况下,各轮插入操作循环 $n - 1$ , $n-2$ , $\cdots$ , $2$ , $1$ 次,求和为 $\frac{(n - 1) n}{2}$ ,使用 $O(n^2)$ 时间。 diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index 4c2d966..a8b31c9 100644 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -7,7 +7,6 @@ comments: true 「归并排序 Merge Sort」是算法中 “分治思想” 的典型体现,其有「划分」和「合并」两个阶段: 1. **划分阶段:** 通过递归不断 **将数组从中点位置划分开**,将长数组的排序问题转化为短数组的排序问题; - 2. **合并阶段:** 划分到子数组长度为 1 时,开始向上合并,不断将 **左、右两个短排序数组** 合并为 **一个长排序数组**,直至合并至原数组时完成排序; ![merge_sort_preview](merge_sort.assets/merge_sort_preview.png) @@ -104,6 +103,51 @@ comments: true } ``` +=== "Python" + + ```python title="merge_sort.py" + """ + 合并左子数组和右子数组 + 左子数组区间 [left, mid] + 右子数组区间 [mid + 1, right] + """ + def merge(nums, left, mid, right): + # 初始化辅助数组 借助 copy模块 + tmp = nums[left:right + 1] + # 左子数组的起始索引和结束索引 + left_start, left_end = left - left, mid - left + # 右子数组的起始索引和结束索引 + right_start, right_end = mid + 1 - left, right - left + # i, j 分别指向左子数组、右子数组的首元素 + i, j = left_start, right_start + # 通过覆盖原数组 nums 来合并左子数组和右子数组 + for k in range(left, right + 1): + # 若 “左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if i > left_end: + nums[k] = tmp[j] + j += 1 + # 否则,若 “右子数组已全部合并完” 或 “左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + elif j > right_end or tmp[i] <= tmp[j]: + nums[k] = tmp[i] + i += 1 + # 否则,若 “左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else: + nums[k] = tmp[j] + j += 1 + + """ 归并排序 """ + def merge_sort(nums, left, right): + # 终止条件 + if left >= right: + return # 当子数组长度为 1 时终止递归 + # 划分阶段 + mid = (left + right) // 2 # 计算中点 + merge_sort(nums, left, mid) # 递归左子数组 + merge_sort(nums, mid + 1, right) # 递归右子数组 + # 合并阶段 + merge(nums, left, mid, right) + ``` + 下面重点解释一下合并方法 `merge()` 的流程: 1. 初始化一个辅助数组 `tmp` 暂存待合并区间 `[left, right]` 内的元素,后续通过覆盖原数组 `nums` 的元素来实现合并; diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index 8918971..d29d80d 100644 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -61,6 +61,25 @@ comments: true } ``` +=== "Python" + + ```python title="quick_sort.py" + """ 哨兵划分 """ + def partition(self, nums, left, right): + # 以 nums[left] 作为基准数 + i, j = left, right + while i < j: + while i < j and nums[j] >= nums[left]: + j -= 1 # 从右向左找首个小于基准数的元素 + while i < j and nums[i] <= nums[left]: + i += 1 # 从左向右找首个大于基准数的元素 + # 元素交换 + nums[i], nums[j] = nums[j], nums[i] + # 将基准数交换至两子数组的分界线 + nums[i], nums[left] = nums[left], nums[i] + return i # 返回基准数的索引 + ``` + !!! note "快速排序的分治思想" 哨兵划分的实质是将 **一个长数组的排序问题** 简化为 **两个短数组的排序问题**。 @@ -93,6 +112,21 @@ comments: true } ``` +=== "Python" + + ```python title="quick_sort.py" + """ 快速排序 """ + def quick_sort(self, nums, left, right): + # 子数组长度为 1 时终止递归 + if left >= right: + return + # 哨兵划分 + pivot = self.partition(nums, left, right) + # 递归左子数组、右子数组 + self.quick_sort(nums, left, pivot - 1) + self.quick_sort(nums, pivot + 1, right) + ``` + ## 算法特性 **平均时间复杂度 $O(n \log n)$ :** 平均情况下,哨兵划分的递归层数为 $\log n$ ,每层中的总循环数为 $n$ ,总体使用 $O(n \log n)$ 时间。 @@ -149,6 +183,29 @@ comments: true } ``` +=== "Python" + + ```python title="quick_sort.py" + """ 选取三个元素的中位数 """ + def median_three(self, nums, left, mid, right): + # 使用了异或操作来简化代码 + # 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if (nums[left] > nums[mid]) ^ (nums[left] > nums[right]): + return left + elif (nums[mid] < nums[left]) ^ (nums[mid] > nums[right]): + return mid + return right + + """ 哨兵划分(三数取中值) """ + def partition(self, nums, left, right): + # 以 nums[left] 作为基准数 + med = self.median_three(nums, left, (left + right) // 2, right) + # 将中位数交换至数组最左端 + nums[left], nums[med] = nums[med], nums[left] + # 以 nums[left] 作为基准数 + # 下同省略... + ``` + ## 尾递归优化 **普通快速排序在某些输入下的空间效率变差。** 仍然以完全倒序的输入数组为例,由于每轮哨兵划分后右子数组长度为 0 ,那么将形成一个高度为 $n - 1$ 的递归树,此时使用的栈帧空间大小劣化至 $O(n)$ 。 @@ -175,3 +232,21 @@ comments: true } } ``` + +=== "Python" + + ```python title="quick_sort.py" + """ 快速排序(尾递归优化) """ + def quick_sort(self, nums, left, right): + # 子数组长度为 1 时终止 + while left < right: + # 哨兵划分操作 + pivot = self.partition(nums, left, right) + # 对两个子数组中较短的那个执行快排 + if pivot - left < right - pivot: + self.quick_sort(nums, left, pivot - 1) # 递归排序左子数组 + left = pivot + 1 # 剩余待排序区间为 [pivot + 1, right] + else: + self.quick_sort(nums, pivot + 1, right) # 递归排序右子数组 + right = pivot - 1 # 剩余待排序区间为 [left, pivot - 1] + ```