【原创首发】跟着小灰学Python-排序算法

211 阅读7分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

声明:版权归本人所有,违者必究。 
转载请注明来源 https://juejin.cn/post/7111942157082034212

1.1. 排序算法

排序,是将一组任意序列的数据按照某种规律(升序或降序)排列成有序的序列。排序算法,就是对这些数据进行排列的方法。常见的排序算法包括冒泡排序、快速排序、插入排序、希尔排序、选择排序、堆排序、归并排序、计数排序、桶排序、基数排序。各排序算法复杂度如下:

1.1.1. 插入排序

插入排序算法是基于某序列已经有序排列的情况下,通过一次插入一个元素的方式按照原有排序方式增加元素。这种比较是从该有序序列的最末端开始执行,即要插入序列中的元素最先和有序序列中最大的元素比较,若其大于该最大元素,则可直接插入最大元素的后面即可,否则再向前一位比较查找直至找到应该插入的位置为止。插入排序的基本思想是,每次将1个待排序的记录按其关键字大小插入到前面已经排好序的子序列中,寻找最适当的位置,直至全部记录插入完毕。执行过程中,若遇到和插入元素相等的位置,则将要插人的元素放在该相等元素的后面,因此插入该元素后并未改变原序列的前后顺序。我们认为插入排序也是一种稳定的排序方法。插入排序分直接插入排序、折半插入排序和希尔排序3类。

 

算法描述: 

一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:

(1)从第一个元素开始,该元素可以认为已经被排序;

(2)取出下一个元素,在已经排序的元素序列中从后向前扫描;

(3)如果该元素(已排序)大于新元素,将该元素移到下一位置;

(4)重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;

(5)将新元素插入到该位置后;

(6)重复步骤2~5。

 

代码实现:

【例1-1】 插入排序:

def insertionSort(arr):
    for i in range(len(arr)):
        preIndex = i - 1
        current = arr[i]
        while preIndex >= 0 and arr[preIndex] > current:
            arr[preIndex + 1] = arr[preIndex]
            preIndex-=1
        arr[preIndex + 1] = current
    return arr

if __name__ == '__main__':
    print(insertionSort([5,3,1,4,2,4]))

执行结果:

[1, 2, 3, 4, 4, 5]**

1.1.2. 冒泡排序

冒泡排序算法是把较小的元素往前调或者把较大的元素往后调。这种方法主要是通过对相邻两个元素进行大小的比较,根据比较结果和算法规则对该二元素的位置进行交换,这样逐个依次进行比较和交换,就能达到排序目的。冒泡排序的基本思想是,首先将第1个和第2个记录的关键字比较大小,如果是逆序的,就将这两个记录进行交换,再对第2个和第3个记录的关键字进行比较,依次类推,重复进行上述计算,直至完成第(n一1)个和第n个记录的关键字之间的比较,此后,再按照上述过程进行第2次、第3次排序,直至整个序列有序为止。排序过程中要特别注意的是,当相邻两个元素大小一致时,这一步操作就不需要交换位置,因此也说明冒泡排序是一种严格的稳定排序算法,它不改变序列中相同元素之间的相对位置关系。 

算法描述:

(1)比较相邻的元素。如果第一个比第二个大,就交换它们两个;

