zoukankan      html  css  js  c++  java
  • leetcode字节跳动专题(35题)

    挑战字符串

     无重复字符的最长子串

    给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

    示例 1:

    输入: "abcabcbb"
    输出: 3 
    解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
    

    示例 2:

    输入: "bbbbb"
    输出: 1
    解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
    

    示例 3:

    输入: "pwwkew"
    输出: 3
    解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
         请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

    题解

    利用set的无重复性。我们记录两个指针l和r,分别表示左右边界,移动r指针,如果在set集合里面出现了s[r],则移动左指针,直到set里面没有s[r],重复上诉步骤;

    参考代码:

     1 class Solution {
     2 public:
     3     int lengthOfLongestSubstring(string s) {
     4         set<char> st;
     5         int l=0,r=0,ans=0;
     6         while(r<s.size())
     7         {
     8             if(!st.count(s[r]))
     9             {
    10                 st.insert(s[r]);
    11                 ans=max(ans,(int)st.size());
    12                 r++;
    13             }
    14             else st.erase(s[l++]);
    15         }
    16         return ans;  
    17     }
    18 };
    C++

    最长公共前缀

    编写一个函数来查找字符串数组中的最长公共前缀。

    如果不存在公共前缀,返回空字符串 ""

    示例 1:

    输入: ["flower","flow","flight"]
    输出: "fl"
    

    示例 2:

    输入: ["dog","racecar","car"]
    输出: ""
    解释: 输入不存在公共前缀。
    

    说明:

    所有输入只包含小写字母 a-z 。

    题解:就是按照题意匹配就行了.(O(n^2))

    参考代码:

     1 class Solution {
     2 public:
     3     string longestCommonPrefix(vector<string>& strs) {
     4         string ans;
     5         if(strs.empty())
     6             return ans;
     7         for(int i=0;i<strs[0].size();i++)
     8         {
     9             char c=strs[0][i];
    10             for(int j=1;j<strs.size();j++)
    11             {
    12                 if(i>=strs[j].size() || strs[j][i]!=c)
    13                     return ans;
    14             }
    15             ans+=c;
    16         }
    17         return ans;
    18     }
    19 };
    C++

    字符串的排列

     

    给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。

    换句话说,第一个字符串的排列之一是第二个字符串的子串。

    示例1:

    输入: s1 = "ab" s2 = "eidbaooo"
    输出: True
    解释: s2 包含 s1 的排列之一 ("ba").
    

    示例2:

    输入: s1= "ab" s2 = "eidboaoo"
    输出: False
    

    注意:

    1. 输入的字符串只包含小写字母
    2. 两个字符串的长度都在 [1, 10,000] 之间

     题解:考虑是全排列,那么只要在一段长为s1.length()的区间内s2的每个字符的数量和s1的每个字符的数量相同,那么返回true,否则,没有出现过,返回false;

    参考代码:

    class Solution {
    public:
        bool judge(int a[])
        {
            for(int i=0;i<26;++i)
            {
                if(a[i]!=0) return false;
            }
            return true;
        }
        
        bool checkInclusion(string s1, string s2) 
        {
            if(!s1.length() || !s2.length() || s1.length()>s2.length())
                return false;
            int a[30]={0},flag=0;
            
            for(int i=0;i<s1.length();++i)
                a[s1[i]-'a']--,a[s2[i]-'a']++;
            
            for(int i=s1.length();i<s2.length();++i)
            {
                if(judge(a))
                {
                    flag=1;
                    break;
                }
                a[s2[i-s1.length()]-'a']--;
                a[s2[i]-'a']++;
            }
            if(judge(a)) flag=1;
            
            if(flag) return true;
            else return false;
        }
    };
    C++

     字符串相乘

     

    给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

    示例 1:

    输入: num1 = "2", num2 = "3"
    输出: "6"

    示例 2:

    输入: num1 = "123", num2 = "456"
    输出: "56088"

    说明:

    1. num1 和 num2 的长度小于110。
    2. num1 和 num2 只包含数字 0-9
    3. num1 和 num2 均不以零开头,除非是数字 0 本身。
    4. 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。

     题解:考虑两个数相乘得到的数最后的位数一定 最长是len1+len2长度。

    对于s[i]*s[j] 的一定是 ans[i+j+1]的,对于第一个数的每一个i位,我们只要记录一个add就行了

    参考代码:

    class Solution {
    public:
        string multiply(string num1, string num2) {
            int l1=num1.size(),l2=num2.size();
            string res(l1+l2,'0');
            if(l1==0||l2==0) return "";
            
            for(int i=l1-1;i>=0;--i)
            {
                int add=0;
                for(int j=l2-1;j>=0;--j)
                {
                    int mul=(num1[i]-'0')*(num2[j]-'0');
                    int num=res[i+j+1]+add+mul%10-'0';
                    res[i+j+1]=num%10+'0';
                    add=mul/10+num/10;
                }
                res[i]+=add;
            }
            for(int i=0;i<l1+l2;++i)
                if(res[i]!='0') return res.substr(i);
            return "0";
        }
    };
    C++

    翻转字符串里的单词

     

    给定一个字符串,逐个翻转字符串中的每个单词。

    示例 1:

    输入: "the sky is blue"
    输出: "blue is sky the"
    

    示例 2:

    输入: "  hello world!  "
    输出: "world! hello"
    解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
    

    示例 3:

    输入: "a good   example"
    输出: "example good a"
    解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
    

    说明:

    • 无空格字符构成一个单词。
    • 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
    • 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

    进阶:

    请选用 C 语言的用户尝试使用 O(1) 额外空间复杂度的原地解法。

    题解:用栈的性质,先存进去(注意把首尾的空格去掉和判断原串是否为空)即可。

    参考代码:

    class Solution {
    public:
        string reverseWords(string s) {
            stack<string> str;
            string s0 = "";
            if(s.empty())
            {
                s = "";
                return s;
            }
            for(int i=0;i<s.length();i++)
            {
                if(s[i]!=' ')
                {
                    s0+=s[i];
                    continue;
                } 
                else if(!s0.empty())
                {
                    str.push(s0); 
                    s0="";   
                }
            }
            if(!s0.empty())
            {
                str.push(s0);
                s0="";
            }
            while(!str.empty())
            {
                s0+=str.top(); 
                str.pop();
                s0+=" ";
            }
            if(s0.empty())
            {
                s = "";
                return s;
            }
            s0.erase(s0.end()-1);
            s = s0;
            return s;
        }
    };
    C++

    简化路径

    以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。

    在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径

    请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。

    示例 1:

    输入:"/home/"
    输出:"/home"
    解释:注意,最后一个目录名后面没有斜杠。
    

    示例 2:

    输入:"/../"
    输出:"/"
    解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。
    

    示例 3:

    输入:"/home//foo/"
    输出:"/home/foo"
    解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
    

    示例 4:

    输入:"/a/./b/../../c/"
    输出:"/c"
    

    示例 5:

    输入:"/a/../../b/../c//.//"
    输出:"/c"
    

    示例 6:

    输入:"/a//b////c/d//././/.."
    输出:"/a/b/c"


    题解:用vector按照题意模拟即可复原IP地址

    参考代码:
    class Solution {
    public:
        string simplifyPath(string path) 
        {
            vector<string> s;
            int i=0;
            while(i<path.length())
            {
                while(path[i]=='/'&&i<path.length()) ++i;
                if(i==path.length()) break;
                
                int start=i;
                while(path[i]!='/'&&i<path.length()) ++i;
                int end=i;
                
                string cs=path.substr(start,end-start);
                
                if(cs=="..") 
                {
                    if(!s.empty())
                        s.pop_back();
                }
                else if(cs!=".") s.push_back(cs);
            }
            if(s.empty()) return "/";
            string ans="";
            for(int j=0;j<s.size();++j)
                ans=ans+'/'+s[j];
            
            return ans;
        }
    };
    C++

     复原IP地址

    给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

    示例:

    输入: "25525511135"
    输出: ["255.255.11.135", "255.255.111.35"]

    题解:根据Ip地址的性质:4个数字每个数字的范围为0~255,然后暴力枚举每个数字的长度,记录合法的数量即可。

    参考代码:
    class Solution {
    public:
        int getint(string ss)
        {
            if(ss.length()>1 && ss[0]=='0') return 256;
            int num=0;
            for(int i=0;i<ss.length();++i)
                num=num*10+ss[i]-'0';
            return num;
        }
        
        vector<string> restoreIpAddresses(string s) 
        {
            vector<string> v;
            for(int i=1;i<=3;++i)
                for(int j=1;j<=3;++j)
                    for(int k=1;k<=3;++k)
                        for(int l=1;l<=3;++l)
                        {
                            if(i+j+k+l==s.length())
                            {
                                int A=getint(s.substr(0,i));
                                int B=getint(s.substr(i,j));
                                int C=getint(s.substr(i+j,k));
                                int D=getint(s.substr(i+j+k,l));
                                if(A<=255 && B<=255 && C<=255 && D<=255)
                                {
                                    string res=s.substr(0,i)+"."+s.substr(i,j)+"."+s.substr(i+j,k)+"."+s.substr(i+j+k,l);
                                    if(res.length()==s.length()+3) v.push_back(res);
                                }
                            }
                        }
            
            return v;
            
        }
    };
    View Code

    数组与排序

    三数之和

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

    注意:答案中不可以包含重复的三元组。

    例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
    
    满足要求的三元组集合为:
    [
      [-1, 0, 1],
      [-1, -1, 2]
    ]
    题解:我们先对数组sort一下,然后我们知道a+b+c=0;那么这三个数里面的最小的那个数一定是负数,我们枚举最小的那个数的位
    注意一定是负数才行,并且和上一个数不同,然后我们定两个指针l=pos+1,r=size()-1;如果a[l]+a[r]>target,r--;如果
    a[l]+a[r]<target,则l++,如果等于的话,就记录该情况即可。时间复杂度是O(n^2)

    参考代码:
    class Solution {
    public:
        vector<vector<int>> threeSum(vector<int>& nums) 
        {
            vector<vector<int>> ans;
            int len=nums.size();
            sort(nums.begin(),nums.end());
            if (nums.empty() || nums.back() < 0 || nums.front() > 0 || len<3) return {};
            
            for(int k=0;k<nums.size();++k)
            {
                if(nums[k]>0) break;
                if(k>0 && nums[k]==nums[k-1]) continue;
                
                int i=k+1,j=nums.size()-1,target=-nums[k];
                while(i<j)
                {
                    if(nums[i]+nums[j]==target) 
                    {
                        ans.push_back({nums[k],nums[i],nums[j]});
                        while(i<j && nums[i]==nums[i+1]) ++i;
                        while(i<j && nums[j]==nums[j-1]) --j;
                        ++i;--j;                }
                    else if(nums[i]+nums[j]<target) ++i;
                    else --j;
                    
                }
            }
            return ans;
        }
    };
    View Code

    岛屿的最大面积

     

    给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围着。

    找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为0。)

    示例 1:

    [[0,0,1,0,0,0,0,1,0,0,0,0,0],
     [0,0,0,0,0,0,0,1,1,1,0,0,0],
     [0,1,1,0,1,0,0,0,0,0,0,0,0],
     [0,1,0,0,1,1,0,0,1,0,1,0,0],
     [0,1,0,0,1,1,0,0,1,1,1,0,0],
     [0,0,0,0,0,0,0,0,0,0,1,0,0],
     [0,0,0,0,0,0,0,1,1,1,0,0,0],
     [0,0,0,0,0,0,0,1,1,0,0,0,0]]
    

    对于上面这个给定矩阵应返回 6。注意答案不应该是11,因为岛屿只能包含水平或垂直的四个方向的‘1’。

    示例 2:

    [[0,0,0,0,0,0,0,0]]

    对于上面这个给定的矩阵, 返回 0

    注意: 给定的矩阵grid 的长度和宽度都不超过 50。

    题解:典型的DFS题目,我们对每个点打个标记是否被访问过,如果没被访问过并未为‘1’的话,表示找到新的联通快,我们dfs该联通快求得其面积,然后去个最大值即可。

    参考代码:

    class Solution {
    public:
        //int vis[52][52];
        int dfs(vector<vector<int>>& grid,int ii,int j)
        {
            //vis[ii][j]=1;
            int n=grid.size();int m=grid[0].size();
            int dx[4]={0,0,1,-1};
            int dy[4]={1,-1,0,0};
            grid[ii][j]=0;
            int sum=1;
            
            for(int i=0;i<4;i++)
            {
                int x=ii+dx[i];
                int y=j+dy[i];
                if(x>=0&&x<n&&y>=0&&y<m&&grid[x][y]==1)
                    sum+=dfs(grid,x,y);
            }
            return sum;
        }
        int maxAreaOfIsland(vector<vector<int>>& grid) 
        {
            int n=grid.size();int m=grid[0].size();
            if(n==0)
                return 0;
            int ans=0;
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<m;j++)
                {
                    //if(vis[i][j]) continue;
                    if(grid[i][j]==1)
                    {
                        ans=max(dfs(grid,i,j),ans);
                    }
                }
            }
            return ans;
        }
    };
    View Code

    搜索旋转排序数组

     

    假设按照升序排序的数组在预先未知的某个点上进行了旋转。

    ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

    搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

    你可以假设数组中不存在重复的元素。

    你的算法时间复杂度必须是 O(log n) 级别。

    示例 1:

    输入: nums = [4,5,6,7,0,1,2], target = 0
    输出: 4
    

    示例 2:

    输入: nums = [4,5,6,7,0,1,2], target = 3
    输出: -1
    题解:二分。如果右半部分是递增的(a[r]>a[mid]),如果target是介于a[mid]<target<a[r],那么答案在右边。否则答案在左边。
    如果左半部分单调,那么如果a[l]<target<a[mid],答案在左边,否则在右边。
     
    参考代码:
    class Solution {
    public:
        int search(vector<int>& nums, int target) 
        {
            int ans=-1;
            int l=0,r=nums.size()-1;
            while(l<=r)
            {
                int mid=l+r>>1;
                if(nums[mid]==target) 
                {
                    ans=mid;
                    break;
                }
                if(nums[mid]<nums[r])
                {
                    if(nums[mid]<target&&target<=nums[r]) l=mid+1;
                    else r=mid-1;
                }
                else
                {
                    if(nums[l]<=target&&target<nums[mid]) r=mid-1;
                    else l=mid+1;
                }
            }
            return ans;
        }
    };
    View Code

    最长连续递增序列

     

    给定一个未经排序的整数数组,找到最长且连续的的递增序列。

    示例 1:

    输入: [1,3,5,4,7]
    输出: 3
    解释: 最长连续递增序列是 [1,3,5], 长度为3。
    尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。 
    

    示例 2:

    输入: [2,2,2,2,2]
    输出: 1
    解释: 最长连续递增序列是 [2], 长度为1。
    

    注意:数组长度不会超过10000。

    题解:直接记录就行了O(N)。

    参考代码:

    class Solution {
    public:
        int findLengthOfLCIS(vector<int>& nums) 
        {
            if(nums.size()==0) return 0; 
            int ans=1,tmp=nums[0],res=1;
            
            for(int i=1;i<nums.size();++i)
            {
                if(nums[i]>tmp)
                {
                    ++res;
                    tmp=nums[i];
                }
                else
                {
                    ans=max(ans,res);
                    res=1;tmp=nums[i];
                }
            }
            return max(ans,res);
        }
    };
    View Code

    数组中的第K个最大元素

     

    在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

    示例 1:

    输入: [3,2,1,5,6,4] 和 k = 2
    输出: 5
    

    示例 2:

    输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
    输出: 4

    说明:

    你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

     题解:类似快排思想。我们每次取a[l],然后从l+1~r,将比a[l]小的数放到a[l]左边,比a[l]大的数放到a[l]右边。最后返回a[l]的位置pos和n-k比较,如果pos==n-k,那么pos就是答案,如果pos<n-k,l=pos+1.如果pos>n-k,那么r=pos-1.

    期望时间复杂度O(N)。

    参考代码:

    class Solution {
    public:
        
        int patition(vector<int>&nums,int L,int R)
        {
            int tmp=nums[L];
            int l=L+1,r=R;
            while(l<=r)
            {
                while(l<=r && nums[r]>=tmp) --r;
                while(l<=r && nums[l]<=tmp) ++l;
                if(l>r) break;
                swap(nums[l],nums[r]);
                l++,r--;
            }
            swap(nums[L],nums[r]);
            return r;
        }
            
        
        int findKthLargest(vector<int>& nums, int k) 
        {
            int l=0,r=nums.size()-1;
            int n=r+1;
            if(n==1) return nums[0];
            
            while(true)
            {
                int pos=patition(nums,l,r);
                if(pos==n-k) return nums[pos];
                if(pos>n-k) r=pos-1;
                else l=pos+1;
            }
        }
    };
    View Code

    最长连续序列

     

    给定一个未排序的整数数组,找出最长连续序列的长度。

    要求算法的时间复杂度为 O(n)

    示例:

    输入: [100, 4, 200, 1, 3, 2]
    输出: 4
    解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。

    题解:由于时间复杂度是O(N),所以我们可以想到unordered_set<int>,可以再O(N)时间内保证里面的元素有序。
    然后对于一个数nums[pos],我们判断是否为开始就行了,如果是,依次判断后面的数是否都有即可。

    参考代码:
    class Solution {
    public:
        int longestConsecutive(vector<int>& nums) 
        {
            int res=0;
            unordered_set<int> st(nums.begin(),nums.end());
            for(auto num:nums)
            {
                if(st.count(num-1)==0)
                {
                    int x=num+1;
                    while(st.count(x)) ++x;
                    res = max(res, x-num);
                }
            }
            return res;
        }
    };
    View Code

    第k个排列

     

    给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。

    按大小顺序列出所有排列情况,并一一标记,当 = 3 时, 所有排列如下:

    1. "123"
    2. "132"
    3. "213"
    4. "231"
    5. "312"
    6. "321"

    给定 n 和 k,返回第 k 个排列。

    说明:

    • 给定 n 的范围是 [1, 9]。
    • 给定 的范围是[1,  n!]。

    示例 1:

    输入: n = 3, k = 3
    输出: "213"
    

    示例 2:

    输入: n = 4, k = 9
    输出: "2314"

    题解:对有个位置pos,如果pos对应的书生一位,那么对用增加其后面所有数的全排列。

    参考代码:

    class Solution {
    public:
        string getPermutation(int n, int k) 
        {
            string s="123456789",ans="";
            vector<int> f(n,1);
            for(int i=1;i<n;++i)
                f[i]=f[i-1]*i;
            --k;
            for(int i=n-1;i>=0;--i)
            {
                int j=k/f[i];
                k%=f[i];
                ans=ans+s[j];
                s.erase(j,1);
            }
            return ans;
        }
    };
    View Code

    朋友圈

     

    班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

    给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

    示例 1:

    输入: 
    [[1,1,0],
     [1,1,0],
     [0,0,1]]
    输出: 2 
    说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
    第2个学生自己在一个朋友圈。所以返回2。
    

    示例 2:

    输入: 
    [[1,1,0],
     [1,1,1],
     [0,1,1]]
    输出: 1
    说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。
    

    注意:

    1. N 在[1,200]的范围内。
    2. 对于所有学生,有M[i][i] = 1。
    3. 如果有M[i][j] = 1,则有M[j][i] = 1。

    题解:并查集即可。

    参考代码:

    class Solution {
    public:
        int fa[210],vis[210];
        int Find(int x){return x==fa[x]? x:fa[x]=Find(fa[x]);}
        void Union(int x,int y)
        {
            int fx=Find(x),fy=Find(y);
            fa[fx]=fy;
        }
        int findCircleNum(vector<vector<int>>& M) 
        {
            int N=M.size(),ans=0;
            for(int i=1;i<=N;++i) fa[i]=i;
            for(int i=0;i<N;++i)
            {
                for(int j=0;j<N;++j)
                {
                    if(M[i][j]==1) 
                        Union(i+1,j+1);
                }
            }
            for(int i=1;i<=N;++i)
                vis[Find(i)]=1;
            for(int i=1;i<=N;++i) if(vis[i]==1) ans++;
            return ans;
        }
    };
    View Code

    合并区间

     

    给出一个区间的集合,请合并所有重叠的区间。

    示例 1:

    输入: [[1,3],[2,6],[8,10],[15,18]]
    输出: [[1,6],[8,10],[15,18]]
    解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
    

    示例 2:

    输入: [[1,4],[4,5]]
    输出: [[1,5]]
    解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。

    题解:按照左边界为第一关键字,由边界为第二关键字,排下序。然后合并区间就行了。

    参考代码:

    class Solution {
    public:
        vector<vector<int>> merge(vector<vector<int>>& v) 
        {
            vector<vector<int>> ans;
            int len=v.size();
            if(len==0) return {};
            
            pair<int,int> p[len];
            for(int i=0;i<len;++i)
            {
                p[i].first=v[i][0];
                p[i].second=v[i][1];
            }
            sort(p,p+len);
            pair<int,int> pi;
            pi.first=p[0].first;pi.second=p[0].second;
            
            for(int i=1;i<len;++i)
            {
                if(p[i].first<=pi.second)
                    pi.second=max(pi.second,p[i].second);
                else
                {
                    ans.push_back({pi.first,pi.second});
                    pi.first=p[i].first;pi.second=p[i].second;
                }
            }
            ans.push_back({pi.first,pi.second});
            
            return ans;
        }
    };
    View Code

      接雨水

    给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

    上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。

    示例:

    输入: [0,1,0,2,1,0,1,3,2,1,2,1]
    输出: 6

    题解:先找到最高位置pos,然后从最左边和最右边往中间靠。记录另一个边界的最大值。根据情况判断加或不加。

    参考代码:

    class Solution {
    public:
        int trap(vector<int>& height) {
            int n=height.size();
            if(n<=2)
                return 0;
            int maxx=-1;int id;
            for(int i=0;i<n;i++)
            {
                if(height[i]>maxx)
                {
                    maxx=height[i];
                    id=i;
                }
            }
            int ans=0;int t=height[0];
            for(int i=0;i<id;i++)
            {
                if(t<height[i])
                    t=height[i];
                else
                    ans+=(t-height[i]);
            }
            t=height[n-1];
            for(int i=n-1;i>id;i--)
            {
                if(t<height[i])
                    t=height[i];
                else
                    ans+=(t-height[i]);
            }
            return ans;
        }
    };
    View Code 

    链表和树

    合并两个有序链表

     

    将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

    示例:

    输入:1->2->4, 1->3->4
    输出:1->1->2->3->4->4

    题解:创建新的头指针,然后当l1和l2都不是空的时候,判断哪一个小,让新的头指向该节点,并将小的链表向后移动一位。然后l1和l2至少有一个为空,这时候,我们找到不空的,把它接到l3后面即可,最后返回l3的头指针。

    参考代码:

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    class Solution {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) 
        {
            ListNode l3=new ListNode(0);
            ListNode l4=l3;
            while(l1!=null && l2!=null)
            {
                if(l1.val<=l2.val) 
                {
                    l3.next=l1;
                    l1=l1.next;
                }
                else 
                {
                    l3.next=l2;
                    l2=l2.next;
                }
                l3=l3.next;
            }
            if(l1!=null) l3.next=l1;
            else if(l2!=null) l3.next=l2;
            return l4.next;
        }
    }
    Java

     反转链表

     

    反转一个单链表。

    示例:

    输入: 1->2->3->4->5->NULL
    输出: 5->4->3->2->1->NULL

    进阶:
    你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

     题解:记录三个指针即可。

    参考代码:

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            ListNode *ans=NULL;
            ListNode *pre=NULL;
            ListNode *temp=head;
            while(temp!=NULL)
            {
                ListNode *nextt=temp->next;
                if(nextt==NULL)
                    ans=temp;
                temp->next=pre;
                pre=temp;
                temp=nextt;
            }
            return ans;
        }
    };
    C++
    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    class Solution {
        public ListNode reverseList(ListNode head) 
        {
            if(head.next==null) return head;
            if((head.next).next==null)
            {
                (head.next).next=head;
                head.next=null;
                return head.next;
            }
            ListNode pre=new ListNode(0),now=new ListNode(0),nxt=new ListNode(0);
            while(head!=null)
            {
                now=head;
                nxt=head.next;
                
                now.next=pre;
                pre=nxt.next;
                nxt.next=now;
                now.next=pre;
                pre=now;now=nxt;
                
                head=head.next;
            }
            return pre;
        }
    }
    Java

    两数相加

     

    给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

    如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

    您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

    示例:

    输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
    输出:7 -> 0 -> 8
    原因:342 + 465 = 807

    题解:

    参考代码:

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    class Solution {
        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            ListNode l3 = new ListNode(0);
            ListNode res = l3;
            int value = 0;
            int flag = 0;
            while (l1 != null || l2 != null || flag == 1) {
                int sum = flag;
                sum += (l1 != null ? l1.val : 0) + (l2 != null ? l2.val : 0);
                l1 = (l1 != null ? l1.next : null);
                l2 = (l2 != null ? l2.next : null);
                l3.next = new ListNode(sum % 10);
                flag = sum / 10;
                l3 = l3.next;
            }
            return res.next;
        }
    }
    Java

    排序链表

     

    在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

    示例 1:

    输入: 4->2->1->3
    输出: 1->2->3->4
    

    示例 2:

    输入: -1->5->3->4->0
    输出: -1->0->3->4->5

    题解:

    吧链表中的数存到vector中,然后sort一下,重新插入到链表中去。

    参考代码:

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode* sortList(ListNode* head) {
            vector<int> v;
            ListNode *p=head;
            while(p!=NULL)
            {
                v.push_back((p->val));
                p=p->next;
            }
            sort(v.begin(),v.end());
            p=head;
            int i=0;
            while(p!=NULL)
            {
                p->val=v[i++];
                
                p=p->next;
            }
            return head;
        }
    };
    C++
    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    class Solution {
        public ListNode sortList(ListNode head) 
        {
            Vector<int> v=new Vector<int>();
            ListNode p=head;
            while(p!=null)
            {
                v.push_back(p.val);
                p=p.next;
            }
            sort(v.begin(),v.end());
            p=head;
            while(p!=null)
            {
                p.val=v[0];
                v.pop_back();
            }
            return head;
        }
    }
    Java

    环形链表 II

     

    给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

    为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

    说明:不允许修改给定的链表。

    示例 1:

    输入:head = [3,2,0,-4], pos = 1
    输出:tail connects to node index 1
    解释:链表中有一个环,其尾部连接到第二个节点。
    

    示例 2:

    输入:head = [1,2], pos = 0
    输出:tail connects to node index 0
    解释:链表中有一个环,其尾部连接到第一个节点。
    

    示例 3:

    输入:head = [1], pos = -1
    输出:no cycle
    解释:链表中没有环。
    

    进阶:
    你是否可以不用额外空间解决此题?

    题解:

      快慢指针法。f(快指针),l(慢指针).每次快指针移动两步,慢指针移动一步。如果有一个指针为null,那么不存在环,当快指针追上慢指针的时候,此时快指针比慢指针多走了一个环的距离,而慢指针则走了一个环的长度,。然后我们让快指针指向链表头,然后再次移动,此时快慢指针每次都移动一步,当再次碰面的时候,快指针指向的节点就是环的入口,返回即可。

    参考代码:

    /**
     * Definition for singly-linked list.
     * class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) {
     *         val = x;
     *         next = null;
     *     }
     * }
     */
    public class Solution {//快慢指针法
        public ListNode detectCycle(ListNode head) 
        {
            ListNode f=head,l=head;
            while(true)
            {
                if(f==null || f.next==null) return null;
                f=f.next.next;
                l=l.next;
                if(f==l) break;
            }
            f=head;
            while(f!=l)
            {
                f=f.next;
                l=l.next;
            }
            return f;
        }
    }
    Java

    相交链表

     

    编写一个程序,找到两个单链表相交的起始节点。

    如下面的两个链表:

    在节点 c1 开始相交。

    示例 1:

    输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
    输出:Reference of the node with value = 8
    输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
    

    示例 2:

    输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
    输出:Reference of the node with value = 2
    输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
    

    示例 3:

    输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
    输出:null
    输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
    解释:这两个链表不相交,因此返回 null。
    

    注意:

    • 如果两个链表没有交点,返回 null.
    • 在返回结果后,两个链表仍须保持原有的结构。
    • 可假定整个链表结构中没有循环。
    • 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

    题解:分别使用两个指针l1,l2指向两个链表,然后假设(l1>l2,长度),当l2走到尽头的时候,令l2指向第一个链表的头,继续走。当l1为null的时候,令l1指向第一个链表的头,此时,l1和l2距离末尾的长度都是l2的长度,然后,两个指针同时移动,当l1==l2时,该节点就是相交的第一个节点,返回即可。

    参考代码:

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) {
     *         val = x;
     *         next = null;
     *     }
     * }
     */
    public class Solution {
        public ListNode getIntersectionNode(ListNode headA, ListNode headB) 
        {
            if(headA==null || headB==null) return null;
            ListNode la=headA,lb=headB;
            while(la!=lb)
            {
                if(la==null) la=headB;else la=la.next;
                if(lb==null) lb=headA;else lb=lb.next;
            }
            return la;
        }
    }
    Java

     合并K个排序链表

     

    合并 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

    示例:

    输入:
    [
      1->4->5,
      1->3->4,
      2->6
    ]
    输出: 1->1->2->3->4->4->5->6

    题解:

    暴力找最小的,然后把对应的节点往后移动一位即可。

    参考代码:

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    class Solution {
        public ListNode mergeKLists(ListNode[] lists) 
        {
            ListNode ans=new ListNode(0);
            ListNode res=ans;
            while(true)
            {
                int min_val=Integer.MAX_VALUE;
                int pos=-1;
                for(int i=0;i<lists.length;++i)
                {
                    if(lists[i]!=null)
                    {
                        if(lists[i].val<min_val)
                        {
                            min_val=lists[i].val;
                            pos=i;
                        }
                    }
                }
                if(min_val==Integer.MAX_VALUE)
                    break;
                ListNode node=new ListNode(min_val);
                ans.next=node; ans=ans.next;
                
                lists[pos]=lists[pos].next;
            }
            return res.next;
        }
    }
    Java

     二叉树的最近公共祖先

     

    给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

    百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

    例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4]

    示例 1:

    输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
    输出: 3
    解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
    

    示例 2:

    输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
    输出: 5
    解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
    

    说明:

    • 所有节点的值都是唯一的。
    • p、q 为不同节点且均存在于给定的二叉树中。

    题解:递归。

      两个节点要么是父子关系,要么在一个root下,我们们判断root是否为p或q,是的话,就返回root.

    然后递归左右子树,如果left!=null&&right!=null则返回root.

    如果left==null,则返回右子树的递归结果,否则返回左子树的递归结果。

    参考代码:

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) 
        {
            if(root==null) return root;
            if(root==p||root==q) return root;
            TreeNode left=lowestCommonAncestor(root.left,p,q);
            TreeNode right=lowestCommonAncestor(root.right,p,q);
            
            if(left!=null && right!=null) return root;
            if(left==null) return right;
            else return left;
        }
    }
    Java

    二叉树的锯齿形层次遍历

     

    给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

    例如:
    给定二叉树 [3,9,20,null,null,15,7],

        3
       / 
      9  20
        /  
       15   7
    

    返回锯齿形层次遍历如下:

    [
      [3],
      [20,9],
      [15,7]
    ]

    题解:类似BFS.我们用队列存储每一层的节点,并记录深度,然后一层一层往下扩展,如果深度为偶数,则反转list即可。

    参考代码:

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    public class Solution {
        public List<List<Integer>> data = new ArrayList<List<Integer>>();
    
        public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
            if(root == null){
                return data;
            }
            Queue<TreeNode> queue = new LinkedList<TreeNode>();
            queue.offer(root);
            int level = 1;
            while(!queue.isEmpty()){
                int size = queue.size();
                List<Integer> list = new ArrayList<Integer>();
                for(int  i = 0; i < size; i++){
                    TreeNode node = queue.poll();
                    list.add(node.val);
                    if(node.left != null){
                        queue.offer(node.left);
                    }
                    if(node.right != null){
                        queue.offer(node.right);
                    }
                }
                if(level % 2 != 0){
                    data.add(list);
                }else{
                    Collections.reverse(list);
                    data.add(list);
                }
                level++;
            }
            return data;
        }
    }
    Java

    动态或贪心

    买卖股票的最佳时机

     

    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

    如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

    注意你不能在买入股票前卖出股票。

    示例 1:

    输入: [7,1,5,3,6,4]
    输出: 5
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
         注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
    

    示例 2:

    输入: [7,6,4,3,1]
    输出: 0
    解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

    题解:

    记录前缀最小值即可。

    参考代码:

    class Solution {
    public:
        int maxProfit(vector<int>& p) 
        {
            if(p.size()==0) return 0;
            int min_val=p[0],ans=0;
            for(int i=1,len=p.size();i<len;++i)
            {
                ans=max(ans,p[i]-min_val);
                min_val=min(min_val,p[i]);
            }
            return ans;
        }
    };
    C++

    买卖股票的最佳时机 II

     

    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

    设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

    注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

    示例 1:

    输入: [7,1,5,3,6,4]
    输出: 7
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
         随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
    

    示例 2:

    输入: [1,2,3,4,5]
    输出: 4
    解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
         注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
         因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
    

    示例 3:

    输入: [7,6,4,3,1]
    输出: 0
    解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

    题解:如果递减就一直更新找到最小值,然后如果一直增加就先不买,当下一天降的时候,就买了,然后继续前面的操作,记录和即可。

    参考代码:

    class Solution {
    public:
        int maxProfit(vector<int>& p) 
        {
            if(p.size()==0) return 0;
            int ans=0,min_val=p[0],max_val=p[0];
            int len=p.size(),pos=1;
            
            while(pos<len)
            {
                while(min_val>=p[pos]) 
                {
                    min_val=p[pos];
                    pos++;
                    if(pos>=len) break;
                }
                if(pos>=len) break;
                max_val=min_val;
                int res=0;
                while(max_val<=p[pos])
                {
                    max_val=p[pos];
                    res=max_val-min_val;
                    pos++;
                    if(pos>=len) break;
                }
                min_val=p[pos];
                ans+=res;
            }
             return ans;
        }
       
    };
    C++

    最大正方形

     

    在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。

    示例:

    输入: 
    
    1 0 1 0 0
    1 0 1 1 1
    1 1 1 1 1
    1 0 0 1 0
    
    输出: 4

    题解:DP。

      对于一全1的边长你为n正方形的右下角那个1,则其左边,上边和左上边,一定全部是边长为n-1的正方形。如果不是,则取这三个正方形边长的最小值即可。

      转移方程:dp[i][j]=min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]))+1;

    参考代码:

    class Solution {
    public:
        int maximalSquare(vector<vector<char>>& matrix) 
        {
            if(matrix.size()==0) return 0;
            if(matrix[0].size()==0) return 0;
            int n=matrix.size(),m=matrix[0].size();
            
            int dp[n][m],ans=0;
            memset(dp,0,sizeof dp);
            for(int i=0;i<n;++i)
            {
                if(matrix[i][0]=='1')
                    dp[i][0]=1,ans=1;
            }
            for(int i=0;i<m;++i)
            {
                if(matrix[0][i]=='1')
                    dp[0][i]=1,ans=1;
            }
            
            for(int i=1;i<n;++i)
            {
                for(int j=1;j<m;++j)
                {
                    if(matrix[i][j]=='1')
                    {
                        dp[i][j]=min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]))+1;
                        ans=max(ans,dp[i][j]);
                    }
                }
            }
            return (ans*ans);
        }
    };
    C++

    最大子序和

     

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例:

    输入: [-2,1,-3,4,-1,2,1,-5,4],
    输出: 6
    解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
    

    进阶:

    如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

     题解:

    记录最小前缀和,和前缀和。

    参考代码:

    普通:

    class Solution {
    public:
        int maxSubArray(vector<int>& nums) 
        {
            if(nums.size()==0) return 0;
            int ans=nums[0],min_val=nums[0],now=nums[0];
            
            for(int i=1;i<nums.size();++i)
            {
                now+=nums[i];
                ans=max(ans,max(now,now-min_val));
                min_val=min(min_val,now);
            }
            return ans;
        }
    };
    C++

    进阶:

    class Solution {
    public:
        int work(int l,int r,vector<int>&v)
        {
            if(l==r) return v[l];
            int mid=l+r>>1;
            int ans=max(work(l,mid,v),work(mid+1,r,v));
            int numl=v[mid],numr=v[mid+1],res=v[mid];
            for(int i=mid-1;i>=l;--i)
            {
                res+=v[i];
                numl=max(numl,res);
            }
            res=v[mid+1];
            for(int i=mid+2;i<=r;++i)
            {
                res+=v[i];
                numr=max(numr,res);
            }
            return max(ans,numl+numr);
        }
        
        
        int maxSubArray(vector<int>& nums) 
        {
            if(nums.size()==0)
                return 0;
            return work(0,nums.size()-1,nums);
        }
    };
    C++

     三角形最小路径和

     

    给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

    例如,给定三角形:

    [
         [2],
        [3,4],
       [6,5,7],
      [4,1,8,3]
    ]
    

    自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。

    说明:

    如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。

    题解:DP;

    dp[i][j]:表示第i行第j个位置时的最小路径和。这里我们可以优化一维空间,dp[i]和p[j]两个数组分别表示当前行和上一行的路径和即可。

    时间复杂度O(N^2); 空间复杂度O(N);

    参考代码:

    class Solution {
    public:
        int minimumTotal(vector<vector<int>>& t) 
        {
            if(t.size()==0) return 0;
            int n=t.size(),m=t.size();
            int dp[m]={0},d[m]={0};
            
            for(int i=0;i<m;++i)
                d[i]=t[n-1][i];
            
            for(int i=n-2;i>=0;--i)
            {
                for(int j=0;j<t[i].size();++j)
                {
                    dp[j]=min(d[j],d[j+1])+t[i][j];
                    d[j]=dp[j];
                }
            }
            return d[0];
        }
    };
    C++

    俄罗斯套娃信封问题

     

    给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h) 出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。

    请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。

    说明:
    不允许旋转信封。

    示例:

    输入: envelopes = [[5,4],[6,4],[6,7],[2,3]]
    输出: 3 
    解释: 最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。

    题解:DP。

    dp[i]:表示前i个信封所能得到的最大值。先按信封的w,h为第一,第二关键字排下序。

    转移方程:dp[i]=max(dp[i],dp[j]+1);

    参考代码:

    class Solution {
    public:
        int maxEnvelopes(vector<vector<int>>& e) 
        {
            if(e.size()==0) return 0;
            int n=e.size();
            pair<int,int> p[n];
            for(int i=0;i<e.size();++i)
            {
                p[i].first=e[i][0];
                p[i].second=e[i][1];
            }
            sort(p,p+n);
            
            int dp[n],ans=1;
            for(int i=0;i<n;++i) dp[i]=1;
            
            for(int i=0;i<n;++i)
            {
                for(int j=0;j<i;++j)
                {
                    if(p[j].first<p[i].first&&p[j].second<p[i].second)
                        dp[i]=max(dp[i],dp[j]+1);
                }
                ans=max(ans,dp[i]);
            }
            return ans;
        }
    };
    C++

    数据结构

    最小栈

     

    设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。

    • push(x) -- 将元素 x 推入栈中。
    • pop() -- 删除栈顶的元素。
    • top() -- 获取栈顶元素。
    • getMin() -- 检索栈中的最小元素。

    示例:

    MinStack minStack = new MinStack();
    minStack.push(-2);
    minStack.push(0);
    minStack.push(-3);
    minStack.getMin();   --> 返回 -3.
    minStack.pop();
    minStack.top();      --> 返回 0.
    minStack.getMin();   --> 返回 -2.

    题解:

    用两个栈模拟即可。一个普通栈,一个单调栈。

    参考代码:

    class MinStack {
    public:
        /** initialize your data structure here. */
        MinStack() {}
        
        void push(int x) {
            s1.push(x);
            if (s2.empty() || x <= s2.top()) s2.push(x);
        }
        
        void pop() {
            if (s1.top() == s2.top()) s2.pop();
            s1.pop();
        }
        
        int top() {
            return s1.top();
        }
        
        int getMin() {
            return s2.top();
        }
        
    private:
        stack<int> s1, s2;
    };
    C++

    LRU缓存机制

     

    运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

    获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
    写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

    进阶:

    你是否可以在 O(1) 时间复杂度内完成这两种操作?

    示例:

    LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
    
    cache.put(1, 1);
    cache.put(2, 2);
    cache.get(1);       // 返回  1
    cache.put(3, 3);    // 该操作会使得密钥 2 作废
    cache.get(2);       // 返回 -1 (未找到)
    cache.put(4, 4);    // 该操作会使得密钥 1 作废
    cache.get(1);       // 返回 -1 (未找到)
    cache.get(3);       // 返回  3
    cache.get(4);       // 返回  4

    题解:

    按题意的来嘛,用MapStack就可以解决了。

    参考代码:

    class LRUCache {
     
        Map<Integer,Integer> map ;
        Stack<Integer> stack;
        int size;
        
        public LRUCache(int capacity) {
            stack = new Stack<>();
            map = new HashMap<>(capacity);
            size = capacity;
        }
        
        public int get(int key) {
            if(!stack.contains(key)){
                return -1;
            }
            boolean flag = stack.remove(Integer.valueOf(key));
            stack.push(key);
            return map.get(key);
        }
        
        public void put(int key, int value) {
            if(stack.contains(key)){
                stack.remove(Integer.valueOf(key));
            }else{
                if(stack.size() == size){
                    int count = stack.remove(0);
                    map.remove(count);
                }
            }
            stack.push(key);
            map.put(key,value);
        }
    }
    C++
  • 相关阅读:
    [译]The multi Interface
    [译]libcurl_tutorial
    [译]curl_multi_perform
    [译]curl_multi_info_read
    [译]libcurl错误码
    YumRepo Error: All mirror URLs are not using ftp, http[s] or file
    linux 修改文件用户组和所有者
    linux禁止root用户直接登录sshd并修改默认端口
    修改root登录用户名减少阿里云Linux主机“被暴力破解”警告
    CentOS云主机安全之新增ssh登录账户、禁止ROOT登陆
  • 原文地址:https://www.cnblogs.com/csushl/p/11953704.html
Copyright © 2011-2022 走看看