前端常考算法题总结-JS实现(持续更新)

2,931 阅读4分钟

实现快速排序算法

今天记录两种递归快排算法和一种非递归快排算法,我们用JavaScript来实现。

(一)空间复杂度更高(递归版)

function quickSort1(arr){
    if(arr.length <= 1){
      return arr;
    }
    var left = [];
    var right = [];
    var base = arr[0];
    for(var i = 1; i < arr.length; i++){
      if(arr[i]<=base){
        left.push(arr[i]);
      } else {
        right.push(arr[i]);
      }
    }
    return quickSort1(left).concat(base, quickSort1(right));
  }

(二)空间复杂度更低(递归版)

start和end的初始值分别为0,arr.length-1.

function quickSort2(arr,start,end){
    if(end - start < 1){return }
    const target = arr[start];
    let [l,r] = [start,end];
    while(l < r){
      while(l < r && arr[r]>=target){
        r--;
      }
      arr[l] = arr[r];
      while(l < r && arr[l] < target){
        l++;
      }
      arr[r] = arr[l]
    }
    arr[l] = target;
    quickSort2(arr,start,l-1);
    quickSort2(arr,l+1,end);
    return arr;
  }

非递归版

function quickSort3(arr,s,e){
let list = [[s,e]];
while(list.length){
  let now = list.pop();
  if(now[0] >= now[1]){
   continue;
  }
  let [l,r] = [now[0],now[1]];
  let target = arr[now[0]];
while(l < r){
  while(l < r && arr[r] >= target){
    r--;
  }
  arr[l] = arr[r];
  while(l < r && arr[l] < target){
    l++;
  }
  arr[r] = arr[l]
}
arr[l] = target;
list.push([now[0],l-1]);
list.push([l+1,now[1]]);
}
return arr
}
  • 快排是否稳定: 不稳定
  • 最优时间复杂度: O(nlog2n),即每次选取的基数都能平分整个数组。
  • 最差时间复杂度:O(n2),即每次选取的基数都是数组中的最大值或者最小值。

冒泡排序

(一)基础版

function bubbleSort(arr){
  for(var j = arr.length - 1; j >= 1; j--){
      for(var i = 0; i <= j - 1; i++){
            if(arr[i] > arr[i + 1]){
              var temp = arr[i + 1];
              arr[i + 1] = arr[i];
              arr[i] = temp;
            }
        }
  }
  return arr;
}
console.log(bubbleSort([89,67,54,12,45,5,1,4,3,2]));
//[1, 2, 3, 4, 5, 12, 45, 54, 67, 89]

(二)加强版

function bubbleSort(arr){
  for(var j = arr.length - 1; j >= 1; j--){
    var done = true;
      for(var i = 0; i <= j - 1; i++){
            if(arr[i] > arr[i + 1]){
              var temp = arr[i + 1];
              arr[i + 1] = arr[i];
              arr[i] = temp;
              done = false;
            }
        }
        if(done){
          break;
          }
  }
  return arr;
}

