LeetCode热题(JS版)- 912. 排序数组(附赠十大排序算法源码)

111 阅读2分钟

题目

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

输入:nums = [5,2,3,1]
输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

提示:

1 <= nums.length <= 5 * 104
-5 * 104 <= nums[i] <= 5 * 104

思路:快排

这里十种算法都实现一遍

  • 选择排序
  • 插入排序
  • 归并排序
  • 快速排序
  • 堆排序
  • 希尔排序
  • 冒泡排序
  • 基数排序
  • 计数排序
  • 桶排序
// 1. 选择排序。每次选择后面最小的放到未排序部分最前面
const selectSort = (nums: number[]): number[] => {
    const len = nums.length;
    for(let i = 0; i < len - 1; i++) {
        let minIndex = i;
        for(let j = i + 1; j < len; j++) {
            if(nums[j] < nums[minIndex]) {
                minIndex = j
            }
        }
        [nums[minIndex], nums[i]] = [nums[i], nums[minIndex]]
    }

    return nums
}

// 2. 插入排序。为每个元素,在排好序的数组中找出位置,并逐个后移。
const insertSort = (nums: number[]): number[] => {
    const len = nums.length;
    for(let i = 0; i < len; i++) {
        const temp = nums[i];
        let j = i;
        while(j > 0 && nums[j - 1] > temp) {
            nums[j] = nums[j - 1];// 后移腾出空间
            j--;
        }
        nums[j] = temp;// 插入
    }

    return nums;
}

// 3. 归并排序
function mergeSort(arr) {
  if (arr.length <= 1) {
    return arr; // 如果数组长度小于等于1,直接返回
  }
  
  const mid = Math.floor(arr.length / 2); // 找到数组的中间位置
  const leftArr = arr.slice(0, mid); // 将数组拆分为左右两个子数组
  const rightArr = arr.slice(mid);

  // 递归地对左右两个子数组进行归并排序
  const sortedLeftArr = mergeSort(leftArr);
  const sortedRightArr = mergeSort(rightArr);

  // 合并左右两个有序数组
  return merge(sortedLeftArr, sortedRightArr);
}

function merge(leftArr, rightArr) {
  let i = 0, j = 0;
  const mergedArr = [];

  while (i < leftArr.length && j < rightArr.length) {
    if (leftArr[i] < rightArr[j]) {
      mergedArr.push(leftArr[i]);
      i++;
    } else {
      mergedArr.push(rightArr[j]);
      j++;
    }
  }

  // 将剩余的元素添加到结果数组中
  while (i < leftArr.length) {
    mergedArr.push(leftArr[i]);
    i++;
  }

  while (j < rightArr.length) {
    mergedArr.push(rightArr[j]);
    j++;
  }

  return mergedArr;
}
 
// 4. 快速排序
function quickSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }
  
  const pivot = arr[0]; // 选择第一个元素作为基准
  const left = [];
  const right = [];

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]); // 比基准小的元素放入左边数组
    } else {
      right.push(arr[i]); // 比基准大的元素放入右边数组
    }
  }

  return [...quickSort(left), pivot, ...quickSort(right)]; // 递归地对左右两个子数组进行快速排序,并合并结果
}

// 5. 堆排序
function heapSort(arr) {
  const len = arr.length;

  // 建立最大堆
  for (let i = Math.floor(len / 2) - 1; i >= 0; i--) {
    heapify(arr, len, i);
  }

  // 依次取出最大值,将其放在数组末尾,并重新调整堆
  for (let i = len - 1; i > 0; i--) {
    swap(arr, 0, i); // 将最大值与当前位置交换
    heapify(arr, i, 0); // 重新调整堆
  }

  return arr;
}

// 调整堆:将以root为根的子树调整为最大堆
function heapify(arr, len, root) {
  let largest = root;
  const left = 2 * root + 1;
  const right = 2 * root + 2;

  if (left < len && arr[left] > arr[largest]) {
    largest = left;
  }

  if (right < len && arr[right] > arr[largest]) {
    largest = right;
  }

  if (largest !== root) {
    swap(arr, root, largest);
    heapify(arr, len, largest);
  }
}

