十大排序总结(js实现稳定性内外部排序区别时间空间复杂度冒泡快速直接选择堆直接插入希尔桶基数归并计数排序)(代码片段)

YF-SOD YF-SOD     2023-03-04     460

关键词:

目录

排序相关概念

稳定性

 内部排序

外部排序

 十种排序算法特点总结

交换排序

冒泡排序(数组sort方法的原理)

图解

 js实现

特点

快速排序

图解

js实现

特点

选择排序

直接选择排序

图解

 js实现

特点

堆排序

大(小)顶堆

非叶节点

js实现 

特点

插入排序 

直接插入排序

图解

js实现 

特点

希尔排序

图解

js实现

特点

其它排序

桶排序

图解

js实现

特点

基数排序

图解

js实现

特点

归并排序

图解

 js实现

特点

计数排序

图解

 js实现

特点


排序相关概念

稳定性

不改变相同数值的顺序为稳定。例如在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的(记录的相对次序保持不变);否则称为不稳定的。

 内部排序

指待排序列完全存放在内存中所进行的排序过程,适合不太大的元素序列。有冒泡、选择、插入、希尔、快速、堆排序(通常快速排序为最好的)

外部排序

外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。有归并、计数、桶、基数排序

 十种排序算法特点总结

 

交换排序

冒泡排序(数组sort方法的原理)

1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。

2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

3.针对所有的元素重复以上的步骤,除了最后一个。

4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

图解

 js实现

function swap(arr, x, y) 
    let tmp = arr[x]
    arr[x] = arr[y]
    arr[y] = tmp

function bubbleSort(arr) 
    let len = arr.length;
    for (let i = 0; i < len - 1; i++) 
        //比较出一轮中的最大值放入最后
        for (let j = 0; j < len - 1 - i; j++) 
            if (arr[j] > arr[j + 1]) 
                swap(arr, j, j + 1)
            
        
    
    return arr;

特点

  1. 时间复杂度:O(N^2)
  2. 空间复杂度:O(1)
  3. 稳定性:稳定

 

快速排序

1.以数组第一位为基准值,将小于的元素放在左边,大于的元素放在右边,分为左右两块。

2.在左右2块中重复第一步,不断循环,直到分区的大小为1则停止。

图解

注意左右分区,我们可以通过将基准元素和左边分区最右端元素交换实现

js实现

function swap(arr, x, y) 
    let tmp = arr[x]
    arr[x] = arr[y]
    arr[y] = tmp

//用于一次以基准值进行排序
function partition(arr, left, right) 
    //设定基准值pivot
    let pivot = left
    let index = pivot + 1
    for (let i = index; i <= right; i++) 
        //小于基准值的放左边,大于的不变
        if (arr[i] < arr[pivot]) 
            swap(arr, i, index)
            index++
        
    
    //和小于基准值的最右边一个交换位置,实现左边小于基准值,右边大于基准值
    swap(arr, pivot, index - 1)
    //返回基准值的位置
    return index - 1

function quickSort(arr, left, right) 
    let len = arr.length
    left = typeof left != 'number' ? 0 : left
    right = typeof right != 'number' ? len - 1 : right
    if (left < right) 
        //进行一次排序
        let partitionIndex = partition(arr, left, right)
        //递归对左边进行排序
        quickSort(arr, left, partitionIndex - 1)
        //递归对右边进行排序
        quickSort(arr, partitionIndex + 1, right)
    
    return arr

console.log(quickSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5]))

特点

  1. 时间复杂度:O(N*logN)
  2. 空间复杂度:O(logN)
  3. 稳定性:不稳定

选择排序

直接选择排序

每一次遍历待排序的数据元素从中选出最小(或最大)的一个元素,存放在序列的起始(或者末尾)位置(和原存放位置上的元素交换顺序),直到全部待排序的数据元素排完。

图解

 js实现

//交换数组下标x位和y位的元素
function swap(arr, x, y) 
    arr[x] = arr[x] - arr[y]
    arr[y] = arr[y] + arr[x]
    arr[x] = arr[y] - arr[x]
    return arr

