leetcode刷题

120 阅读8分钟

Array数组

03 数组中的重复数字 04 二维数组中的查找 以上两题都是双指针;

offer05 替换空格

用到了js里面的split和join;计算空格存在,然后给出新长度,利用双指针实现替换;

118&119 杨辉三角

118那里学会 row[]=rec[][]+rec[][] 这种跨越行的运算;
119学会了滚动算法,减少了空间,但是内存运算还是不变的,本身还是一种遍历,通过focus在所需的数组上,避免别的数组空间占用;

169多数元素

排序一下,最中间那个就是了,不过速度有点慢;
还学会了搞对象,hhh;
学会了摩尔投票法,简称同归于尽法,不过这种我认知是只可以用再有一个或两个主元素的情况下;

229 求众数

升序排序后用lastIndexOf法从右向左遍历;
原来摩尔投票法也可以用在这里,就是else if写得有点麻烦,希望能发现简写的办法;目前可知摩尔投票法运用再1/2和1/3这类简单情况适用;

274.引用H

var hIndex = function(citations) {
    citations.sort((a,b)=>a-b) //如果b-a就是降序排列
    let h = citations.length;
    for(let i=0; i<citations.length;i++){
        if(citations[i]<h){
            h--;
        }
    };
    return h;
};

因为例子里面有个3,被引用篇数的数目也是3,所以卡住了,画图比较好想出来。答案思路等于是一个正方形在坐标轴上不断缩小,直到缩小到范围内最大;
本来想的是降序排列,但是好像图像上无法直观想象出来;
这个解法用的h--,follow了for循环。

275.引用H 2

同上; 标记说可以用二分法做。二分法适用在循环中减小复杂度 O(lgn)

var hIndex = function (citations) {
    let left = 0;
    let right = citations.length - 1;
    let N = citations.length;
    let res = 0
    while (left <= right) { 
        let mid = left + right >> 1 //Math.floor()
        if (citations[mid] >= N - mid) { //能够想出来这个条件就很难了
            res = N - mid;
            right =  mid - 1;
        } else {
            left = mid + 1
        }
    }
    return res
};

217&&219&&220 重复数

219
用哈希表做很快;
双指针基本原理
双指针轻松但是运行久;且要记得遇到两个for循环时候,外面的for要包住里面的for;
Math.abs()是取绝对值;
220
想尝试能不能继续Hash表,但是看到一个python写的需要另建一个有序集合,用上ceiling和floor,这种做法不够简洁,遂放弃;
桶排序 之前见过这种方法解了一个hard题;
滑动窗口 感觉上就是在Hash表+滚动算法;

55&&45 跳跃游戏

55
动态规划&&贪心算法
这个视频好给力,介绍了动态规划里面的top-down和bottom-up,bottom-up原理和贪心算法很像,而且也比top-down简单常见;
一句话总结bottom-up收获就是:在遍历i的时候,设定了maxJump,在它的数值范围之内再遍历是否有跳跃可能性;
有一个贪心算法的疑问:为什么就肯定成功路径里面一定包含倒二个呢?//ans:会逆着开始倒二个只是再扩大可能性,任何能跳到最后一个位置上的点一定也能跳掉倒二个,而能跳到倒二个也能跳到最后一个;
45
这题是top-down的贪心算法,疑问:可不可以用bottom-up的贪心算法?
// ans:不可,top-down是为了求最优解;

121-123&&128 买卖股票最佳时机

121
第一个想法就是双指针,暴力两层for,但是超出时限了。看到别人的思路,两层for可以精简的;
用dp存储前i天的最大利润,底层思路和上面一样,也是对比取最小,空间和时间占用都比上面大;
因为dp在这里用到的意义不大,所以第三种可以把dp给省略掉;
122
通俗易懂的贪心算法
简而言之,使得每步看起来都是当前的最优解;
通俗易懂的动态规划
一共三个组成部分:初始值,方程式和最终值;
斐波那契数列属于一维的动态规划;
123

    let buy1=-prices[0],buy2=-prices[0];
    let sell1=0,sell2=0;

    for(i=1;i<n;i++){
        buy1=Math.max(buy1,-prices[i]);
        sell1=Math.max(sell1,buy1+prices[i])
        buy2=Math.max(buy2,sell1-prices[i])
        sell2=Math.max(sell2,buy2+prices[i])
    }

    return sell2;


此题对于动态规划的疑惑:每到i天,遇到更优的,buy1和sell1也会改变;可是结果是要选出两个最大的利润,buy2和sell2是如何不被buy1和sell1的变化影响的呢?//我做了图表,也把每一步的b1s1b2s2求解,但是还是不能够理解为什么b2s2可以不受影响//ans如下

        fstBuy: 在该天第一次买入股票可获得的最大收益 
        fstSell: 在该天第一次卖出股票可获得的最大收益
        secBuy: 在该天第二次买入股票可获得的最大收益
        secSell: 在该天第二次卖出股票可获得的最大收益
        分别对四个变量进行相应的更新, 最后secSell就是最大
        收益值(secSell >= fstSell)


