常见排序算法之python实现

1. 冒泡排序

时间复杂度为O(n^2), 稳定的排序算法

思路:一开始比较的区间是【0,n-1】,依次比较相邻两数,哪个数大哪个数就放在后面,这样一次遍历数组后,最大的数会在数组的最后一个位置,然后比较区间从【0,n-1】缩小到了【0,n-2】,在进行一次遍历下来,第二大的数会被放在数组倒数第二的位置,依次类推。直到把范围缩小到一个数的时候,数组就有序了。

def bubbleSort(arr):
    
  length
= len(arr) for i in range(length-1): #趟数 for j in range(length-i-1): #每趟元素数 if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr

 2. 选择排序

时间复杂度为O(n^2),不稳定的排序算法

思路:一开始从【0,n-1】选出一个最小值放在位置0上;第二次从【1,n-1】上选择一个最小值放在位置1上;依次类推;直到数组访问剩下一个元素时,数组有序了。

def selectionSort(arr):
    """选择排序
    """
    length = len(arr)

    for i in range(length):
        min_i = i # 最小值索引
        for j in range(i, length):
            if arr[min_i] > arr[j]:
                min_i = j

        arr[min_i], arr[i] = arr[i], arr[min_i]

    return arr

3. 插入排序*

时间复杂度为O(n^2),稳定的排序算法

:当数组几乎有序的情况下,时间复杂度为O(n*k),k为最多移动的距离;当数组逆序时,时间复杂度最大为O(n^2)

思路:把n个待排序的元素看成是一个有序表和一个无序表,开始时有序表只有一个元素,无序表有n-1个元素。排序过程中每次从无序表中取出一个元素,把它的排序码依次与有序表中元素比较(后->前),将它插入有序表的适当位置,使之成为新的有序表。

def insertSort(arr):
    """插入排序
    """
    length = len(arr)

    for i in range(length-1):
        o_i = i # 有序表最后一个元素索引
        u_v = arr[o_i+1] # 无序表的元素值
        
        while o_i >= 0 and arr[o_i] > u_v:
            arr[o_i+1] = arr[o_i]
            o_i -= 1
        arr[o_i+1] = u_v # 带插入元素合适位置,+1是因为前面-1

    return arr

4. 归并排序*

时间复杂度为O(nlogn),稳定的排序算法

思路:首先让数组每一个元素成为长度为1的有序区间;然后将两个长度为1的有序区间进行合并,生成长度为2的有序区间;然后再将两个长度为2的有序区间进行合并,生成长度为4的有序区间;依次类推,直至数组有序。1合2,2合4,4合8,8合16,……

def mergerSort(arr):
    """归并排序

        划分成左右两部分; 两两合并,成有序数组
    """
    if len(arr) <= 1:
        return arr

    mid = len(arr) // 2
    # 左右划分, 生成长度为1的有序数组时开始返回
    leftArr = mergerSort(arr[:mid])
    rightArr = mergerSort(arr[mid:])
    # 两两合并
    return merger(leftArr, rightArr)

def merger(left, right):
    """合并两个有序数组,形成新的有序数组返回
    """
    res = []
    l, r = 0, 0
    while l < len(left) and r < len(right):
        if left[l] <= right[r]:
            res.append(left[l])
            l += 1
        else:
            res.append(right[r])
            r += 1

    res += left[l:]
    res += right[r:]
    return res

5. 快速排序**

时间复杂度为O(nlogn), 不稳定的排序算法

:在数组完全无序的情况下,快排效果最好,为O(nlogn);在有序情况下效果最差,为O(n^2)。

思路:随机排序 - 在数组中随机选择一个数,使得数组中大于它的数放在它的右边、小于等于它的数放在它的左边,然后左右两边的数递归的调用快排的过程。

