数据结构与算法复习 排序算法(I)

Posted wsmrzx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法复习 排序算法(I)相关的知识,希望对你有一定的参考价值。

这篇文章将会介绍最常见的排序算法(使用 javascript 语言实现)

PS:这里我会尽量使用语言无关的语法去写,大家不要太在意语言,重要的是算法的实现思路

1、冒泡排序

将数组分为有序区(左边)和无序区(右边)

每次从无序区的最后一个元素开始,一直向前冒泡到无序区的第一个位置,使其变成有序

function swap(A, i, j) {
    if (i === j) return
    [A[i], A[j]] = [A[j], A[i]]
}

function bubbleSort(A) {
    for (let i = 0, ii = A.length - 1; i < ii; i++) {
        let done = true
        for (let j = A.length - 1; j > i; j--) {
            if (A[j] < A[j - 1]) {
                swap(A, j, j - 1)
                done = false
            }
        }
        if (done) break
    }
}

2、选择排序

将数组分为有序区(左边)和无序区(右边)

每次从无序区中选择一个合适的元素,并将其交换到无序区的第一个位置,使其变成有序

function swap(A, i, j) {
    if (i === j) return
    [A[i], A[j]] = [A[j], A[i]]
}

function selectionSort(A) {
    for (let i = 0, ii = A.length; i < ii; i++) {
        let minIdx = i
        for (let j = i, jj = A.length; j < jj; j++) {
            if (A[j] < A[minIdx]) minIdx = j
        }
        swap(A, i, minIdx)
    }
}

3、插入排序

将数组分为有序区(左边)和无序区(右边)

每次将无序区中的第一个元素,一直向前交换到有序区中的合适位置,使其变成有序

function insertionSort(A) {
    for (let i = 1, ii = A.length; i < ii; i++) {
        let v = A[i]
        let j = i - 1
        while (j >= 0) {
            if (A[j] > v) {
                A[j + 1] = A[j]
                j -= 1
            } else break
        }
        A[j + 1] = v
    }
}

4、归并排序

递归进行,每次将数组一分为二,然后对两个数组排序后,合并两个数组

function mergeSort(arr) {
    if (arr.length <= 1) return arr
    let mid = Math.floor(arr.length / 2)
    let left = arr.slice(0, mid)
    let right = arr.slice(mid)
    return merge(mergeSort(left), mergeSort(right))
}

function merge(left, right) {
    let rs = [], lp = 0, rp = 0
    while (lp < left.length && rp < right.length)
        (left[lp] < right[rp]) ? rs.push(left[lp++]) : rs.push(right[rp++])
    while (lp < left.length)
        rs.push(left[lp++])
    while (rp < right.length)
        rs.push(right[rp++])
    return rs
}

优化:原地排序,这样可以减少新建数组

function mergeSortHelper(A, T, i, j) {
    if (i === j) return
    let m = Math.floor((i + j) / 2)
    mergeSortHelper(A, T, i, m)
    mergeSortHelper(A, T, m + 1, j)
    // merge
    for (let k = i; k <= j; k++) T[k] = A[k]
    let l = i, r = m + 1
    for (let curr = i; curr <= j; curr++) {
        if (l > m) A[curr] = T[r++]
        else if (r > j) A[curr] = T[l++]
        else if (T[l] < T[r]) A[curr] = T[l++]
        else A[curr] = T[r++]
    }
}

function mergeSort(A) {
    mergeSortHelper(A, [], 0, A.length - 1)
}

优化:临时数组后半部分反向插入,这样可以不用检测边界情况

function mergeSortHelper(A, T, i, j) {
    if (i === j) return
    let m = Math.floor((i + j) / 2)
    mergeSortHelper(A, T, i, m)
    mergeSortHelper(A, T, m + 1, j)
    // merge
    for (let k = i; k <= m; k++) T[k] = A[k]
    for (let k = 1; k <= j - m; k++) T[j - k + 1] = A[k + m]
    let l = i, r = j
    for (let curr = i; curr <= j; curr++) {
        if (T[l] < T[r]) A[curr] = T[l++]
        else A[curr] = T[r--]
    }
}

