排序算法

nothing-sy2023-11-14算法 排序算法

选择排序

思路

  1. 初始状态下,所有元素未排序,即未排序(索引)区间为 [0,n-1]。
  2. 选取区间 [0,n-1]中的最小元素,将其与索引 0处元素交换。完成后,数组前 1 个元素已排序。
  3. 选取区间 [1,n-1]中的最小元素,将其与索引 1 处元素交换。完成后,数组前 2 个元素已排序。
  4. 以此类推。经过 n-1 轮选择与交换后,数组前 n-1 个元素已排序。
  5. 仅剩的一个元素必定是最大元素,无须排序,因此数组排序完成。

记住选择排序只有在选择区间找到最小值以后才会置换元素(即在外层循环置换),而不是每次内循环都置换

代码实现

/* 选择排序 */
function selectionSort(arr) {
 const len = arr.length;
 for (let i = 0; i < len - 1; i++) {
   let minIndex = i;
   for (let j = i + 1; j < len; j++) {
     if (arr[j] < arr[minIndex]) {
       minIndex = j;
     }
   }
   [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
 }
 return arr;
}

// 示例
const arr = [64, 34, 25, 12, 22, 11, 90];
const sortedArr = selectionSort(arr);
console.log("排序后的数组:", sortedArr);

冒泡排序

思路

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

代码实现

function bubbleSort(arr) {
  const len = arr.length;
  for (let i = 0; i < len - 1; i++) {
    for (let j = 0; j < len - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
}


插入排序

思路(类似于抽牌然后插入到已经有序的牌中)

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置(这个内循环类似冒泡,只不过被插入的元素不参与位置替换,而是把大于被插入元素的数往后移动、冒泡)
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

代码实现

function insertionSort(arr) {
    for (let i = 1; i < arr.length; i++) {
        let temp = arr[i];
        let j = i - 1;
        while (j >= 0 && arr[j] > temp) {
            arr[j + 1] = arr[j]; //当前要排序的temp已经存储,将已排序区域大于temp的数往后排
            j--;
        }
        arr[j + 1] = temp;//最终不符合内循环条件后的j+1位置就是temp的插入位置(比temp大的数据已经都往后移动了1位,并占据了temp原有的索引位置)
      }
      return arr;
    }

快速排序

思路

  1. 从数列中挑出一个元素,称为“基准”(pivot)。
  2. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的元素可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
  4. 递归到最底部时,数列仅有一个元素,该元素就是有序的。

代码实现

TIP

下面的代码由codegeex AI提供,借助了额外的数组以简化快排的复杂程度。

// 写一个快速排序方法
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数组
            left.push(arr[i]);
        } else { // 比基准数大的放在right数组
            right.push(arr[i]);
        }
    }
    return quickSort(left).concat([pivot], quickSort(right)); // 递归调用
}

TIP

以下是较复杂的写法,但是节省额外空间

//交换函数
 function swap(nums,left,right){
  let temp = nums[left];
  nums[left] = nums[right];
  nums[right] = temp;
 }   

function quickSort(nums,left,right){
// 子数组长度为 1 时终止递归
if (left >= right) return;
let i = left,j = right;
while(i < j){
//下面>=nums[left]是以最左侧元素为基准值,(不可以以中间元素为基准值,因为当i,j相遇的时候无法保证是在基准值左边还是右边,交换基准位置就变成了无序)
  while(i < j && nums[j] >= nums[left]) {//必须加上i < j条件,如果一直找不到比基准值更小,则i,j相遇,后面的while循环不会执行,此时交换i,j其实是同一个元素交换。最终交换基准值和i,j交界索引
    j-=1
  }

  while(i < j && nums[i] <= nums[left]) {
    i+=1
  }
swap(nums,i,j)
}
swap(nums,left,i)
quickSort(nums,left,i-1)
quickSort(nums,i+1,right)
return nums
}

const arr = [1,7,2,5,2,9,88]

console.log(quickSort(arr,0,arr.length-1))

归并排序

思路

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的递归思想,先递归分解数组,再合并数组。

代码实现

function mergeSort(arrs){
  if(arrs.length === 1){
    return arrs
  }
  let mid = Math.floor((arrs.length - 1)/2);
  let left = arrs.slice(0,mid+1);
  let right = arrs.slice(mid+1);

 
  return sort(mergeSort(left),mergeSort(right))
}

function sort(left,right){
let  res = [];
let l = 0,r = 0
while(l<left.length&& r < right.length) {
if(left[l] < right[r]){
  res.push(left[l])
  l++
} else {
  res.push(right[r])
  r++
}
}
 if(l<left.length){
  res.push(...left.slice(l))
} else {
  res.push(...right.slice(r))
}
return res
}

var arr = [1,2,8,4,2,3,6,99,5,24,56]
var r= mergeSort(arr)
console.log(r)
Last Updated 2025-02-20 03:36:19