Python数据结构与算法分析(第二版)学习笔记——排序

23 阅读3分钟

排序是指将集合元素按某种顺序排列的过程。影响排序算法的因素包括元素的数目和交换次数。

冒泡排序

冒泡排序是遍历列表,比较相邻的元素,如果大小不符合要求则交换元素次序,若有n个元素,则第一次要比较n-1次,第二次要比较n-2次,完成n-1轮后完成排序。

image.png

冒泡排序的python实现如下:

def bubblesrt(alist):
    for passnum in range(len(alist)-1,0,-1):
        for x in range(passnum):
            if alist[x] > alist[x+1]:
                temp = alist[x]
                alist[x] = alist[x+1]
                alist[x + 1] = temp

在完成这个排序的过程中,会利用到临时变量:

                temp = alist[x]
                alist[x] = alist[x+1]
                alist[x + 1] = temp

冒泡排序总的比较次数是前n-1个整数之和,为n*(n+1)/2,因此冒泡排序的时间复杂度是O(n^2)。

image.png

冒泡排序还有一种改良方法,一旦在一轮遍历中没有发生交换,可以确定元素已经完成排序,因此可以终止后面的排序。

def shortbubblesrt(alist):
    changenum = len(alist) - 1
    change = True

    while changenum > 0 and change:
        change = False
        for x in range(changenum):
            if alist[x] > alist[x + 1]:
                change = True
                temp = alist[x]
                alist[x] = alist[x + 1]
                alist[x + 1] = temp
        changenum -= 1

选择排序

选择排序对冒泡排序进行了改进,每次只进行一次交换,比如对一个升序排序来说,就每次比较出最大的元素并放到最后一位,而每轮排序完成后,下一轮只比较排在最大值以前的元素,因此每轮比较的元素会少一个。给n个元素排序,需要比较n-1轮。

image.png

# 选择排序
def selectionsort(alist):
    for changenum in range(len(alist)-1,0,-1):
        posmax = 0
        for location in range(1,changenum + 1):
            if alist[location] > alist[posmax]:
                posmax = location

        temp = alist[changenum]
        alist[changenum] = alist[posmax]
        alist[posmax] = temp

选择排序比较的次数和冒泡排序相同,因此时间复杂度也是O(n^2)。

插入排序

image.png

插入排序将0处的元素看作是只含单个元素的有序子列表,从1到n-1,每轮都将当前元素与有序子列表的元素比较,在有序子列表中,比他大的右移,比他小或抵达终点则插入元素。

# 插入排序

def insertionsort(alist):
    for index in range(1,len(alist)):
        concurrentvalue = alist[index]
        pos = index

        while pos > 0 and alist[pos - 1] > concurrentvalue:
            alist[pos] = alist[pos] - 1
            pos -= 1

        alist[pos] = concurrentvalue

image.png

算法需要遍历n-1轮,其时间复杂度同样是O(n^2)。

希尔排序

希尔排序是对插入排序的改进,他将列表拆分为数个子列表,并对子列表应用插入排序,i是步长,他将间隔为i的元素组成子列表。

image.png

在本例中,先为n/2个列表排序,再为n/4个列表排序。

def shellsort(alist):
    subliscount = alist // 2
    while subliscount > 0:
        for start in range(subliscount):
            gapinsertionsort(alist, start, subliscount)

        print(subliscount)
        subliscount = alist // 2
def gapinsertionsort(alist, start, gap):
    for index in range(start + gap,len(alist),gap):
        concurrentvalue = alist[index]
        pos = index

        while pos >= gap and alist[pos - gap] > concurrentvalue:
            alist[pos] = alist[pos - gap]
            pos -= gap
        alist[pos] = concurrentvalue

希尔排序的时间复杂度在O(n)和O(n^2)之间。

归并排序

分治策略的第一个算法是归并排序,他是递归算法,他将每个列表一分为二,当列表只有一个元素或没有元素认为他有序。当每个列表都有序了,再使用归并将列表合并为大的有序列表。

image.png

def mergesort(alist):
    print("s",alist)

    if len(alist) > 1:
        mid = len(alist) // 2
        left = alist[:mid]
        right = alist[mid:]
        mergesort(left)
        mergesort(right)

        i = 0
        j = 0
        k = 0
        while i < len(left) and j < len(right):

            if left[i] < right[j]:  #合并大列表前半部分
                alist[k] = left[i]
                i += 1
            else:
                alist[k] = right[j]
                j += 1
            k += 1

        while i < len(left): #合并大列表后半部分
            alist[k] = left[i]
            k += 1
            i += 1

        while j < len(right):
            alist[k] = right[j]
            k += 1
            j += 1

    print(alist)

快速排序

快速排序同样分治策略,但不占用额外的空间,他先选出一个基准值帮助切分列表。并利用递归重复切分来实现排序。

下面的例子以第一个元素作为切分点:

image.png

首先加大leftmark,直到有一个大于基准值的元素,然后缩小rightmarket。直到遇到一个小于基准值的元素,这样就互换两个元素的位置,直到leftmark>rightmark,这样rightmark就是分割点,分割点左侧元素小于他,右侧大于他,在左右两侧递归调用函数,直到列表长度小于等于1.

def quicksort(alist):
    quicksorthelp(alist,0,len(alist)-1)
def quicksorthelp(alist,first,last):
    if first < last:
        splitpoint = partition(alist,first,last)
        quicksorthelp(alist,first,splitpoint -1 )
        quicksorthelp(alist,splitpoint+1,last)
def partition(alist,first,last):
    pivotvalue = alist[first]
    leftmarket = first + 1
    rightmarket = last

    done = False
    while not done:
        while leftmarket <= rightmarket and alist[leftmarket] <= pivotvalue:
            leftmarket += 1

        while alist[rightmarket] >=pivotvalue and rightmarket >= leftmarket:
            rightmarket -= 1

        if leftmarket < rightmarket:
            done = True
        else:
            temp = alist[leftmarket]
            alist[leftmarket] = alist[rightmarket]
            alist[rightmarket] = temp

        temp = alist[first]
        alist[first] = alist[rightmarket]
        alist[rightmarket] = temp

        return rightmarket