八大基本排序

163 阅读6分钟

本文都是使用javascript书写,学习用时两个月,希望能帮助大家。

1.冒泡排序

冒泡排序比较相邻元素,每次都将较大值放在后面,每轮循环结束可以找出一个最大值 平均时间复杂度O(n^2)

let arr = [9,7,8,2,1,4,5]
//使用冒泡排序实现从小到大
let temp
let flag
for(let i = 0; i < arr.length - 1; i++) {
  flag = true
  for(let j = 0; j < arr.length -1 - i; j++) {
    //相邻元素进行比较,将较大的元素放在后面
    if(arr[j] > arr[j+1]) {
      flag = false
      temp = arr[j]
      arr[j] = arr[j+1]
      arr[j+1] = temp 
    }
  }
  //可以优化之处,如果某一轮循环结束,没有出现元素交换顺序,则代表此数组已经有序
  if(flag) {
    break
  }
}

2.选择排序

在每一轮循环中,找出一个最大值,标记该最大值,循环结束后进行交换。 与冒泡排序法进行对比,此排序方式不再每一次发现一个更大值就立即交换。 平均时间复杂的O(n^2)

let arr = [9,7,8,6,4,3,2,5]
let max,maxIndex
for(let i = 0; i < arr.length - 1; i++) {
  max = arr[i]
  maxIndex = i
  for(let j = i + 1; j < arr.length; j++) {
    if(arr[j] > max) {
      max = arr[j]
      maxIndex = j
    }
  }
  arr[maxIndex] = arr[i]
  arr[i] = max
}

3.插入排序

将无序队列插入有序队列,先取第一个元素,让第二个元素与之前元素比较排序成为有序队列。 然后取第三个元素插入到前两个元素组成的有序队列中 平均时间复杂的O(n^2)

let arr = [1,3,0,5,-2,15,10]
let insertSort = (arr) => {
  for(let i = 1; i < arr.length; i++) {
    let insertVal = arr[i] //待插入元素值
    let insertIndex = i - 1 //待插入索引
    //插入一个元素,即将它后面的元素向后移动
    while(insertIndex >= 0 && insertVal < arr[insertIndex]) {
      arr[insertIndex + 1] = arr[insertIndex]
      insertIndex--
    }
    arr[insertIndex + 1] = insertVal
  }
}
insertSort(arr)

4.希尔排序

相对于插入排序进行比较,如果是升序排列,当最小的数要插入数组中时,他要将原来已经有序的队列每一个元素往后移动一位。针对这种情况,希尔排序可以优化,希尔排序把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。 平均时间复杂的O(nlogn)

let arr = [7,8,9,4,5,6,1,2,3,0]
for(let gap = Math.floor(arr.length / 2); gap > 0; gap = Math.floor(gap / 2)) {
  for(i = gap; i < arr.length; i++) {
    let inseartVal = arr[i]
    let inseartIndex = i - gap
    while(inseartIndex >= 0 && arr[inseartIndex] > inseartVal) {
      arr[inseartIndex + gap] = arr[inseartIndex]
      inseartIndex -= gap
    }
    arr[inseartIndex + gap] = inseartVal
  }
}

5.快速排序

快速排序是对冒泡排序的一种优化,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列 平均时间复杂的O(nlogn)

let arr = [8,7,9,5,6,4,2,3,1,0,7,5,0]
let temp
let quickSort = (arr,left,right) => {
  let pivot = arr[Math.floor((left + right)/2)]
  let l = left
  let r = right
  
  while(l < r) {
    while(arr[l] < pivot) {
      l++
    }
    while(arr[r] > pivot) {
      r--
    }
    if(l >= r) {
      break
    }
    temp = arr[l]
    arr[l] = arr[r]
    arr[r] = temp
    if(arr[l] == pivot) {
      r--
    }
    if(arr[r] == pivot) {
      l++
    }
  }
  if(l === r) {
    l += 1
    r -= 1
  }
  if(left < r) {
    quickSort(arr,left,r)
  }
  if(l < right) {
    quickSort(arr,l,right)
  }
}
quickSort(arr,0,arr.length-1)

6.归并排序

