js手撕经典排序算法:冒泡、选择、插入、堆排序、快排...

这五种排序算法,时间复杂度最低的是堆排序和快排,为O(nlogn),剩下的冒泡、选择、插入排序复杂度较高为O(n2)。js中内置的数组排序Array.sort()api底层也是通过快排写的,复杂度为O(nlogn)。

插入排序


// 冒泡排序,从小到大
// 冒泡排序的排序逻辑就是:每一轮排序都会将最大的数排到最后面
function bubbleSort(arr = []) {
    // 双层for循环
    let temp = 0;
    for (let i = 0; i < arr.length; i++) {
        // 注意:这里是从0开始,每一轮最后一位都减去i
        for (let j = 0; j < arr.length - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp
            }
        }
    }
    console.log(arr);
}

bubbleSort([1, 3, 2, 5, 8, 4, 9, 0, 8])  //[ 0, 1, 2, 3, 4,5, 8, 8, 9]

选择排序

// 选择排序
// 也就是每轮循环,都会将最小的数字放到这轮循环的首位处
function selectSort(arr = []) {
    // 双层for循环
    let temp = 0;
    for (let i = 0; i < arr.length; i++) {
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[i] > arr[j]) {
                temp = arr[i]
                arr[i] = arr[j]
                arr[j] = temp
            }
        }
    }
    console.log(arr);
}

selectSort([1, 3, 2, 5, 8, 4, 9, 0, 8]) //[ 0, 1, 2, 3, 4,5, 8, 8, 9]

插入排序

// 插入排序
// 每一论循环都将前i个数字进行排好顺序,下一轮再将第i+1个元素插入到前i个元素    
function insertSort(arr = []) {
    // 双层for循环
    let temp = 0;
    for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < i; j++) {
            if (arr[i] < arr[j]) {
                temp = arr[i]
                arr[i] = arr[j]
                arr[j] = temp
            }
        }
    }
    console.log(arr);
}

insertSort([1, 3, 2, 5, 8, 4, 9, 0, 8]) //[0, 1, 2, 3, 4,5, 8, 8, 9]

堆排序

堆排序的重点就是首先把一无序序列转化为一大顶堆(若是从小到大排序),然后再对这个形成的对进行排序。

注意:堆一定是一个完全二叉树数据结构,且堆分为大顶堆和小顶堆,大顶堆是整个完全二叉树中的所有子树中父节点值要大于此父节点直接相连的子节点值,小顶堆相反,父节点值小于子节点值。

一般在做堆排序时,是将这课完全二叉树转化为数组形式(因为完全二叉树是连续索引)。

父节点和子节点的关系如下:

假如某个节点索引为i,则:

其父节点parent的索引为:(i-1)/2

其子节点的索引分别是:c1 = 2i +1 、c2 = 2i + 2

// 由一无序序列构造一个大顶堆,从下到上即从最后一个非叶子节点做堆化操作
var build_heap = function (tree, n) {
    // 最后一个非叶子节点,这里是因为:n为最后一个节点索引,那么我们只需要找到最后一个节点的父节点就可以是我们要找的最后一个非叶子节点索引了,即父节点=(n-1)/2
    let last_not_leaf_node = parseInt((n - 1) / 2)
    console.log(last_not_leaf_node);
    // 然后对其做堆化
    for (let i = last_not_leaf_node; i >= 0; i--) {
        heapify(tree, n, i)
        console.log(tree);
    }
}


// tree为这个二叉完全树根据索引构成的数组,n为完全二叉树的最大索引,i指的是完全二叉树中当前节点的索引,设定root根节点为索引0
// 表示从上到下即从根到底一直对子树做堆排序,使其子树(只包含父和直接连接的孩子节点)
var heapify = function (tree, n, i) {
    // 递归出口
    if (i >= n) {
        return
    }
    // 所以当前节点的孩子节点索引为 2i+1, 2i+2
    let c1 = 2 * i + 1
    let c2 = 2 * i + 2
    let max = i
    // 比较父节点与子节点大小,如果子节点值大于父节点值,则交换
    if (c1 <= n && tree[c1] > tree[max]) {
        max = c1
    }
    if (c2 <= n && tree[c2] > tree[max]) {
        max = c2
    }
    // 交换元素
    if (max !== i) {
        [tree[max], tree[i]] = [tree[i], tree[max]]
        // 对他的底层子树递归做堆排序
        heapify(tree, n, max)
    }
}

// 堆排序
var heap_sort = function (tree, n) {
    // 首先现将此任意无序序列构造成一个大顶堆
    build_heap(tree, n)
    // 变成大顶堆后,索引为0即整棵树的根节点一定是值最大的节点
    for (let i = n; i >= 0; i--) {
        // 交换树的根节点和最后一个节点
        [tree[0], tree[i]] = [tree[i], tree[0]]
        // 对树的i-1个节点进行堆化,也就是不再包含最后一个节点
        heapify(tree, i - 1, 0)

    }
}
let treeArray = [1, 4, 2, 10, 5, 7]
// heapify(treeArray, 5, 0)
// build_heap(treeArray, 5)
heap_sort(treeArray, 5)
console.log(treeArray); // [ 1, 2, 4, 5, 7, 10 ]

快速排序

快速排序算法俗称快排。快排的思想是首先找到一个基准值,然后将比这个基准值大的元素都放在它的右侧,比基准值小的元素放在其左侧,这就是一趟排序,以后再用相同的操作递归左序列和右序列

// 快速排序算法俗称快排
// 快排的思想是首先找到一个基准值,然后将比这个基准值大的元素都放在它的右侧,比基准值小的元素放在其左侧,这就是一趟排序,以后再用相同的操作递归左序列和右序列
function fastSort(arr = [], L, R) {
    if (L >= R) return
    // 记录初始的左指针
    let left = L
    // 记录初始的右指针
    let right = R
    // 设置基准元素
    let base = arr[left]
    let temp;
    while (left != right) {
        // 首先先找右边的第一个比基准元素小的索引
        while (left < right && arr[right] >= base) {
            right--
        }
        // 再找左边的元素第一个比基准元素大的索引
        while (left < right && arr[left] <= base) {
            left++
        }
        // 交换左右方元素
        temp = arr[left]
        arr[left] = arr[right]
        arr[right] = temp
    }
    //循环一遍后,将基准元素插入最终循环的索引
    arr[L] = arr[left]
    arr[left] = base
    fastSort(arr, L, left - 1)
    fastSort(arr, left + 1, R)
}
let arr = [1, 3, 2, 5, 8, 4, 9, 0, 8]
fastSort(arr, 0, 8)
console.log(arr);
/**
 * @param {number[]} arr
 * @param {number} k
 * @return {number[]}
 */
// 手写快排
function fastSort(arr, left, right) {
    if (left >= right) return
    let l = left
    let r = right
    let base = arr[left]
    while (left != right) {
        // !!!重要  !首先先找右边的第一个比基准元素小的索引而不是先找left,这里不能直接在条件里right--,因为会立即执行right--
        while (left < right && arr[right] >= base) {
            right--
        }
        // 再找左边的元素第一个比基准元素大的索引
        while (left < right && arr[left] <= base) {
            left++
        }
        // 然后将其调换位置
        [arr[left], arr[right]] = [arr[right], arr[left]]
        // let temp = arr[left]
        // arr[left]=arr[right]
        // arr[right]=temp 
    }
    // arr[l]=arr[left]
    // arr[left] = base    
    [arr[left], arr[l]] = [base, arr[left]]
    fastSort(arr, l, left - 1)
    fastSort(arr, left + 1, r)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

veggie_a_h

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值