快排的一次划分过程  -  时间复杂度为O(n):

  首先,我们随机选择数组中的一个作为划分值;然后把划分值与数组的最后一个元素进行交换,使得划分值放到数组的最后一个位置(如b);然后我们定义一个小于等于区间,初始时,小于等于区间为空,放在数组的最左边(如c);然后开始遍历数组,如果元素大于划分值,继续遍历下一个元素,如果元素值小于等于划分值,就让该元素与小于等于区间的下一个元素进行交换,扩增小于等于区间(如d,e);依次执行这个过程,直到数组中只剩一个数;然后数组最后一个数(划分值)与小于等于区间的下一个数进行交换,快排的一次划分过程就结束了(如f,g)。

     

def quickSort(arr, left, right):
    """快速排序

        对left到right区间上的数组进行快排
    """
    if left >= right:
        return None

    midValue = arr[(left+right)//2] # 划分值
    l, r = left, right
    while l < r:
        while arr[l] < midValue: l += 1
        while arr[r] > midValue: r -= 1

        if l >= r: break

        arr[l], arr[r] = arr[r], arr[l]
        r -= 1
        l += 1

    if l == r:
        l += 1
        r -= 1

    if left < r: quickSort(arr, left, r)
    if right > l: quickSort(arr, l, right)

    return None

 6. 堆排序*

时间复杂度:O(nlogn),不稳定的排序算法,常用于查找top x

思路:首先将数组【0,n-1】区间上的所有元素构建大根堆,然后将堆顶元素与数组最后一个元素进行交换,这样数组最后一个元素为最大值,作为数组有序部分存在下来;然后将数组【0,n-2】区间上的所有元素构建大根堆,然后执行堆顶元素与数组的倒数第二个元素进行交换,这样数组后两位元素分别是数组的最大值和第二大的值存在下来;然后将数组区间【0,n-3】区间构建大根堆,交换;重复以上过程直到数组有序。

def heapSort(arr):
    """堆排序
    """
    arr = buildMaxHeap(arr)

    for i in list(range(len(arr)))[::-1]:
        arr[0], arr[i] = arr[i], arr[0]
        adjustHeap(arr, 0, i) # n表示数组的大小(不是堆)

    return arr

def buildMaxHeap(arr):
    """建立大根堆

        从下向上构建
    """
    for i in list(range(len(arr)//2))[::-1]:
        adjustHeap(arr, i, len(arr))

    return arr

def adjustHeap(arr, start, size):
    """将数组[start:]调整成堆结构

        参数
        ---
            start: int
                数组起始index
            size:int
                数组大小
    """
    lChild = 2 * start + 1
    rChild = 2 * start + 2

    maxi = start

    if maxi < size//2: # 说明有孩子
        if lChild < size and arr[lChild] > arr[maxi]:
            maxi = lChild
        if rChild < size and arr[rChild] > arr[maxi]:
            maxi = rChild

        if maxi != start: # == 说明已经是最大堆了,所以这里不是
            arr[maxi], arr[start] = arr[start], arr[maxi]
            adjustHeap(arr, maxi, size)

 7. 希尔排序

时间复杂度为O(nlogn),不稳定的排序算法

思路:希尔排序是插入排序的改良,希尔排序相比于插入排序区别在于步长是逐步调整的,也就是希尔排序在执行完指定步长的插入排序后,逐渐调整步长,直至执行步长为1的插入排序为止。一般来说,初始步长gap=length/2,然后不断以gap=gap/2减小步长,直至降为1。希尔排序的成败和步长的选取相关,步长选取越优时间复杂度越低,步长选取越劣时间复杂度越是接近O(n^2)。

def shellSort(arr):
    """希尔排序

        使用希尔增量
    """
    n = len(arr)
    gap = n // 2 # 初始化步长
    while gap >= 1:
        for i in range(gap, n):
            index = i
            while index >= gap:
                if arr[index-gap] > arr[index]:
                    arr[index-gap], arr[index] = arr[index], arr[index-gap]
                    index -= gap
                else:
                    break

        gap //= 2 # 缩减步长
    return arr

 

posted @ 2017-10-26 21:46  ML小菜鸟  阅读(934)  评论(0编辑  收藏  举报