归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之) 平均时间复杂的O(nlogn)

let arr = [8,7,9,6,2,1,3,4,5,0]
let temp = []
//归并排序,将问题分解然后再组合回去。
let merge = (arr,left,mid,right,temp) => {
  let i = left
  let j = mid + 1
  let t = 0
  //将某一轮,左右两边有序的队列合并成一个有序队列并返回
  while(i <= mid && j <= right) {
    if(arr[i] < arr[j]) {
      temp[t] = arr[i]
      t++
      i++
    } else {
      temp[t] = arr[j]
      t++
      j++
    }
  }
  while(i <= mid) {
    temp[t] = arr[i]
    t++
    i++
  }
  while(j <= right) {
    temp[t] = arr[j]
    t++
    j++
  }
  //将数组返回,这里非常关键!   并不是将整个数组进行返回,而是将当前合并的两个数组进行返回
  let leftIndex = left
  t = 0
  while(leftIndex <= right) {
    arr[leftIndex] = temp[t]
    t++
    leftIndex++
  }
}
let mergeSort = (arr,left, right, temp) => {
  let mid = Math.floor((left + right) / 2)
  if(left < right) {
    mergeSort(arr,left,mid,temp)
    mergeSort(arr,mid + 1, right,temp)
    merge(arr,left,mid,right,temp)
  }
}
mergeSort(arr,0,arr.length -1,temp)

7.桶排序

将所有待比较数据统一为同样的数位长度,数位较短的数前面补0,从最低位开始依次排序 平均时间复杂的O(nlogn)

let arr = [53, 3, 542, 748, 14, 214]
//确定进行多少轮,由此数组最大值数据长度决定
let max = arr[0]
for(let i = 1; i < arr.length - 1; i++) {
  if(max < arr[i]) {
    max = arr[i]
  }
}
let length = max.toString().length //确定大循环次数
let tongs
let tongsIndex
let t
for(let i = 0, n = 1; i < length; i++, n*=10) {
  tongs = []
  tongsIndex = []
  for(let i = 0; i < 10; i++) {
    tongs[i] = []
    tongsIndex[i] = 0
  }
  //将元素放入桶中
  for(let i = 0; i<arr.length; i++) {
    let num = Math.floor(arr[i]/n) % 10
    tongs[num].push(arr[i])
    tongsIndex[num]++
  }
  //将元素从桶中取出
  t = 0
  for(let i = 0; i < tongs.length; i++) {
    if(tongsIndex[i] != 0) {
      for(let j =0; j< tongsIndex[i];j++) {
        arr[t] = tongs[i][j]
        t++
      }
    }
  }
}

8.堆排序

堆排序是利用堆这种数据结构进行排序 平均时间复杂的O(nlogn)

let arr = [4,6,8,5,9]
let adjustHeap = (arr, i, length) => {
  let temp = arr[i];//先取出当前元素的值,保存在临时变量
  //开始调整
  //说明
  //1. k = i * 2 + 1 k 是 i结点的左子结点
  for(let k = i * 2 + 1; k < length; k = k * 2 + 1) {
    if(k+1 < length && arr[k] < arr[k+1]) { //说明左子结点的值小于右子结点的值
      k++; // k 指向右子结点
    }
    if(arr[k] > temp) { //如果子结点大于父结点
      arr[i] = arr[k]; //把较大的值赋给当前结点
      i = k; //!!! i 指向 k,继续循环比较
    } else {
      break;//!
    }
  }
  //当for 循环结束后,我们已经将以i 为父结点的树的最大值,放在了 最顶(局部)
  arr[i] = temp;//将temp值放到调整后的位置
}
let heapSort = (arr) => {
  let temp
  //完成我们最终代码
  //将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
  for(let i = Math.floor(arr.length / 2)-1; i >=0; i--) {
    adjustHeap(arr, i, arr.length);
  }    
  /*
  * 2).将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
&emsp;&emsp;3).重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
  */
  for(let j = arr.length-1;j >0; j--) {
    //交换
    temp = arr[j];
    arr[j] = arr[0];
    arr[0] = temp;
    adjustHeap(arr, 0, j); 
  }
}
heapSort(arr)