常用的排序算法
一.排序较慢的三个排序方法
1.冒泡排序
自然语言描述
按照列表中待排序的先后顺序,依次比较相邻的两个数,若两者是升序则不做任何操作,否则交换两者位置。
核心算法举例
以第一趟为例
1 5 7 3 9 2 6 8 1 与 5比较,不变
1 5 7 3 9 2 6 8 5 与 7比较,不变
1 5 3 7 9 2 6 8 7 与 3比较,交换位置
1 5 3 7 9 2 6 8 7 与 9比较,不变
1 5 3 7 2 9 6 8 9 与 2比较, 交换位置
1 5 3 7 2 6 9 8 9 与 6比较,交换位置
1 5 3 7 2 6 8 9 9 与 8比较,交换位置
所以第一趟结束后,排序结果为
1 5 3 7 2 6 8 9 9为最大数,后续不需要比较
算法优劣分析
- 一共比较((N-1)+(N-2)+...+2+1)次
- 最好情况下交换0次
- 最坏情况下交换((N-1)+(N-2)+...+2+1)次
- 平均时间复杂度O(n²)
def bubble_sort(lists): for i in range(len(lists)): for v in range(i + 1, len(lists)): if lists[i] > lists[v]: lists[i], lists[v] = lists[v], lists[i] return lists list1 = [67, 23, 89, 35, 28, 90, 10, 24] print(bubble_sort(list1))
2.选择排序
自然语言描述
- 遍历列表所有元素,最小(大)的元素放在最左(右)边。
- 确定第一个元素位置后,遍历剩下的所有元素,最小(大)的元素放在最左(右)边。
- 以此类推,直到倒数第二个元素。
核心算法举例
第二趟比较为例(以找最小数为例)
此时min_index = 1
1 7 5 9 4
7 与 5 作比较,min_index更新为2
5 与 9 作比较,min_index不变
5 与 4 作比较,min_index更新为4
将index = 4与index=1的元素交换位置
1 4 5 9 7
算法优劣分析
- 一共比较((N-1)+(N-2)+...+2+1)次,交换N次。
- 平均时间复杂度O(n²)
def select_sort(lists): count = len(lists) for i in range(count): min_num = i for v in range(i + 1, count): if lists[min_num] > lists[v]: min_num=v lists[min_num], lists[i]=lists[i], lists[min_num] return lists list1 = [67, 23, 89, 35, 28, 90, 10, 24] print(select_sort(list1)) # 每次都把最小的数给取出来排序
3.插入排序
自然语言描述
在前面已经排好序的列表中插入新元素。步骤:
- 将第二元素与第一个元素比较,如果小于第一个元素则交换位置,反之不变。
- 将第三个元素分别与前两个元素比较(这里的算法用的是与之前一个的位置比较),插入合适位置。
- 以此类推,直到最后一个元素。
核心算法举例
第二趟比较为例
此时 current_value = 1
5 7 1
current_value = 1 与7作比较
5 7 7
current_value = 1 与5作比较
5 5 7
将 current_value = 1 插入
1 5 7
算法优劣分析
- 最坏的情况下,一共比较并交换(1+2+...+(N-1))次
- 最好的情况下,只需比较(N-1)次,无需交换
- 平均时间复杂度O(n²)
- 稳定
def insert_sort(lists): count = len(lists) for i in range(1, count): key = lists[i] j = i - 1 while j >= 0: if lists[j] > key: lists[j + 1] = lists[j] lists[j] = key j -= 1 return lists list1 = [67, 23, 89, 35, 28, 90, 10, 24] print(insert_sort(list1))
二.排序比较快的三个排序方法
1.快速排序
自然语言描述
对冒泡排序的有效改进。快速排序是一种不稳定的排序算法,即多个相同的值的相对位置也许会在算法结束时产生变动。
假设要排序的列表a[0],a[1]...a[n-1],首先任意选取一个数据(通常选用a[0])作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。整个排序过程可以递推进行,从而使整个列表变得有序。
def quick_sort(lists, left, right): """ :param lists: 需要排序的列表 :param left: 列表的开始索引 :param right: 列表的结束索引 :return: """ if left > right: return lists key = lists[left] low = left high = right while left < right: while left < right and lists[right] >= key: right -= 1 lists[left] = lists[right] while left < right and lists[left] <= key: left += 1 lists[right] = lists[left] lists[right] = key # 把大小分成两部分后,在对着两部分进行排序 quick_sort(lists, low, left - 1) quick_sort(lists, left + 1, high) return lists list1 = [67, 23, 89, 35, 28, 90, 10, 24] left = 0 right = len(list1) - 1 print(quick_sort(list1, left, right))
2.堆排序
选择排序 每次在n个记录中选择一个最小的需要比较n-1次,但是这样的操作并没有把每一趟的比较结果保存下来,在后一趟的比较中,有许多的比较在前一趟就已经做过了,但是由于前一趟排序时并未保存这些比较结果,所以后一趟排序时又重复执行了这些比较操作,因而记录的比较次数比较多
如果可以做到每次在选择最小记录时,并根据比较结果对其他记录做出相应的调整,那样排序的总体效率就会非常高了,而堆排序就是对简单选择排序进行的一种改进,这种改进的效率是非常明显的
一.堆结构
1.堆是具有下列性质的完全二叉树:
(1)每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;
(2)或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆;
从堆的定义可知:根节点一定是堆中所有结点中共的最大值或者最小值
2.按照层序遍历的方式给结点进行编号,那么非叶结点的编号满足如下关系:
1<=i<=[n/2] [n/2]表示不超过n/2的最大整数
因为完全二叉树的性质:(这里的i指的是编号)
(1)如果2i>n,那么这个i对应的节点是叶节点,且没有左孩子,反之,我们知道不是叶节点的节点就满足2i<=n,即得到了上面的表达式
(2)编号为i的节点的左右子节点编号分别是2i和2i+1
那么按照层序遍历的方式,将最大堆和最小堆存入数组,那么一定满足上面的关系
二.堆排序算法
1.基本思想
将待排序的序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点,将它移走,然后将剩余的n-1个序列重新构造一个堆,这样就会得到n个元素中的次大值,如此反复执行,便能得到一个有序序列了
那么实现这个思想要解决两个问题:
(1)如何由一个无序序列构建成一个堆
(2)在输出堆顶元素后,如何调整剩余元素称为一个新的堆
# 调整堆 def adjust_heap(lists, i, size): lchild = 2 * i + 1 rchild = 2 * i + 2 max = i if i < size / 2: if lchild < size and lists[lchild] > lists[max]: max = lchild if rchild < size and lists[rchild] > lists[max]: max = rchild if max != i: lists[max], lists[i] = lists[i], lists[max] adjust_heap(lists, max, size) # 创建堆 def build_heap(lists, size): for i in range(0, (size // 2))[::-1]: adjust_heap(lists, i, size) # 堆排序 def heap_sort(lists): size = len(lists) build_heap(lists, size) for i in range(0, size)[::-1]: lists[0], lists[i] = lists[i], lists[0] adjust_heap(lists, 0, i) return lists list1 = [67, 23, 89, 35, 28, 90, 10, 24] print(heap_sort(list1))
3.归并排序
归并排序:
归并排序(英语:Merge sort,或mergesort),是创建在归并操作上的一种有效的排序算法,效率为O(n log n)。1945年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。
分治法:
字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
分治法的设计思想是:
将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
# 归并排序 def merge(left, right): i, j = 0, 0 result = [] while i < len(left) and j < len(right): if left[i] <= right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 result += left[i:] result += right[j:] return result def merge_sort(lists): # 归并排序 if len(lists) <= 1: return lists num = len(lists) // 2 left = merge_sort(lists[:num]) right = merge_sort(lists[num:]) return merge(left, right) list1 = [67, 23, 89, 35, 28, 90, 10, 24] print(merge_sort(list1))

浙公网安备 33010602011771号