对比基础版和加强版,可以发现,加强版在每一轮循环中设置了一个标志位。它的作用是:当排序在某次循环中已全部完成时,就无需再次循环直至结束。例如:[1,2,4,3],在第一轮循环后就变成了[1,2,3,4],就无需进入接下来的循环了。

  • 冒泡排序是否稳定:稳定
  • 最优时间复杂度: O(n)
  • 最差时间复杂度: O(n2
  • 平均时间复杂度: O(n2

空间复杂度这里指临时变量temp所占内存.

  • 最优空间复杂度: 0(已经全部有序)
  • 最差空间复杂度: O(n)
  • 平均空间复杂度: O(1)

选择排序

function selectionSort(arr) {
    var temp;
    for(var i = 0; i < arr.length - 1; i++){
            for(var j = i + 1; j < arr.length; j++){
              if(arr[i] > arr[j]){
                temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
              }
            }
    }
    return arr;
}
  • 选择排序是否稳定:不稳定
  • 最优时间复杂度: O(n2
  • 最差时间复杂度: O(n2
  • 平均时间复杂度: O(n2

空间复杂度这里指临时变量temp所占内存.

  • 最优空间复杂度: 0(已经全部有序)
  • 最差空间复杂度: O(n)
  • 平均空间复杂度: O(1)

插入排序

function insertSort(arr){
  for(var i = 1; i <= arr.length - 1; i++){
    var temp = arr[i];
    var j = i;
    while(j > 0 && (arr[j - 1] >= temp)){
      arr[j] = arr[j - 1];
      j--;
    }
    arr[j] = temp;
  }
  return arr;
}
  • 插入排序是否稳定:稳定
  • 最优时间复杂度: O(n)
  • 最差时间复杂度: O(n2
  • 平均时间复杂度: O(n2

空间复杂度这里指临时变量temp所占内存.

  • 平均空间复杂度: O(1)

当数据量较大时,速度上,插入>选择>冒泡。

大数相乘

function fn(nums1, nums2){
            let res = new Array(nums1.length + nums2.length).fill(0);
            for(let i = 0; i < nums1.length; i++){
                for(let j = 0; j < nums2.length; j++){
                    res[i+j+1] += nums1[i] * nums2[j];
                }
            }
            for(let k = res.length-1; k > 0; k--){
                if(res[k] >= 10){
                    res[k-1] += Math.floor(res[k] / 10);
                    res[k] = res[k] % 10;
                }
            }
            return res;
        }

数组去重的四种方法

(一)直接遍历

function duplicate(numbers) {
    if(numbers.length < 2){return false;}
    var res = [numbers[0]];
    for(var i = 1; i < numbers.length; i++){
        var repeat = false;
        for(var j = 0; j < res.length; j++){
            if(numbers[i]==res[j]){
                repeat = true;
                break;
            }
        }
        if(!repeat){
            res.push(numbers[i]);
        }
    }
    return res;
}

(二)先排序

 function duplicate(numbers) {
    if(numbers.length < 2){return false;}
    numbers.sort((a,b) => a-b);
    var res = [numbers[0]];
    for(var i = 1; i < numbers.length; i++){
             if(numbers[i]!==res[res.length - 1]){
               res.push(numbers[i]);
        }
    }
    return res;
}

(三)利用对象的属性

function duplicate(numbers) {
    if(numbers.length < 2){return false;}
    var obj = {};
    var res = [];
    for(var i = 0; i < numbers.length; i++){
      if(!obj[numbers[i]]){
        obj[numbers[i]] = 1;
        res.push(numbers[i]);
      }
    }
    return res;
}

(四)利用下标查找

function duplicate(numbers) {
    if(numbers.length < 2){return false;}
    var res = [numbers[0]];
      for(var i = 1; i < numbers.length; i++){
        if(res.indexOf(numbers[i])===-1){
               res.push(numbers[i]);
        }
      }
      return res;
}

二叉树的遍历

(一)前序遍历

function preOrderTraversal(pRoot){
  if(!pRoot){
    return;
  }
  console.log(pRoot.val);
  var left = pRoot.left;
  var right = pRoot.right;
  preOrderTraversal(left);
  preOrderTraversal(right);
}

(二)中序遍历

var inorderTraversal = function(root) {
    var arr = [];
      inorder(root,arr);
      return arr;
};
function inorder(root,arr){
if(!root){return;}
       var left = root.left;
       var right = root.right;
       inorder(left,arr);
       arr.push(root.val);
       inorder(right,arr);
}

(三)后序遍历

unction postOrderTraversal(pRoot){
  if(!pRoot){
    return;
  }
  var left = pRoot.left;
  var right = pRoot.right;
  postOrderTraversal(left);
  postOrderTraversal(right);
  console.log(pRoot.val);
}

斐波那契数列用JS闭包实现

function fib(n){
  if(n < 0){
    throw new Error('BBB')
  }
  let arr = [0,1];
  function fibnaci(n){
    if(arr[n] !== undefined){
      return arr[n];
    }
    let data = fibnaci(n - 1) + fibnaci(n - 2);
    arr[n] = data;
    return data;
  }
  return fibnaci(n);
}

贪心算法,利用二分查找

小Q的父母要出差N天,走之前给小Q留下了M块巧克力。小Q决定每天吃的巧克力数量不少于前一天吃
的一半,但是他又不想在父母回来之前的某一天没有巧克力吃,请问他第一天最多能吃多少块巧克力
function total(n,mid){
      let sum = 0;
      for(let i = 1; i <=n; i++){
      sum += mid;
      mid = Math.ceil(mid/2);
    }
    return sum;
    }
    function maxChocolate(n,M){
      let first = 1;
      let last = M;
      let sum = 0;
      let mid;
      if(n === 1){return M}else{
      while(first <= last){
              mid = Math.ceil((first+last)/2);
              sum = total(n,mid);
              if(sum < M){
                first = mid + 1 ;
              }else if(sum === M){
                return mid;
              }else{
                last = mid - 1;
              }
            }
            return first-1;
      }
    }

编码

假定一种编码的编码范围是a ~ y的25个字母,从1位到4位的编码,如果我们把该编码按字典序排序,
形成一个数组如下: a, aa, aaa, aaaa, aaab, aaac, … …, b, ba, baa, baaa, baab,
baac … …, yyyw, yyyx, yyyy 其中a的Index为0,aa的Index为1,aaa的Index为2,以此类推。
编写一个函数,输入是任意一个编码,输出这个编码对应的Index.
function f(str){
    let carry = [1+25+25*25+25*25*25,1+25+25*25,1+25,1];
    let res = 0;
    for(let i = 0; i < str.length; i++){
      res += (str.charCodeAt(i) - 'a'.charCodeAt(0))*carry[i] + 1;
    }
    return res-1;
  }