排序

121 阅读2分钟

冒泡排序

function swap(list, i, j){
  var t = list[i];
  list[i] = list[j];
  list[j]=t;
}

function bubble_sort(arr){
  for(var i=arr.length-1;i>=1;i-- ){
    for(var j=1;j<=i;j++){
      arr[j-1]>arr[j] && swap(arr, j-1, j)
    }
  }
}
var arr = [3,6,1,7,0,6,4,6,99,22];

bubble_sort(arr)
console.log(arr)

插入排序(双循环)

function insert_sort(list){

  for(var i=1;i<list.length;i++){

    var temp=list[i] // 这里很重要,取出当前项,保存起来(原理就是:外层循环取出每一项,也就是目标对象,当作即将要被插入的项)
    var j=i

    while(j>0&&list[j-1]>temp){ // 取前面的每一项,和当前项比较,直到前面项小于当前项(有个前提是:前面都是已经排好顺序的)
      list[j] = list[j-1] // 前面大,把前面赋值给后面。(前面暂时不赋值,还是等于它自己)
      j-- // 往下继续
    }
    list[j]=temp // 这里是兜底的。这个j就是空缺的一项(就是上面“还是等于它自己”的那一项),这里是把它补上。
  }
  
}
var arr=[5,4,1,3,1,3,2]
insert_sort(arr)

插入一项(单循环)

function insert(list,target){ // 前提是list已经排好序了
  var j = list.length-1
  // 从后往前遍历
  while(j>0 && list[j]>target){
    list[j+1]=list[j] // 这里的j+1就是因为数组是新增了一项,所以length要加1
    j--
  }
  list[j+1]=target
}
var arr=[1,2,3,5,8]
insert(arr,4)

快速排序(简写)

function quickSort(arr){
  var len = arr.length
  if(len<=1){
    return arr
  }
  var targetIndex = Math.floor(len/2)
  var target =  arr.splice(targetIndex,1)[0] // 从原数组删除这项
  // var target =  arr[targetIndex] // 这样不行,栈溢出,必须从原数组删除删除这项。如果不删除这项,left和right加起来等于原数组,下面就不需要concat([target])了,每次递归,数组就会增加一项
  var left=[]
  var right=[]
  arr.forEach(item=>{
    if(item<target){
      left.push(item)
    }else{
      right.push(item)
    }
  })


  return quickSort(left).concat([target], quickSort(right))
}
var list = [4,2,6,83,5,7,2,5,13,3]
quickSort(list)

快速排序(原理)

// 一次遍历就找到了基准值应该在的位置,并且调整了数组,让基准值左边的数都比他小,右边的都比他大。我们来实现下这个方法。
const partition = (arr) => {
  let x = arr[0];
  let length = arr.length;
  let i = 0;
  let j = length - 1;

  while(i < j) {
    // 先从后往前找小的, 没找到继续找
    while(i < j && arr[j] > x) {
      j--;
    }
    // 找到了,将值填入坑里, a[j]又变成了坑
    if(i < j) {
      a[i] = a[j];
    }

    // 然后从前往后找大的,没找到继续找
    while(i < j && arr[i] < x) {
      i++;
    }
    // 找到了,将值填入之前的坑里
    if(i < j) {
      a[j] = a[i];
    }
  }

  // 将基准值填入坑
  a[i] = x;

  return arr;
}

const a = [3, 6, 2, 1, 4, 5, 9, 8, 7];

// 测试下
let result = partition(a);
console.log(result);   // [1, 2, 3, 6, 4, 5, 9, 8, 7]

// 在前面思路的基础上继续递归的对基准值左右两边调用这个调整方法,就能将数组的每个数字都放到正确的位置上,这就是快速排序,这种思想叫分治法。前面调整数组的方法我们需要进行微调,让他接受开始位置和结束位置并返回基准值的位置。

