数据结构与算法复习(一) 排序算法(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‘)

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

原文地址:https://www.cnblogs.com/wsmrzx/p/12545457.html

时间: 2024-11-08 05:17:38

数据结构与算法复习(一) 排序算法(I)的相关文章

数据结构与算法分析之简单排序算法

在排序算法中,简单排序主要有三种,分别为冒泡排序.选择排序.插入排序,学习理解好这三种排序算法有助于进一步研究数据结构与算法分析.下面,简单地谈一谈冒泡排序.选择排序.插入排序的原理及区别. 冒泡排序原理: 1.比较相邻的元素.如果前一个比后一个大,它们就交换. 2.每对元素都要进行同样的动作,从后往前比较. 3.每趟都会确定一个位置的元素,因此n个元素,需要n-1趟才能确定各个元素的位置. 例如:对23,4,56,11四个数进行冒泡排序. 第一趟 4,23,11,56 第二趟 4,11,23,

数据结构(十二)——排序算法

数据结构(十二)--排序算法 一.排序简介 1.排序的一般定义 排序是计算机中经常进行的操作,目的在于将一组无序的数据元素调整为有序的数据元素.序列:1,20,45,5,2,12排序后:1,2,5,12,20,45 2.排序的数学定义 3.排序的稳定性 如果序列中的两个元素R[i].R[j],关键字分别为K[i].K[j],并且在排序之前R[i]排在R[j]前面,如果排序操作后,元素R[i]仍然排在R[j]前面,则排序方法是稳定的:否则排序是不稳定的. 4.排序实现的关键 比较:任意两个数据元素

算法快速回顾——排序算法

常用排序算法有以下几种:冒泡排序.插入排序.快速排序.归并排序.堆排序. 本文将对五种常用算法分析并实现. //交换两个元素的值 这里列出几种不同写法 void swap(int *a, int *b) { int c = *a; *a = *b; *b = c; } void swap(int *a,int *b) { *a = (*a)^(*b); *b = (*b)^(*a); *a = (*a)^(*b); } void swap(int *a,int *b) { *a = *a + *

普林斯顿公开课 算法3-7:排序算法复杂度

算法复杂度用来表示在解决某个问题时,算法的性能表现. 复杂度上限,就是某个具体的已经实现的算法能够保证在一定时间内解决问题 复杂度下限,就是通过数学方法证明,所有的算法都必须花费一定的时间才能解决问题 最优化算法,就是可能达到的最小复杂度的算法,通常介于复杂度上限和下限之间 比如排序问题中: 计算模型为决策树 使用比较次数作为开销依据 复杂度上限:使用归并排序可以达到N lgN复杂度 复杂度下限:? 最优化算法:? 决策树举例 有三个不同的元素a b c,通过比较的方式来得出排序结果.那么它的决

数据结构复习之排序算法的总结回顾

根据排序过程中借助的主要操作,我们将内排序分为四类: 插入排序类 直接插入排序 希尔排序 选择排序类 简单选择排序 堆排序 交换排序类 冒泡排序 快速排序 归并排序类 归并排序 从算法的简单性来看,我们将7种算法分为两类: 简单算法:冒泡排序.简单选择排序.直接插入排序 改进算法:希尔排序.堆排序.归并排序.快速排序 对7中算法的各种指标进行对比: 平均情况看:显然最后三种算法要胜过希尔算法,并远远超过前3种简单算法. 最好情况看:反而冒泡和直接插入排序要更胜一筹,也就是说,如果待排序序列总是基

数据结构实践——大数据集上排序算法性能的体验

本文是针对[数据结构基础系列(9):排序]的实践项目. [项目 - 大数据集上排序算法性能的体验] 设计一个函数,产生一个至少5万条记录的数据集合.在同一数据集上,用直接插入排序.冒泡排序.快速排序.直接选择排序.堆排序.归并排序.基数排序等算法进行排序,记录所需要的时间,经过对比,得到对复杂度不同的各种算法在运行时间方面的感性认识. 提示1:这一项目需要整合多种排序算法,可以考虑先建设排序算法库,作为我们这门课算法库的收官之作: 提示2:本项目旨在获得对于复杂度不同算法的感性认识,由于数据分布

数据结构与算法之——八大排序算法

附:关于这个主题,网上好的文章已经数不胜数,本篇是整合后的文章. 正文: 一.概述 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 本文所指八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短: 二.排序算法详述 1.

数据结构算法基础-内部排序算法

1 arr = [1,23,12,9,8,8,9,1,1,8,] 2 def quickSortCore(arr,start,end): 3 if start < end: 4 index = partition(arr,start,end) 5 quickSortCore(arr,start,index-1) 6 quickSortCore(arr,index+1,end) 7 8 def partition(arr,start,end): 9 key = arr[start] 10 whil

吴裕雄--天生自然数据结构:十大经典排序算法——冒泡排序

冒泡排序 冒泡排序(Bubble Sort)也是一种简单直观的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成.这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端. 作为最简单的排序算法之一,冒泡排序给我的感觉就像 Abandon 在单词书里出现的感觉一样,每次都在第一页第一位,所以最熟悉.冒泡排序还有一种优化算法,就是立一个 flag,当在一趟序列遍历中