前端算法学习,包含复杂度、双指针、滑动窗口、二叉树、堆等常见题型和方法,含leetcode例题

前端算法题

学习

复杂度

  1. 时间复杂度
    代码的运行时间随着数据规模增长的趋势
  • 最好情况的时间复杂度:O(1)
  • 最坏情况的时间复杂度
  • 平均情况下的时间复杂度
  • 均摊复杂度:
  1. 空间复杂度

双指针

两个指针同向、背向移动

  1. 快慢指针
    可以用于判断链表中是否有环

  2. 背向指针

//长度为n的数组nums和目标值target,从nums选中三个整数,使它们的和与target最接近
//返回这三个数的和

function threeSumClosest(nums, target) {
    //思路:先排序,循环target-数组里的数字,此时就变成两数之和
    //两个指针分别从头和尾向中间走
    let length=nums.length;
    let res=Infinity;
    nums.sort((a,b)=>a-b);

    for(let i =0;i<length;i++){
        //依次获取其中元素
        let left=i+1;
        let right=length-1;

        //变成判断两数之和与curr最接近
        while(left<right){
            let currRes=nums[i]+nums[left]+nums[right];
            if(Math.abs(currRes-target)<Math.abs(res-target)){
                res=currRes;
            }
            
            if(currRes<target){
                left++
            }else if(currRes>target){
                right--
            }else{
                return res
            }
        }
    }
    return res
}

console.log(threeSumClosest([-1,2,1,-4],1))
//给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。
//如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。

var findLongestWord = function(s, dictionary) {
    //双指针
    let left=0;
    let right=0;
    let str='';

    //进行判断
    for(let word of dictionary){
        //左指针在等到判断的字符串s中
        //右指针在等待判断的数组的字符串中
        left = 0;
        right = 0;

        //如果字符串长度大于原字符串,直接跳过
        if(word.length>s.length){
            continue;
        }
        while(left<s.length && right <word.length){
            //如果字母相同,指针移动
            if(s.charAt(left)===word.charAt(right)){
                right++;
            }
            left++;
        }

        if (right === word.length) {
            if (!str || (word.length > str.length || (word.length === str.length && word < str))) {
                str = word;
            }
        }
    }

    return str;
};

console.log(findLongestWord("abpcplea",["ale","apple","monkey","plea", "abpcplaaa","abpcllllll","abccclllpppeeaaaa"]))

双指针题目思路:在循环前判断是否需要排序、遍历过程中通过双指针根据结果判断是否需要重新赋值

滑动窗口

窗口大小可变

//给定一个字符串 s ,请你找出其中不含有重复字符的 最长连续子字符串 的长度。


//滑动窗口题思路:右侧指针移位->判断是否符合预期->判断左指针是否需要移位->下一次循环
function lengthOfLongestSubstring(s) {
    if(s.length<=1){
        return s.length;
    }
    let left=0;
    let right=1;
    let maxLength=0;
    let temp=''
    while(right<s.length){
        temp=s.slice(left,right);
        //如果窗口的下一个元素被包含在temp中,窗口就移动
        if(temp.indexOf(s[right])!=-1){
            left++;
            continue;
        }else{
            right++;
            maxLength=Math.max(maxLength,right-left);
        }
    }
    return maxLength;
}

console.log(lengthOfLongestSubstring("abcabcbb"))

二叉树

常见问题是求任意两个节点的最小的公共祖先

二叉树问题要搞清楚排序的方式和当前树和节点的逻辑(处于同一侧or处于不同侧)

/**
 * 寻找最近公共祖先
 * @param {TreeNode} root 
 * @param {TreeNode} p 
 * @param {TreeNode} q 
 * @returns
 */
function lowestCommonAncestor(root, p, q) {
    //如果是null,已到达叶子节点的边界,没有找到目标
    //如果是p或q,说明当前节点就是最近祖先,直接返回
    if (root == null || root == p || root == q) return root;


    const left = lowestCommonAncestor(root.left, p, q);
    const right = lowestCommonAncestor(root.right, p, q);

    //如果left和right都不为空,说明p和q分别位于左右子树中,当前节点就是最近祖先
    if (left && right) return root;
    //只有一个left或right不为空,目标节点在同一侧子树中,直接返回对应子树的根节点
    return left || right;
}

涉及top、最大、最小的题目,用堆

//仓库管理员以数组 stock 形式记录商品库存表,
//其中 stock[i] 表示对应商品库存余量。
//请返回库存余量最少的 cnt 个商品余量,返回 顺序不限。


//使用堆
//1. 原生API
// return stock.sort((a, b) => a - b).slice(0, cnt);
//2.计数排序:核心在于将输入的数据转化为key,使用空间换时间
var inventoryManagement = function(stock, cnt) {
    return countingSort(stock, cnt)

};

//计数排序
const countingSort=function(stock,cnt){
    let bucket=new Array()
    let sortedIndex=0


    for(let i=0;i<stock.length;i++){
        bucket[stock[i]]=bucket[stock[i]]+1 || 1
    }


    let res=[];
    for(let j=0;j<bucket.length;j++){
      while(bucket[j]-- > 0 && sortedIndex<cnt){
        res[sortedIndex++]=j
      }

      if(sortedIndex==cnt){
        break
      }
    }
    return res
}

console.log(countingSort([0,2,3,6],2))
//给定一个整数数组 nums 和一个整数 k ,请返回其中出现频率前 k 高的元素。可以按 任意顺序 返回答案。

var topKFrequent = function(nums, k) {
  let map=new Map()
  for(let i=0;i<nums.length;i++){
    if(map.get(nums[i])){
      map.set(nums[i],map.get(nums[i])+1)
    }else{
      map.set(nums[i],1)
    }
    }
    //将map转为数组,数组中每个元素也都是一个数组
    let entries=Array.from(map.entries())

    return entries.sort((a,b)=>b[1]-a[1]).slice(0,k).map(item=>item[0])
};

console.log(topKFrequent([1,1,1,2,2,3],2))


//使用桶排序的方法

var topKFrequent = function(nums, k) {
  //桶排序,将数据划分到有限个桶中,再将桶进行排序
  //用map存储频次,用数组表达桶,频次作为数组下标
  let map=new Map()
  for(let i=0;i<nums.length;i++){
    if(map.has(nums[i])){
      map.set(nums[i],map.get(nums[i])+1)
    }else{
      map.set(nums[i],1)
    }
  }
  if(map.size<=k) return [...map.keys()]

  return bucketSort(map,k)

};

//桶排序
const bucketSort=(map,k)=>{
  let arr=[]
  let res=[]

  //map每个元素
  map.forEach((value,key)=>{
    //使用频次作为下标,将数据分配到桶中
    if(arr[value]){
      arr[value].push(key)
    }else{
      arr[value]=[key]
    }
  })
  
  for(let i=arr.length;i>=0 && res.length<k;i--){
    if(arr[i]){
      res.push(...arr[i])
    }

  }
  return res
}

console.log(topKFrequent([1,1,1,2,2,3],2))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值