常见排序算法总结

136 阅读2分钟

概述

排序算法是最经典的算法知识。熟练掌握排序算法思想及其特点并能够熟练地手写代码至关重要。下面介绍几种常见的排序算法:冒泡排序、选择排序、插入排序、归并排序、快速排序、希尔排序、堆排序、计数排序、桶排序、基数排序的思想,其代码均采用 Golang 实现。

冒泡排序

算法描述

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  3. 针对所有的元素重复以上的步骤,除了最后一个;
  4. 重复步骤1~3,直到排序完成。

算法实现

func BubbleSort(arr []int){
    n := len(arr)
    for i := n; i > 0; i--{
        for j := 0; j < i; j++{
            if arr[i] > arr[j]{
                arr[i], arr[j] = arr[j], arr[i]
            }
        }
    } 
}
// 代码优化:
//    增加一个`swap`的标志,当前一轮没有进行交换时,说明数组已经有序,
//    说明没有必要再进行下一轮的循环了,直接退出

func BubbleSort(arr []int) {
    n := len(arr)
    flag := true
    for i := 0; i < n && flag; i++ {
        flag = false
        for j := 0; j < n-i-1; j++ {
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
                flag = true
            }
        }
    }
}

选择排序

算法描述

  1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
  2. 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。

算法实现

func SelectSort(arr []int) {
    n := len(arr)
    for i := 0; i < n; i++ {
        minIndex := i
        for j := i+1; j < n; j++ {
            if arr[j] < arr[minIndex] {
                minIndex = j
            }
        }
        if minIndex != i {
            arr[minIndex], arr[i] = arr[i], arr[minIndex]
        }
    }
}

插入排序

算法描述

  1. 把待排序的数组分成已排序和未排序两部分,初始的时候把第一个元素认为是已排好序的。
  2. 从第二个元素开始,在已排好序的子数组中寻找到该元素合适的位置并插入该位置。
  3. 重复上述过程直到最后一个元素被插入有序子数组中。

算法实现

func InsertSort(arr []int) {
    n := len(arr)
    for i := 0; i < n-1; i++ {
        if arr[i+1] < arr[i] {
            for j := i+1; j > 0 && arr[j] < arr[j-1]; j-- {
                    arr[j], arr[j-1] = arr[j-1], arr[j]
            }
        }
    }
}

归并排序

算法描述

  1. 将 n 个元素分成个含 n/2 个元素的子序列
  2. 对步骤 1 中的子序列进行排序,排序后每个子序列包含两/一个元素
  3. 若此时序列数不是 1 则将上述序列再次归并,形成 n/4 个序列,每个序列包含四/三个元素
  4. 重复步骤 3 ,直到所有元素排序完毕,即序列数为1

算法实现

func mergeSort(nums []int) []int {
    if len(nums) < 2{
        // 分治,两两拆分,一直拆到基础元素才向上递归。
        return nums
    }
    i := len(nums) / 2
    left := mergeSort(nums[0:i])
    // 左侧数据递归拆分
    right := mergeSort(nums[i:])
    // 右侧数据递归拆分
    result := merge(left, right)
    // 排序 & 合并
    return result
}

func merge(left, right []int) []int {
    result := make([]int, 0)
    i, j := 0, 0
    l, r := len(left), len(right)
    for i<l && j<r{
        if left[i] > right[j]{
            result = append(result, right[j])
            j++
        }else {
            result = append(result, left[i])
            i++
        }
    }
    result = append(result, right[j:]...)
    result = append(result, left[i:]...)
    return result
}

快速排序

算法描述

  1. 从数列中挑出一个元素,称为"基准"(pivot),
  2. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。

算法实现

func QuickSort(nums []int, low int, high int) {
   if low < high {
      pivotloc := partition(nums, low, high)
      QuickSort(nums, low, pivotloc-1)
      QuickSort(nums, pivotloc+1, high)
   }
}

func partition(nums []int, low int, high int) int {
   pivotkey := nums[low]
   for low < high {
      for low < high && nums[high] >= pivotkey {
         high--
      }
      nums[low] = nums[high]
      for low < high && nums[low] <= pivotkey {
         low++
      }
      nums[high] = nums[low]
   }
   nums[low] = pivotkey
   return low
}

希尔排序

算法描述

  1. 取一个小于 N 的整数 d1,将位置是 d1 整数倍的数们分成一组,对这些数进行直接插入排序。
  2. 接着取一个小于 d1 的整数 d2,将位置是 d2 整数倍的数们分成一组,对这些数进行直接插入排序。
  3. 接着取一个小于 d2 的整数 d3,将位置是 d3 整数倍的数们分成一组,对这些数进行直接插入排序。
  4. 直到取到的整数 d=1,接着使用直接插入排序。

算法实现

// 增量序列折半的希尔排序
func SortShell(list []int) {
    // 数组长度
    n := len(list)
    // 每次减半,直到步长为 1
    for step := n / 2; step >= 1; step /= 2 {
        // 开始插入排序,每一轮的步长为 step
        for i := step; i < n; i += step {
            for j := i - step; j >= 0; j -= step {
                // 满足插入那么交换元素
                if list[j+step] < list[j] {
                        list[j], list[j+step] = list[j+step], list[j]
                        continue
                }
                break
            }
        }
    }
}