基础算法(三):常用排序算法Python实现

本文主要是用python实现几种常用的比较排序算法,包括冒泡排序、选择排序、插入排序(二分插入排序和希尔排序)、堆排序、归并排序和快速排序,主要参考的博客:常用排序算法总结(一),参考博客已经总结的非常nice了,里面有排序过程的动态效果图和细致的分析,只不过是使用C++实现的,本文则是python实现的简化版,排序均指从小到大排序,想看细致分析的可以参考C++版本的本文错误之处还望不吝指正。常见的比较排序算法性能分析表:


1:冒泡排序(Bubble Sort)

基本思想:从左到右,比较相邻的两个元素,选择最大的元素逐步移至最右边,依次循环,直到选择出较大的n-1个数为止。

#  __author__ = 'czx'
# coding=utf-8

def swap(A,i,j):      # 元素A[i]和元素A[j]交换
    tmp =A[i] 
    A[i]=A[j]
    A[j] =tmp

def bubblesort(A):    # 冒泡排序
    n = len(A)
    for j in range(n-1):         # 只需循环n-1次,每次都是选择最大的数后移
        for i in range(n-1-j):   # 这里只需要对n-1-j个数据进行排序,因为有j个较大值已经排序好了
            if A[i] > A[i+1]:    # 相邻元素比较,前者大则后移
                swap(A,i,i+1)

def cocktailsort(A):  # 鸡尾酒排序:从左到右选最大,从右到左选最小
    n = len(A)
    left = 0
    right = n-1
    while left < right :                # 对数组A[left,right]进行排序,直到left==right
        for i in range(left,right):     # 从左到右选最大
            if A[i] > A[i+1]:
                swap(A,i,i+1)
        right = right - 1               # 选好了最大值时,right-1

        for i in range(right,left,-1):  # 从右到左选最小
            if A[i-1] > A[i]:
                swap(A,i-1,i)
        left = left + 1                 # 选好了最小值时,left+1

if __name__=='__main__':
    A = [6,12,5,11,3,9,10,1,8,7,2,4]
    print A
    cocktailsort(A)                     # 选择排序算法,bubblesort 或者 cocktailsort
    print A

2:选择排序(Selection Sort)

基本思想:选择最小的元素放在数组最前端,作为已排序的部分,对于未排序的部分,同样选择未排序中的最小的放在未排序部分的最前端,也就是已排序的末端。

#  __author__ = 'czx'
# coding=utf-8

def swap(A,i,j):       # 元素A[i]和元素A[j]交换  
    tmp =A[i]
    A[i]=A[j]
    A[j] =tmp

def selectionsort(A):  #选择排序
    n = len(A)
    for i in range(n-1):        # 同样只需要依次选择n-1个未排序的最小的值即可
        min = i                 # 记住最小值的下标,用于交换
        for j in range(i+1,n):  # 已排好i-1个元素,从第i个元素开始比较选择最小值
            if A[min] > A[j]:   # 如果找到更小的值,记下该更小值的下标
                min = j
        if min != i:            # 只有在下标和每次初始i不同时才交换位置
            swap(A,min,i)

if __name__=='__main__':
    A = [6,12,5,11,3,9,10,1,8,7,2,4]
    print A
    selectionsort(A)
    print A

3:插入排序(Insertion Sort)

基本思想:想象打扑克,手上抓第一张牌的时候只有一张,抓第二张的时候跟第一张比较该放在左边还是右边,依次循环,每抓到一张牌,我们就选择合适的位置放这张牌,使得手上的牌是有序的。那么对于排序,我们从左到右选择元素(新抓到的牌)依次和已经排序好的元素(手上的牌)比较,选择合适的位置插入新牌(从手牌右端最大的逐个向左比较)。其实好多玩扑克的人都不按照从小到大的顺序放牌的,哈哈,我就是。

#  __author__ = 'czx'
# coding=utf-8

def swap(A,i,j):          # 元素A[i]和元素A[j]交换
    tmp =A[i]
    A[i]=A[j]
    A[j] =tmp

def insertionsort(A):     # 插入排序
    n = len(A)
    for i in range(1,n):               # 抓牌(初始时候默认手上有一张牌,即元素A[0])
        tmp = A[i]                     # 记下抓到什么牌
        j = i - 1                      # 和之前的i张牌进行比较
        while j>=0 and A[j]>tmp:       # 从右到左比较,新抓的牌较小时,手牌后移
            A[j+1] = A[j]
            j =j -1
        A[j+1]=tmp                     # 找到合适的位置时插入手牌