贪心算法的解法

287 寻找重复数

可以排序数列或者用桶做;

贪心算法

322 玩硬币

这里贪心算法在[1,4,5],n=12的状况下会算错吧?还是要用DP做比较好;

1217 玩筹码

比较奇数偶数谁更多;题目有点难看懂;

55 跳跃游戏

动态规划

509 斐波拉契数列

62 不同路径

需要两个for循环,属于二维动态规划;

121 售出股票最佳时机

动态规划到每个阶段,求当前最大利润;

70 爬楼梯

就是一道典型的斐波拉契数列;

279 完全平方数

这题真的绝了,是322玩硬币的进阶版;

offer10-1 斐波那契数列

记得按题目要求取模,不然会报错;

offer10-2 小青蛙跳格子

终点的全部可能性=倒数第一跳一步到终点+倒数第二只两步到终点;

offer11 旋转数组最小数字

String

28.实现strStr()

学会了用substring()进行多个对比;

14.最长公共前缀

break之后在for外面取for里面的数;

58 最后一个单词的长度

没有考虑到结尾是空格的情况,需要用到两个while;

387 字符串中的第一个唯一一个字符

    str.charAt(0) // H 给字母
    str.charCodeAt(0)//a的unicode值为97

用新数列计算出现的字母;

383 赎金信

哈希表;

344 反转字符串

for(let left=0,right=n-1;left<right;left++,right--){//双指针;
        [s[left],s[right]]=[s[right],s[left]];//学到了[]=[]置换;
    }

链表List

先从基础的单链表做起

offer06 从头到尾打印链表

遍历用上unshift;复杂一点就用push加上reverse(自定义func);
链表和数组的区别是一个面试的必考题!

21 合并两个有序链表


学到了l1.next=mergeTwoLists(l2,l1.next)

offer 18 删除链表中的节点


链表题目画图做思路会很清晰,这里就是pre.next=cur.next,跳过cur;

offer 22 链表中倒数第k个节点


我的思路:把链表一共几个节算出来;然后length-k+1算出倒数第k个所在的位置;接下来遍历去找;
大牛的思路:1快慢指针快的那个指针跑快k步,直到快指针为null则慢指针是想要的答案。2栈方法把所有链表存入栈stack.push(),接着从屁股开始出栈k个stack.pop()。

var getKthFromEnd = function(head, k) {
    let p = head, q = head;
    let i = 0;
    while (p) {
        if (i >= k) {
            q = q.next;
        }
        p = p.next;
        i++;
    }
    return i < k ? null : q; //如此的简洁!
};

二叉树

offer07 重建二叉树

二叉树理论基础
TreeNode \ buildTree

Stack

20 有效的括号

我的思路:取第一个和最后一个括号,如果不符合()[]{},就return false; 报错!没有想到(){[]}的情况。
大佬的思路: 用map存储括号(前半部分作为值,后半部分作为键); 把括号入栈,一旦有遍历到后半部分的括号(map可以get到),则出栈; 直到stack的长度为零,return true;

offer 9 用两个栈实现队列

我的思路:直接创立一个栈,然后进行pop和unshift操作;
大佬的思路:需要有两个栈来模拟入栈和出栈;

155 最小栈

我的思路:对stack进行for循环,找最小值;
官方的思路:用辅助栈,在每次push新元素时,同样往辅助栈中push当前最小值;

496 下一个更大元素I

我的思路:遍历nums1,在nums2中再遍历找到对应的nums1中数值的位置,然后往下再套遍历找比它大的家伙???
神仙思路:运用stack和hash表,把每个nums2的item都push到stack里,push的过程里,如果item比栈顶元素大,则淘汰栈顶元素,把栈顶元素对应的右边较大值形成键值(栈顶元素:item)对存储到hash表中;这么结束后,剩余在stack中的item都是找不到右边较大值的,都以item:-1的键值对存到hash表里面;最后,把nums1对应hash.get到的所有值都push到ans里面,则解;

71 简化路径

我的思路:试图用哈希表存储‘/’‘.’‘..’,然后遍历过程中,遇到这些家伙就……?
大牛的思路:先将str以‘/’split开来;遇到‘’‘.’就跳过;遇到‘..’并且stack不为空,就pop栈顶元素;其余情况直接push;最后把得到的stack用‘/’组合起来,在前头再加上个‘/’,return即可。关于‘’的处理,这里非常巧妙的给它忽略掉,这样,join的时候前后都不会出现‘/’了。