秋招面试被问到数组排序算法,我只答的出快排,然后就没有然后了. 果然还是得吃点苦头才有动力学习,这几个排序见过无数次,但是一直看不进去 都是基于自己的理解写的笔记内容,希望对不懂的小伙伴有些帮助,欢迎指点问题
冒泡排序
数组分成两部分,每次在未排序的元素中通过一一比较并交换相邻的位置,将一个最大的或者最小的值移动到指定位置,重复进行这个步骤得到完整排序
平均时间复杂度为 O(n^2),最好是O(n)(已经排好序的情况),最差是O(n^2)(完全逆序)
稳定的排序算法 (稳定:存在相等元素时,它们之间的相对顺序不变)
缺点是它的交换次数较多,每一轮都可能进行多次交换。
js
function bubbleSort(arr) {
let len = arr.length
for(let i = 0; i < len - 1; i++){
for(let j = 0; j < len - i - 1; j++){
if(arr[j] > arr[j + 1]){
let temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
return arr
}
// 示例
let arr = [5, 3, 8, 4, 2];
console.log(bubbleSort(arr)); // 输出 [2, 3, 4, 5, 8]
选择排序
数组分成两部分,每次在未排序的元素中得到一个最大值或者最小值,通过交换位置将这个放到已排序队列的末尾
平均时间复杂度为 O(n^2),最好是O(n)(已经排好序的情况),最差是O(n^2)(完全逆序)
不稳定
缺点是它的比较次数是固定的,无论数组是否已经部分有序,都需要进行 n(n-1)/2 次比较。
js
function selectionSort(arr){
let len = arr.length
for(let i = 0; i < len - 1; i++){
let min = i
for(let j = i + 1; j < len; j++){
if(arr[j] < arr[min]){
min = j
}
}
//交换位置
let temp = arr[min]
arr[min] = arr[i]
arr[i] = temp
}
return arr
}
// 示例
var arr = [5, 3, 8, 4, 2];
console.log(selectionSort(arr)); // 输出 [2, 3, 4, 5, 8]
插入排序
将数组分成两部分,在未排序的元素中选择索引值最小的那一个,将它和已排序部分一一比较,插入到合适的位置
平均时间复杂度为 O(n^2),最好情况下(数组已经有序)的时间复杂度为 O(n),最坏情况下(数组完全逆序)的时间复杂度也为 O(n^2)。
稳定的排序算法
js
function insertionSort(arr) {
let len = arr.length
for(let i = 1; i < len; i++){
let cur = arr[i]
let j = i - 1
while(j >= 0 && arr[j] > cur){
arr[j + 1] = arr[j]
j--
}
arr[j + 1] = cur
}
return arr
}
var arr = [5, 3, 8, 2, 1];
console.log(insertionSort(arr)); // 输出 [1, 2, 3, 5, 8]
快排
选定一个基准值,将数组分为两部分,大于基准值和小于基准值,然后对这两部分的数组递归此步骤
平均时间复杂度为 O(nlogn),最坏情况下(数组已经有序或完全逆序)的时间复杂度为 O(n^2)。
不稳定的排序算法
js
function quickSort (arr) {
if(arr.length <= 1) {
return arr
}
let pivotIndex = Math.floor(arr.length / 2);
let pivot = arr.splice(pivotIndex, 1)[0];
let left = []
let right = []
for(let i = 0; i < arr.length; i++){
if(arr[i] < pivot) {
left.push(arr[i])
}
else{
right.push(arr[i])
}
}
return quickSort(left).concat([pivot],quickSort(right))
}
console.log(quickSort([23,43,53,2,3,5332,34]));
归并排序
将数组分成两部分,每部分做到部分内排序,然后在合并两个数组,递归此步骤
时间复杂度始终为 O(nlogn),无论是在最好、最坏还是平均情况下。
稳定的排序算法
缺点是需要额外的空间来存储临时数组,因此空间复杂度为 O(n)。
js
/*
* @Author: xiangyue_li
* @Date: 2023-07-16 13:38:03
* @LastEditors: xiangyue_li
*/
function mergeSort(arr) {
if (arr.length <= 1) {
return arr;
}
//将数组分成两个子数组
let mid = Math.floor(arr.length / 2);
let left = arr.slice(0, mid);
let right = arr.slice(mid);
//递归调用归并排序算法
return merge(mergeSort(left),mergeSort(right));
}
function merge(left,right){
let result = []
let i = 0,j = 0
//比较两个子数组,元素较小的放入新数组
while(i < left.length && j < right.length) {
if(left[i] <= right[j]){
result.push(left[i])
i++
}
else{
result.push(right[j])
j++
}
}
//将剩余元素放入新的数组中
while(i < left.length){
result.push(left[i])
i++
}
while(j < right.length){
result.push(right[j])
j++
}
return result
}
var arr = [5, 3, 8, 2, 1];
console.log(mergeSort(arr)); // 输出 [1, 2, 3, 5, 8]
堆排序
构建大顶堆(大顶堆就是根节点是所有节点中最大的那一个)
构建好大顶堆之后,遍历数组,每次将索引为0(根节点)的元素与未排序数组的最后一位交换
然后堆的节点数减一,重新调整大顶堆,获得剩余数组中的最大值,重复上述步骤
时间复杂度为 O(nlogn),并且具有原地排序的特点,即不需要额外的空间来存储临时数组
不稳定排序算法
js
function heapSort(arr) {
// 构建最大堆
buildHeap(arr);
console.log(arr)
// 排序
for (var i = arr.length - 1; i > 0; i--) {
swap(arr, 0, i); // 将堆顶元素与堆的最后一个元素交换,大顶堆中根元素是最大的,这里将最大的元素换到数组末尾
console.log(arr)
heapify(arr, 0, i); // 调整堆使其满足堆的性质,注意这里len = i,就是说每次将最大元素交换到末尾之后,这个元素不再参与堆的调整
}
return arr;
}
function buildHeap(arr) {
var len = arr.length;
var lastNonLeafIndex = Math.floor(len / 2) - 1; //拿到最后一个非叶子节点的索引值,完全二叉树中,叶子结点是所有结点的一半(向下取整)
for (var i = lastNonLeafIndex; i >= 0; i--) {
heapify(arr, i, len);
}
}
function heapify(arr, i, len) {
var largest = i; // 假设当前节点是最大的
var left = 2 * i + 1; // 左子节点索引
var right = 2 * i + 2; // 右子节点索引
// 如果左子节点存在且大于当前节点,则更新最大值索引
if (left < len && arr[left] > arr[largest]) {
largest = left;
}
// 如果右子节点存在且大于当前节点,则更新最大值索引
if (right < len && arr[right] > arr[largest]) {
largest = right;
}
// 如果最大值索引不等于当前节点索引,则交换位置,并继续向下调整
if (largest !== i) {
swap(arr, i, largest);
heapify(arr, largest, len);
}
}
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
var arr = [5, 3, 8, 2, 1];
console.log(heapSort(arr)); // 输出 [1, 2, 3, 5, 8]