7 Sort Methods

# -*- coding: utf-8 -*-
# 一、冒泡排序 BubbleSort,时间复杂度O(n2)
# 冒泡排序的原理非常简单,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。
# 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
# 对第0个到第n-1个数据做同样的工作。这时,最大的数就“浮”到了数组最后的位置上。
# 针对所有的元素重复以上的步骤,除了最后一个。
# 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
def bubble_sort(arry):
    n = len(arry)
    for i in range(n):
        for j in range(1,n-i):    # 从第二个数到上次沉淀的数前的数,开始往前比较
            if arry[j-1] > arry[j]:
                arry[j-1],arry[j] = arry[j],arry[j-1]      #如果前者比后者大,则交换两者
    return arry
print bubble_sort([1,3,2,5,6,4])

# 优化1、在i的情况下,如所有j都不需要和j-1交换顺序,则i不用再往后走了;如交换顺序,则一直往后走,设置flag
def bubble_sort1(arry):
    n = len(arry)
    for i in range(n):
        flag=1
        for j in range(1,n-i):
            if arry[j-1] > arry[j]:
                arry[j-1],arry[j] = arry[j],arry[j-1]
                flag=0
        if flag:    # 如果所有j都没有交换顺序,说明该i后面的arry都是升序的,跳出该循环
            break
    return arry
print bubble_sort1([1,3,2,5,6,4])

# 优化2、每次遍历一边j,记录最后交换的位置j,该位置后已经排好序了,下一个i可以只走到该位置前
def bubble_sort2(arry):
    n = len(arry)
    stop=n
    for i in range(n):
        flag=1
        for j in range(1,stop):    # 对于每个i,j只需要遍历到stop位置
            if arry[j-1] > arry[j]:
                arry[j-1],arry[j] = arry[j],arry[j-1]
                flag=0
                stop=j    # 记录位置,之后的已经排好序
        if flag:
            break
    return arry
print bubble_sort2([1,3,2,5,6,4])


# 二、选择排序 SelectionSort,时间复杂度O(n2)
# 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
# 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的起始位置。
# 以此类推,直到所有元素均排序完毕。
def select_sort(arry):
    n = len(arry)
    for i in range(0,n):
        min = i                             # 初始位置标记
        for j in range(i+1,n):
            if arry[j] < arry[min] :
                min = j                     # 找到最小值的位置
        arry[min],arry[i] = arry[i],arry[min]   #交换两者
    return arry
print select_sort([1,3,2,5,6,4])


# 三、插入排序 InsertionSort,时间复杂度O(n2)
# 插入排序的工作原理是,对于每个未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
# 从第一个元素开始,该元素可以认为已经被排序
# 取出下一个元素,在已经排序的元素序列中从后向前扫描
# 如果被扫描的元素(已排序)大于新元素,将该元素后移一位
# 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
# 将新元素插入到该位置后
# 重复步骤2~5
def insert_sort(ary):
    n = len(ary)
    for i in range(1,n):
        if ary[i] < ary[i-1]:
            temp = ary[i]    #待插入前面已排序序列的数
            index = i        #待插入的下标
            for j in range(i-1,-1,-1):  #从i-1 倒着循环到 0
                if ary[j] > temp :
                    ary[j+1] = ary[j] #j往后移动一位,j本身暂时不变
                    index = j   #记录待插入下标。即j,j+1(待插入的数)=j(待插入的位置),j
                else :
                    break
            ary[index] = temp
    return ary
print insert_sort([1,3,2,5,6,4])


# 四、希尔排序 ShellSort,时间复杂度O(nlogn)~O(n2)
# 将数组分为几行列在一个表中,对每列分别进行插入排序,重复该过程的时候使用更少步长更多列。最后整个表就只有一列了。
def shell_sort(ary):
    n = len(ary)
    gap = n//2      #设置初始步长 , 数组分为两行(或多一个元素的第三行)
    while gap > 0 :
        for i in range(gap,n):        #从第二行开始,每一列的元素与上排相同位置对比,进行插入排序 , 从gap 到 n-1
            temp = ary[i]
            j = i
            while ( j >= gap and ary[j-gap] > temp ):    #与上一行相同位置的数对比
                ary[j] = ary[j-gap]
                j = j - gap
            ary[j] = temp
        gap = gap//2                     #重新设置步长
    return ary
