LeetCode912. 排序数组(JavaScript手撕各种排序算法)

题目链接:https://leetcode-cn.com/problems/sort-an-array/submissions/

这是一道好题目,可以用来练习手撕各种排序算法,别直接调用api错过了这道好题哦!

目录

一、插入排序

(1)直接插入排序:超时

(2)折半插入排序:AC

(3)希尔排序:AC

二、交换排序

(1)冒泡排序:AC

(2)快速排序【递归】:AC

(3)快读排序【非递归】:AC

三、选择排序

(1)选择排序:AC

(2)堆排序:AC

四、归并排序

(1)归并排序:AC

五、JavaScript 内部 API

(1)直接调用API:AC 


一、插入排序

(1)直接插入排序:超时

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
    sort(nums);
    return nums;
};

//插入排序
//稳定排序
//时间复杂度O(n2)
function sort(arr) {
    //首先把数组当做第一个元素为有序的序列,然后将后面的元素依次插入到合适的位置
    let i, j;
    for (i = 1; i < arr.length; i++) {
        if (arr[i] < arr[i - 1]) {
            let temp = arr[i]; //哨兵
            for (j = i - 1; temp < arr[j]; j--) {
                arr[j+1] = arr[j]; //直接往后覆盖移动,数据已经被哨兵保存
            }
            arr[j+1] = temp;
        }
    }
}

(2)折半插入排序:AC

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
    sort(nums);
    return nums;
};

//稳定排序,之后小于才会引起移动,等于不会引起移动
//时间复杂度O(n2)
//折半插入排序
function sort(arr) {
    //先用折半查找的方式,找到待插入的位置,然后直接后移
    let i, j, low, high, mid;
    for (i = 1; i < arr.length; i++) {
        let temp = arr[i] //哨兵暂存
        low = 0; //默认i之前的所有元素已经是有序的
        high = i - 1;
        while (low <= high) {
            mid = ((high - low) > 2) + low;
            if (arr[mid] > temp) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        for(j = i-1;j >=high+1;j--){
            arr[j+1] = arr[j];
        }
        arr[high+1] = temp;
    }
}

(3)希尔排序:AC

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
    shellSort(nums);
    return nums;
};

//不稳定排序
//时间复杂度O(n2)
//希尔排序
function shellSort(arr) {
    let len = arr.length;
    let gap = Math.floor(len / 2), i
    for (gap; gap > 0; gap = Math.floor(gap / 2)) {
        for (i = gap; i < len; i++) {
            if (arr[i] < arr[i - gap]) {
                let temp = arr[i]
                for (j = i - gap; j >= 0 && temp < arr[j]; j -= gap) {
                    arr[j + gap] = arr[j];
                }
                arr[j + gap] = temp;
            }
        }
    }
}

二、交换排序

(1)冒泡排序:AC

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
    bubbleSort(nums);
    return nums;
};

//稳定排序
//时间复杂度最好O(n)
//冒泡排序
function bubbleSort(arr) {
    let flag; //表示本趟冒泡是否发生交换的标志
    for (let i = 0; i < arr.length - 1; i++) {
        flag = false;
        for (let j = arr.length - 1; j > i; j--) { //i之前的所有元素都是排好序的
            if (arr[j - 1] > arr[j]) {
                let temp = arr[j];
                arr[j] = arr[j - 1]
                arr[j - 1] = temp
                flag = true;
            }
        }
        if (flag === false) {  //本趟未发生交换,则说明已经有序,可以剪枝
            return;
        }
    }
}

(2)快速排序【递归】:AC

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
    let low = 0, high = nums.length - 1
    quickSort(nums, low, high);
    return nums;
};
//手撕快排
//时间复杂度最好O(ologn)
//不稳定排序
//找到枢值的准确排序位置
function partition(arr, low, high) {
    let pivot = arr[low]; //默认第一个元素为枢轴值
    while (low < high) {
        while (low < high && arr[high] >= pivot)
            --high;
        arr[low] = arr[high];
        while (low < high && arr[low] <= pivot)
            ++low;
        arr[high] = arr[low];
    }
    arr[low] = pivot;
    return low;
}

function quickSort(arr, low, high) {
    if (low < high) {
        let pivotPos = partition(arr, low, high);
        quickSort(arr, low, pivotPos - 1);
        quickSort(arr, pivotPos + 1, high);
    }
}

