【准备面试|算法】常考排序算法

176 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情

前言

十大基础算法中,有三个算法可能经常会在面试中遇到:归并排序、快速排序、拓扑排序。它们的核心思想都是分而治。将复杂问题简单化去解决。

合并排序(归并排序)

基本思路

核心就是分而治之,将一个复杂问题拆解成两个或多个相似的子问题,然后再继续拆解成更小的子问题,直接拆解成可以简单求解的问题进行解决,最后再合并。

归并排序,先划分成2个子数组,再对每个子数组划分成更小的数组,直到子数组只有一个元素再开始排序

[3, 44, 38, 5, 47, 15, 2, 26, 27]

[3, 44, 38, 5 ,47 ] [15, 2, 26, 27]

[3,44,38] [5,47] [15,2] [26, 27]

[3,44] [38] [5] [47] [15] [2] [26][27]

 [3] [44]    

merge

[3, 44] [38] [5] [47]. [15] [2][26][27]

[3,38,44] [5,47] [2,15] [26,27]

。。。                           

代码示例

func sort(arr []int, left int, right int) {
    if left >= right { // 只剩下最后一个元素则直接返回
        return
    }
    mid := left + (right-left)/2
    sort(arr, left, mid)
    sort(arr, mid+1, right)

    // 排好序后进行组合
    merge(arr, left, mid, right)
}
func merge(arr []int, left int, mid int, right int) {
    tempArr := arr
    k := left
    i := left
    j := mid + 1

    for {
        if i > mid {
            k += 1
            j += 1
            arr[k] = tempArr[j]
        } else if j > right {
            k += 1
            i += 1
            arr[k] = tempArr[i]
        } else if tempArr[j] < tempArr[i] {
            k += 1
            j += 1
            arr[k] = tempArr[j]
        } else {
            k += 1
            i += 1
            arr[k] = tempArr[i]
        }
        if k <= right {
            break
        }
    }
}

快速排序

基本思路

快速排序也是采用分治的思想。大致可以分成三个步骤:

  • 选择一个元素作为“基准“ pivot
  • 所有小于“基准”元素,都转移到基准的左边,大于的都转移到右边。
  • 对“基准”左右两边的两个子集,继续重复第一步和第二步,直到所有的子集只剩下一个元素为止。

[3, 44, 38, 5, 47, 15, 2, 26, 27]

第一轮,选择3为基准,遍历到 2 小于基准,那么需要将 2 转移到基准之前。

将 2 和 3 的位置置换:[2, 44, 38, 5, 47, 15, 3, 26, 27] ,记录基准值位置应是 2 ,基准值被置换位置为 3 (暂存位置)

循环完后,将基准值置换回真实位置[2, 3, 38, 5, 47, 15, 44, 26, 27],此时 3左边都小于3 右边都大于3

第二轮:拆分成2个数组继续循环处理: [2] 和 [38, 5, 47, 15, 44, 26, 27]

第三轮: [38, 5, 47, 15, 44, 26, 27] 继续获取基准值 38,拆出2个数组 [5,15,26,27]  和[44,47]

依次操作下去... ...

代码示例

// 分区函数,主要实现将数据转移到基准的两边
func partition(arr []int, left, right int) int {
    pivot := arr[left] // 默认第一个值为基准值
    i := left // 标识基准值应该的位置
    ci := left // 被交换的位置
    for j := left + 1; j <= right; j++ {
        if arr[j] < pivot {
            arr[j], arr[i] = arr[i], arr[j]
            i++ // 标识基准值应该的位置
            ci = j
        }
    }
    // 将基准值置换到真正基准的位置
    arr[i], arr[ci] = arr[ci], arr[i]
    return i
}

func quickSort(arr []int, left, right int) {
    if left > right {
        return
    }
    //获取基准s数据的位置 poivet
    poivetIndex := partition(arr, left, right)
    quickSort(arr, left, poivetIndex-1)
    quickSort(arr, poivetIndex+1, right)
}