【js】-【排序-相关】-笔记
API相关
在javascript中, sort
方法用于根据一定条件对数组元素进行排序。如果调用sort()方法时没有传递参数,则按字母顺序
对数组中的元素进行排序;如果提供一个函数参数,则根据该函数提供的顺序来对数组中的元素进行排序。
sort从小到大排序
s.sort((a,b)=>(a-b))
sort
使用的是插入排序
和快速排序
结合的排序算法。
数组长度不超过10时,使用插入排序
。
长度超过10使用快速排序
。
在数组较短时插入排序
更有效率
function swap(arr, i, j) {
[arr[i], arr[j]] = [arr[j], arr[i]]
}
在未排序的数组中找到第 k 个
最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
降序
const findKthLargest = function(nums, k) {
// 将数组逆序
const sorted = nums.sort((a,b)=> {
return b-a
})
# 取第k大的元素
return sorted[k-1]
};;
1 冒泡排序(稳定,O(n2))
- 就是从第一个元素开始,重复比较相邻的两个项,若第一项比第二项更大,则交换两者的位置;反之不动。
- 每一轮操作,都会将这一轮中最大的元素放置到数组的末尾。假如数组的长度是 n,那么当我们重复完 n 轮的时候,整个数组就有序了。
外层length次,内层length-i-1次
var sortArray = function (nums) {
for (let i = 0; i < nums.length; i++) {
for (let j = 0; j < nums.length - i -1; j++) {
if (nums[j] > nums[j + 1]) {
//swap(nums, j, j + 1);
[nums[j],nums[j+1]]=[nums[j+1],nums[j]];
}
}
}
return nums;
};
改进,加一个标志符,标志位可以帮助我们在第一次冒泡的时候就定位到数组是否完全有序,进而节省掉不必要的判断逻辑,将最好情况下的时间复杂度定向优化为 O(n)
var sortArray = function (nums) {
for (let i = 0; i < nums.length; i++) {
#1 区别在这里,我们加了一个标志位
let flag = false;
for (let j = 0; j < nums.length - i -1; j++) {
if (nums[j] > nums[j + 1]) {
#2 标志位
flag=true;
[nums[j],nums[j+1]]=[nums[j+1],nums[j]];
}
}
if(flag==false) return;
}
return nums;#3 若一次交换也没发生,则说明数组有序,直接放过
};
2 选择排序(不稳定,O(n2))
选择排序的关键字是“最小值”:循环遍历数组,每次都找出当前范围内的最小值,把它放在当前范围的头部;然后缩小排序范围,继续重复以上操作,直至数组完全有序为止。
- 先遍历1-n范围内最小的,和第一个位置的交换
- 再从2-n范围内最小的,和第二个交换
- 全部遍历完,就排好序了
var sortArray = function (nums) {
for (let i = 0; i < nums.length; i++) {
let minIndex = i;
// 已排序区间 [0, i) ,未排序区间 [i+1 , len)
# 遍历 i+1 之后的元素找到最小元素的索引
for (let j = i + 1; j < nums.length; j++) {
if (nums[j] < nums[min]) {
minIndex = j;
}
}
if(minIndex!=i)
[nums[i],nums[minIndex]]=[mums[minIndex],mums[i]];
}
return nums;
};
3 插入排序(稳定,O(n2))
插入排序的核心思想是“找到元素在它前面那个序列中的正确位置”
var sortArray = function (nums) {
// 从下标为1的元素开始选择合适的位置插入,因为下标为0的只有一个元素,默认是有序的
// 已排序区间 [0, i) ,未排序区间 [i , len)
// 将 nums[i] 插入到区间 [0, i) 使之成为有序数组
for (let i = 1; i < nums.length; i++) {
// 从右往左遍历
for (let j = i; j > 0 && nums[j] < nums[j - 1]; j--) {
// 只要nums[j]比前一个元素nums[j-1]小,就交换这两个元素
[nums[j],nums[j-1]]=[nums[j-1],nums[j]];
}
}
return nums;
};
4 归并排序(稳定,O(nlog(n)))
function mergeSort(arr) {
// 处理边界情况
if(arr.length <= 1) return arr
# 计算分割点
const mid = Math.floor(arr.length/ 2)
#1 递归分割左、右子数组,然后合并为有序数组
const leftArr = mergeSort(arr.slice(0, mid))
const rightArr = mergeSort(arr.slice(mid,arr.length))
#2 合并左右两个有序数组
arr = mergeArr(leftArr, rightArr)
return arr
}
# 这部分就是合并有序数组
function mergeArr(arr1, arr2) {
# 初始化两个指针,分别指向 arr1 和 arr2
let i = 0, j = 0
const res = []
# 合并两个子数组,哪个小,先添加哪个
while(i < arr1.length && j < arr2.length ) {
if(arr1[i] < arr2[j]) {
res.push(arr1[i])
i++
} else {
res.push(arr2[j])
j++
}
}
# 哪个还剩余,直接拼接剩余部分
if(i<len1) {
return res.concat(arr1.slice(i))
} else {
return res.concat(arr2.slice(j))
}
}
5 快速排序(不稳定,O(nlog(n)))
调用代码
var sortArray = function (nums) {
const N = nums.length;
quickSort(nums, 0, N - 1);
return nums;
};
快排
function quickSort(nums, left, right) {
if (right <= left) {
return;
}
let pIndex = partition(nums, left, right);
quickSort(nums, left, pIndex - 1);
quickSort(nums, pIndex + 1, right);
}
以基准值为轴心,划分左右子数组的过程
function partition(nums, left, right) {
let pivot = nums[left];
// 为两个数组分别定义一个指针
let i = left + 1;
let j = right;
while (true) {
while (i <= right && nums[i] <= pivot) {
i++;
}
while (j > left && nums[j] > pivot) {
j--;
}
if (i >= j) {
break;
}
swap(nums, i, j);
i++;
j--;
}
swap(nums, left, j);
return j;
}
6 堆排序(不稳定,O(nlog(n)))
/**
* 堆排序
* @param {*} nums
* @returns
*/
function sortArray(nums) {
const N = nums.length;
// 建堆 找到第一个非叶子节点,向上遍历
for (let i = Math.floor(N / 2 - 1); i >= 0; i--) {
// 对 i位置节点 调整堆
heapify(nums, i, N);
}
// 排序过程 每一次循环都找出当前最大值(根节点),数组长度减一
for (let i = N - 1; i > 0; i--) {
// 根节点与最后一个节点交换位置(将此时最大元素移动到数组末尾)
swap(nums, 0, i);
// 对 此时的根节点 调整堆 最后的元素不用参与调整
heapify(nums, 0, i);
}
return nums;
}
/**
* 对节点i进行 调整堆
* 满足:i节点以下的子堆是一个大顶堆
* 调整范围 [i, length)
* @param {*} nums
* @param {*} i
* @param {*} length
*/
function heapify(nums, i, length) {
// 将i节点的值保存,这个过程就是给temp找到一个合适的位置
let temp = nums[i]
// j指向i的左孩子节点
let j = 2 * i + 1;
// 循环遍历[i, length)
while (j < length) {
if (j + 1 < length && nums[j] < nums[j + 1]) {
// 父节点有右孩子 并且 左孩子小于右孩子 将j指向右孩子
j++;
}
// 此时 j 指向 i 的孩子节点中较大的那个节点
if (temp < nums[j]) {
// 如果 父节点小于 j节点
// 交换i,j的元素
swap(nums, i, j);
// 将i和j都下移一位
i = j;
j = 2 * i + 1;
} else {
// 父节点 大于 孩子节点中最大的元素,就退出循环
break;
}
}
}