function selectionSort(arr) 
    let t = arr.length
    let x = 0
    while (x != t) 
        let min = 0;
        let tmp = Number.MAX_VALUE
        //选出最小的元素的下标
        for (let i = x; i < t; i++) 
            if (tmp > arr[i]) 
                tmp = arr[i]
                min = i
            
        
        //不是当前位置,则交换顺序
        if (x != min) 
            swap(arr, x, min)
        
        x++
    
    return arr

console.log(selectionSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5]))

特点

  1. 时间复杂度:O(N^2)
  2. 空间复杂度:O(1)
  3. 稳定性:不稳定

堆排序

1.将元素排序为一个大(小)顶堆,则顶部元素为最大(小)元素,将其与元素尾(头)部元素交换顺序。

2.重新排序剩下元素,再次形成大(小)顶堆,重复1操作,直到只剩一个元素。

大(小)顶堆

每个节点的值都大于(小于)或等于其子节点的值,在堆排序算法中用于升(降)序排列;

代码中表示为:

大顶堆arr[i]>=arr[2i+1]和arr[i]>=arr[2i+2]        父节点大于左右子节点

小顶堆arr[i]<=arr[2i+1]和arr[i]<=arr[2i+2]         父节点小于左右子节点

非叶节点

有孩子节点的节点。最后一个非叶节点在数组中的序号为元素个数除以2向下取整。

js实现 

// 建立大顶堆
function buildMaxHeap(arr) 
    let len = arr.length;
    //Math.floor(len/2)表示最后一个非叶节点(含有子节点的节点),
    //从后往前调整每一个非叶子节点的顺序,使堆变为大顶堆
    for (let i = Math.floor(len / 2); i >= 0; i--) 
        heapify(arr, i, len);
    

// 调整堆
function heapify(arr, i, len) 
    let left = 2 * i + 1,
        right = 2 * i + 2,
        largest = i;
    //通过2次判断选出父节点和2个子节点中最大的一个
    if (left < len && arr[left] > arr[largest]) 
        largest = left;
    
    if (right < len && arr[right] > arr[largest]) 
        largest = right;
    
    //将最大的节点和父节点交换顺序
    if (largest != i) 
        swap(arr, i, largest);
        //递归对换顺序的节点进行调整,使其还是大顶堆
        heapify(arr, largest, len);
    

function swap(arr, i, j) 
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;

function heapSort(arr) 
    // 建立大顶堆
    buildMaxHeap(arr);
    let len = arr.length
    //每次建立一个大顶堆,将最大的元素(堆顶元素)放入尾部达到排序效果
    for (let i = len - 1; i > 0; i--) 
        swap(arr, 0, i);
        len--;
        heapify(arr, 0, len);
    
    return arr;

console.log(heapSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5]))

特点

  1. 时间复杂度:O(N*logN)
  2. 空间复杂度:O(1)
  3. 稳定性:不稳定

 

插入排序 

直接插入排序

每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序(默认是从后向前比较)。

图解

蓝色部分为无序表,黄色部分为有序表:

js实现 

使用的是一个数组,将数组下标为i之前的为有序,之后为无序。

 注意用的是改变原数组的方法,可以不用返回。

//将数组下标为的x元素取出插入为y的
function popInsert(arr,x,y)
    arr.splice(y,0,...arr.splice(x,1))

function insertSort(arr)
    let t=arr.length
    for (let i=1;i<t;i++)
        let j=i-1
        do
            if(arr[j]<arr[i])
                popInsert(arr,i,j+1)
                break
            
            j--
            //比较到下标为0的时候直接插入
            if(j<0)
                popInsert(arr,i,0)
            
        while(j>=0)
    
    return arr

console.log(insertSort([3,1,2,3134,113,342,123412,55,111,4,234,5,5]))

特点

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定

 

希尔排序

希尔排序在直接排序之前,进行预排列,将某些极端数据更快的排列到数列前面,构成一个接近排列好的序列,最后再进行一次直接插入排序。

