数据结构与算法简记--排序算法(1)

排序算法


  • 如何分析一个“排序算法”?三个方面着手:

  • 执行效率
  1. 最好情况,最坏情况,平均情况时间复杂度
  2. 时间复杂度的系数,常数,低阶
  3. 比较次数和交换(或移动)次数
  • 内存消耗
  1. 原地排序,空间复杂度O(1)
  • 稳定性
  1. 关注的是值相同的项,排序前后的先后顺序是否不变,一致则表示是稳定算法
  • 排序比较

排序算法动画演示:https://visualgo.net/zh/sorting

插入排序比冒泡排序高效--冒泡排序的数据交换有三个操作,插入排序数据移动只有一个操作,也就是复杂度系数低

排序算法 是否稳定 是否原地排序 最好情况 最坏情况 平均情况
冒泡排序 O(n) O(n2) O(n2)
插入排序 O(n) O(n2) O(n2)
选择排序 O(n2) O(n2) O(n2)
归并排序 否--O(n) O(nlogn) O(nlogn) O(nlogn)
快速排序 O(n) O(n2) O(nlogn)
  • 冒泡排序--操作相邻数据比较互换

  • 插入排序--分已排序和未排序区间,每次从未排序取一项插入到已排序区间的合适位置(通过比较确定,比较过程中完成数据移动)

  • 选择排序--分已排序和未排序区间,每次取未排序中最小项放到已排序区间末尾,交换完成插入。

  • 归并排序--分治思想,递归实现:把整个数据分成两部分,分别将两部分排序,再把排序后的两部分合并,递归处理以上步骤。

  1. 分割过程:取数组A中间位置,分成两部分,对两部分分别排序,递归处理直到不能再分割。
  2. 合并过程--将两个有序部分进行合并:从头遍历每次取两部分头部的较小值放入新的数组,直到一个数组结束,将另一个数组的剩余部分移入新数组末尾,合并完成则排序完成。
  3. 伪代码
// 归并排序算法, A是数组,n表示数组大小
merge_sort(A, n) {
  merge_sort_c(A, 0, n-1)
}

// 递归调用函数
merge_sort_c(A, p, r) {
  // 递归终止条件
  if p >= r  then return

  // 取p到r之间的中间位置q
  q = (p+r) / 2
  // 分治递归
  merge_sort_c(A, p, q)
  merge_sort_c(A, q+1, r)
  // 将A[p...q]和A[q+1...r]合并为A[p...r]
  merge(A[p...r], A[p...q], A[q+1...r])
}
merge(A[p...r], A[p...q], A[q+1...r]) {
  var i := p,j := q+1,k := 0 // 初始化变量i, j, k
  var tmp := new array[0...r-p] // 申请一个大小跟A[p...r]一样的临时数组
  while i<=q AND j<=r do {
    if A[i] <= A[j] {
      tmp[k++] = A[i++] // i++等于i:=i+1
    } else {
      tmp[k++] = A[j++]
    }
  }
  
  // 判断哪个子数组中有剩余的数据
  var start := i,end := q
  if j<=r then start := j, end:=r
  
  // 将剩余的数据拷贝到临时数组tmp
  while start <= end do {
    tmp[k++] = A[start++]
  }
  
  // 将tmp中的数组拷贝回A[p...r]
  for i:=0 to r-p do {
    A[p+i] = tmp[i]
  }
}
  • 快速排序--分治思想,递归处理:每次取末尾项作中位数,将小于等中位数的放左边,大于的放右边,再对左右分区分别递归执行上述步骤,直到无法分区(只剩一项)。

  1. 利用快排思想查找第K大元素:先按快排分区,比较K与中位数的"下标index+1",K==index+1,则中位数就是第K大元素,K<index+1则在左边,K>index+1则在右边,对所在边递归执行上述步骤,直到找到该元素。

 

原文地址:https://www.cnblogs.com/wod-Y/p/11959650.html