排序算法 | 青训营笔记

80 阅读2分钟

插入排序

插入排序是一种简单的排序算法,它的基本思路是将待排序的元素插入到已排序的序列中的正确位置。具体的实现方法是从第二个元素开始,将当前元素依次与已排序的元素进行比较,找到其正确的位置并插入。插入排序的时间复杂度为O(n^2),在处理小规模数据时效率较高。

func insertionSort(arr []int) {
    for i := 1; i < len(arr); i++ {
        key := arr[i]
        j := i - 1
        for j >= 0 && arr[j] > key {
            arr[j+1] = arr[j]
            j--
        }
        arr[j+1] = key
    }
}

快速排序

快速排序是一种基于分治思想的排序算法,它的基本思路是选择一个元素作为支点,将待排序的元素分成两个子序列,使得左子序列的所有元素都小于支点,右子序列的所有元素都大于支点,然后对左右子序列分别进行排序。快速排序的时间复杂度为O(nlogn),是常用的排序算法之一。

func quickSort(arr []int) {
    if len(arr) <= 1 {
        return
    }
    pivot := arr[len(arr)-1]
    left, right := 0, len(arr)-2
    for left <= right {
        if arr[left] <= pivot {
            left++
        } else {
            arr[left], arr[right] = arr[right], arr[left]
            right--
        }
    }
    arr[left], arr[len(arr)-1] = arr[len(arr)-1], arr[left]
    quickSort(arr[:left])
    quickSort(arr[left+1:])
}

堆排序

堆排序是一种利用二叉堆进行排序的排序算法,它的基本思路是将待排序的元素构建成一个大顶堆,然后依次将堆顶元素移除并放置到已排序的序列中,再对剩余的元素重新构建大顶堆,重复该过程直到所有元素都被放置到已排序的序列中。堆排序的时间复杂度为O(nlogn),它是一种原地排序算法,不需要额外的存储空间。

func heapSort(arr []int) {
    n := len(arr)
    for i := n/2 - 1; i >= 0; i-- {
        heapify(arr, n, i)
    }
    for i := n - 1; i > 0; i-- {
        arr[0], arr[i] = arr[i], arr[0]
        heapify(arr, i, 0)
    }
}

func heapify(arr []int, n, i int) {
    largest := i
    left, right := 2*i+1, 2*i+2
    if left < n && arr[left] > arr[largest] {
        largest = left
    }
    if right < n && arr[right] > arr[largest] {
        largest = right
    }
    if largest != i {
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)
    }
}

pdqsort

pdqsort是一种快速排序的改进算法,它的基本思路是将快速排序分成三个阶段:划分阶段、排序阶段和合并阶段。在划分阶段中,它使用了三个支点来将待排序的元素划分成三个子序列,其中一个子序列包含所有小于第一个支点的元素,一个子序列包含所有等于第一个支点的元素,一个子序列包含所有大于第一个支点的元素。在排序阶段中,它对三个子序列分别进行递归排序。在合并阶段中,它将三个子序列合并成一个有序的序列。pdqsort是一种自适应的排序算法,它在处理小规模数据时效率较高。

func pdqSort(arr []int) {
    if len(arr) <= 7 {
        insertionSort(arr)
        return
    }
    pivot1, pivot2 := pdqPivot(arr)
    left, right := 0, len(arr)-1
    for i := 0; i <= right; {
        if arr[i] < pivot1 {
            arr[left], arr[i] = arr[i], arr[left]
            left++
            i++
        } else if arr[i] >= pivot2 {
            arr[right], arr[i] = arr[i], arr[right]
            right--
        } else {
            i++
        }
    }
    pdqSort(arr[:left])
    pdqSort(arr[left+1:right])
    pdqSort(arr[right+1:])
}

func pdqPivot(arr []int) (int, int) {
    n := len(arr)
    s := n / 8
    m := medianOf3(arr[s], arr[n/2], arr[n-s-1])
    k := n / 2
    l := medianOf3(arr[0], arr[k], arr[n-1])
    r := medianOf3(arr[s], arr[n-s-1], m)
    return l, r
}

func medianOf3(a, b, c int) int {
    if a > b {
        a, b = b, a
    }
    if b > c {
        b, c = c, b
    }
    if a > b {
        a, b = b, a
    }
    return b
}