function mergeSort(A) {
    mergeSortHelper(A, [], 0, A.length - 1)
}

5、快速排序

递归进行,每次在数组中选择一个基准,根据基准将数组一分为二,然后对两个数组排序后,拼接两个数组和基准

function quickSort(arr) {
    if (arr.length <= 1) return arr
    // find pivot
    let pivotIndex = Math.floor(arr.length / 2)
    let pivot = arr.splice(pivotIndex, 1)[0]
    // partition
    let left = arr.filter(item => item <= pivot)
    let right = arr.filter(item => item > pivot)
    // recursive
    return [...quickSort(left), pivot, ...quickSort(right)]
}

优化:原地排序,这样可以减少新建数组

function swap(A, i, j) {
    if (i === j) return
    [A[i], A[j]] = [A[j], A[i]]
}

function quickSortHelper(A, i, j) {
    if (j <= i) return
    // find pivot
    let pivotIndex = Math.floor((i + j) / 2)
    let pivot = A[pivotIndex]
    // put pivot at last
    swap(A, pivotIndex, j)
    // partition
    let l = i - 1
    let r = j
    do {
        while (A[++l] < pivot) {}
        while (l < r && pivot < A[--r]) {}
        swap(A, l, r)
    } while (l < r);
    // put pivot in place
    swap(A, j, l)
    // recursive
    quickSortHelper(A, i, l - 1)
    quickSortHelper(A, l + 1, j)
}

function quickSort(A) {
    quickSortHelper(A, 0, A.length - 1)
}

优化:使用栈替代递归

function swap(A, i, j) {
    if (i === j) return
    [A[i], A[j]] = [A[j], A[i]]
}

function quickSortHelper(A, i, j) {
    let stack = []
    stack.push(i)
    stack.push(j)
    while (stack.length > 0) {
        j = stack.pop()
        i = stack.pop()
        // find pivot
        let pivotIndex = Math.floor((i + j) / 2)
        let pivot = A[pivotIndex]
        // put pivot at last
        swap(A, pivotIndex, j)
        // partition
        let l = i - 1
        let r = j
        do {
            while (A[++l] < pivot) {}
            while (l < r && pivot < A[--r]) {}
            swap(A, l, r)
        } while (l < r);
        // undo the last swap
        swap(A, l, r)
        // put pivot in place
        swap(A, l, j)
        // load up stack
        if (l - 1 > i) {
            stack.push(i)
            stack.push(l - 1)
        }
        if (j > l + 1) {
            stack.push(l + 1)
            stack.push(j)
        }
    }
}

function quickSort(A) {
    quickSortHelper(A, 0, A.length - 1)
}

6、测试程序

var origin = Array.from({ length: 100000 }, x => Math.floor(Math.random() * 100000))
var arr4bubble = JSON.parse(JSON.stringify(origin))
var arr4selection = JSON.parse(JSON.stringify(origin))
var arr4insertion = JSON.parse(JSON.stringify(origin))
var arr4merge = JSON.parse(JSON.stringify(origin))
var arr4quick = JSON.parse(JSON.stringify(origin))

console.time(‘q‘)
quickSort(arr4quick)
console.timeEnd(‘q‘)

console.time(‘m‘)
mergeSort(arr4merge)
console.timeEnd(‘m‘)

console.time(‘i‘)
insertionSort(arr4insertion)
console.timeEnd(‘i‘)

console.time(‘s‘)
selectionSort(arr4selection)
console.timeEnd(‘s‘)

console.time(‘b‘)
bubbleSort(arr4bubble)
console.timeEnd(‘b‘)

【 阅读更多数据结构与算法系列文章,请看 数据结构与算法复习

以上是关于数据结构与算法复习 排序算法(I)的主要内容,如果未能解决你的问题,请参考以下文章

(算法)排序复习

排序算法复习-冒泡

排序算法的程序实现——冒泡排序复习

算法复习之排序

经典算法复习-插入排序算法

数据结构与算法之排序算法:插入排序