经典排序算法 | 青训营笔记

41 阅读2分钟

排序算法是计算机科学中的重要部分,也是日常编程中经常遇到的问题。常用的排序算法包括插入排序、快速排序、堆排序等。这些算法各有优缺点,需要结合具体场景选择不同的算法进行优化。

本篇笔记主要介绍Go语言实现的插入排序、快速排序和堆排序。

  1. 插入排序

(1)原理及特点

插入排序是一种简单直观的排序算法,其基本思路是:将待排序的数据分成已排序部分和未排序部分。每次从未排序部分中选取一个元素,插入到已排序部分的正确位置上。这个过程一直进行到未排序部分为空,排序完成。

插入排序的时间复杂度为 O(n2)O(n^2),空间复杂度为 O(1)O(1),最好情况下可以达到 O(n)O(n),具有稳定性。

(2)代码实现

下面是Go语言实现的插入排序代码:

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

(3)测试示例

下面是一个基本测试示例:

func TestInsertionSort(t *testing.T) {
    arr := []int{2, 3, 5, 1, 4}
    insertionSort(arr)
    want := []int{1, 2, 3, 4, 5}
    if !reflect.DeepEqual(arr, want) {
        t.Errorf("want %v but got %v", want, arr)
    }
}
  1. 快速排序

(1)原理及特点

快速排序是一种常见的排序算法,其基本思路是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小。然后再分别对前后两部分记录继续进行快速排序,直到整个序列有序。

快速排序的时间复杂度为 O(nlogn)O(n\log n),空间复杂度为 O(logn)O(\log n),不稳定。

(2)代码实现

下面是Go语言实现的快速排序代码:

func quickSort(arr []int, left, right int) {
    if left >= right {
        return
    }
    p := partition(arr, left, right)
    quickSort(arr, left, p-1)
    quickSort(arr, p+1, right)
}

func partition(arr []int, left, right int) int {
    pivot := arr[left]
    for left < right {
        for left < right && arr[right] >= pivot {
            right--
        }
        arr[left] = arr[right]
        for left < right && arr[left] <= pivot {
            left++
        }
        arr[right] = arr[left]
    }
    arr[left] = pivot
    return left
}

(3)测试示例

下面是一个基本测试示例:

func TestQuickSort(t *testing.T) {
    arr := []int{2, 3, 5, 1, 4}
    quickSort(arr, 0, len(arr)-1)
    want := []int{1, 2, 3, 4, 5}
    if !reflect.DeepEqual(arr, want) {
        t.Errorf("want %v but got %v", want, arr)
    }
}
  1. 堆排序

(1)原理及特点

堆排序利用堆的性质进行排序,其基本思路是:将待排序的序列构造成一个大根堆(或小根堆),然后将堆顶元素与末尾元素进行交换,此时末尾元素就为最大(或最小)值,再将堆调整为大根堆(或小根堆),重复以上步骤,直到整个序列有序。

堆排序的时间复杂度为 O(nlogn)O(n\log n),空间复杂度为 O(1)O(1),不稳定。

(2)代码实现

下面是Go语言实现的堆排序代码:

func heapSort(arr []int) {
    n := len(arr)
    // 构造大根堆
    for i := n/2 - 1; i >= 0; i-- {
        heapify(arr, i, n)
    }
    // 进行堆排序
    for i := n - 1; i > 0; i-- {
        arr[0], arr[i] = arr[i], arr[0]
        heapify(arr, 0, i)
    }
}

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

(3)测试示例

下面是一个基本测试示例:

func TestHeapSort(t *testing.T) {
    arr := []int{2, 3, 5, 1, 4}
    heapSort(arr)
    want := []int{1, 2, 3, 4, 5}
    if !reflect.DeepEqual(arr, want) {
        t.Errorf("want %v but got %v", want, arr)
    }
}

在编写排序算法的过程中,我们需要注意编写高效、简洁、易读的代码,并保证代码的正确性。因此,在编写代码之前,我们可以充分理解算法的原理和特点,并通过测试来保证代码的正确性。