(2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;

(3)针对所有的元素重复以上的步骤,除了最后一个;

(4)重复步骤1~3,直到排序完成。

 

代码实现:

【例1-1】 冒泡排序:

def bubbleSort(arr):
    for i in range(len(arr) - 1):
        for j in range(len(arr)-1-i):
            if arr[j] > arr[j+1]:                       # 相邻元素两两对比
                arr[j], arr[j+1] = arr[j+1], arr[j]         # 元素交换
    return arr

if __name__ == '__main__':
    print(bubbleSort([5,3,1,4,2,4]))

执行结果:

[1, 2, 3, 4, 4, 5]**

1.1.3. 选择排序

选择排序算法的基本思路是为每一个位置选择当前最小的元素。选择排序的基本思想是,基于直接选择排序和堆排序这两种基本的简单排序方法。首先从第1个位置开始对全部元素进行选择,选出全部元素中最小的给该位置,再对第2个位置进行选择,在剩余元素中选择最小的给该位置即可;以此类推,重复进行“最小元素”的选择,直至完成第(n-1)个位置的元素选择,则第n个位置就只剩唯一的最大元素,此时不需再进行选择。使用这种排序时,要注意其中一个不同于冒泡法的细节。举例说明:序列58539.我们知道第一遍选择第1个元素“5”会和元素“3”交换,那么原序列中的两个相同元素“5”之间的前后相对顺序就发生了改变。因此,我们说选择排序不是稳定的排序算法,它在计算过程中会破坏稳定性。  

 

算法描述:

n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:

(1)初始状态:无序区为R[1..n],有序区为空;

(2)第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个的新无序区;

(3)n-1趟结束,数组有序化了。

 

代码实现:

【例1-1】 选择排序:

def selectionSort(arr):
    for i in range(len(arr)-1):
        min_index = i
        for j in range(i+1, len(arr)):
            if arr[j] < arr[min_index]:                     # 寻找最小的数
                min_index = j                          # 将最小数的索引保存
        arr[i], arr[min_index] = arr[min_index], arr[i]         # 元素交换
    return arr

if __name__ == '__main__':
    print(selectionSort([5,3,1,4,2,4]))

执行结果:

[1, 2, 3, 4, 4, 5]**

 

1.1.4. 快速排序

快速排序的基本思想是:通过一趟排序算法把所需要排序的序列的元素分割成两大块,其中,一部分的元素都要小于或等于另外一部分的序列元素,然后仍根据该种方法对划分后的这两块序列的元素分别再次实行快速排序算法,排序实现的整个过程可以是递归的来进行调用,最终能够实现将所需排序的无序序列元素变为一个有序的序列。 

 

算法描述:

快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

(1)从数列中挑出一个元素,称为 “基准”(pivot);

(2)重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

(3)递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

 

代码实现:

【例1-1】 快速排序:

def quick_sort(alist, start, end):
    if start >= end:
        # 退出递归
        return
    pivot = alist[start]
    right = end
    left = start

    # 控制right -= 1不满足条件交换
    while left < right:
        while left < right and alist[right] > pivot:
            right -= 1
        else:
            # 交换
            alist[left] = alist[right]
        # 控制 left += 1 , 不满足条件交换
        while left < right and alist[left] < pivot:
            left += 1
        else:
            alist[right] = alist[left]

    # 退出循环 left = right
    # left 或者 right 对应的位置 赋值为基准值
    alist[left] = pivot

    # 递归自己调用自己
    quick_sort(alist, start, left - 1)  # 对左边排序
    quick_sort(alist, left + 1, end)  # 对右边排序

if __name__ == '__main__':
    arr =  [5, 3, 1, 4, 2, 4]
    print(quick_sort(arr, 0, len(arr) - 1))

执行结果:

[1, 2, 3, 4, 4, 5]**

1.1.5. 归并排序

归并排序算法就是把序列递归划分成为一个个短序列,以其中只有1个元素的直接序列或者只有2个元素的序列作为短序列的递归出口,再将全部有序的短序列按照一定的规则进行排序为长序列。归并排序融合了分治策略,即将含有n个记录的初始序列中的每个记录均视为长度为1的子序列,再将这n个子序列两两合并得到n/2个长度为2(当凡为奇数时会出现长度为l的情况)的有序子序列;将上述步骤重复操作,直至得到1个长度为n的有序长序列。需要注意的是,在进行元素比较和交换时,若两个元素大小相等则不必刻意交换位置,因此该算法不会破坏序列的稳定性,即归并排序也是稳定的排序算法。

 

算法描述:

(1)把长度为n的输入序列分成两个长度为n/2的子序列;

(2)对这两个子序列分别采用归并排序;

(3)将两个排序好的子序列合并成一个最终的排序序列。

 

代码实现:

【例1-1】 归并排序:

def merge(a, b):
    c = []
    h = j = 0
    while j < len(a) and h < len(b):
        if a[j] < b[h]:
            c.append(a[j])
            j += 1
        else:
            c.append(b[h])
            h += 1
    if j == len(a):
        for i in b[h:]:
            c.append(i)
    else:
        for i in a[j:]:
            c.append(i)
    return c

def merge_sort(lists):
    if len(lists) <= 1:
        return lists
    middle = len(lists)//2
    left = merge_sort(lists[:middle])
    right = merge_sort(lists[middle:])
    return merge(left, right)

if __name__ == '__main__':
    print(merge_sort([5,3,1,4,2,4]))

执行结果:

[1, 2, 3, 4, 4, 5]**