leetcode面试100道题

地址:
https://leetcode.com/problemset/top-100-liked-questions/?difficulty=Easy

done 表示写代码完成了 undone 表示看了思路但是没敲代码 predone 是之前做过了,这一次只看没做 neverdone 表示没做也没看

Easy:

predone (1)两数之和——用哈希表做,键存数字,值存位置

predone (20)有效的括号——用栈实现

undone (21)合并两个有序链表——递归,非递归

done (53)最大子序和——简单的动态规划

undone(70)爬楼梯——简单动态规划(用非递归求解)

undone(101)对称二叉树——递归(左右都为空,左右中有一个为空,左右不为空但是值不相等),非递归(类似层序遍历,用两个队列实现,分别存左右孩子)

undone(104)二叉树的最大深度——递归(深度搜索),广度搜索

predone(121)买卖股票的最佳时机——以f(i)表示以第i天结尾卖出的最大收益,用一个值pre记录第i天之前的最低价格,则f(i)=d[i]-res d[i]表示第i天的价格,最后求所有f(i)的最大值即为所求

done(136)只出现一次的数字——异或求解(^)

undone(141)环形链表——判断链表是否有环,快慢指针做

。。。(没看完)

Medium

undone(2)两数相加——链表加法

done(3)无重复字符的最长子串——动态规划

undone(5)最长回文子串——动态规划,P[i][j]表示第i到第j组成的序列是回文串(是为true,不是为false),P[i,i]=true:当且仅当P[i+1,j-1] = true && (s[i]==s[j]),矩阵填充方式可以学习一下

undone(11)盛水最多的容器——双指针法,每次将较短的那根线段向内侧移动

predone(15)三数之和——先排序,然后固定一个数,另外两个数用双指针法找

undone(17)电话号码的字母组合——从每个桶里取一个元素的全部组合(递归和非递归)

undone(19)删除链表的倒数第N个节点——快慢指针

undone(22)括号生成——递归方式,当时没太看明白

predone(31)下一个排列——如: 8 4 7 6 5 3 1。从后往前找第一个不是升序排列的数字4,那么下一个比他大的数字在该位置上的数字应该大于4,故从后往前找第一个比他大的数5并交换位置,交换位置后,该位置的5已经保证了他要比之前的数字大了,因此将其后的升序序列反序。

neverdone(33)搜索旋转排序数组——二分查找(剑指offer上有类似的)

neverdone(34)在排序数组中查找元素的第一个和最后一个位置——二分查找

predone(39)组合总数——回溯法,深度优先搜索,递归。找和为target的序列,对于节点i来说,下一步是找和为target-i的序列

done(46)全排列——回溯法,递归。先将序列的每个元素和第一个元素交换,求除第一个元素外的全排列,最后将交换的元素换回去。

neverdone(48)旋转图像——找规律。先按照从左上角到右下角的斜对角线为对称线换元素。然后根据中线换元素

neverdone(49)字母异位词分组——将异位词分成一组,异位词是指字母相同但是顺序不同的词。先对每个词进行排序,那么所有异位词排序后都相同。然后用unordered_map实现,键是排序后的词,值是保存原词的vector

predone(55)跳跃游戏——贪心算法。对于每个点,求出可到达的最远距离。

predone(56)合并区间——按照区间的开头进行排序,每次合并相邻区间

done(62)不同路径——简单的动态规划

done(64)最小路径和——简单动态规划,和62类似。主要看答案中对动态规划空间复杂度的优化

predone(75)颜色分类——双指针做,分别指向第一个1和最后一个1.

neverdone(78)子集——回溯法,分支为取当前数字和不取当前数字

done(79)单词搜索——回溯法,DFS。1从空节点开始的分支要写在主函数里,之后的分支在递归函数里。2由于最终不用输出具体结果字符串,因此没保存路径。3每次搜索的下一个分支有4种,分别为上,下,左,右。4需要标记矩阵中已经走过的点,避免重复走

predone(94)二叉树的中序遍历——栈实现。口诀:不为空,压栈指左,为空,弹栈指右

neverdone(96)不同的二叉搜索树——递归,以i为根节点的二叉搜索树总和,而以i为根节点的二叉树数目为左子树的数目*右子树的数目。卡塔兰数。