def binaryinsertsort(A):   # 二分插入排序:逐个比较移动太麻烦?那就二分比较吧
    n = len(A)
    for i in range(1,n):               # 取n-1次元素(初始时候默认手上已经有一个元素)
        tmp = A[i]
        left = 0                       # 左端
        right = i-1                    # 右端
        while left<=right:
            mid =(left+right)/2        # 中间一个的下标
            if A[mid]>tmp:             # 比较新元素和已排序列的中间元素,如果中间元素比新元素大
                right = mid - 1        # 选取左半序列作为新的二分查找序列
            else:
                left = mid + 1         # 否则选取右半序列
        j = i - 1
        while j>=left:                 # 向右移动较大的元素
            A[j+1] = A[j]
            j = j - 1
        A[left]=tmp                    # 正确位置插入新元素

def shellsort(A): # 希尔排序:利用‘插入排序对已经排序好的序列效率高(比较移动的次数少)’的特性
    step = 0
    n = len(A)
    while step<=n/3: # 选取一系列增量step
        step = 3*step + 1
    while step>=1:
        for i in range(step,n):        #  从step之后逐个选值
            j = i-step
            tmp = A[i]
            while j>=0 and A[j]>tmp:   # 和对应step之前的对应元素比较,如果step当前值较小
                A[j+step] = A[j]       # 数据后移
                j = j-step             # 以step递减
            A[j+step] = tmp            # 插入正确的位置
        step = (step-1)/3              # 递减增量
        
if __name__=='__main__':
    A = [6,12,5,11,3,9,10,1,8,7,2,4]
    print A
    shellsort(A)                       # 排序方法:insertionsort 或者 binaryinsertsort 或者shellsort
    print A

4:归并排序(Merge Sort)

基本思想:分治,将待排序数组不断细分至单个元素为一个小数组,两两合并成较大的数组,依次合并成一个数组。实现的方法有递归和非递归的。

#  __author__ = 'czx'
#coding:utf-8

def merge(A,left,mid,right):    # 归并以mid为界的左右子序列
    len = right-left+1          # 序列长度
    tmp = []                    # 临时空间,存储左右子序列的比较排序结果
    i = left                    # 左序列起点
    j = mid + 1                 # 有序列起点
    while i<=mid and j<=right:  # 直到有任意一个序列的所有元素比较完成
        if A[i]<=A[j]:          
            tmp.append(A[i])    # 选取较小的值加入tmp
            i = i + 1
        else:
            tmp.append(A[j])
            j = j + 1
    while i <= mid:             # 右子序列元素比较完成后直接将左子序列元素加入到tmp
        tmp.append(A[i])
        i = i + 1
    while j <= right:           # 左子序列元素比较完成后直接将右子序列元素加入到tmp
        tmp.append(A[j])   
        j = j + 1
    for k in range(len):        # 将排序好的tmp赋值给A[left,right],实现子序列A[left,right]的排序
        A[left]=tmp[k]
        left = left +1

def mergesortrecursion(A,left,right):   # 递归实现归并排序
    if left==right:
        return
    mid =(left+right)/2                 # 通过mid将A一分为二
    mergesortrecursion(A,left,mid)      # 以mid为界,归并A的左半序列
    mergesortrecursion(A,mid+1,right)   # 归并A的右半部分
    merge(A,left,mid,right)             # 对左半子序列A[left,mid]和右半子序列A[mid+1,right]归并

def mergesortiteration(A,len):          # 非递归实现并归排序
    i = 1                               # i表示待归并子序列大小,初始化为1
    while i<len:                        # 子序列大小不超过A的长度,但会大于等于A长度的一半
        left = 0                        # left:初始化为0
        while left + i < len:           # 直到当前子序列只包含不到两个更小的子序列,比如i=8时,右侧只剩3个元素
            mid = left + i - 1          # mid:相邻两子序列中间分界
            if mid + i <len:            # right:右侧子序列长度可能越界,比如不是完全二分的情况
                right = mid + i
            else:
                right = len-1          
            merge(A,left,mid,right)     # 归并操作
            left = right +1             # 归并完前面两个子序列后归并后面的两个子序列
        i = i * 2                       # 每次翻倍,比如归并1和1大小的子序列后变为2,此时便归并2和2子序列