const partition = (arr, left, right) => {
  let x = arr[left];// 基准值(默认就是指定的开始下标的值)
  let i = left;
  let j = right;

  while(i < j) {
    // 先从后往前找小的, 没找到继续找
    while(i < j && arr[j] > x) {
      j--;
    }
    // 找到了,将值填入坑里, a[j]又变成了坑
    if(i < j) {
      a[i] = a[j];
    }

    // 然后从前往后找大的,没找到继续找
    while(i < j && arr[i] < x) {
      i++;
    }
    // 找到了,将值填入之前的坑里
    if(i < j) {
      a[j] = a[i];
    }
  }

  // 将基准值填入坑
  a[i] = x;

  return i;
}

const quickSort = (arr, left, right) => {
  const length = arr.length;
  const start = left || 0;
  const end = right !== undefined ? right : length - 1;

  if(start < end) {
    const index = partition(arr, start, end); // 这一步,即返回了基准值位置,又排了一次序
    quickSort(arr, start, index - 1); // 调整基准值左边
    quickSort(arr, index + 1, end); // 调整基准值右边
  }

  return arr;
}

const a = [3, 6, 2, 1, 4, 5, 9, 8, 7];

// 测试下
let result = quickSort(a);
console.log(result);   // [1, 2, 3, 4, 5, 6, 7, 8, 9]

归并排序

function merge(arr1,arr2){
  var len1= arr1.length
  var len2= arr2.length
  var i= 0
  var j= 0
  var newList=[]

  while(i<len1 && j<len2){
    var cur1 = arr1[i]
    var cur2 = arr2[j]
    if(cur1<=cur2){
      newList.push(cur1)
      i++
    }else{
      newList.push(cur2)
      j++
    }
  }

  // arr1遍历完了
  if(i===len1 && j<len2){
    while(j<len2){
      newList.push(arr2[j])
      j++
    }
  }
  // 同理,arr2遍历完了
  if(j===len2 && i<len1){
    while(i<len1){
      newList.push(arr1[i])
      i++
    }
  }

  return newList
}

function mergeSort(arr){
  var len = arr.length
  if(len<=1){
    return arr
  }
  var middleIndex = Math.floor(len/2)
  var left = arr.slice(0,middleIndex)
  var right = arr.slice(middleIndex)
  return merge(mergeSort(left), mergeSort(right))
}

var list = [4,2,6,83,5,7,2,5,13,3]
mergeSort(list)

// 简单的merge方法
[链接](https://segmentfault.com/a/1190000008866524)

function merge(leftArr, rightArr){  
    var result = [];  
    while (leftArr.length > 0 && rightArr.length > 0){  
      if (leftArr[0] < rightArr[0])  
        result.push(leftArr.shift()); //把最小的最先取出,放到结果集中   
      else   
        result.push(rightArr.shift());  
    }   
    return result.concat(leftArr).concat(rightArr);  //剩下的就是合并,这样就排好序了  
}  

function mergeSort(array){  
    if (array.length == 1) return array;  
    var middle = Math.floor(array.length / 2);       //求出中点  
    var left = array.slice(0, middle);               //分割数组  
    var right = array.slice(middle);  
    return merge(mergeSort(left), mergeSort(right)); //递归合并与排序  
}  

var arr = mergeSort([32,12,56,78,76,45,36]);
console.log(arr);   // [12, 32, 36, 45, 56, 76, 78]

二分查找

  let l =0,
      r = A.length-1,
      guess // 自定义
  while(l<=r){
    guess = Math.floor((l+r)/2) // 这里是猜测在中间位置,并向下取整。
    if(A[guess]===x) return x // 找到了
    else if(A[guess]>x) r = guess-1 // 在左边区域,改变右边街
    else l = guess+1 // 或在右边,改变左边街
  }
  return -1 // 找不到,就返回-1
}

const A = [3,5,19,22,25,33,46,47,66,78]
console.log(A,88)
console.log(A,68)
console.log(A,22)