neverdone(98)验证二叉搜索树——递归。递归函数规定了下一个节点的数值范围,左子树节点应该小于当前节点,右子树节点应该大于当前节点。(代码还是和自己想的不一样,还需要看一看)

done(102)二叉树的层次遍历——一个队列实现,还需要一个数值保存当前层有多少个节点

done(105)从前序和中序遍历序列构造二叉树——递归

neverdone(114)二叉树展开为链表——递归,非递归,前序遍历三种方法

done(139)单词拆分——动态规划,二维。以i结尾的词是否可以被拆分由i之前的某个以j结尾的序列是否可拆分成功和j-i这段序列是否在字典中来确定。

done(142)环形链表——快慢指针看是否有环,一个从头走,一个从相遇点走,步幅都为1,相遇点就是环的入口

done(148)合并排序链表——归并排序链表,时间复杂度nlogn,常数空间复杂度,因为是链表,合并时不需要开辟额外空间。思路:找中间节点断开,各自排序,然后合并两个有序链表。

neverdone(152)乘积最大子序列——动态规划。需要保存以每个点结尾的最大乘积和最小乘积,因为之后的负数有可能让最小的(负数)变成最大的。因此需要两个数组。优化,可以用两个变量来替代数组。

done(207)课程表——图判断是否有环(拓扑排序),两种方式,深度优先和广度优先(当时照着答案敲了一边代码,但是未单独敲过)

done(208)前缀树——节点的孩子存在unordered_map里(vector里也行,下标为字母,值为指针),节点还需要一个表明是否是单词结尾的属性(照着答案敲了一遍)

predone(215)数组中的第k个最大元素——优先队列或者partition来做

neverdone(221)最大正方形——动态规划(二维数组动态规划)。第(i,j)位置的正方形边长等于(i-1,j), (i,j-1), (i-1,j-1)位置处边长最小值+1。

neverdone(236)二叉树的最低公共祖先——没看懂

Easy:

21.合并两个有序链表
递归:https://leetcode.com/problems/merge-two-sorted-lists/discuss/9713/A-recursive-solution
非递归:(新加一个节点作为头结点)https://leetcode.com/problems/merge-two-sorted-lists/discuss/9714/14-line-clean-C%2B%2B-Solution

70.爬楼梯
https://leetcode.com/problems/climbing-stairs/discuss/25299/Basically-it’s-a-fibonacci.

101.对称二叉树
递归:https://leetcode.com/problems/symmetric-tree/discuss/33056/15-lines-of-c%2B%2B-solution-8-ms
非递归:https://leetcode.com/problems/symmetric-tree/discuss/33089/My-C%2B%2B-Accepted-code-in-16ms-with-iteration-solution

104.二叉树的最大深度
https://leetcode.com/problems/maximum-depth-of-binary-tree/discuss/34207/My-code-of-C%2B%2B-Depth-first-search-and-Breadth-first-search

141.环形链表
https://leetcode.com/problems/linked-list-cycle/discuss/44604/My-faster-and-slower-runner-solution

medium

3.无重复字符的最长子串
自己博客中有

5.最长回文子串
自己博客中有
https://leetcode.com/problems/longest-palindromic-substring/discuss/147548/Direct-c%2B%2B-DP

https://www.cnblogs.com/leavescy/p/5878336.html

11.盛水最多的容器
两线段之间形成的区域总是会受到其中较短那条长度的限制。此外,两线段距离越远,得到的面积就越大。我们在由线段长度构成的数组中使用两个指针,一个放在开始,一个置于末尾。 此外,我们会使用变量 maxareamaxarea 来持续存储到目前为止所获得的最大面积。 在每一步中,我们会找出指针所指向的两条线段形成的区域,更新 maxareamaxarea,并将指向较短线段的指针向较长线段那端移动一步。(来源于leetcode官方解析)

15.三数之和
自己博客中有

17.电话号码的字母组合
自己博客有

19.删除链表的倒数第N个节点
https://leetcode.com/problems/remove-nth-node-from-end-of-list/discuss/8843/C%2B%2B-solution-easy-to-understand-with-explanations.

22.括号生成
https://www.cnblogs.com/ariel-dreamland/p/9133613.html

33.下一个排列
自己博客有