if __name__ == '__main__':
    A = [6,12,5,11,3,9,10,1,8,7,2,4]
    print A
    #mergesortrecursion(A, 0, len(A)-1)
    mergesortiteration(A,len(A))
    print A

5:堆排序(Heap Sort)

基本思想:根据最大堆(最小堆)的性质实现排序功能,初始化堆,不断用堆尾数据替换堆顶数据并将堆顶数据输出,堆减小至1,实现最后的排序。堆树相关知识可参考:堆树(最大堆和最小堆)

#  __author__ = 'czx'
# coding=utf-8

def swap(A,i,j):                # 元素A[i]和A[j]交换
    tmp =A[i]
    A[i]=A[j]
    A[j] =tmp

def heapify(A,i,size):                      # 父亲节点i的调整(递归)
    left = 2*i+1                            # 左节点
    right = 2*i+2                           # 右节点
    max = i                                 # 最大节点值的下标
    if left<size and A[left]>A[max]:        # 如果有左节点且左节点值较大
        max = left                          # 最大下标变为left
    if right<size and A[right]>A[max]:      # 如果有右节点且右节点值较大
        max = right                         # 最大下标为right
    if max!=i:                              # 当前节点不符合最大堆性质时进行调整
        swap(A,i,max)                       # 先交换数据位置使得符合性质
        heapify(A,max,size)                 # 调整对应的子节点使得其符合最大堆性质

def buildheap(A,n):         # 建堆,堆树是一个完全二叉树结构
    i = n/2-1               # 根据完全二叉树性质找到最末尾节点的父亲节点,然后调整该节点,使得符合最大堆性质
    while i>=0:             # 自下往上调整直到整棵树的堆顶
        heapify(A,i,n)      # 数组A,父亲节点i,堆的大小n
        i = i - 1

def heapsort(A,n):          # 堆排序
    buildheap(A,n)          # 初始化建堆
    size = n                # 堆的大小n
    while size > 1:         
        size = size - 1     # 堆元素减一
        swap(A,0,size)      # 数据元素下标从0开始到n-1,所以size-1放在数据交换之前
        heapify(A,0,size)   # 调整堆


if __name__=='__main__':
    A = [6,12,5,11,3,9,10,1,8,7,2,4]
    print A
    heapsort(A,len(A))
    print A

6:快速排序(Quick Sort)

基本思想:每次选取一个基准(比如将待排序数组的最右端数值作为基准),将小于基准值的数放在最左端,大于基准数的值放在最右端,依次递归实现快速排序。

#  __author__ = 'czx'
# coding=utf-8

def swap(A,i,j):     # 交换元素A[i]和A[j]
    tmp =A[i]
    A[i]=A[j]
    A[j] =tmp

def partition(A,left,right):   数组的A[left]到A[right]作为待比较的子序列
    pivot = A[right]           # 最右端的值作为基准
    tail = left -1             # 当前子序列左边序列的尾端
    i = left                   # 最左端开始和基准值比较
    while i<right:             # 直到最右端
        if A[i]<=pivot:        # 如果当前值比基准值小
            tail = tail + 1    # 将数据放在左边子序列的尾端(连接起来)
            if tail !=i:       # 如果tail即使i处的值,便无需交换
                swap(A,tail,i) # 否则,tail一定小于i,并交换两者的位置
        i = i + 1
    swap(A,tail+1,right)       # 确定此时左子序列的队尾位置,并与基准值交换
    return tail + 1            # 返回当前左子序列队尾下标,作为下次递归的分界点

def quicksort(A,left,right):           # 快速排序
    if left>=right:                    # 递归出口,当要排序的子序列的左端left<=right右端
        return
    pivot = partition(A,left,right)    # 返回基准值的下标索引
    quicksort(A,left,pivot-1)          # 根据下标索引,对左子序列进行快速排序
    quicksort(A,pivot+1,right)         # 对右边子序列进行快速排序



if __name__=='__main__':
    A = [6,12,5,11,3,9,10,1,8,7,2,4]
    print A[:-1]
    print A
    quicksort(A,0,len(A)-1)
    print A






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值