前端算法题
学习
复杂度
- 时间复杂度
代码的运行时间随着数据规模增长的趋势
- 最好情况的时间复杂度:O(1)
- 最坏情况的时间复杂度
- 平均情况下的时间复杂度
- 均摊复杂度:
- 空间复杂度
双指针
两个指针同向、背向移动
-
快慢指针
可以用于判断链表中是否有环 -
背向指针
//长度为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))