算法速成记录贴Day0

为了一个面试,挑战三天速通算法(不是

因为时间有限,只看高频题目,共计112道。(忽略其中的Hard)

三天时间,一天38道,早中晚各12道,每道题控制在10分钟内。

......阿门。

一.链表篇(13)

LeetCode19: 删除链表的第K个节点

  • 技巧:双指针
  • 技巧::添加dummy结点 
    ListNode* dummyHead = new ListNode(0);
  • 思路:双重指针间的距离为n,后指针指到nullptr时,删除前指针指向节点。


牛客Top200高频: 删除有序链表中重复出现的元素

  • 思路:无需双指针,判断前后两个节点是否相等,相等则删除后继续判断,不相等才next。

LeetCode25: K个一组翻转链表  Hard Pass

剑指 Offer II 024. 反转链表

  • 技巧:存三个节点
  • 思路:存三个节点后,cur->next=pre; ,后移动指针。

LeetCode24: 两两交换链表中的节点

  • 技巧:添加dummy结点 
  • 技巧:存三个节点(不需要四个
  • 思路:递归 高逼格 没时间看
  • 思路:迭代 注意顺序
    temp.next = node2
    node1.next = node2.next
    node2.next = node1

LeetCode142: 环形链表II

  • 技巧:双指针(快慢指针)
  • 思路:每走一次,fast指针比slow快1,若有环,两指针一定相遇。


LeetCode160: 相交链表

  • 题外话:这个题居然是Easy哎(周迅脸
  • 提示:走过你来时的路~~~
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *A = headA, *B = headB;
        while (A != B) {
            A = A != nullptr ? A->next : headB;
            B = B != nullptr ? B->next : headA;
        }
        return A;
    }
};

作者:Krahets
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists/solutions/12624/intersection-of-two-linked-lists-shuang-zhi-zhen-l/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode234: 回文链表

  • 技巧:快慢指针
  • 思路1:复制链表值到数组列表中,利用双指针判回文。
  • 思路2:反转后半部分列表

LeetCode206: 翻转链表 == 剑指 Offer II 024. 反转链表
LeetCode445: 两数相加II

  • 技巧:巧妙使用已经解决的题目。
  • 思路1:翻转链表
  • 思路2:利用栈,先压后构造新链表

LeetCode21: 合并两个有序链表

  • 思路1:递归
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == NULL) {
            return l2;
        }
        if (l2 == NULL) {
            return l1;
        }
        if (l1->val <= l2->val) {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        }
        l2->next = mergeTwoLists(l1, l2->next);
        return l2;
    }
};

作者:腐烂的橘子
链接:https://leetcode.cn/problems/merge-two-sorted-lists/solutions/103891/yi-kan-jiu-hui-yi-xie-jiu-fei-xiang-jie-di-gui-by-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 思路2:设定一个哨兵节点,不断调整其指针pre。

LeetCode23: 合并K个排序链表  Hard Pass
LeetCode148: 排序链表

  • 技巧:自顶向下
  1. 找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中点可以使用快慢指针的做法,快指针每次移动 2 步,慢指针每次移动 1 步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。

  2. 对两个子链表分别排序。

  3. 将两个排序后的子链表合并,得到完整的排序后的链表。可以使用「21. 合并两个有序链表」的做法,将两个有序的子链表进行合并。

return merge(sortList(head, mid), sortList(mid, tail));

LeetCode143: 重排链表

  • 思路1:线性表,利用线性表可以下标访问的特点。
     vector<ListNode *> vec;
     ListNode *node = head;
     while (node != nullptr) {
            vec.emplace_back(node);
            node = node->next;
        }
    
  • 思路2:寻找链表中点 + 链表逆序 + 合并链表。

面试题16.25: LRU缓存 太难 Pass

二.位运算&递归(2)

LeetCode143: 只出现一次的数字

  • 技巧:异或

LeetCode22: 括号生成

  • 技巧:DFS,具体来说是回溯,回溯=DFS+状态重置
  • 思路:如果左括号数量不大于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。
    class Solution {
        void backtrack(vector<string>& ans, string& cur, int open, int close, int n) {
            if (cur.size() == n * 2) {
                ans.push_back(cur);
                return;
            }
            if (open < n) {
                cur.push_back('(');
                backtrack(ans, cur, open + 1, close, n);
                cur.pop_back();
            }
            if (close < open) {
                cur.push_back(')');
                backtrack(ans, cur, open, close + 1, n);
                cur.pop_back();
            }
        }
    public:
        vector<string> generateParenthesis(int n) {
            vector<string> result;
            string current;
            backtrack(result, current, 0, 0, n);
            return result;
        }
    };
    
    作者:力扣官方题解
    链接:https://leetcode.cn/problems/generate-parentheses/solutions/192912/gua-hao-sheng-cheng-by-leetcode-solution/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