// 交换数组中两个元素的位置
function swap(arr, i, j) {
  const temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

// 6. 希尔排序
const shellSort = (arr) => {
    // gap 不停拆分
    let gap = Math.floor(arr.length / 2) 
    for (;gap >  0; gap = Math.floor(gap / 2)) {
        // 从gap开始后移
        for (let i = gap; i < arr.length; i++) {
            // 每一组交换
            let temp = arr[i]// 取出当前值进行比较,如果之前的值更小那就
            let j = i; 
            for(j >=0; j-=gap;) {
                if(arr[j - gap] < arr[j]) {
                    arr[j] = arr[j - gap];// 把小的后移
                }
            }
            arr[j] = temp;// 把大的值移动到挪出的位置
        }
    }
}

// 7. 冒泡排序
const bubbleSort = (nums) => {
    const len = nums.length;
    let i, j;
    for(i = 0; i < len - 1; i++) {
        // len - 1趟扫描
        for(j = 0; j < len - 1 - i; i ++) {
            // 每趟确定最后一个值,所以只有 len - 1 - i 个元素需要比较
            if (nums[j] > nums[j+1]) {
                // 相邻元素比较
                swap(nums, j, j + 1);
            }
        }
    }

}

// 8. 计数排序
const countSort = (nums) => {
    const counts = new Array(nums.length).fill(0);
    nums.forEach(d => {
        counts[d] ++
    });

    const ret = []
    for(let i = 0; i < counts.length; i++) {
        for(let k = counts[i]; k > 0; k--) {
            ret.push(i)
        }
    }

    return ret;
}

// 基数排序
const radixSort = (nums) => {
    // 先定有几位
    const digit = 3;
    const counter = [];
    let mod = 10;
    let dev = 1;
    for(let i = 0; i < digit; i++, dev *= 10, mod *= 10) {
        // 按基数从低位到高位排序
        for(let j = 0; j < nums.length; j++) {
            const bucket = Math.floor(nums[j] % mod / dev);
            if(!counter[bucket]) counter[bucket] = [];
            counter[bucket].push(nums[j])
        }
        
        // 取出当前基数的顺序
        let pos = 0;
        for(let j = 0; j < counter.length; j++) {
            if(!counter[j]) continue;

            let value = null;
            while((value = counter[j].shift()) !== null) {
                nums[pos++] = value;
            }
        }
    }
}

// 桶排序
const bucketSort = (nums) => {
    const size = 9;// 每只桶的大小
    const [min, max] = [Math.min.apply(Math, nums), Math.max.apply(Math, nums)];
    const count = Math.floor((max - min) / size);
    const buckets = new Array(count);

    // 分配桶
    for (let i = 0; i < nums.length; i++) {
        buckets[Math.floor((nums[i] - min) / size)].push(nums[i]);
    }

    // 每只桶排序输出
    nums.length = 0;
    buckets.forEach(bucket => {
        bucket.sort();// 任何一种都行

        bucket.forEach(d => {
            nums.push(d);
        })
    })
}

function sortArray(nums: number[]): number[] {
    // 1. 选择排序。
    // return selectSort(nums)
    
    // 2. 插入排序。
    return insertSort(nums);

    // 3. 归并排序
    // mergeSort(nums, 0, nums.length - 1, []);
    
    // 4. 快速排序
    // quickSort(nums, 0, nums.length - 1);

    // 5. 堆排序:选择排序的改进
    // heapSort(nums)

    // 6. 希尔排序:插入排序的改进
    // shellSort(nums)

    // 7. 冒泡排序
    // bubbleSort(nums)
    
    // 三种非比较排序:一个数该放在哪里,是由这个数本身的大小决定的
    // 8. 计数排序:快
    // return countSort(nums)
    // 9. 基数排序
    // radixSort(nums)
    // 10. 桶排序:计数排序的优化
    // bucketSort(nums);
    return nums;
};

快排这个地方的解释

image.png

基数排序的解释:

image.png

参考: