排序算法总结

排序算法总结

一、文章内容简介

  • 基本概念
  • 稳定性、衡量标准(时空复杂度)
  • 分类:内排序、外排序
  • 重点掌握堆排序、快排、归并(手写三个排序)
  • 插入排序
    • 直接插入排序
    • 折半插入排序
    • 希尔排序
  • 交换排序
    • 简单选择排序
    • 堆排序
  • 归并排序
  • 基数排序
  • 外表排序
  • 各种内排序算法比较与应用
    各排序算法实现的时候只是简单的实现了算法的思想,没考虑特殊输入的处理(如空数组,只包含1个元素的数组,low>high,high超出数组长度等情况)。面试手撕代码时,记得考虑各种特殊情况。

二、 插入排序

  1. 直接插入排序
    1. 基本思想
    2. python实现
      def insert_sort(arr):
      
          for i in range(1, len(arr)):
              key = arr[i]
              # if arr[i] > arr[i-1]:
              j = i - 1
              while arr[j] > key and j >= 0:
                  arr[j+1] = arr[j]
                  j -= 1
              arr[j+1] = key
          return arr
      
      
      if __name__ == '__main__':
          arr = [9,7,8,6,2,4,3,5,1]
          # arr = []
          # arr = [1]
          # arr = [9,8,7,6,5,4,3,2,1]
          # arr = [1,2,3,4,5,6,7,8,9]
          # print(build_max_heap(arr, len(arr)-1))
          print(insert_sort(arr))
      
      
  2. 折半插入排序
  3. 希尔排序

三、交换排序

  1. 冒泡排序

    1. 关键步骤:比较、交换
    2. python实现
      def bubble_sort(arr):
      
          for i in range(len(arr)):
              flag = False
              j = len(arr) - 1
              while j > i:
                  if arr[j-1] > arr[j]:
                      arr[j-1], arr[j] = arr[j], arr[j-1]
                      flag = True
                  j -= 1
              if not flag: return arr
          return arr
      
      
      def bubble_sort2(arr):
          flag = False
          for i in range(len(arr)):
              for j in range(len(arr)-1-i):
                  if arr[j] > arr[j+1]:
                      arr[j], arr[j+1] = arr[j+1], arr[j]
                      flag = True
              if not flag: return arr
          return arr
      
      
      if __name__ == '__main__':
          arr = [7,9,8,6,2,4,3,5,1]
          print(bubble_sort(arr))
      
    3. 空间效率:O(1)
    4. 时间效率:
      1. 比较次数:n(n-1)/2
      2. 移动次数:3n(n-1)/2
      3. 最好:O(n^2)
      4. 平均:O(n^2)
    5. 稳定性:稳定
  2. 快速排序

    • 基于分治的思想:待排序表L[0···n]中任意去一个数作为基准pivot(通常选择L[0]),通过一趟排序将待排序表划分为独立的两个部分L[0···k-1]和L[k+1···n],使得L[0···k-1]的所有元素小于pivot,L[k+1···n]的所有元素大于pivot,pivot放在最终的位置L[k]上,这个过程成为一趟快速排序。然后分别递归对两个子表进行上述过程,直到每个部分内只有一个元素或空为止,即所有元素放在了最终位置上。
    • python实现代码如下:
      def quick_sort(arr, low, high):
      	if not arr: return None
          if low < high:
              pivotpos = partition(arr, low, high)
              quick_sort(arr, low, pivotpos-1)
              quick_sort(arr, pivotpos+1, high)
          return arr
      
      
      def partition(arr, low, high):
          pivot = arr[low]
          while low < high:
              while low < high and arr[high] >= pivot: high -= 1
              arr[low] = arr[high]
              print("arr1:", arr)
              while low < high and arr[low] <= pivot: low += 1
              arr[high] = arr[low]
              print("arr2:", arr)
          arr[low] = pivot
          return low
      
      
      if __name__ == '__main__':
          arr = [7,9,8,6,2,4,3,5,1]
          print(quick_sort(arr, 0, len(arr)-1))
      
    • 空间效率:
      • 平均: O(ln)
      • 最坏:O(n)
      • 最好:O(ln)
    • 时间效率:
      • 最坏:O(n^2)
      • 最好:O(n·ln)
      • 平均:O(n·ln)
    • 稳定性:不稳定。L(3,2,2)
    • 快排是所有内排序算法中平均性能最优的排序算法

四、选择排序