三.字符串(3)

剑指offer 67: 把字符串转换成整数

  • 提示:细节题,难点主要在越界判断上。
class Solution {
public:
    int strToInt(string str) {
        bool sign = true;   //默认为正数
        //先舍弃开头可能存在的空格
        int i = 0;
        while(i < str.size() && str[i] == ' ') i++;
        //接着判断首个字符是否为正负号
        if(str[i] == '-') {
            sign = false;  //该字符串片段为负数
            i++;          //移至下一个字符接着判断
        }
        else if(str[i] == '+') i++;   //如果首个字符为‘+’则sign已经默认为true而无须更改,直接移动到下一位即可
        //下面开始对非正负符号位进行判断
        if(str[i] < '0' || str[i] > '9') return 0;  //如果第一个正负号字符后的首个字符就不是数字字符(也可能第一个字符就不是正负号),那么直接返回0
        int res = 0;   //这里res用的int型,需要更加仔细考虑边界情况,但如果用long的话可以省去一些麻烦
        int num;      //用来单独存储单个字符转换而成的数字
        int border = INT_MAX / 10;  //用来验证计算结果是否溢出int范围的数据
        while(i < str.size()){
            if(str[i] < '0' || str[i] > '9') break;  //遇到非数字字符则返回已经计算的res结果
            if(res > border || res == border && str[i] > '7')  //注意这句话要放在字符转换前,因为需要验证的位数比实际值的位数要少一位
//这里比较巧妙的地方在于 1. 用低于int型数据长度一位的数据border判断了超过int型数据长度的值 2. 将超过最大值和低于最小值的情况都包括了
            return sign == true ? INT_MAX : INT_MIN;
            //开始对数字字符进行转换
            num = str[i] - '0';
            res = res * 10 + num;
            i++;
        }
        //最后结果根据符号添加正负号
        return sign == true ? res : -res;
    }
};

LeetCode76: 最小覆盖子串 Hard Pass

LeetCode3: 无重复字符的最长子串

  • 技巧:利用vector<char>实现滑动窗口。
  • 什么是滑动窗口?其实就是一个队列,比如 abcabcbb。现在,队列 abc 满足题目要求。而当a再次入队,队列就变成了 abca,不满足题目要求。所以,我们需要移动这个队列,把队列的左边的元素移出。
    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            vector<char> unset;
            int maxLen = 0;
            for(int i = 0; i < s.size(); ++i){
                vector<char>::iterator it = find(unset.begin(),unset.end(),s[i]);
                if(it != unset.end() ) unset.erase(unset.begin(),it+1);
                unset.push_back(s[i]);
                maxLen = max(maxLen,int(unset.size()));
            }
            return maxLen;
        }
    };

四.栈和队列(6)

LeetCode20: 有效的括号

  • 思路:栈,重点是如何实现。
class Solution 
{
public:
    bool isValid(string s) 
    {
        stack<char>S;
        for (char ch : s)
        {
            //如果当前栈为空,但是ch是右括号应该返回false
            if (S.empty() && ch == ')' || S.empty() && ch == ']' || S.empty() && ch == '}')
                return false;
            //如果ch是左括号,应该将其入栈,等待匹配
            if (ch == '(' || ch == '[' || ch == '{')
                S.push(ch);
            
            else
            {
                //如果输入的右括号和当前栈顶元素匹配,那么弹出栈顶元素
                if (S.top() == '(' && ch == ')' || S.top() == '[' && ch == ']' || S.top() == '{' && ch == '}')
                    S.pop();
                //当前栈顶元素与ch不匹配,直接返回false
                else
                    return false;
            }
        }
        //如果栈为空,返回true
        if (S.empty())
            return true;
        else
            return false;
    }
};

LeetCode84: 柱状图中的最大矩形 单调栈 但Hard Pass

LeetCode42: 接雨水 单调栈 但Hard Pass

......算了,不Pass了,来学习一下单调栈吧。

主要逻辑:围绕栈顶元素。

// 栈不为空且当前柱子高度<栈顶索引对应的柱子高度

// 说明栈顶元素的右边界已经确定,就是索引为i的柱子(不含)

// 此时将栈顶元素出栈,栈顶矩形左边界为栈顶元素下面的索引(首个小于栈顶)

class Solution {
    public int largestRectangleArea(int[] heights) {
        /*
        只做单调栈思路:参考"编程狂想曲"思路比较好理解
        1.核心思想:求每条柱子可以向左右延伸的长度->矩形最大宽度;矩形的高->柱子的高度
            计算以每一根柱子高度为高的矩形面积,维护面积最大值
        2.朴素的想法:遍历每一根柱子的高度然后向两边进行扩散找到最大宽度
        3.单调栈优化:因为最终的目的是寻找对应柱子height[i]右边首个严格小于height[i]的柱子height[r]
            左边同理找到首个严格小于height[i]的柱子height[l]
            维护一个单调递增栈(栈底->栈顶),那么每当遇到新加入的元素<栈顶便可以确定栈顶柱子右边界
            而栈顶柱子左边界就是栈顶柱子下面的柱子(<栈顶柱子)
            左右边界确定以后就可以进行面积计算与维护最大面积
        时间复杂度:O(N),空间复杂度:O(N)
        */
        // 引入哨兵
        // 哨兵的作用是 将最后的元素出栈计算面积 以及 将开头的元素顺利入栈
        // len为引入哨兵后的数组长度
        int len = heights.length + 2;
        int[] newHeight = new int[len];
        newHeight[0] = newHeight[len - 1] = 0;
        // [1,2,3]->[0,1,2,3,0]
        for(int i = 1; i < len - 1; i++) {
            newHeight[i] = heights[i - 1];
        }
        // 单调递增栈:存储每个柱子的索引,使得这些索引对应的柱子高度单调递增
        Stack<Integer> stack = new Stack<>();
        // 最大矩形面积
        int res = 0;
        // 遍历哨兵数组
        for(int i = 0; i < len; i++) {
            // 栈不为空且当前柱子高度<栈顶索引对应的柱子高度
            // 说明栈顶元素的右边界已经确定,就是索引为i的柱子(不含)
            // 此时将栈顶元素出栈,栈顶矩形左边界为栈顶元素下面的索引(首个小于栈顶)
            while(!stack.empty() && newHeight[i] < newHeight[stack.peek()]) {
                // 栈顶索引出栈并记录
                int pop = stack.pop();
                // 计算出栈顶元素矩形的宽度如(0,1,2)->[1,2,1],两边都不包含
                // 因此右索引-左索引-1=矩形宽度
                int w = i - stack.peek() - 1;
                // 栈顶索引对应的柱子高度就是矩形的高度
                int h = newHeight[pop];
                // 计算矩形面积
                int area = w * h;
                // 维护矩形面积最大值
                res = Math.max(res, area);
            }
            // 每当弹出一个索引就计算一个矩形面积
            // 直到当前元素>=栈顶元素(或者栈为空)时,栈顶柱子的右边界还没确定
            // 因此当前元素索引入栈即可
            stack.push(i);
        }
        return res;
    }
}

LeetCode155: 最小栈

  • 技巧:同步辅助栈
    class MinStack {
        stack<int> x_stack;
        stack<int> min_stack;
    public:
        MinStack() {
            min_stack.push(INT_MAX);
        }
        
        void push(int x) {
            x_stack.push(x);
            min_stack.push(min(min_stack.top(), x));
        }
        
        void pop() {
            x_stack.pop();
            min_stack.pop();
        }
        
        int top() {
            return x_stack.top();
        }
        
        int getMin() {
            return min_stack.top();
        }
    };
    
    作者:力扣官方题解
    链接:https://leetcode.cn/problems/min-stack/solutions/242190/zui-xiao-zhan-by-leetcode-solution/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode232: 用栈实现队列

  • 技巧:双栈
  • 提示:实现in2out函数
    class MyQueue {
    private:
        stack<int> inStack, outStack;
        void in2out() {
            while (!inStack.empty()) {
                outStack.push(inStack.top());
                inStack.pop();
            }
        }
    
    public:
        MyQueue() {}
        void push(int x) {
            inStack.push(x);
        }
    
        int pop() {
            if (outStack.empty()) {
                in2out();
            }
            int x = outStack.top();
            outStack.pop();
            return x;
        }
    
        int peek() {
            if (outStack.empty()) {
                in2out();
            }
            return outStack.top();
        }
    
        bool empty() {
            return inStack.empty() && outStack.empty();
        }
    };
    
    作者:力扣官方题解
    链接:https://leetcode.cn/problems/implement-queue-using-stacks/solutions/632369/yong-zhan-shi-xian-dui-lie-by-leetcode-s-xnb6/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指offer59 - I: 滑动窗口最大值 Hard Pass

参考链接

LeetCode算法题高频整理(精华篇)_翻滚的小@强的博客-CSDN博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值