力扣笔记

54 阅读8分钟

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--;

image.png

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++

image.png

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()

image.png

5. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

 示例 1

输入: n = 2

输出: 2

解释: 有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶

  2. 2 阶

思路:dfn(n) = dfs(n-1)+dfs(n-2)   

爬到n阶的方式数=爬到n-1阶的方式数+爬到n-2阶的方式数

优化:设置一个数组,用来标记递归入参是否重复

l  如果一个状态(递归入参)是第一次遇到,那么可以在返回前,把状态及其结果记到一个 memo 数组中。

l  如果一个状态不是第一次遇到(memo 中保存的结果不等于 memo 的初始值),那么可以直接返回 memo 中保存的结果

image.png 6. 和为k的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 **k ******的子数组的个数 **。子数组是数组中元素的连续非空序列。

输入: nums = [1,1,1], k = 2

输出: 2

输入: nums = [1,2,3], k = 3

输出: 2

思路:前缀和:

什么是前缀和?

-----s[0]=0,

image.png ----s[i+1]=nums[0]+nums[1]+⋯+nums[i]

设置一个map:用于存放每个前缀和的值(键)和这个值出现的次数

image.png ``

cnt.get(sj - k) ?? 0:这行代码尝试从cnt Map对象中获取键为(sj - k)的值,如果这个键不存在,则使用??运算符返回0

image.png 7. ****反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

image.png 思路:明确指针指向

定义三个指针:next/pre/cur

image.png 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. ****柯里化-如何实现一个柯里化

实现: 利用递归

image.png 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写法

image.png


 

Promise.race写法

image.png


---手撕浅拷贝

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 ()

               

            }))

           

           

       

    )

}