34.在排序数组中查找元素的第一个和最后一个位置
自己博客有二分及变形的总结

39.组合总数
自己博客有

46.全排列
回溯法的博客里有
https://leetcode.com/problems/permutations/discuss/18247/My-elegant-recursive-C%2B%2B-solution-with-inline-explanation

48.旋转图像
https://www.cnblogs.com/jimmycheng/p/7199624.html
在这里插入图片描述
49.字母异位词分组
https://blog.csdn.net/hua111hua/article/details/87921568

55.跳跃游戏
自己博客有

56.区间合并
自己博客有

62.不同路径
https://leetcode.com/problems/unique-paths/discuss/22954/C%2B%2B-DP

64.最小路径和
https://leetcode.com/problems/minimum-path-sum/discuss/23457/C%2B%2B-DP
答案中有对空间复杂度的优化。二维矩阵;两个一维矩阵;一个一维矩阵。

75.颜色分类
自己博客有

78.子集
https://blog.csdn.net/camellhf/article/details/73551410

class Solution {
public:
    void dfs(vector<vector<int>> &res, vector<int> &nums, vector<int> temp, int i) {//将nums中i位以后的序列求子集,用tmp保存一条路径的结果,当走到叶节点时,将结果保存到res中
        if (i == nums.size()) {
            res.push_back(temp);
            return ;
        }

        dfs(res, nums, temp, i + 1); //不选当前数字
        temp.push_back(nums[i]);
        dfs(res, nums, temp, i + 1);//选当前数字
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> temp;

        dfs(res, nums, temp, 0);

        return res;
    }
};

79.单词搜索
自己博客有

94.二叉树的中序遍历
自己博客有

96不同的二叉搜索数
https://blog.csdn.net/qq874455953/article/details/82811832
使用两个状态来记录:
G(n):长度为n的序列的所有唯一的二叉树。
F(i,n),1<=i<=n:以i作为根节点的二叉树的数量。
G(n)就是我们要求解的答案,G(n)可以由F(i,n)计算而来。
G(n)=F(1,n)+F(2,n)+…+F(n,n) (1)
G(0)=1,G(1)=1
对于给定的一个序列1…n,我们取i作为它的根节点,那么以i作为根节点的二叉树的数量F(i)可以由下面的公式计算而来:
F(i,n)=G(i-1)*G(n-i) 1<=i<=n (2)
综合公式(1)和公式(2),可以看出:
G(n) = G(0) * G(n-1) + G(1) * G(n-2) + … + G(n-1) * G(0)

98.验证二叉搜索树
https://blog.csdn.net/hlk09/article/details/81320283

class Solution {
public:
    bool isValidBST(TreeNode * root) {
        return isValidBSTHelper(root, INT64_MIN, INT64_MAX);
    }
    
    bool isValidBSTHelper(TreeNode *root, long lower, long upper){
        if(root == NULL)    return true;
        if(root->val>=upper || root->val<=lower)    return false; //当前节点范围不对
        return isValidBSTHelper(root->left,lower,root->val)&&isValidBSTHelper(root->right,root->val,upper);//左节点应该小于当前节点,即upper为root->val.右节点应该大于当前节点
    }
};

102.二叉树的层次遍历

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if(root==nullptr) return res;
        queue<TreeNode*> q;
        q.push(root);
        int count=1;
        while (!q.empty())
        {
            vector<int> cur_val; //保存当前层的节点值
            for(int i=0;i<count;++i) //count为当前层的节点数目,如果没有count则不知道当前层应该对队列pop几次
            {
                TreeNode* tmp=q.front();
                if(tmp->left) q.push(tmp->left); //左不为空入左
                if(tmp->right) q.push(tmp->right); //右不为空入右
                q.pop();
                cur_val.push_back(tmp->val);
            }
            res.push_back(cur_val);
            count=q.size(); //由于上一层的节点全部pop了,因此队列的大小就是当前层的节点数目
        }
        return res;

    }
};

参考:
https://leetcode.com/problems/binary-tree-level-order-traversal/discuss/33709/Short-8ms-C%2B%2B-solution-with-queue

105.从前序和中序遍历序列构造二叉树

