算法

292 阅读4分钟

冒泡及优化

常规冒泡是两层循环,外层控制循环的几遍,第几遍就找出第几小(or大)的元素。内层循环用来比较相邻的两个数,不符合顺序的交换。

function Bubbling (arr) {
  var temp;
  var count = 0;
  for(var i=0; i<arr.length-1; i++) {
    for(var j=0; j<arr.length-1-i; j++) {
      if(arr[j]>arr[j+1]){
        temp = arr[j];
        arr[j] = arr[j+1];
        arr[j+1] = temp;
      }
    }
  }}

优化1:如果前一遍循环没有发生交换,说明整个数组已经排好序,就不用进行后面的比较了。所以设立一个标识位,用来控制第一层循环的条件。

function BubblingPlus (arr) {
var temp;
var count = 0;
var flag = true;
for(var i=0; i<arr.length-1&&flag; i++) {
  flag = false;
  for(var j=0; j<arr.length-1-i; j++) {
    if(arr[j]>arr[j+1]){
      temp = arr[j];
      arr[j] = arr[j+1];
      arr[j+1] = temp;
      flag = true;
    }
  }
} 
}

优化2: 记录每遍循环时最后一次交换发生的位置。即该位置之后的元素已经有序,下一遍循环的时候,内层遍历到该位置就可以停止了。用来做第二层循环的条件

function BubblingPluss (arr) {
  var temp;
  var count = 0;
  var flag = true;
  var post = arr.length-1;
  for(var i=0; i<arr.length-1&&flag; i++) {
    flag = false;
    for(var j=0, last=post; j<last; j++) {
      if(arr[j]>arr[j+1]){
        temp = arr[j];
        arr[j] = arr[j+1];
        arr[j+1] = temp;
        flag = true;
        post = j
      }
}}}

选择排序及优化

常规的选择排序两层循环。外层循环第i遍选出第i小的元素,放在第i位。内层循环逐个比较元素,找出第i小元素的坐标,如果该坐标不等于i,那么交换这两个元素

function choose(a) {
  var min;
  var temp;
  for(var i=0; i<a.length-1; i++){
    min = i;
    for(var j=i+1; j<a.length; j++) {
      if(a[j]<a[min]) min = j;
    }
    if(min!=i) {
      temp = a[i];
      a[i] = a[min];
      a[min] = temp;
    }
  }
}

优化: 常规的是每遍只选第i小的,优化版可以把第i大的一起找出来。外层循环次数减半。但是要注意的是,有可能刚开始假设的最小值的位置刚好是最大值,最大值的位置刚好是最小值。交换两次的话等于没有交换。所以需要处理一下特殊情况

function choose(a) {
  var min, max;
  var temp;
  for(var i=0,j=a.length-1; i<(a.length)/2; i++,j--){
    min = i;
    max = j
    //第一种方法是判断最初假设的最大值如果小于最小值,就交换元素
    if(a[min]>a[max]) {
        temp = a[min];
        a[min] = a[max];
        a[max] = temp;
    }
    for(var p=i; p<j+1; p++) {
      if(a[p]<a[min]) min = p;
      if(a[p]>a[max]) max = p;
      count++;
    }
    if(min!=i) {
      temp = a[i];
      a[i] = a[min];
      a[min] = temp;
    }
    if(max!=j) {
      temp = a[j];
      a[j] = a[max];
      a[max] = temp;
    }
  } 
}
function choose(a) {
  var min, max;
  var temp;
  for(var i=0,j=a.length-1; i<(a.length)/2; i++,j--){
    min = i;
    max = j
    for(var p=i; p<j+1; p++) {
      if(a[p]<a[min]) min = p;
      if(a[p]>a[max]) max = p;
      count++;
    }
    if(min!=i) {
      temp = a[i];
      a[i] = a[min];
      a[min] = temp;
    }
    //第二种是先交换最初假设的最小元素和实际的最小元素。
    如果最大元素的坐标刚好等于最初假设的最小元素的坐标,
    那么由于刚刚假设的最小元素和实际最小元素交换了,
    所以现在最大元素的坐标应该是交换前的最小元素的坐标。
    if(max == i) {
        max = min
    }
    if(max!=j) {
      temp = a[j];
      a[j] = a[max];
      a[max] = temp;
    }
  } 
}

插入排序及优化

常规插入的思想是。假设第一个数是有顺序的,将后面的数一次插入到前面的有序的序列中。外层循环控制将每个数插入进去。内层循环控制该数的插入位置

//找到插入位置之后再集体移动
function insert(a) {
  var temp;
  for(var i=1; i<arr.length; i++){
    for(var j=0; j<i; j++){
      if(a[i]<a[j]) break;
    }
    temp = a[i];
    for(var k=i; k>=j; k--){
      a[k] = a[k-1];
    }
    a[j] = temp
  }
}
//边找位置边移动
function insert1(a) {
  var temp;
  for(var i=1; i<arr.length; i++){
    temp = a[i]
    for(var j=i-1; j>=0&&temp<a[j]; j--){
     a[j+1] = a[j]
    }
    a[j+1] = temp;
  }
}

优化: 查找的时候可以用二分法查找

快排

var quickSort = function(arr) {

    if (arr.length <= 1) { return arr; }

    var pivotIndex = Math.floor(arr.length / 2);

    var pivot = arr.splice(pivotIndex, 1)[0];

    var left = [];

    var right = [];

    for (var 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));

};

归并排序

function mergeSort(arr) {
    const len = arr.length
    // 处理边界情况
    if(len <= 1) {
        return arr
    }   
    // 计算分割点
    const mid = Math.floor(len / 2)    
    // 递归分割左子数组,然后合并为有序数组
    const leftArr = mergeSort(arr.slice(0, mid)) 
    // 递归分割右子数组,然后合并为有序数组
    const rightArr = mergeSort(arr.slice(mid,len))  
    // 合并左右两个有序数组
    arr = mergeArr(leftArr, rightArr)  
    // 返回合并后的结果
    return arr
}
  
function mergeArr(arr1, arr2) {  
    // 初始化两个指针,分别指向 arr1 和 arr2
    let i = 0, j = 0   
    // 初始化结果数组
    const res = []    
    // 缓存arr1的长度
    const len1 = arr1.length  
    // 缓存arr2的长度
    const len2 = arr2.length  
    // 合并两个子数组
    while(i < len1 && j < len2) {
        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))
    }
}