冒泡排序
  • 工作原理:

    • 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
    • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
    • 针对所有的元素重复以上的步骤,除了最后一个。
    • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
  • 分析:

    • 对于n个元素的序列,需要进行n-1次冒泡过程
    • 冒泡过程每增加1次,该次冒泡过程中需要两两比较元素的次数就减少1次
  • 代码实现:

    def bubble_sort(alist):
        '''冒泡排序'''
        l = len(alist)
        for j in range(l-1):
            for i in range(l-1-j):
                if alist[i] > alist[i+1]:
                    alist[i],alist[i+1] = alist[i+1],alist[i]
        return alist
  • 时间复杂度

    • 最优时间复杂度:O(n)
    • 最坏时间复杂度:O(n2)
    • 稳定性:稳定
选择排序
  • 工作原理:

    • 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
    • 然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
    • 以此类推,直到所有元素均排序完毕。
  • 代码实现:

    def select_sort(alist):
        '''选择排序'''
        l = len(alist)
        # 需要进行l-1轮选择操作
        for j in range(l-1):
            #记录最小元素下标
            min_index = j
            #从j位置开始,筛选出最小元素,然后放置到j位置
            for i in range(j+1,l):
                if alist[i] < alist[min_index]:
                    min_index = i
            #如果数据不在正确位置,则进行交换
            if j != min_index:
                alist[j],alist[min_index] = alist[min_index],alist[j]
        return alist
  • 时间复杂度

    • 最优时间复杂度:O(n2)
    • 最坏时间复杂度:O(n2)
    • 稳定性:不稳定(比如5,5,2,第一遍选择选出2,然后2和5进行交互,则两个5的相对顺序会发生改变)
插入排序
  • 工作原理:

    • 通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
  • 思路:

    • 需要将原始序列分成两部分:有序部分和无序部分
    • 初始情况下,有序部分为乱序序列的第一个元素,无序部分为剩下的n-1个元素
    • 将无序部分的元素,逐一插入到有序部分的对应位置上
  • 代码实现

    def insert_sort(alist):
        '''插入排序'''
        #从下标为1的元素开始,逐个向前插入
        for i in range(1,len(alist)):
            # 从第i个元素开始与前面进行比较,比前面数小,则交换位置
            while i>0:
                if alist[i] < alist[i-1]:
                    alist[i],alist[i-1] = alist[i-1],alist[i]
                    i -= 1
                else:
                    break
        return alist
希尔排序
  • 希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
  • 工作原理:

    • 将整个待排元素序列按照下标的指定增量进行分组,对每组使用直接插入排序算法排序;
    • 随着增量逐渐减少,每组包含的元素越来越多,当增量减至1时,整个序列恰被分成一组,算法便终止。
  • 思路:

    • 首先要理解插入排序就是增量为1的希尔排序
    • 实现插入排序的代码,将增量1改为gap(初始值len(alist) // 2)
    • 循环执行插入排序的代码,每循环1次,增量gap减小为gap // 2
    • 当增量减至1时,循环最后一次执行
  • 代码实现:

    def shell_sort(alist):
        '''希尔排序'''
        #初始增量
        gap = len(alist) // 2
        while gap >= 1:
            #按照指定增量进行插入排序
            for i in range(gap,len(alist)):
                while i > 0:
                    if alist[i] < alist[i-gap]:
                        alist[i],alist[i-gap] = alist[i-gap],alist[i]
                        i -= gap
                    else:
                        break
            #缩减增量
            gap //= 2
        return alist
  • 时间复杂度

    • 最坏时间复杂度:O(n2)
    • 稳定性:不稳定(同插入排序)
快速排序
  • 工作原理:

    • 将序列中的第一个元素设为基准,赋值给mid变量
    • 然后将序列中所有比基准小的元素移动到基准左侧,比基准大的元素移动到右侧
    • 再按此方法对基准两侧的序列分别进行快速排序
    • 整个排序过程可以递归进行,以此达到整个序列变成有序序列。
  • 思路:

    • 定义两个指针:low指向序列起始元素,high指向序列末位元素
    • 将序列中的起始元素定为基准元素
    • 首先移动右侧指针,当指针指向的元素比基准小时停止移动,将该元素移动到左指针位置上,然后开始移动左侧指针,否则继续移动,直到左右两个指针重合
    • 移动左侧指针时,当指针指向的元素大于基准时停止移动,将该元素移动到右指针位置上,然后开始移动右侧指针,否则继续移动,直到左右两个指针重合
    • 两指针位置重合时,将基准元素放置在该重合位置上,基准左侧的元素均小于它,右侧的元素均大于它
    • 然后分别让基准左右两侧的序列递归执行上述过程
  • 代码实现

    # coding:utf-8
    
    def quick_sort(alist,left,right):
        '''快速排序'''
        #递归退出条件
        if right <= left:
            return
        mid = alist[left] #序列起始元素为基准
        low = left #左指针
        high = right #右指针
        #左右指针未重合时,循环执行
        while low < high:
            #先移动右侧指针
            while low < high:
                if alist[high] >= mid:
                    high -= 1
                else:
                    alist[low] = alist[high]
                    break
            #再移动左侧指针
            while low < high:
                if alist[low] <= mid:
                    low += 1
                else:
                    alist[high] = alist[low]
                    break
        alist[low] = mid #左右指针重合,将基准放置到重合位置上
        quick_sort(alist,left,low-1) #基准左侧序列进行快速排序
        quick_sort(alist,high+1,right) #基准右侧序列进行快速排序
    
    if __name__ == '__main__':
        alist = [4,2,7,6,8,1,9,3,5,0]
        quick_sort(alist,0,len(alist)-1)
        print(alist)
  • 时间复杂度

    • 最优时间复杂度:O(nlogn)
    • 最坏时间复杂度:O(n2)
    • 稳定性:不稳定
归并排序
  • 归并排序是采用分治法的一个非常典型的应用,归并排序的思想就是先递归分解序列,再合并序列。
  • 思路:

    • 将序列从中间位置分解成两个数列
    • 再将这两个子序列按照第一步继续二分下去
    • 直到所有子序列的长度都为1时,再两两合并成一个有序序列
      • 基本思路:比较两个序列的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。
      • 然后再比较,直至一个序列为空,最后把另一个序列的剩余部分复制过来即可。
  • 代码实现

    def merge_sort(alist):
        n = len(alist)
        #结束递归的条件
        if n <= 1:
            return alist
        #中间索引
        mid = n//2
    
        left_li = merge_sort(alist[:mid])
        right_li = merge_sort(alist[mid:])
    
        #指向左右表中第一个元素的指针
        left_pointer,right_pointer = 0,0
        #合并数据对应的列表:该表中存储的为排序后的数据
        result = []
        while left_pointer < len(left_li) and right_pointer < len(right_li):
            #比较最小集合中的元素,将最小元素添加到result列表中
            if left_li[left_pointer] < right_li[right_pointer]:
                result.append(left_li[left_pointer])
                left_pointer += 1
            else:
                result.append(right_li[right_pointer])
                right_pointer += 1
        #当左右表的某一个表的指针偏移到末尾的时候,比较大小结束,将另一张表中的数据(有序)添加到result中
        result += left_li[left_pointer:]
        result += right_li[right_pointer:]
        return result
  • 时间复杂度

    • 最优时间复杂度:O(nlogn)
    • 最坏时间复杂度:O(nlogn)
    • 稳定性:稳定