预排列的原理也是插入排列,只不过这里的将数组分成了gap组,分别对每一个小组进行插入排序。

图解

对于升序,当gap从5 – 2 – 1的过程中,排在后面的数值小的数能更快排到前面,当gap为1的时候实际上就是进行了一次插入排序

js实现

每次将下标位置差为gap的数进行直接排序,其中第一次gap=Math.floor(arr.length/2),之后每次gap=Math.floor(gap/2)。

function shellsSort(arr)
    let t=arr.length
    let gap=t
    let tmp
    //gap取不同值的排序
    do
        //每次的分组
        gap=Math.floor(gap/2)
        for(let i=gap;i<t;i++)
            //只和上一个间距为gap的元素进行比较,判断是否要插入,还是维持原顺序
            if(arr[i-gap]>arr[i])
                tmp = arr[i]
                let j=i-gap
                //将插入位置之后的元素整体后移
                do
                    arr[j+gap]=arr[j]
                    j-=gap
                while(j>=0&&arr[j]>tmp)
                //插入对应位置
                arr[j+gap]=tmp
            
        
    while(gap>1)
    return arr

console.log(shellsSort([3,1,2,3134,113,342,123412,55,111,4,234,5,5]))

 

特点

  1. 希尔排序是对直接插入排序的优化当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比
  2. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,一般来说为O(n^1.3) 
  3. 稳定性:不稳定

其它排序

桶排序

选出待排序元素中的最大最小值,根据最大最小值划分多个区域(桶),通过映射函数将元素分到不同区域(桶)中,对区域(桶)中的元素进行排序后,依次取出即可。

图解

将排序的元素放入对应的桶中

 对桶中的元素进行排序

js实现

//插入排序
function insertionSort(arr) 
    let len = arr.length;
    let preIndex, current;
    for (let i = 1; i < len; i++) 
        preIndex = i - 1;
        current = arr[i];
        while (preIndex >= 0 && arr[preIndex] > current) 
            arr[preIndex + 1] = arr[preIndex];
            preIndex--;
        
        arr[preIndex + 1] = current;
    
    return arr;

function bucketSort(arr, bucketSize) 
    let minValue = arr[0];
    let maxValue = arr[0];
    //找到数组中的最大和最小值
    for (let i = 1; i < arr.length; i++) 
        if (arr[i] < minValue) 
            minValue = arr[i];
         else if (arr[i] > maxValue) 
            maxValue = arr[i];
        
    
    //桶的初始化
    let DEFAULT_BUCKET_SIZE = 5;
    // 设置一个桶能存储的默认数量为5
    bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
    //桶的个数
    let bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
    let buckets = new Array(bucketCount);
    for (let i = 0; i < buckets.length; i++) 
        buckets[i] = [];
    
    //利用映射函数将元素分配到各个桶中
    for (let i = 0; i < arr.length; i++) 
        buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
    
    //删除arr数组中的所有元素
    arr.length = 0;
    for (let i = 0; i < buckets.length; i++) 
        // 对每个桶进行排序,这里使用了插入排序
        insertionSort(buckets[i]);
        // 将每个桶的数据从小到大拿出                    
        for (let j = 0; j < buckets[i].length; j++) 
            arr.push(buckets[i][j]);
        
    
    return arr;

console.log(bucketSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5]))

特点

  1. 时间复杂度:O(N+K)
  2. 空间复杂度:O(N+K)
  3. 稳定性:稳定

基数排序

确定最大数的位数,先按第一位进行排序,在按第二位进行排序,直到按最后一位进行排序为止。

图解

js实现

function radixSort(arr) 
    let counter = [];
    //获取元素的最大位数
    let maxDigit = `$Math.max(...arr)`.length
    let mod = 10;
    let dev = 1;
    //从小到大依次对元素的每一位进行循环排序
    for (let i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) 
        for (let j = 0; j < arr.length; j++) 
            //获取元素对应位上的数值
            let bucket = parseInt((arr[j] % mod) / dev);
            if (counter[bucket] == null) 
                counter[bucket] = [];
            
            //根据对应的数值,将元素放入对应的桶中
            counter[bucket].push(arr[j]);
        
        let pos = 0;
        //从0~9的桶中将元素取出,完成一次排序
        for (let j = 0; j < counter.length; j++) 
            let value = null;
            if (counter[j] != null) 
                while ((value = counter[j].shift()) != null) 
                    arr[pos++] = value;
                
            
        
    
    return arr;

console.log(radixSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5], 6))

特点

  1. 时间复杂度:O(N*K)
  2. 空间复杂度:O(N+K)
  3. 稳定性:稳定

归并排序

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

  4. 重复步骤 3 直到某一指针达到序列尾;

  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

图解

 js实现

function mergeSort(arr)   // 采用自上而下的递归方法
    let len = arr.length;
    if (len < 2) 
        return arr;
    
    let middle = Math.floor(len / 2),
        left = arr.slice(0, middle),
        right = arr.slice(middle);
    return merge(mergeSort(left), mergeSort(right));

function merge(left, right) 
    let result = [];

    while (left.length && right.length) 
        if (left[0] <= right[0]) 
            result.push(left.shift());
         else 
            result.push(right.shift());
        
    

    while (left.length)
        result.push(left.shift());

    while (right.length)
        result.push(right.shift());

    return result;

console.log(mergeSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5], 6))

特点

  1. 时间复杂度:O(N*logN)
  2. 空间复杂度:O(N)
  3. 稳定性:稳定

计数排序

1.找出待排序的数组中最大和最小的元素

2.统计数组中每个值为i的元素出现的次数,存入数组C的第i项

3.对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)

4.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

图解

 js实现

function countingSort(arr) 
    let maxValue = Math.max(...arr)
    let bucket = new Array(maxValue + 1),
        sortedIndex = 0;
    arrLen = arr.length,
        bucketLen = maxValue + 1;

    for (let i = 0; i < arrLen; i++) 
        if (!bucket[arr[i]]) 
            bucket[arr[i]] = 0;
        
        bucket[arr[i]]++;
    

    for (let j = 0; j < bucketLen; j++) 
        while (bucket[j] > 0) 
            arr[sortedIndex++] = j;
            bucket[j]--;
        
    

    return arr;

console.log(countingSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5]))

特点

  1. 时间复杂度:O(N+K)
  2. 空间复杂度:O(K)
  3. 稳定性:稳定

十大经典排序算法

...线性时间非比较类排序。时间复杂度&空间复杂度&稳定性时间复杂度&空间复杂度&稳定性稳定:如果a原本在 查看详情

十大经典排序算法的算法描述和代码实现(代码片段)

...数排序的非比较类排序,分析了各种排序算法的复杂度和稳定性,还有JAVA代码的详细实现。对冒泡排序、插入排序、选择排序和堆排序等十种算法进行了详细的思想总结。一、算法概述1、算法分类十种常见排序算法可以分为两... 查看详情

超详细十大经典排序算法总结

0、排序算法说明0.1排序的定义对一序列对象根据某个关键字进行排序。0.2术语说明稳定 :如果a原本在b前面,而a=b,排序之后a仍然在b的前面;不稳定 :如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;内排... 查看详情

js十大排序算法收藏(代码片段)

...lace:占用常数内存,不占用额外内存Out-place:占用额外内存稳定性:排序后2个相等键值的顺序和排序之前它们的顺序相同冒泡排序(BubbleSort)冒泡排序须知:作为最简单的排序算法之一,冒泡排序给我的感觉就像Abandon在单词书里... 查看详情

经典排序及总结(python实现)(代码片段)

目录1.排序的基本概念和分类排序的稳定性:内排序和外排序影响内排序算法性能的三个因素:根据排序过程中借助的主要操作,可把内排序分为:按照算法复杂度可分为两类:2.冒泡排序BubbleSort3.选择排序SelectionSort4.插入排序In... 查看详情

数据结构排序(代码片段)

...、插入排序1.原理2.代码实现3.时间复杂度4.空间复杂度5.稳定性6.应用场景二、希尔排序1.原理2.代码实现3.时间复杂度4.空间复杂度5.稳定性6.应用场景三、选择排序1.原理2.代码实现3.时间复杂度4.空间复杂度5.稳定性6.应用场景四、... 查看详情

十大排序算法对比和c++实现(代码片段)

...排序一、性能对比算法平均时间复杂度最好情况最坏情况稳定性优点缺点插入排序O(n2)O(n^2)O(n2)O(n)O(n)O(n)O(n2)O(n^2)O(n2)稳定比较次数少 查看详情

十大排序算法对比和c++实现(代码片段)

...排序一、性能对比算法平均时间复杂度最好情况最坏情况稳定性优点缺点插入排序O(n2)O(n^2)O(n2)O(n)O(n)O(n)O(n2)O(n^2)O(n2)稳定比较次数少 查看详情

十大经典排序算法(代码片段)

前言说明十大排序算法可以说是每个程序员都必须得掌握的了,花了一天的时间把代码实现且整理了一下,为了方便大家学习,我把它整理成一篇文章,每种算法会有简单的算法思想描述,为了方便大家理解,我还找来了动图演... 查看详情

数据结构与算法笔记——十大经典排序及算法的稳定性(代码片段)

一、十大经典排序算法排序算法是《数据结构与算法》中最基本的算法之一。排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排... 查看详情

八大排序算法c语言过程图解+代码实现(插入,希尔,选择,堆排,冒泡,快排,归并,计数)(代码片段)

...据结构中很重要的一章,先介绍几个基本概念。排序稳定性:多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后... 查看详情

一文搞定十大经典排序算法(java实现)

本文总结十大经典排序算法及变形,并提供Java实现。参考文章:十大经典排序算法总结(Java语言实现)快速排序算法—左右指针法,挖坑法,前后指针法,递归和非递归快速排序及优化(三路划分等)一、排序算法概述1、... 查看详情

十大经典排序算法(代码片段)

...。线性阶(O(n))排序基数排序,此外还有桶、箱排序。关于稳定性:稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序。不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。名词解释:n:数据规模k:“桶”... 查看详情

十大经典排序算法

...法平均时间复杂度最好情况最坏情况空间复杂度排序方式稳定性冒泡排序O(n^2)O(n)O(n^2)O(1)In-place稳定选择排序O(n^2)O(n^2)O(n^2)O(1)In-place不稳定插入排序O(n^2)O(n)O(n^2)O(1)In-place稳定希尔排序O(nlog(n))O( 查看详情

[algorithm]十大排序算法动图图解及java代码实现(代码片段)

...部的排序记录,在排序过程中需要访问外存排序算法稳定性:排序前后2个相等数据的相对位置不发生变化一、冒泡排序算法冒泡排序算法基本思想:重复走访待排序的序列,依次比较两个元素,如果他们的大... 查看详情

排序专题之各个内外排序算法的比较

1.稳定性比较 插入排序、冒泡排序、二叉树排序、二路归并排序及其他线形排序是稳定的 选择排序、希尔排序、快速排序、堆排序是不稳定的2.时间复杂性比较 插入排序、冒泡排序、选择排序的时间复杂性为O(n2) 其它非线形... 查看详情

十大经典排序算法(代码片段)

0.1 排序的定义对一序列对象根据某个关键字进行排序。0.2术语说明稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;内排序:所有排序操作都... 查看详情

数据结构排序(代码片段)

...、插入排序1.原理2.代码实现3.时间复杂度4.空间复杂度5.稳定性6.应用场景二、希尔排序1.原理2.代码实现3.时间复杂度4.空间复杂度5.稳定性6.应用场景三、选择排序1.原理2.代码实现3.时间复杂度4.空间复杂度5.稳定性6.应用场景四、... 查看详情