1. 三数之和(双指针)****
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例 1 :
输入: nums = [-1,0,1,2,-1,-4]
输出: [[-1,-1,2],[-1,0,1]]
思路:去重
(1) 排序,若序后数组首位>0 or末尾<0 or 数组不满3个元素,直接返回空数组
(2) 遍历数组
2.1 在遍历时定义left=i+1和right指针,确保每次都是从i后遍历完了完整数组
2.2去重: while(left<right)大循环去重, 如果i+left+right===0,
(1)while小循环去重:left<right && nums[left]===num[left+1]
Left++
(2)while小循环去重:left<right && nums[right]===num[right-1]
Right—
(3)最后left++;right--;
2.3其他情况
else if(nums[i]+nums[left]+nums[right]<0)
left++;
else if(nums[i]+nums[left]+nums[right]>0)
right--;
2. 无重复字符的最长子串(滑动窗口)
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串的长度。****
输入 : s = "abcabcbb"
输出 : 3
解释 : 因为无重复字符的最长子串是 "abc",所以其长度为 3。
思路:双指针+滑动窗口(map实现)map 的清空函数:map.clear()
left = 0;right = left+1 ;判断条件:map.has(nums[right])
(1) 如果有,则当前窗口包含重复字母
Map.clear(), count=right-left
更新窗口最大长度:result = Math.max(result,count)
重置滑动窗口到left的左边:left++; right = left+1;
(2) 如果没有,加大窗口:map.set(nums[right],0)
count=right-left+1
更新窗口最大长度:result = Math.max(result,count)
Right++
3. 最长连续数列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) **的算法解决此问题。
示例 1 :
输入: nums = [100,4,200,1,3,2]
输出: 4
解释: 最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
思路:使用set()对象存储滑动窗口
(1)如果set.has(nums[i]-1),则存入nums[i],更新count和result值
(2)若set中没有,则set.clear(),再重新加入当前nums[i],并更新count和result值
var longestConsecutive = function(nums) {
if(nums.length === 1)
return 1;
else if(!nums)
return 0;
else{
nums.sort((a,b)=>a-b);
// console.log(('nums',nums));
let count=0;
let result = 0;
let i =0;
let set = new Set();
for(let i=0;i<nums.length;i++){
if(set.has(nums[i]-1)){
set.add(nums[i]);
count = set.size;
result = Math.max(count,result);
}
else{
set.clear();
set.add(nums[i]);
count = set.size;
result = Math.max(count,result);
}
}
return result
}
};
4. 找到字符串中所有字母异位词
给定两个字符串 s 和 p,找到 s ****中所有 p ****的 异位词 ****的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 ****指由相同字母重排列形成的字符串(包括相同的字符串)。
示例 1:
输入 : s = "cbaebabacd", p = "abc"
输出 : [0,6]
解释 :
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词
思路:创建与p大小相同的窗口,比较s中每个窗口中每个字母出现个数是否与P中字母出现个数相同(s[i].charCodeAt()-‘a’.charCodeAt()),
最后两个窗口比较:p.toString()===s_window.toString()
5. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1 :
输入: n = 2
输出: 2
解释: 有两种方法可以爬到楼顶。
-
1 阶 + 1 阶
-
2 阶
思路:dfn(n) = dfs(n-1)+dfs(n-2)
爬到n阶的方式数=爬到n-1阶的方式数+爬到n-2阶的方式数
优化:设置一个数组,用来标记递归入参是否重复
l 如果一个状态(递归入参)是第一次遇到,那么可以在返回前,把状态及其结果记到一个 memo 数组中。
l 如果一个状态不是第一次遇到(memo 中保存的结果不等于 memo 的初始值),那么可以直接返回 memo 中保存的结果
6. 和为k的子数组
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 **k ******的子数组的个数 **。子数组是数组中元素的连续非空序列。
输入: nums = [1,1,1], k = 2
输出: 2
输入: nums = [1,2,3], k = 3
输出: 2
思路:前缀和:
什么是前缀和?
-----s[0]=0,
----s[i+1]=nums[0]+nums[1]+⋯+nums[i]
设置一个map:用于存放每个前缀和的值(键)和这个值出现的次数
``
cnt.get(sj - k) ?? 0:这行代码尝试从cnt Map对象中获取键为(sj - k)的值,如果这个键不存在,则使用??运算符返回0
7. ****反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
思路:明确指针指向
定义三个指针:next/pre/cur
var reverseList = function(head) {
let cur= head;
let pre = null;
let nxt = null;
if(!head || !head.next)
return head;
while(cur){
nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
};
8. 买卖股票最佳时机
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
输入: [7,1,5,3,6,4]
输出: 5
参数:
最小 买入值let minbuy=prices[0]
最大 利润值let result=0
var maxProfit = function(prices) {
let result = 0;
let minbuy = prices[0]
for(let p of prices){
result = Math.max(result,p-minbuy);
minbuy = Math.min(p,minbuy)
}
return result;
};
9. 二叉树的前序/中序/后序问题(什么序由根结点的位置决定)
所有的二叉树问题在递归遍历时的return与递归函数在同一级
思路:递归 前序: result.push(root.val)
postorder(root.left);
postorder(root.right);
中序: postorder(root.left); 后序:postorder(root.left);
result.push(root.val) postorder(root.right);
postorder(root.right); result.push(root.val)
var postorderTraversal = function(root) {
const result = [];
const postorder = (root)=>{
if(!root){
return ;
}
postorder(root.left);
postorder(root.right);
result.push(root.val)
}
postorder(root)
return result
};
10. 二叉树的最大深度
从根结点到最远叶子节点的最长路径上的节点数
var maxDepth = function(root) {
if(!root)
return 0;
else{
let left = maxDepth(root.left);
let right = maxDepth(root.right);
return Math.max(left,right)+1;
}
};
11. 扁平化数组
两个核心函数:
(1) Array.isArray() 是JavaScript中的一个静态方法,用于确定传递给它的值是否是一个数组。这个方法返回一个布尔值:如果传递的值是一个数组,则返回true;否则返回false。
(2) concat 被用来“展平”数组的一个层级。concat 可以接收任意数量的参数,并将它们合并到调用它的数组后面,返回一个新的数组。当使用扩展运算符 ... 与 concat 结合时,可以一次性将多个数组的元素合并到一起。
var flat = function (arr, n) {
// n代表被展开的层数
while(n>0 && arr.some(Array.isArray)){
arr = [].concat(...arr);
n--;
}
console.log(arr);
return arr;
};
12. 全排列
给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。
示例 1 :
输入: nums = [1,2,3]
输出: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
var permute = function(nums) {
const path =[];
const res = [];
const used = new Array(nums.length).fill(false);
const dfs = ()=>{
if(path.length === nums.length){
res.push(path.slice());
return
}
for(let i = 0;i<nums.length;i++){
if(used[i])continue;
path.push(nums[i]);
used[i]=true;
dfs();
path.pop();
used[i] = false;
}
}
dfs();
return res;
};
13. 快排
function quickSort(nums){
if(nums.length<2) //递归出口
return nums
let index = nums[0]; //将数组第一位元素作为基准值
let right = []; //用于存放数组中比基准值大的元素
let left = []; //用于存放数组中比基准值小的元素
for(let i = 1;i<nums.length;i++){
if(nums[i]<index)
left.push(nums[i])
else
right.push(nums[i])
}
return [...quickSort(left),index,...quickSort(right)] //注意左右返回的数组需要。。。解构
}
14. ****柯里化-如何实现一个柯里化
实现: 利用递归
function add(){
let args = Array.from(arguments);//将伪数组转化成真数组
// console.log(args);
let inner = function(){ //这里定义Inner必须采用这种形式,用箭头函数会不对
args.push(...arguments)//arguments中的伪数组被解构成元素加入arg[]
return inner;//柯里化关键
}
inner.toString = function(){
return args.reduce((pre,cur)=>pre+cur)
}
return inner;//柯里化
}
console.log(+add(1,2,3)(1,2))
手撕函数:
实现 instanceof: (注意一直循环原型链中的每一个原型)
function myinstanceof(L, R) {
while (L !== null) {
if (L.proto === R.prototype){
return true;
}
L = L.proto;
}
return false;
}
手写call:
Function.prototype.mycall = function(context,...args){
context = context || window; //原指针
// 如果 context 参数为空,则默认为 window 对象
const fn = Symbol(); // 使用 Symbol 函数创建一个唯一的标识符
context[fn] = this; // 将原始函数存储为 context 对象的属性
const result = contextfn;
// 调用函数并将结果存储在 result 变量中
delete context[fn]; // 删除 context 对象的属性
return result;
}
promise.all写法
Promise.race写法
---手撕浅拷贝
function qkb(obj){
let newObj = {};
for( let i in obj){
if(obj.hasOwnProperty(i))
//这步是关键,注意hasOwnProperty(i)的传参以及是obj整个大对象调用的
newObj[i] = obj[i];
}
return newObj;
}
-----手撕深拷贝:
function skb(obj){
let newObj = {};
for(let key in obj){
if(obj.hasOwnProperty(key)){
//关键
if(obj[key] instanceof Object){
newObj[key] = skb(obj[key])
}
else
newObj[key] = obj[key]
}
}
return newObj
}
从后端获取数据并生成一个下拉列表
async function fetchOptions() {
return ['aaa', 'bbb', 'ccc'];
}
let pro = fetchOptions();
const xl = () => {
const (valuearr, setValuearr) = useState([]);
useEffect(() => {
pro.then(data => {
setValuearr(data)
})
}, [])
return (
<select value={ } >
(valuearr.map((item,i) =>{
return ()
}))
)
}