print shell_sort([1,3,2,5,6,4])


# 五、归并排序 MergeSort,时间复杂度O(nlogn)
# 归并排序是采用分治法的一个非常典型的应用。归并排序的思想就是先递归分解数组,再合并数组。
# 先考虑合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。
# 再考虑递归分解,基本思路是将数组分解成left和right,如果这两个数组内部数据是有序的,那么就可以用上面合并数组的方法将这两个数组合并排序。如何让这两个数组内部是有序的?可以再二分,直至分解出的小组只含有一个元素时为止,此时认为该小组内部已有序。然后合并排序相邻二个小组即可。
def merge_sort(ary):
    if len(ary) <= 1:
        return ary
    num = int(len(ary)/2)       #二分分解
    left = merge_sort(ary[:num])
    right = merge_sort(ary[num:])
    return merge(left,right)    #合并数组

def merge(left,right):
    l,r = 0,0           #left与right数组的下标指针
    result = []
    while l<len(left) and r<len(right) :
        if left[l] < right[r]:
            result.append(left[l])
            l += 1
        else:
            result.append(right[r])
            r += 1
    result += left[l:]  # 加left数组剩余的数
    result += right[r:] # 加right数组剩余的数
    return result
print merge_sort([1,3,2,5,6,4])


# 六、快速排序 QuickSort,时间复杂度O(nlogn)
# 从数列中挑出一个元素作为基准数。
# 分区过程,将比基准数大的放到右边,小于或等于它的数都放到左边。
# 再对左右区间递归执行第二步,直至各区间只有一个数。
def quick_sort(ary):
    return qsort(ary,0,len(ary)-1)

def qsort(ary,left,right):
    #快排函数,ary为待排序数组,left为待排序的左边界,right为右边界
    if left >= right:
        return ary      # 如数组为空或只有一个元素
    key = ary[left]     #取最左边的为基准数
    lp = left           #左指针
    rp = right          #右指针
    while lp < rp :
        while ary[rp] >= key and lp < rp :
            rp -= 1    # 找到最右边小于基数的数
        while ary[lp] <= key and lp < rp :
            lp += 1    # 找到最左边大于基数的数
        ary[lp],ary[rp] = ary[rp],ary[lp]    #交换
    ary[left],ary[lp] = ary[lp],ary[left]    # 基数左右全部交换完后,将左边界left与最左边的数交换
    qsort(ary,left,lp-1)    # 左区间做相同排序
    qsort(ary,rp+1,right)   # 右区间做相同排序
    return ary
print quick_sort([1,3,2,5,6,4])


# 七、堆排序 HeapSort,时间复杂度O(nlogn)
# 构建堆,将数组依次放入堆,并使上面都大于下面
# 将调整好的堆从上到下放入数组(不是有序的)
# 从堆的上面开始,一层一层往下比较,把下层较大的数和上层比较,并把其中大的换到上面。等到最下层,将顶端的大数换到最右下角,从堆中删去。
# 由于每次都是将最大的数并入到后面的有序区间,故操作完后整个数组就是有序的了。

def heap_sort(ary) :
    n = len(ary)
    first = int(n/2-1)       #最后一个非叶子节点,也就是倒数第二层最右边
    for start in range(first,-1,-1) :     #第一步,构造大根堆,将最顶端到first倒着遍历一遍
        max_heapify(ary,start,n-1)
    for end in range(n-1,0,-1):           #第二步,堆排,将大根堆转换成有序数组
        ary[end],ary[0] = ary[0],ary[end]  # 最上端和最下端交换后删除最下端最大数,再转换剩下的
        max_heapify(ary,0,end-1)
    return ary

#最大堆调整:将堆的末端子节点作调整,使得子节点永远小于父节点
#start为当前需要调整最大堆的位置,end为调整边界
def max_heapify(ary,start,end):
    root = start
    while True :
        child = root*2 +1               #调整节点的子节点
        if child > end : break
        if child+1 <= end and ary[child] < ary[child+1] :
            child = child+1             #取较大的子节点
        if ary[root] < ary[child] :     #较大的子节点成为父节点
            ary[root],ary[child] = ary[child],ary[root]     #交换
            root = child    # 移动指针到该子节点
        else :
            break
print heap_sort([1,3,2,5,6,4])


# 来源http://wuchong.me/blog/2014/02/09/algorithm-sort-summary/

blogroll

social