(3)快读排序【非递归】:AC

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
    let stack = []
    quickSort(nums, stack);
    return nums;
};
//手撕快排-非递归
function quickSort(arr, stack) {
    let l = 0,r = arr.length-1;
    stack.push(l);
    stack.push(r);
    while(stack.length !== 0){
        let rr = stack.pop();
        r = rr;
        let ll = stack.pop();
        l = ll;
        let pivot = arr[ll];
        if(ll < rr){
            while(ll < rr){
                while(ll < rr && arr[rr] >= pivot) rr--;
                arr[ll] = arr[rr];
                while(ll < rr && arr[ll] <= pivot) ll++;
                arr[rr] = arr[ll];
            }
            arr[ll] = pivot;
            stack.push(l);
            stack.push(ll-1);
            stack.push(ll+1);
            stack.push(r);
        }
    }
}

三、选择排序

(1)选择排序:AC

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
    selectSort(nums);
    return nums;
};

//不稳定排序
//时间复杂度O(n2)
//选择排序
function selectSort(arr) {
    let min;
    for (let i = 0; i < arr.length - 1; i++) { //只需要比较n-1趟
        min = i;
        for (let j = i + 1; j < arr.length; j++) {
            if(arr[j] < arr[min])
                min = j;
        }
        if(min !== i){
            let tmp = arr[min];
            arr[min] = arr[i];
            arr[i] = tmp;
        }
    }
}

(2)堆排序:AC

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
    nums.unshift(-1);
    return HeapSort(nums);
};

//不稳定排序
//时间复杂度最好O(nlog2n)
//堆排序
/*
* 堆是一个完全二叉树,满足任意一个非叶节点的值都不大于其左右孩子节点的值(小顶堆)
*                                       不小于                大顶堆
* 思想:将无序序列调节成一个堆,然后从堆中选择堆顶元素的值,将这个值加入有序序列,无序序列减少一个值
*
* 调节顺序:由下至上,由右至左
* */
function HeapSort(arr) {
    let len = arr.length - 1;
    let ans = []
    BuildMinHeap(arr); //建立小顶堆
    for (let i = len; i > 0; i--) {
        let tmp = arr[1];
        arr[1] = arr[i];
        arr[i] = tmp;
        ans.push(arr[i]);
        AdjustDown(arr, 1, i - 1);
    }
    return ans;
}

//建堆,反复调整
function BuildMinHeap(arr) {
    let len = arr.length - 1
    for (let i = Math.floor(len / 2); i > 0; i--) {  //Math.floor(len / 2) 第一个可能需要调整的非叶子节点
        AdjustDown(arr, i, len)
    }
}

//调整一次,k为开始检查点
function AdjustDown(arr, k, len) {
    arr[0] = arr[k];
    for (let i = 2 * k; i <= len; i *= 2) {  //2 * k 为其左孩子
        if (i < len && arr[i] > arr[i + 1])
            i++; //找到左右孩子中值更大的那个
        if (arr[0] <= arr[i]) {
            break   //树顶已经最小
        } else {
            arr[k] = arr[i];
            k = i;
        }
    }
    arr[k] = arr[0];
}

四、归并排序

(1)归并排序:AC

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
    mergeSort(nums, 0, nums.length - 1);
    return nums;
};

//稳定排序
//时间复杂度最好O(nlog2n)
//归并排序
function mergeSort(arr, low, high) {
    if (low < high) { //至少保证有2个元素,使其归并合一
        let mid = Math.floor((low + high) / 2);
        mergeSort(arr, low, mid); //递归分组
        mergeSort(arr, mid + 1, high);
        merge(arr, low, mid, high); //两两合并
    }
}

function merge(arr, low, mid, high) {
    let tmpArr = []
    let pos = low, i, j
    for (let k = low; k <= high; k++) {
        tmpArr[k] = arr[k]  //元素复制,左右归并
    }
    for (i = low, j = mid + 1; i <= mid && j <= high; pos++) {
        if (tmpArr[i] <= tmpArr[j]) {
            arr[pos] = tmpArr[i++]
        } else {
            arr[pos] = tmpArr[j++]
        }
    }
    while (i <= mid) arr[pos++] = tmpArr[i++];
    while (j <= high) arr[pos++] = tmpArr[j++];
}

五、JavaScript 内部 API

(1)直接调用API:AC

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {
    return nums.sort((a,b)=>a-b)
};

LeetCode912. 排序数组(JavaScript手撕各种排序算法)_第1张图片

除了直接插入排序超时,其他都可以执行,小伙伴们练手吧~

你可能感兴趣的:(LeetCode,JavaScript,排序算法,算法)