【算法】排序

插入排序

1. 直接插入排序【稳定】

将待排序的记录Ri+1,插入到已排好序的记录表R0, R1 ,…., Ri中,得到一个新的、记录数增加1的有序表。 直到所有的记录都插入完为止。
在这里插入图片描述
如上图,将每一个数在比较之后,插入到适当的位置上。

(1)思路

比较过程中,首先第一个循环是从第1 ~ 第n-1的数进行比较,接着第二个循环将第i个数与前面的每个数进行比较。

(2)代码

function insertSort(arr) {
    for (var i = 1; i < arr.length; i++) {
        for (j = i; j > 0; j--) {
            if (arr[j] < arr[j - 1]) {
                //1. 使用解构赋值
                [arr[j - 1], arr[j]] = [arr[j], arr[j - 1]];

                /*2. 使用中间变量
                var temp = arr[j - 1];
                arr[j - 1] = arr[j];
                arr[j] = temp;*/
            }
        }
    }
}

(3)空间复杂度:O(1)

就地排序,没有用到单独空间,所以空间复杂度为O(1)。

(4)时间复杂度:O(2)

内外两个n次的循坏,所以时间复杂度为O(n²)。
最好的时间复杂度:O(n)。

2. 希尔排序【不稳定】

先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

(1)思路

采用Knuth序列:h=h*3+1,对数组进行分组。分组后,基于直接插入排序对每个分组进行排序。

(2)代码

function shellSort(arr) {
    var h = 1;
    while (h <= arr.length) {
        h = 3 * h + 1;    //采用Knuth序列
    }
    for (var gap = h; gap > 0; gap = (gap - 1) / 3) {
        for (var i = gap; i < arr.length; i++) {
            for (var j = i; j > gap - 1; j -= gap) {
            	//基于直接插入排序
                if (a[j] < a[j - gap]) {
                    [a[j - gap], a[j]] = [a[j], a[j - gap]];
                }
            }
        }
    }
}

(3)时间复杂度:O(1)

(4)时间复杂度:O(1.3)

效率比直接插入排序高的原因是:间隔大的时候,移动的次数少;间隔小的时候,移动的距离短。


选择排序

1. 直接选择排序【不稳定】

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

(1)思路

声明一个值min,将这个值指向待排序的数据元素中值最小的索引。若一次比较完,min不等于当前待排序元素的第一个索引,则将min索引的值与第一个值进行交换。

(2)代码

function selectSort(arr) {
    for (var i = 0; i < arr.length - 1; i++) {
        var min = i;
        for (var j = i + 1; j < arr.length; j++) {
            if (a[j] < a[min]) {
                min = j;
            }
        }
        if (min !== i) {
            [a[i], a[min]] = [a[min], a[i]];
        }
    }
}

(3)空间复杂度:O(1)

(4)时间复杂度:O(n2)

2. 堆排序【不稳定】

(1)创建堆(最大/小堆)

步骤:最后一个非终端结点开始往前逐步调整,让每个双亲大于(或小于)子女,直到根结点为止。

下图为创建最大堆。
在这里插入图片描述

(2)堆排序

  • 步骤:
    ① 对一组待排序的记录,按堆的定义建立最大堆;
    将堆顶记录和最后一个记录交换位置,则前n-1个记录是无序的,而最后一个记录是有序的;
    ③ 堆顶记录被交换后,前n-1个记录不再是堆,需将前n-1个待排序记录重新组织成为一个堆,然后将堆顶记录和倒数第二个记录交换位置,即将整个序列中次小关键字值的记录调整(排除)出无序区;
    ④ 重复上述步骤,直到全部记录排好序为止。
  • 堆的调整思想——筛选
    ① 输出堆顶元素之后,以堆中最后一个元素替代之
    ②然后将根结点值与左、右子树的根结点值进行比较,并与其中大者进行交换;
    ③重复上述操作,直到是叶子结点或其关键字值大于等于左、右子树的关键字的值,将得到新的堆。

下图为堆排序图示:
在这里插入图片描述

(3)空间复杂度:O(1)

(4)时间复杂度:O(nlog2n)。


