排序算法总结(Python实现)
- 算法介绍
- 算法分类
- 相关概念
- 1. 冒泡排序(Bubble Sort)
- 1.1 思想
- 1.2 python实现
- 1.3 复杂度
- 1.4 稳定性
- 2. 快速排序(Quick Sort)
- 2.1 思想(伪代码)
- 2.2 python实现
- 2.3 复杂度
- 2.4 稳定性
- 3.直接插入排序(Insert Sort)
- 3.1 思想
- 3.2 python实现
- 3.3 复杂度
- 3.4 稳定性
- 4. 希尔排序(Shell Sort)
- 4.1 思想
- 4.2 python实现
- 4.3 复杂度
- 4.4 稳定性
- 5. 直接选择排序
- 5.1 思想
- 5.2 python实现
- 5.3 复杂度
- 5.4 稳定性
- 6. 堆排序(Heap Sort)
- 6.1 思想
- 6.2 python实现
- 7. 归并排序
- 7.1 思想
- 7.2 python实现
- 8. 基数排序
- 8.1 思想
- 8.2 python实现
算法介绍
算法分类
内部排序:数据在内存里排序
外部排序:数据处理在硬盘上的排序(如数据太多没法都读到内存)
- 交换排序:比较两个记录键值的大小,如果这两个记录键值的大小出现逆序,则交换这两个记录,这样将键值较小的记录向序列前部移动,键值较大的记录向序列后部移动。
- 插入排序:依次将每个记录插入到一个已排好序的有序表中去
- 选择排序:依次在未排序序列中找到最小元素,存放到排好序列的起始位置。
相关概念
1. 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
2. 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
3. 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
4. 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
1. 冒泡排序(Bubble Sort)
1.1 思想
1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
3. 针对所有的元素重复以上的步骤,除了最后一个。
4. 重复步骤1~3,直至排序完成。
begin BubbleSort(list)for all elements of listif list[i] > list[i+1]swap(list[i], list[i+1])end if...
- 冒泡排序只对n个数据操作n-1轮,每轮找出最大(小)值
- 通过比较与交换相邻两个数,每轮将未排序序列中最大(小)值“冒泡”至列首(尾)。
1.2 python实现
def BubbleSort(lst):n = len(lst)if n < = 1:return lstfor i in range(n - 1): # 循环次数,每一轮确定一个最值for j in range(n - i - 1): # 待比较与交换的数if lst[j] > lst[j+1]: # 比较两个相邻的数,如果后者<前者,交换lst[j], lst[j+1] = lst[j+1], lst[j] return lst
arr = list(map(int, input().split()))
arr = BubbleSort(arr)
for i in arr:print(i, end =' ')
1.3 复杂度
- 时间复杂度:O(n2),每轮操作O(n),共O(n)轮。
- 空间复杂度:O(1),额外空间开销出在交换数据时的一个过渡空间。
1.4 稳定性
- 稳定
2. 快速排序(Quick Sort)
2.1 思想(伪代码)
1. 从数列中挑出一个元素,称为 “基准”(pivot);
2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
- 快速排序基于选择划分,是简单选择排序的优化。
- 每次划分将数据选到基准值两边,循环对两边的数据进行划分,类似于二分法。
- 算法的整体性能取决于划分的平均程度,即基准值的选择,此处衍生出快速排序的许多优化方案,甚至可以划分为多块。
#分区操作#
function partitionFunc(left, right, pivot)leftPointer = leftrightPointer = right - 1while True dowhile A[++leftPointer] < pivot do//do-nothing end whilewhile rightPointer > 0 && A[--rightPointer] > pivot do//do-nothing end whileif leftPointer >= rightPointerbreakelse swap leftPointer,rightPointerend ifend while swap leftPointer,rightreturn leftPointerend function
#递归快排#
procedure quickSort(left, right)if right-left <= 0returnelse pivot = A[right]partition = partitionFunc(left, right, pivot)quickSort(left,partition-1)quickSort(partition+1,right) end if end procedure
2.2 python实现
参考link
def QuickSort(lst):def partition(arr,left,right):pivot = arr[left] # 划分参考数索引,默认为第一个数为基准数,可优化while left < right:# 如果列表后边的数,比基准数大或相等,则前移一位直到有比基准数小的数出现while left < right and arr[right] >= pivot:right-=1# 此时right指向一个比基准数小的数,将right指向的数放去left的位置上,此时right指向的位置空着,接下来移动left找到符合条件的数放在此处arr[left] = arr[right]# 如果列表前边的数,比基准数小或相等,则后移一位直到有比基准数小的数出现while left < right and arr[left] <= pivot:left+=1# 此时left指向一个比基准数大的数,将left指向的数放去right的位置上,此时left指向的位置空着,接下来进入下一次循环,将right找到符合条件的数放在此处arr[right] = arr[left]# 推出循环后,left和right重合,此时所致位置即为基准元素正确的位置,此时,左边的元素都比pivot小,右边的都比pivot大arr[left] = pivot # 将基准元素放到该位置return left # 返回基准元素位置def quicksort(arr, left, right):if left >= right: # 递归退出条件return mid = partition(arr,left,right) # 分区,得到基准元素位置# 递归调用快排quicksort(arr,left,mid-1) # 对基准元素左边的子序列快排quicksort(arr,mid+1,right) # 对基准元素右边的子序列快排# 主函数n=len(lst)if n<=1:return lstquicksort(lst,0,n-1) # 调用快排return lstif __name__ == "__main__":arr = list(map(int, input().split()))arr = QuickSort(arr)for i in arr:print(i, end =' ')
2.3 复杂度
- 时间复杂度:O(nlogn),划分次数O(logn),每次划分遍历比较O(n)
- 空间复杂度:O(logn),额外空间开销出在暂存基准值,O(logn)划分需要O(logn)个。
2.4 稳定性
- 不稳定
3.直接插入排序(Insert Sort)
3.1 思想
基本思想:依次将每个记录插入到一个已排好序的有序表中去,从而得到一个新的、记录数增加1的有序表
- 步骤:
- 从第一个元素开始,该元素可以被认为已经被排好序;
- 取出下一个元素,在已经排好序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
- 将新元素插入到该位置后;
- 重复步骤2~5,直至最后一个元素被插入到合适的位置。
3.2 python实现
def InsertSort(lst):n = len(lst)if n <=1:return lstfor i in range(1,n):j = inum = lst[i] # 每次循环的等待插入的数while j > 0 and num < lst[j-1]: # 比较、后移,给待插入数num腾位lst[j] = lst[j-1]j = j-1lst[j] = num # 把待插入数num插到空位return lstif __name__ == "__main__":arr = list(map(int, input().split()))arr = InsertSort(arr)for i in arr:print(i, end =' ')
3.3 复杂度
- 时间复杂度:O(n2),每轮操作O(n)次,共O(n)轮。
- 空间复杂度:O(1),额外空间开销出在数据移位时那一个过渡空间
3.4 稳定性
- 稳定
4. 希尔排序(Shell Sort)
4.1 思想
基本思想:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序
- 步骤
- 选择一个增量序列t1,t2,…,tk,其中ti>tk,tk=1
- 按增量序列个数k,对序列进行k趟排序
- 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
图源:link
- 希尔排序是插入排序的高效实现,减少直接插入排序移动次数。由于简单插入排序每次插入都要移动大量数据,前后插入时的许多移动都是重复操作,若一步到位移动效率会高很多。
- 若序列基本有序,简单插入排序只需要比较,不必做很多移动操作,效率很高。
- 希尔排序将序列按固定间隔划分为多个子序列,在子序列中简单插入排序,先做远距离移动使序列基本有序;逐渐缩小间隔重复操作,最后间隔为1时即简单插入排序。
4.2 python实现
def ShellSort(lst):def shellinsert(arr, d):n = len(arr)for i in range(d,n):j = i - dnum = arr[i] # 记录要插入的数while (j >= 0 and arr[j] > num): # 从后向前,找到比其小的数的位置arr[j + d] = arr[j] # 向后挪动j -= dif j != i - d:arr[j + d] = numn = len(lst)if n <= 1:return lstd = n // 2while d >= 1:shellinsert(lst, d)d = d // 2return lstif __name__ == "__main__":arr = list(map(int, input().split()))arr = ShellSort(arr)for i in arr:print(i, end =' ')
4.3 复杂度
- 时间复杂度:O(nlogn),对序列划分O(n)次,每次简单插入排序O(logn)
- 空间复杂度:O(1),额外空间开销出在插入过程数据移动需要的一个暂存
4.4 稳定性
- 不稳定
5. 直接选择排序
5.1 思想
核心思想:简单选择排序同样对数据操作n-1轮,每轮找出一个最大(小)值。
- 步骤
- 初始状态:无序区为R[1…n],有序区为空;
- 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
- n-1趟结束,数组有序化了。
5.2 python实现
def SelectSort(lst):n = len(lst)if n<=1:return lstfor i in range(n-1):minIndex = ifor j in range(i+1,n): #比较一遍,记录索引不交换if lst[j]<lst[minIndex]:minIndex = jif minIndex!=i: #按索引交换lst[minIndex],lst[i] = lst[i], lst[minIndex]return lstif __name__ == "__main__":arr = list(map(int, input().split()))arr = SelectSort(arr)for i in arr:print(i, end =' ')
5.3 复杂度
- 时间复杂度:O(2),比较O(n)轮,每轮操作O(n)次
- 空间复杂度:O(1)
5.4 稳定性
- 稳定
6. 堆排序(Heap Sort)
6.1 思想
核心思想:堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
- 步骤
- 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
- 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
- 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
6.2 python实现
7. 归并排序
7.1 思想
7.2 python实现
8. 基数排序
8.1 思想
8.2 python实现
未完待续…