class Solution {
public:
    /* from Preorder and Inorder Traversal */
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return helper(preorder,0,preorder.size(),inorder,0,inorder.size());
    }
    TreeNode* helper(vector<int>& preorder,int ps,int pe,vector<int>& inorder,int is,int ie)
    {
        if(ps>=pe) //注意是大于等于
            return nullptr;
        int root_val = preorder[ps];  //根节点
        auto pos = find(inorder.begin()+is,inorder.begin()+ie,root_val); //在中序遍历序列里找root_val的位置
        int left_num = pos-(inorder.begin()+is); //左子树元素个数
        TreeNode* root = new TreeNode(root_val);
        root->left = helper(preorder,ps+1,ps+left_num+1,inorder,is,is+left_num);//左子树对应序列在中序和后序的起始位置
        root->right = helper(preorder,ps+left_num+1,pe,inorder,is+left_num+1,ie);//右子树对应序列在中序和后序的起始位置
        return root;

    }
};

参考:
https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/discuss/34557/My-neat-C%2B%2B-solution

https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/discuss/34555/The-iterative-solution-is-easier-than-you-think!(非递归方法,用栈实现,还没看)

114.二叉树展开为链表
自己博客有

139.单词拆分
自己博客有

142.环形链表II
https://leetcode.com/problems/linked-list-cycle-ii/discuss/44781/Concise-O(n)-solution-by-using-C%2B%2B-with-Detailed-Alogrithm-Description

148.排序链表
https://leetcode.com/problems/sort-list/discuss/46720/Share-my-C%2B%2B-concise-solutionseasy-to-understand
主要看双指针找中间点的写法,以及循环合并有序链表的写法

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(head== nullptr || head->next== nullptr) //节点为空或者只有一个节点
            return head;
        ListNode* fast = head, *slow = head;
        while (fast->next && fast->next->next) //注意这里都是fast,而不是一个fast,一个slow,因为fast合理slow一定合理。这样写是为了保证循环内的指针一定有效。
        {
            slow=slow->next;  //节点个数为基数时,slow指向中间元素,为偶数时,slow指向中间两个数靠前的那个。比如4个元素,slow最终指向第2个
            fast=fast->next->next;
        }
        fast = slow->next;
        slow->next=nullptr;  //从slow处断开,fast为第二段链表的开头
        ListNode* fir = sortList(head);  //排序第一段链表,因为slow之前断开了,因此此时head包含前半部分链表
        ListNode* sec = sortList(fast);
        return MergeList(fir,sec);
    }
    ListNode* MergeList(ListNode* fir,ListNode* sec) {
        ListNode tmp_node(0); //新建一个头结点
        ListNode *tmp = &tmp_node;
        while (fir!=nullptr && sec!= nullptr)
        {
            if(fir->val<sec->val)
            {
                tmp->next=fir;
                fir=fir->next;
            }
            else
            {
                tmp->next=sec;
                sec=sec->next;
            }
            tmp=tmp->next;
        }
        if(fir!=nullptr) tmp->next=fir;
        if(sec!=nullptr) tmp->next=sec;
        return tmp_node.next;  //注意这里是tmp_node的next,而不是tmp->next
    }
};

152.乘积最大子序列
https://blog.csdn.net/fuxuemingzhu/article/details/83211451
以i结尾的最大乘积为(以i-1结尾的最大乘积当前数,以i-1结尾的最小乘积当前数,当前数)三个数的最大数
以i结尾的最小乘积为(以i-1结尾的最大乘积当前数,以i-1结尾的最小乘积当前数,当前数)三个数的最小数

207.课程表
自己博客有

208.前缀树
自己博客有

215.数组中的第K个最大元素
自己博客有

221.最大正方形
二维动态规划,可以用二维数组或者两个一维数组或者一个一位数组保存中间结果,后两者在空间复杂度上进行了优化。
参考:https://leetcode.com/problems/maximal-square/discuss/61803/C%2B%2B-space-optimized-DP

状态转移方程:https://blog.csdn.net/u014694994/article/details/80524103

还可以只用三个变量优化为常数空间复杂度,但是会修改原输入矩阵,不用仔细看了 https://leetcode.com/problems/maximal-square/discuss/61811/Clear-C%2B%2B-solution-no-extra-space-12-ms.

猜你喜欢

转载自blog.csdn.net/aikudexue/article/details/88952069