源代码--数据结构与算法(Python版)chap10 排序
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
20
交换类
(2)快速排序 快速排序采用分而治之(Divide and Conquer)
的策略将问题分解成若干个较小的子问题,采用 相同的方法一一解决后,再将子问题的结果整合 成最终答案。快速排序的每一轮处理其实就是将 这一的基准数定位,直到所有的数都排序完成 为止。
21
快速排序的基本步骤:
1. 选定一个基准值(通常可选第一个元素); 2. 将比基准值小的数值移到基准值左边,形
14
• 交换类
交换类排序的基本思想是:通过交换无序序列 中的记录得到其中关键字最小或最大的记录,并将 其加入到有序子序列中,最终形成有序序列。交换 类排序可分为冒泡排序和快速排序等。
15
交换类
(1)冒泡排序 两两比较待排序记录的关键字,发现两
个记录的次序相反时即进行交换,直到没有 反序的记录为止。因为元素会经由交换慢慢 浮到序列顶端,故称之为冒泡排序。
3. 最后对这个组进行插入排序。步长的选法 一般为 d1 约为 n/2,d2 为 d1 /2, d3 为 d2/2 ,…, di = 1。
11
【例】给定序列(11,9,84,32,92,26,58,91,35, 27,46,28,75,29,37,12 ),步长设为d1 =5、d2 =3、 d3 =1,希尔排序过程如下:
for i in range(1,len(alist)):
#外循环n-1
for j in range(i,0,-1):
#内循环
if alist[j]<alist[j-1]:
alist[j],alist[j-1]=alist[j-1],alist[j] #交换
li=[59,12,77,64,72,69,46,89,31,9] print('before: ',li) insert_sort(li) print('after: ',li)
第6轮比较 12 46 59 64 69 72 77 89 31 9
第7轮比较 12 46 59 64 69 72 77 89 31 9
第8轮比较 12 31 46 59 64 69 72 77 89 9
第9轮比较 9
12 31 46 59 64 69 72 77 89
4
直接插入排序代码
def insert_sort(alist):
16
冒泡排序的基本步骤:
1. 比较相邻的前后两个数据,如果前面数据 大于后面的数据,则将两个数据交换;
2. 这样对数组的第0个数据到n-1个数据进行 一次遍历后,最大的一个数据就移到数组 第n-1个位置;
3. n←n-1,如果n不为0就重复前面二步,否 则排序完成。
17
冒泡排序:
18
def bubble_sort(alist): for j in range(len(alist)-1,0,-1):
然后再以3为步长进行分组: 11,9 ,75 29,27,12 28,84,32 37,26,58 91,35,92
46 对每组进行排序:
11,9 ,12 28,26,32 29,27,58 37,35,75 46,84,92
91 将排序结果拼接在一起(11,9 ,12,28,26, 32,29,27,58,37,35,75,46,84,92, 91)
8
折半插入排序算法比直接插入算法明显 减少了关键字比较次数,因此速度比直接插 入排序算法有效,但插入时记录移动次数不 变。折半插入排序算法的时间复杂度仍然为 O(n2),与直接插入排序算法相同。
9
插入类
(3)希尔排序 希尔排序插入排序的另一个改进。插入排序
在对几乎已经排好序的数据操作时,效率高,复 杂度可降到O(n);但插入排序每次只能将数据移 动一位,效率较低。为此,希尔排序采用大跨步 间隔比较方式让记录跳跃式接近它的排序位置。
3. 重复执行直至low>high;将high+1作为插入 位置,此位置后所有元素后移一位,并将新 元素插入a[high+1]。
7
折半插入排序代码
def binary_sort(a): for i in range(0, len(a)): index = a[i] low = 0 hight = i - 1 while low <= hight: mid = (low + hight) // 2 if index > a[mid]: low = mid + 1 else: hight = mid - 1 for j in range(i, low, -1): a[j] = a[j - 1] a[low] = index
10
希尔排序的基本流程:
1. 先取一个正整数 d1(d1 < n)作为步长,把 全部记录分成d1组,所有距离为 d1 的倍 数的记录看作一组,然后在各组内进行插 入排序;
2. 取更小的步长 d2(d2 < d1),重复上述 分组和排序操作,直到取 di = 1(i >= 1) 为 止,即所有记录成为一个组;
设给定数组a[0…n-1],直接插入排序的过程如下: • 步骤1:初始时,a[0]自成1个有序区,无序区为 a[1..n-1]; • 步骤2:令i=1,将a[i]并入当前的有序区a[0…i-1]中 形成a[0…i]的有序区间;
• 步骤3:i++并重复第二步直到i=n-1,排序完成。
3
插入类
原始数组 59 12 77 64 72 69 46 89 31 9
23
第一次交换:按照第3步从右向左找到a[j]<key的值 j=6,按照第4步从左向右找到a[i]>key的值i=2,a[j] 和a[i]两者交换,如下图
24
第二次交换:按照第3步从右向左找到a[j]<key的值 j=5;按照第4步从左向右找到a[i]>key的值i=2,a[j] 和a[i]两者交换,如下图
12
最后以1步长进行排序(此时就是简单的插入 排序了),最终排序结果为: (9,11,12,26,27,28,29,32,35, 37,46,58,75,84,92,91)
13
希尔排序代码
def shell_sort(alist): n=len(alist) gap=n//2 while gap>0: for i in range(gap,n): j=i while j>=gap and alist[j-gap]>alist[j]: alist[j-gap],alist[j]=alist[j],alist[j-gap] j-=gap gap =gap//2
19
冒泡排序所需的比较次数和记录移动次 数的最小值为n-1和0。冒泡排序最低时间复 杂度为O(n)。最坏需要进行n-1趟排序,每趟 排序要进行n-i次关键字的比较(1≤i≤n-1),且 每次比较都必须移动记录三次来达到交换记 录位置,在这种情况下,比较和移动次数均 达到最大值O(n(n-1)/2)=O(n2)和O(n2),冒泡 排序的最坏时间复杂度为O(n2)。 综上,冒泡排序总的平均时间复杂度为O(n2)
#外循环 for i in range(j):
#内循环 if alist[i]>alist[i+1]: alist[i],alist[i+1]=alist[i+1],alist[i]
li=[54,26,93,17,77,31,44,55,20] print('before: ',li) bubble_sort(li) print('after: ',li)
第1轮比较 12 59 77 64 72 69 46 89 31 9
第2轮比较 12 59 77 64 72 69 46 89 31 9
第3轮比较 12 59 64 77 72 69 46 89 31 9
第4轮比较 12 59 64 72 77 69 46 89 31 9
第5轮比较 12 59 64 69 72 77 46 89 31 9
第一轮以步长d1=5开始分组,分 组情况如下(每列代表一组):
11,9 ,84,32,92 26,58,91,35,27 46,28,75,29,37
12 对每组进行排序:
11,9 ,75,29,27 12,28,84,32,37 26,58,91,35,92
46 将排序结果拼接在一起(11,9, 75,29,27,12,28,84,32, 37,26,58,91,35,92,46)
右边序列“81,99,61”也模拟刚才的过程,最终 将会得到这样的序列:61,81,99
到此,排序完全结束。综合可得排序后的序列为: 14,30,36,48,61,81,99
26
快速排序代码:
def quick_sort(alist ,start,end): if start>=end: return mid =alist[start] low=start high =end while low < high: while low<high and alist[high]>=mid: high -=1 alist[low]=alist[high] while low<high and alist[low]<mid: low +=1 alist[high]=alist[low] alist[low]=mid quick_sort(alist ,start,low-1) quick_sort(alist ,low+1,end)
成左子序列; 3. 将比基准值大的数值移到基准值右边,形
成右子序列; 4. 分别对左子序列、右子序列执行以上三步
(递归),直到左子序列或右子序列只剩 一个数值或没有数值位置;
22
【例】对a=(48,36,61,99,81,14,30)进 行快速排序。
选定基准值key=a[0]=48,初始状态:i=0, j=6,key=a[0]=48,如图所示
第10章 排序
• 插入类
排序
插入类排序将无序子序列中的一个或几个记录
插入到有序序列中,从而增加记录的有序子序列的 长度,又可细分为直接插入排序和折半插入排序。
2
插入类
(1)直接插入排序: 直接插入排序每次将一个待排序的记录按
其关键字大小插入到前面已经排好序的子序列中 的适当位置,直到全部记录插入完成为止。
25
到此,第一轮探测结束。此时以基准数48为分界点, 48左边的数都小于等于48,48右边的数都大于等于48, 现在基准数48已经归位。采用同样的方法分别处理左右两 个序列。
左边的序列是“14,36,30”。此时14左边没有数 据,只有右边有数据,且都比14大,说明14已经归位。接 下来需要处理14右边的序列“36,30”,处理完毕之后的 序列为“30,36”,到此30已经归位。36左边无序列, 右边序列“36”只有一个数,也不需要进行任何处理,最 后得到的左边序列为:14,30,36
选择类排序的基本思想是从记录的无序 子序列中选择关键字最小或最大的记录,并 将它加入到有序子序列中,最终获得有序序 列。选择类排序又可分为简单选择排序、树 形选择排序和堆排序。
29
选择类排序
1. 简单选择排序 简单选择排序的基本思想:给定一组无
序数据a[0,..,n-1],第一次从a[0]~a[n-1]中选 取最小值与a[0]交换,第二次从a[1]~a[n-1]中 选取最小值与a[1]交换,....,第i次从a[i1]~a[n-1]中选取最小值与a[i-1]交换,.....,第 n-1次从a[n-2]~a[n-1]中选取最小值与a[n-2]交 换,总共通过n-1次,得到一个从小到大排列 的有序序列。
5
插入类
(2)折半插入排序 折半插入排序是对插入排序算法的一种
改进,由于插入算法中前半部分为已排好序 的序列,因此可以引入折半查找方法来加快 寻找插入位置的速度。
6
折半插入排序算法执行流程为:
1. 将有序区域首元素位置设为low,末元素位 置设为high;
2. 将待插入元素k与a[m]比较,其中 m=(low+high)/2表示中间位置。如果k> a[m], 则选择a[low]到a[m-1]为新的插入区域(即 high=m-1);否则,选择a[m+1]到a[high]为 新的插入区域(即low=m+1);
li=[48,36,61,99,81,14,30]
print('before:',li)
quick_sort(li,0,len(li)-1)
print('after:',li)
27
• 快速排序的最差时间复杂度和冒泡排序是 一样的都是O(n2),它的平均时间复杂度为 O(nlogn)。
28
选择类
4.2 排序
交换类
(2)快速排序 快速排序采用分而治之(Divide and Conquer)
的策略将问题分解成若干个较小的子问题,采用 相同的方法一一解决后,再将子问题的结果整合 成最终答案。快速排序的每一轮处理其实就是将 这一的基准数定位,直到所有的数都排序完成 为止。
21
快速排序的基本步骤:
1. 选定一个基准值(通常可选第一个元素); 2. 将比基准值小的数值移到基准值左边,形
14
• 交换类
交换类排序的基本思想是:通过交换无序序列 中的记录得到其中关键字最小或最大的记录,并将 其加入到有序子序列中,最终形成有序序列。交换 类排序可分为冒泡排序和快速排序等。
15
交换类
(1)冒泡排序 两两比较待排序记录的关键字,发现两
个记录的次序相反时即进行交换,直到没有 反序的记录为止。因为元素会经由交换慢慢 浮到序列顶端,故称之为冒泡排序。
3. 最后对这个组进行插入排序。步长的选法 一般为 d1 约为 n/2,d2 为 d1 /2, d3 为 d2/2 ,…, di = 1。
11
【例】给定序列(11,9,84,32,92,26,58,91,35, 27,46,28,75,29,37,12 ),步长设为d1 =5、d2 =3、 d3 =1,希尔排序过程如下:
for i in range(1,len(alist)):
#外循环n-1
for j in range(i,0,-1):
#内循环
if alist[j]<alist[j-1]:
alist[j],alist[j-1]=alist[j-1],alist[j] #交换
li=[59,12,77,64,72,69,46,89,31,9] print('before: ',li) insert_sort(li) print('after: ',li)
第6轮比较 12 46 59 64 69 72 77 89 31 9
第7轮比较 12 46 59 64 69 72 77 89 31 9
第8轮比较 12 31 46 59 64 69 72 77 89 9
第9轮比较 9
12 31 46 59 64 69 72 77 89
4
直接插入排序代码
def insert_sort(alist):
16
冒泡排序的基本步骤:
1. 比较相邻的前后两个数据,如果前面数据 大于后面的数据,则将两个数据交换;
2. 这样对数组的第0个数据到n-1个数据进行 一次遍历后,最大的一个数据就移到数组 第n-1个位置;
3. n←n-1,如果n不为0就重复前面二步,否 则排序完成。
17
冒泡排序:
18
def bubble_sort(alist): for j in range(len(alist)-1,0,-1):
然后再以3为步长进行分组: 11,9 ,75 29,27,12 28,84,32 37,26,58 91,35,92
46 对每组进行排序:
11,9 ,12 28,26,32 29,27,58 37,35,75 46,84,92
91 将排序结果拼接在一起(11,9 ,12,28,26, 32,29,27,58,37,35,75,46,84,92, 91)
8
折半插入排序算法比直接插入算法明显 减少了关键字比较次数,因此速度比直接插 入排序算法有效,但插入时记录移动次数不 变。折半插入排序算法的时间复杂度仍然为 O(n2),与直接插入排序算法相同。
9
插入类
(3)希尔排序 希尔排序插入排序的另一个改进。插入排序
在对几乎已经排好序的数据操作时,效率高,复 杂度可降到O(n);但插入排序每次只能将数据移 动一位,效率较低。为此,希尔排序采用大跨步 间隔比较方式让记录跳跃式接近它的排序位置。
3. 重复执行直至low>high;将high+1作为插入 位置,此位置后所有元素后移一位,并将新 元素插入a[high+1]。
7
折半插入排序代码
def binary_sort(a): for i in range(0, len(a)): index = a[i] low = 0 hight = i - 1 while low <= hight: mid = (low + hight) // 2 if index > a[mid]: low = mid + 1 else: hight = mid - 1 for j in range(i, low, -1): a[j] = a[j - 1] a[low] = index
10
希尔排序的基本流程:
1. 先取一个正整数 d1(d1 < n)作为步长,把 全部记录分成d1组,所有距离为 d1 的倍 数的记录看作一组,然后在各组内进行插 入排序;
2. 取更小的步长 d2(d2 < d1),重复上述 分组和排序操作,直到取 di = 1(i >= 1) 为 止,即所有记录成为一个组;
设给定数组a[0…n-1],直接插入排序的过程如下: • 步骤1:初始时,a[0]自成1个有序区,无序区为 a[1..n-1]; • 步骤2:令i=1,将a[i]并入当前的有序区a[0…i-1]中 形成a[0…i]的有序区间;
• 步骤3:i++并重复第二步直到i=n-1,排序完成。
3
插入类
原始数组 59 12 77 64 72 69 46 89 31 9
23
第一次交换:按照第3步从右向左找到a[j]<key的值 j=6,按照第4步从左向右找到a[i]>key的值i=2,a[j] 和a[i]两者交换,如下图
24
第二次交换:按照第3步从右向左找到a[j]<key的值 j=5;按照第4步从左向右找到a[i]>key的值i=2,a[j] 和a[i]两者交换,如下图
12
最后以1步长进行排序(此时就是简单的插入 排序了),最终排序结果为: (9,11,12,26,27,28,29,32,35, 37,46,58,75,84,92,91)
13
希尔排序代码
def shell_sort(alist): n=len(alist) gap=n//2 while gap>0: for i in range(gap,n): j=i while j>=gap and alist[j-gap]>alist[j]: alist[j-gap],alist[j]=alist[j],alist[j-gap] j-=gap gap =gap//2
19
冒泡排序所需的比较次数和记录移动次 数的最小值为n-1和0。冒泡排序最低时间复 杂度为O(n)。最坏需要进行n-1趟排序,每趟 排序要进行n-i次关键字的比较(1≤i≤n-1),且 每次比较都必须移动记录三次来达到交换记 录位置,在这种情况下,比较和移动次数均 达到最大值O(n(n-1)/2)=O(n2)和O(n2),冒泡 排序的最坏时间复杂度为O(n2)。 综上,冒泡排序总的平均时间复杂度为O(n2)
#外循环 for i in range(j):
#内循环 if alist[i]>alist[i+1]: alist[i],alist[i+1]=alist[i+1],alist[i]
li=[54,26,93,17,77,31,44,55,20] print('before: ',li) bubble_sort(li) print('after: ',li)
第1轮比较 12 59 77 64 72 69 46 89 31 9
第2轮比较 12 59 77 64 72 69 46 89 31 9
第3轮比较 12 59 64 77 72 69 46 89 31 9
第4轮比较 12 59 64 72 77 69 46 89 31 9
第5轮比较 12 59 64 69 72 77 46 89 31 9
第一轮以步长d1=5开始分组,分 组情况如下(每列代表一组):
11,9 ,84,32,92 26,58,91,35,27 46,28,75,29,37
12 对每组进行排序:
11,9 ,75,29,27 12,28,84,32,37 26,58,91,35,92
46 将排序结果拼接在一起(11,9, 75,29,27,12,28,84,32, 37,26,58,91,35,92,46)
右边序列“81,99,61”也模拟刚才的过程,最终 将会得到这样的序列:61,81,99
到此,排序完全结束。综合可得排序后的序列为: 14,30,36,48,61,81,99
26
快速排序代码:
def quick_sort(alist ,start,end): if start>=end: return mid =alist[start] low=start high =end while low < high: while low<high and alist[high]>=mid: high -=1 alist[low]=alist[high] while low<high and alist[low]<mid: low +=1 alist[high]=alist[low] alist[low]=mid quick_sort(alist ,start,low-1) quick_sort(alist ,low+1,end)
成左子序列; 3. 将比基准值大的数值移到基准值右边,形
成右子序列; 4. 分别对左子序列、右子序列执行以上三步
(递归),直到左子序列或右子序列只剩 一个数值或没有数值位置;
22
【例】对a=(48,36,61,99,81,14,30)进 行快速排序。
选定基准值key=a[0]=48,初始状态:i=0, j=6,key=a[0]=48,如图所示
第10章 排序
• 插入类
排序
插入类排序将无序子序列中的一个或几个记录
插入到有序序列中,从而增加记录的有序子序列的 长度,又可细分为直接插入排序和折半插入排序。
2
插入类
(1)直接插入排序: 直接插入排序每次将一个待排序的记录按
其关键字大小插入到前面已经排好序的子序列中 的适当位置,直到全部记录插入完成为止。
25
到此,第一轮探测结束。此时以基准数48为分界点, 48左边的数都小于等于48,48右边的数都大于等于48, 现在基准数48已经归位。采用同样的方法分别处理左右两 个序列。
左边的序列是“14,36,30”。此时14左边没有数 据,只有右边有数据,且都比14大,说明14已经归位。接 下来需要处理14右边的序列“36,30”,处理完毕之后的 序列为“30,36”,到此30已经归位。36左边无序列, 右边序列“36”只有一个数,也不需要进行任何处理,最 后得到的左边序列为:14,30,36
选择类排序的基本思想是从记录的无序 子序列中选择关键字最小或最大的记录,并 将它加入到有序子序列中,最终获得有序序 列。选择类排序又可分为简单选择排序、树 形选择排序和堆排序。
29
选择类排序
1. 简单选择排序 简单选择排序的基本思想:给定一组无
序数据a[0,..,n-1],第一次从a[0]~a[n-1]中选 取最小值与a[0]交换,第二次从a[1]~a[n-1]中 选取最小值与a[1]交换,....,第i次从a[i1]~a[n-1]中选取最小值与a[i-1]交换,.....,第 n-1次从a[n-2]~a[n-1]中选取最小值与a[n-2]交 换,总共通过n-1次,得到一个从小到大排列 的有序序列。
5
插入类
(2)折半插入排序 折半插入排序是对插入排序算法的一种
改进,由于插入算法中前半部分为已排好序 的序列,因此可以引入折半查找方法来加快 寻找插入位置的速度。
6
折半插入排序算法执行流程为:
1. 将有序区域首元素位置设为low,末元素位 置设为high;
2. 将待插入元素k与a[m]比较,其中 m=(low+high)/2表示中间位置。如果k> a[m], 则选择a[low]到a[m-1]为新的插入区域(即 high=m-1);否则,选择a[m+1]到a[high]为 新的插入区域(即low=m+1);
li=[48,36,61,99,81,14,30]
print('before:',li)
quick_sort(li,0,len(li)-1)
print('after:',li)
27
• 快速排序的最差时间复杂度和冒泡排序是 一样的都是O(n2),它的平均时间复杂度为 O(nlogn)。
28
选择类
4.2 排序