题目
给你一个整数数组 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;
};
快排这个地方的解释
基数排序的解释:
参考:
- 十大排序算法介绍:leetcode.cn/problems/so…
- 堆排序原理介绍:baijiahao.baidu.com/s?id=171664…
- 堆排序JS实现:www.runoob.com/w3cnote/hea…
- 希尔排序介绍:www.runoob.com/data-struct…
- 基数排序介绍:www.runoob.com/w3cnote/rad…
- 桶排序介绍:blog.csdn.net/qq_31968791…