交换排序

1. 冒泡排序【稳定】

(1)思路

首先,数组n个元素,前后两个元素两两比较,大的元素往后移,一趟排序后,最大的元素排在最后的位置。接着,对n-1个元素进行相同的步骤。以此类推,直至排序完成。

(2)代码

代码中,设置标志量flag,标记一趟冒泡如果没有交换,则已经排好序,提前结束循环。

function bubbleSort(arr) {
    var flag = 1;
    for (i = arr.length - 1; i > 0 && flag === 1; i++) {
        flag = 0;
        for (j = 0; j < i; j++) {
            if (a[j] > a[j + 1]) {
                flag = 1;
                [a[j], a[j + 1]] = [a[j + 1], a[j]];
            }
        }
    }
}

(3)空间复杂度:O(1)

(4)时间复杂度:O(n2)

最好时间复杂度:O(n)
( 初始排列已经有序,只执行一趟起泡,做 n-1 次关键码比较,不移动数据元素。【使用标志量flag判断】)

2. 快速排序【不稳定】

(1)思路

从序列的两端交替扫描各个记录,将关键字小于基准关键字的记录依次放置到序列的前边;而将关键字大于基准关键字的记录从序列的最后端起,依次放置到序列的后边,直到扫描完所有的记录。

(2)代码

function quickSort(arr, low, high) {
    var i = low,
        j = high,
        temp = arr[i];
    while (i < j) {
        if (i < j && temp <= arr[j]) j--;
        if (i < j) {
            arr[i] = arr[j];
        }
        if (i < j && arr[i] <= temp) i++;
        if (i < j) {
            arr[j] = arr[i];
        }
    }
    arr[i] = temp;
    if (low < i) quickSort(arr, low, i - 1);
    if (i < high) quickSort(arr, j + 1, high);
}

注意:调用时,第2、3个参数分别传入0、arr.length-1

(3)空间复杂度:O(log2n)

(4)时间复杂度:O(nlog2n)


归并排序【稳定】

(1)思路

是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
首先,分阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。
接着,治阶段,我们需要将两个已经有序的子序列合并成一个有序序列

(2)代码

function mergeSort(arr, left, right) {
    if (left < right) {
        let mid = parseInt((left + right) / 2);
        mergeSort(arr, left, mid); //左边归并排序
        mergeSort(arr, mid + 1, right); //右边归并排序
        merge(arr, left, mid, right); //将两个有序子数组合并操作
    }
}
function merge(arr, left, mid, right) {
    let temp = [];
    let len = 0;
    let i = left,
        j = mid + 1;
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[len++] = arr[i++];
        } else {
            temp[len++] = arr[j++];
        }
    }
    while (i <= mid) {
        temp[len++] = arr[i++];
    }
    while (j <= right) {
        temp[len++] = arr[j++];
    }
    let t = 0;
    while (t < len) {
        arr[left++] = temp[t++];
    }
}

注意:调用时,第2、3个参数分别传入0、arr.length-1

(3)空间复杂度:O(log2n)

(4)时间复杂度:O(nlog2n)


基数(桶)排序【稳定】

(1)思路

将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

(2)代码

function radixSort(arr, maxDigit) {
    var counter = [];
    var mod = 10;
    var dev = 1;
    for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
        for (var j = 0; j < arr.length; j++) {
            var bucket = parseInt((arr[j] % mod) / dev);
            if (counter[bucket] == null) {
                counter[bucket] = [];
            }
            counter[bucket].push(arr[j]);
        }
        var pos = 0;
        for (var j = 0; j < counter.length; j++) {
            var value = null;
            if (counter[j] != null) {
                while ((value = counter[j].shift()) != null) {
                    arr[pos++] = value;
                }
            }
        }
    }
    return arr;
}

注意:第二个参数是待比较元素中的最大数位,在传入参数前,先算出最大数位,使用下列代码算出:

var dight = 0;
for (var j = 0; j < a.length; j++) {
    var t = a[j].toString();
    if (dight < t.length) dight = t.length;
}

(3)空间复杂度:O(n)

(4)时间复杂度:O(mn)

m为最大数位。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值