选择排序得基本思想:每一趟(例如第i趟)在后面n-i+1(n=1,2…,n-1)个待排序元素中选取关键字最小得元素,作为有序子序列得第i个元素,直到n-1趟排序完,待排序元素只剩下一个,就不用选了。

  1. 简单选择排序
    • 基本思想:假设排序表为:L[0…n],第i躺排序直接从L[i…n]中选择最小得元素与L[i]交换,每一趟排序可以确定一个元素得最终位置
    • python实现
      def sinple_select_sort(arr):
          for i in range(len(arr)-1):
              min = i
              for j in range(i+1, len(arr)):
                  if arr[j] < arr[min]: min=j
              if min != i: arr[i], arr[min] = arr[min], arr[i]
          return arr
      
      
      if __name__ == '__main__':
          arr = [7,9,8,6,2,4,3,5,1]
          # arr = [1,2,3,4,5,5,6,7,8,9]
          print(sinple_select_sort(arr))
      
      
    • 空间效率:O(1)
    • 时间效率:
      • 移动次数:最好0(表有序),最坏3(n-1)
      • 比较次数:比较次数与表序列得初始状态无关,始终是n(n-1)/2
      • 复杂度:O(n^2)
    • 稳定性:不稳定(L[2,2,1],交换元素的时候,可能导致第i个元素与其含有相同关键字元素得相对位置发生变化)
  2. 堆排序
    堆的定义:n个关键字序列L[1…n]称为堆,当且仅当该序列满足:
    大根堆:L(i)>=L(2i)且L(i)>=L(2i+1)
    小根堆:L(i)<=L(2i)且L(i)<=L(2i+1)
    1. 基本思想:堆排序是一种树形选择排序方法,它的特点是:在排序过程中,将L[1…n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区中选择最大(小)关键元素。通常将L[0]设置为哨兵,节点从L[1]开始。
    2. python实现
      def build_max_heap(arr, len):
          for i in range(len//2, 0, -1):
              adjust_down(arr, i, len)
          return arr
      
      
      def adjust_down(arr, k, length):
          temp = arr[k]
          i = k
          j = 2 * i
          while j <= length:
              if j <= length and j + 1 <= length:
                  if arr[j] < arr[j+1]:
                      j += 1
              if arr[j] > temp:
                  arr[i] = arr[j]
                  i = j
                  j = 2 * i
              else:
                  break
          arr[i] = temp
      
      
      def heap_sort(arr, len):
          build_max_heap(arr, len)
          for i in range(len, 1, -1):
              arr[i], arr[1] = arr[1],arr[i]
              adjust_down(arr, 1, i-1)
          return arr
      
      
      if __name__ == '__main__':
          arr = [0,7,8,6,2,4,3,5,1]
          # arr = []
          # arr = [1]
          # arr = [9,8,7,6,5,4,3,2,1]
          # arr = [1,2,3,4,5,6,7,8,9]
          # print(build_max_heap(arr, len(arr)-1))
          len = len(arr) - 1
          print(heap_sort(arr, len))
      
      

五、归并排序

  1. 二路归并排序

    • 基本思想
    • python实现
      import copy
      
      
      def merge(arr, low, mid, high):
          i = low
          j = mid + 1
          k = low
          backup = copy.deepcopy(arr)
          while i <= mid and j <= high:
              if backup[i] < backup[j]:
                  arr[k] = backup[i]
                  i += 1
              else:
                  arr[k] = backup[j]
                  j += 1
              k += 1
          while i <= mid:
              arr[k] = backup[i]
              i += 1
              k += 1
          while j <= high:
              arr[k] = backup[j]
              j += 1
              k += 1
          return arr
      
      
      def merge_sort(arr, low, high):
          if low < high:
              mid = (low+high) // 2
              merge_sort(arr, low, mid)
              merge_sort(arr, mid+1, high)
              merge(arr, low, mid, high)
          return arr
      
      
      if __name__ == '__main__':
          arr = [7,9,8,6,2,4,3,5,1]
          arr = []
          arr = [1]
          arr = [9,8,7,6,5,4,3,2,1]
          # arr = [1,2,3,4,5,5,6,7,8,9]
          print(merge_sort(arr, 0, len(arr)-1))
      

六、基数排序

七、外部排序

八、各种内排序算法的比较及应用

  1. 比较
    1. 时间复杂度
      1. O(n):简单选择排序、直接插入排序、冒泡排序
      2. O(n·ln):堆排序、快排(平均O(n·ln)、最坏O(n^2))、归并(最好最坏平均都是)
    2. 空间复杂度
      1. O(1):简单选择排序、插入排序、冒泡排序、希尔排序、堆排序
      2. O(n·ln):快排(平均n·ln、最坏n)
      3. O(n):归并
    3. 稳定性
      1. 稳定:插入排序、冒泡排序、归并排序、基数排序
      2. 不稳定:简单选择排序、快排、希尔、堆
    4. 基于比较的排序:直接插入、冒泡、简单选择、希尔、快排、堆排、归并
    5. 分配式排序:基数排序、桶排序
  2. 应用
    1. 选取排序方法需要考虑的因素
      1. 待排序的元素数目n
      2. 元素本身信息量的大小
      3. 关键字的结构及其分布情况
      4. 稳定性要求
      5. 语言工具的条件,存储结构及辅助空间的大小
    2. 排序算法总结
      1. 若n较小(n<=50),则可以采用直接插入排序或简单选择排序。由于直接插入排序所需的记录移动操作较简单选择排序多,因此当记录本身信息量较大时,用简单选择排序较好
      2. 若文件的初始状态基本有序,则使用直接插入排序或冒泡排序为宜
      3. 若n较大时,则应选择时间复杂度为O(n·ln)的排序算法。快排、堆排序、归并。快排被认为是目前基于比较的内排序中最好的方法,当关键字随机分布时,快排平均时间最短。堆排序的辅助空间少于快排,并且不会出现快排可能出现的最坏情况。但两者都是不稳定的,归并排序是稳定的
      4. 若n很大,记录的关键字位数较少且可以分解时,采用基数排序较好。
      5. 当记录本身信息量较大,为了避免耗费大量时间移动记录,可用链表作为存储结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值