目录
概要
冒泡排序
快速排序
概要
插入排序,是基于比较和移动操作实现的排序,从算法效率上看不够高,但是实现简单,适用于数据量比较少的场景。
现在讲一讲基于比较和交换操作实现的排序。
冒泡排序
首先是冒泡排序。冒泡排序效率并不高,和插入排序差不多,但一般讲到基于交换的排序都会提到冒泡排序,就像是个引子,起到抛砖引玉的作用,它实现起来很简单。
冒泡排序的思路是通过一轮的比较、交换操作把序列当中的最大值给交换到序列的最右边,然后再对去掉最大值的序列进行冒泡排序,把序列中第二大的值输出到右边倒数第二的位置,以此类推。
先让我们来看看输出序列中的最大值到最右边的逻辑是怎么样的。
例如一个序列 a1 a2 a3…ai ai+1…an,我们把它做一个子序列划分a1;a1 a2;a1 a2 a3;…;a1 a2 a3…ai ai+1…an,我们来归纳一下。
basis:子序列a1是满足最大值在最右边的序列。
induction:若子序列a1 a2…ai是满足最大值在最右边的序列,如何求a1 a2…ai ai+1的满足最大值在最右边的序列?只需要比较ai和ai+1,若ai<=ai+1则a1 a2…ai ai+1是所求序列,否则交换ai和ai+1得到序列a1 a2…ai-1 ai+1 ai是所求序列。
算法实现很简单:
def bubblesort(source):for i in range(len(source), 0, -1):for j in range(0, i - 1):if source[j] > source[j + 1]:source[j], source[j + 1] = source[j + 1], source[j]return sourceprint(bubblesort([2, 5, 1, 2, 4, 3]))
这个算法其实可以优化,假设a1 a2…ai是满足最大值在最右边的序列,求a1 a2…ai ai+1满足最大值在最右边的序列,若ai<=ai+1,则a1 a2…ai ai+1就是所求的序列,这个时候ai还是这个序列中的第二大值,这样我们可以一下子输出ai ai+1作为序列的最大的两个值,同理我们还可能一下子输出ai-1 ai ai+1作为序列最大的三个值,最极端情况如果序列a1 a2…an本身就是正序,我们一轮比较下来可以直接输出这整个序列,我们只需要使用一个位串来作为特征值来表示各个位置的交换情况,如交换了取1,若没有交换取0,一轮比较交换操作下来,最后得到的位串,尾部有多少个连续个0,则输出序列尾部多少个值作为序列的最大的多少个值。
算法实现如下:
def count_trailing_zeros(n):count = 0while n & 1 == 0:count += 1n >>= 1return countdef bubblesort2(source): end = len(source)while end > 0:bitval = 0for j in range(0, end - 1):if source[j] > source[j + 1]:source[j], source[j + 1] = source[j + 1], source[j]bitval = bitval * 2 + 1else:bitval = bitval * 2if 0 == bitval:breakelse:end -= count_trailing_zeros(bitval)return source
print(bubblesort2([2, 5, 1, 2, 3, 4]))
快速排序
说完了冒泡排序,现在来谈一谈快速排序,快速排序的思路其实和冒泡也相近,它的思路也是通过一轮操作下来输出一个值到它在最终应该在的序列位置,不过它并不是每一轮操作输出剩余序列中的最大值,而是可以任意指定序列中的一个值。
算法逻辑并不复杂,它是在序列的首尾各设一个游标,首部游标向尾部移动,当找到一个比指定值大的值时将这个值和选定的值交换位置,从而把比选定值大的值交换到选定值的右边,尾部游标向首部移动,当找到一个比指定值小的值时将这个值和选定的值交换位置,从而把比选定值小的值交换到选定值的左边,两个游标交替操作,当两个游标汇合的时候,汇合处就是选定值最终应该被放置的位置。
算法实现如下:
def quicksort(source):def resolve_pivot(low, high):mid = int((low + high) / 2)if source[low] < source[mid]:if source[mid] <= source[high]:return midelse:if source[low] >= source[high]:return lowelse:return highelse:if source[mid] > source[high]:return midelse:if source[low] <= source[high]:return lowelse:return highdef sort(low, high):if low >= high:return_low, _high = low, highpivot = resolve_pivot(low, high)chosen = source[pivot]while low < high:while low < pivot and source[low] <= chosen:low += 1else:if low < pivot:source[pivot] = source[low]pivot = lowwhile high > pivot and source[high] >= chosen:high -= 1else:if high > pivot:source[pivot] = source[high]pivot = highsource[pivot] = chosensort(_low, pivot - 1)sort(pivot + 1, _high)sort(0, len(source) - 1)return sourceprint(quicksort([2, 5, 1, 2, 4, 3]))
算法书上,固定取序列的第一个值为选定值,这样实现也非常好,因为这样选处理起来更容易,若选定值不取序列的第一个值先交换选定值和序列的第一个值的位置,从而问题又给转化为取序列的第一个值为选定值的快速排序问题了,这是一个非常好的把普通情形转化为更为简单的特殊情形来处理的示范,值得学习。
下面给出代码。
def quicksort(source):def resolve_pivot(low, high):mid = int((low + high) / 2)if source[low] < source[mid]:if source[mid] <= source[high]:return midelse:if source[low] >= source[high]:return lowelse:return highelse:if source[mid] > source[high]:return midelse:if source[low] <= source[high]:return lowelse:return highdef sort(low, high):if low >= high:return_low, _high = low, highpivot = resolve_pivot(low, high)chosen = source[pivot]source[low], source[pivot] = source[pivot], source[low]while low < high:while high > low and source[high] >= chosen:high -= 1else:source[low] = source[high]while low < high and source[low] <= chosen:low += 1else:source[high] = source[low]source[low] = chosensort(_low, low - 1)sort(low + 1, _high)sort(0, len(source) - 1)return sourceprint(quicksort([2, 5, 1, 2, 4, 3, 1]))