zoukankan      html  css  js  c++  java
  • 化悲痛为力量——暑假刷题走起

    6.21

    【思维】接雨水-力扣

    题目描述不贴了,在上面的链接里↑

      第一遍写的思路过于简单,按每层遍历,还可以递归少写点代码。但时间复杂度O(n*n),导致在复杂数据点超时。
      参考了windliang的题解才想到可以按列遍历,每个柱子能储存的雨水只和左边所有柱子的最高lmax,与右边所有最高rmax有关。两者中的较小者min减去该柱子的高度h得到的结果和0相比的较大值就是答案。如果这个差小于等于0,就应该等于0。
      进一步还可以用单调栈储存数据,使空间复杂度再次降低。(这里下次好好研究一下)

    参考代码:

     1 class Solution {
     2 public:
     3     int trap(vector<int>& height) {
     4         int rmax=0,lmax=0,len=height.size(),sum=0;
     5         int Max1[100000],Max2[100000];
     6         for(int i=0;i<len;++i){
     7             if(i>0) rmax=max(rmax,height.at(i-1));
     8             Max1[i]=rmax;
     9         }
    10         for(int i=len-1;i>=0;--i){
    11             if(i+1<len) lmax=max(lmax,height.at(i+1));
    12             Max2[i]=lmax;
    13         }
    14         for(int i=0;i<len;++i){
    15             int tmp=min(Max1[i],Max2[i]);
    16             sum+=max(tmp-height.at(i),0);
    17         }
    18        return sum;
    19     }
    20 };
    View Code

    6.22

    【思维】模式匹配-力扣

    题目描述在上面的链接里↑  

      一开始想了三个思路。①枚举,搜索匹配串中重复出现的子串。如果有重复的子串可能性多,则效率太低,pass;②从匹配串入手,看能匹配什么模式,再查模式串是否是其中一种情况。实际上与①本质相同,pass;③意识到匹配题最好从模式串入手,结合基本计算,匹配串的可能组合就是有限的,可行。

      核心:解不定方程。anum*alen + bnum*blen=vlen => anum和bnum可以从模式串p中数出、作系数,alen和blen相当于未知数

      特殊情况:重复子串a_re和b_re不能相等,但可以有一个为空串;对于anum和bnum有一个为0、都为0的情况要提前讨论。

    参考代码:

     1 class Solution {
     2 public:
     3     bool patternMatching(string pattern, string value) {
     4         string p=pattern, v=value;
     5         int plen=p.length(), vlen=v.length(), tmp=0, anum=0, bnum=0;
     6         for(int i=0;i<plen;++i){
     7             if(p[i]=='a') anum++;
     8             else bnum++;
     9         }
    10 //根据a和b的数量以及目标串的长度,解二元一次方程    
    11         if(anum+bnum==0){
    12             if(v.length()!=0) return false;
    13             return true;
    14         }
    15         if(anum==0||bnum==0){
    16             int num=anum+bnum;
    17             if( vlen%num !=0) return false;
    18             string tmp,re;
    19             re.assign(v, 0, vlen/num);
    20             for(int i=0;i<vlen;i+=vlen/num){
    21                 tmp.assign(v, i, vlen/num);
    22                 if(tmp!=re) return false;
    23             }
    24             return true;
    25         }
    26         for(int i=0; i<=(vlen/anum); ++i){
    27             if( (vlen-i*anum)%bnum==0 ){
    28                 int alen=i, blen=(vlen-i*anum)/bnum;
    29                 string tmp, re_a, re_b;
    30                 int ptr=0;//比对指针 
    31                 bool a_occur=false, b_occur=false, find=true;
    32                 for(int j=0;j<plen;++j){
    33                     if(a_occur && b_occur && re_a==re_b) break;
    34                     if(p[j]=='a'){
    35                         tmp.assign(v,ptr,alen);
    36                         ptr+=alen;
    37                         if(!a_occur){
    38                             re_a=tmp;
    39                             a_occur=1;
    40                         }
    41                         else{
    42                             if(tmp!=re_a){
    43                                 find=false;
    44                                 break;
    45                             }
    46                         }
    47                     }
    48                     else{
    49                         tmp.assign(v,ptr,blen);
    50                         ptr+=blen;
    51                         if(!b_occur){
    52                             re_b=tmp;
    53                             b_occur=1;
    54                         }
    55                         else{
    56                             if(tmp!=re_b){
    57                                 find=false;
    58                                 break;
    59                             }
    60                         }
    61                     }
    62                 }
    63                 if(re_a!=re_b && find) return true;
    64             }
    65         }
    66         return false;
    67     }
    68 };
    View Code

     6.23

    【手感-进制计算】二进制求和-力扣

    今天忙着投实习,刷题太水了(这个借口真烂x)

      这道是简单题,一遍读懂题意很重要。

    参考代码:

     1 class Solution {
     2 public:
     3     string addBinary(string a, string b) {
     4         string ans="";
     5         int alen=a.length(), blen=b.length();
     6         int p1=alen-1, p2=blen-1, res=0;
     7         while(p1>=0 || p2>=0){
     8             if(p1>=0) res+=a[p1]-'0';
     9             if(p2>=0) res+=b[p2]-'0';
    10             string tmp="x";
    11             tmp[0]=res%2+'0';
    12             ans=tmp+ans;
    13             res/=2;
    14             p1--;
    15             p2--;
    16         }
    17         while(res>0){
    18             string tmp="x";
    19             tmp[0]=res%2+'0';
    20             ans=tmp+ans;
    21             res/=2;           
    22         }
    23         int cnt=0;
    24         if(ans[0]=='0'){
    25             for(int i=0; i<ans.length(); ++i){
    26                 if(i==ans.length()-1){
    27                     string tmp="0";
    28                     tmp[0]=ans[ans.length()-1];
    29                     return tmp;
    30                 }
    31                 if(ans[i]!='0') break;
    32                 else cnt++;
    33             }
    34         }
    35         ans.assign(ans, cnt, ans.length()-cnt);
    36         return ans;
    37     }
    38 };
    View Code

     6.24

    【双指针】最接近的三数之和-力扣

      因为是三数之和、且不是求相等而是相近,用二重循环+二分排序查找似乎比双指针慢很多,但两者都要注意边界情况( |pb-pc| ==1时以及到达此状态前一刻的情况),可以多建几个临时变量储存结果。

    参考代码:

     1 //①排序+二分查找法
     2 class Solution {
     3 public:
     4     int threeSumClosest(vector<int>& nums, int target) {
     5         sort(nums.begin(),nums.end());
     6         int a, b, c, vlen=nums.size();
     7         int minloc[3]={0};
     8         for(int i=vlen-1;i>=0;--i){
     9             for(int j=i-1;j>=0;--j){
    10                 a=nums.at(i);
    11                 b=nums.at(j);
    12                 c=target-a-b;
    13                 int r=0, l=j-1;
    14                 if(l<0) break;
    15                 while(l-r>1){
    16                     if( abs( nums.at(r)-c ) > abs( nums.at(l)-c )){
    17                         r=(r+l)/2;
    18                     }
    19                     else{
    20                         l=(r+l)/2;
    21                     }
    22                 }
    23                 if( abs( nums.at(r)-c ) > abs( nums.at(l)-c )) r=l;
    24                 if((i==vlen-1 && j==vlen-2) || abs(nums.at(minloc[0])+nums.at(minloc[1])+nums.at(minloc[2])-target)>abs(nums.at(i)+nums.at(j)+nums.at(r)-target)){
    25                     minloc[2]=r;
    26                     minloc[0]=i;
    27                     minloc[1]=j;
    28                 }
    29             }
    30         }
    31         return nums.at(minloc[0])+nums.at(minloc[1])+nums.at(minloc[2]);
    32     }
    33 };
    34 
    35 //②排序+双指针法
    36 class Solution {
    37 public:
    38     int threeSumClosest(vector<int>& nums, int target) {
    39         sort(nums.begin(),nums.end());
    40         int vlen=nums.size(), pb, pc, sum, ans=1<<30;
    41         for(int i=vlen-1;i>=0;--i){
    42             pb=i-1;pc=0;
    43             if(pb<=0) break;
    44             while(pb-pc>1){
    45                 sum=nums.at(i)+nums.at(pb)+nums.at(pc);
    46                 if( sum==target ) return target;
    47                 if( sum>target ) pb--;
    48                 else pc++;
    49             }
    50             int sum2=nums.at(i)+nums.at(pb)+nums.at(pc);
    51             if( i>2 && abs(sum2-target)<abs(sum-target) ) sum=sum2;
    52             else if(i<=2) sum=sum2;
    53             if( abs(sum-target)<abs(ans-target) ) ans=sum;
    54         }
    55         return ans;
    56     }
    57 };
    View Code

     6.25

    【动规】单词拆分-力扣

      这道题有点意思,题意很简单,在字典中查找、匹配,问是否能用字典词组成目标串。一开始的思路是对字典排序+二分查找起始点(首字母与当前待匹配字符串首字母相同的字典词)+递归,但超时了。然后动规,建一个set,只储存匹配不到的子字符串(能匹配到的一定是答案,肯定已经return true了),每次递归前先查是否该字符串在set里,是则直接return false。

      另外,有个局部优化是要先贪心匹配最长的字典单词。试了一下,将二分查找到的位置记为r。
      ①未优化的写法是从r开始向两边±i(i=0,1,2,3……)地搜索,往下递归,两边都越界或都不再匹配则退出循环;
      ②优化后,从r开始往后找(sort函数默认按字典序排序,头部相同,尾部更长的单词在后面),找到最后一个首字母能匹配上的位置maxloc,然后再从maxloc往前遍历,每一层向下递归。

      参考代码中写了个小函数bool HeadOf(string A, string B)来判断是否A是B的头部。

    参考代码:

     1 //①动规-未优化方法
     2 class Solution {
     3 public:
     4     set<string> fail;
     5     bool HeadOf(string A, string B){
     6         if(A.length()>B.length()) return false;
     7         for(int i=0;i<A.length();++i){
     8             if(A[i]!=B[i]) return false;
     9         }
    10         return true;
    11     }
    12     bool wordBreak(string s, vector<string>& wordDict) {
    13         auto p=fail.find(s);
    14         if(p!=fail.end()) return false;
    15         if(s=="") return true;
    16         sort(wordDict.begin(), wordDict.end());
    17         int wlen=wordDict.size(), slen=s.length();
    18         if(wlen==0){
    19             fail.insert(s);
    20             return false;
    21         }
    22         int r=0, l=wlen-1, maxloc=-1;
    23         while(l-r>1){
    24             if( wordDict.at( (r+l)/2 )[0] < s[0]) r=(r+l)/2;
    25             else l=(r+l)/2;
    26         }
    27         if( wordDict.at(r)[0]==s[0] || wordDict.at(l)[0]==s[0]){
    28             for(int i=0;;i++){
    29                 if(r+i>=wlen && r-i<0) break;
    30                 if(r+i<wlen && wordDict.at(r+i)[0]==s[0] && HeadOf(wordDict.at(r+i),s) ){
    31                     string tmp;
    32                     tmp.assign(s,wordDict.at(r+i).length(),slen-wordDict.at(r+i).length());
    33                     if(wordBreak(tmp,wordDict)) return true;
    34                 }
    35                 if(i!=0 && r-i>=0 && wordDict.at(r-i)[0]==s[0] && HeadOf(wordDict.at(r-i),s)){
    36                     string tmp;
    37                     tmp.assign(s,wordDict.at(r-i).length(),slen-wordDict.at(r-i).length());
    38                     if(wordBreak(tmp,wordDict)) return true;
    39                 }
    40             }
    41         }
    42         fail.insert(s);
    43         return false;
    44     }
    45 };
    46 
    47 //②动规-优化方法
    48 class Solution {
    49 public:
    50     set<string> fail;
    51     bool HeadOf(string A, string B){
    52         if(A.length()>B.length()) return false;
    53         for(int i=0;i<A.length();++i){
    54             if(A[i]!=B[i]) return false;
    55         }
    56         return true;
    57     }
    58     bool wordBreak(string s, vector<string>& wordDict) {
    59         auto p=fail.find(s);
    60         if(p!=fail.end()) return false;
    61         if(s=="") return true;
    62         sort(wordDict.begin(), wordDict.end());
    63         int wlen=wordDict.size(), slen=s.length();
    64         if(wlen==0){
    65             fail.insert(s);
    66             return false;
    67         }
    68         int r=0, l=wlen-1, maxloc=-1;
    69         while(l-r>1){
    70             if( wordDict.at( (r+l)/2 )[0] < s[0]) r=(r+l)/2;
    71             else l=(r+l)/2;
    72         }
    73         for(int i=r;i<wlen;++i){
    74             if(wordDict.at(i)[0]==s[0]) maxloc=i;
    75         }
    76         if(maxloc==-1){
    77             fail.insert(s);
    78             return false;
    79         }
    80         for(int i=maxloc;i>=r;i--){
    81             if(wordDict.at(i)[0]==s[0] && HeadOf(wordDict.at(i),s) ){
    82                 string tmp;
    83                 tmp.assign(s,wordDict.at(i).length(),slen-wordDict.at(i).length());
    84                 if(wordBreak(tmp,wordDict)) return true;
    85             }
    86         }
    87         fail.insert(s);
    88         return false;
    89     }
    90 };
    View Code

    !且慢——刚刚参考了dalao的代码,发现动规也被我做复杂了orz。可以从另一面考虑,用dp[i]==true/false记录原始待匹配单词的前i个字符是否恰好能被字典词匹配上,这样不用递归都行。另外,dalao还对其代码做了进一步优化,即找到字典中单词的最大长度maxWordLength,在循环计算时,对前i个单词的分割就可以从i-maxWordLength开始,如果这个数<0,就从0开始。

    神奇版代码:

     1 bool wordBreak(string s, vector<string>& wordDict) {
     2     vector<bool> dp(s.size()+1, false);
     3     unordered_set<string> m(wordDict.begin(), wordDict.end());
     4     dp[0] = true;
     5     //获取最长字符串长度
     6     int maxWordLength = 0;
     7     for (int i = 0; i < wordDict.size(); ++i){
     8         maxWordLength = std::max(maxWordLength, (int)wordDict[i].size());
     9     }
    10     for (int i = 1; i <= s.size(); ++i){
    11         for (int j = std::max(i-maxWordLength, 0); j < i; ++j){
    12             if (dp[j] && m.find(s.substr(j, i-j)) != m.end()){
    13                 dp[i] = true;
    14                 break;
    15             }
    16         }
    17     }
    18     return dp[s.size()];
    19 }
    View Code

     后记:以上三种方法的速度差异还是蛮大的,分别是48ms--16ms--0ms,orz……

    6.26

    【手感-链表操作】移除重复节点-力扣

      两种思路,①使用额外内存,用set储存到目前为止出现过的元素,一次遍历,删除出现过的节点;②不使用额外内存,两个指针逐个遍历每个未删除的节点。简单题,只写了第二种方法。

    参考代码:

     1 /**
     2  * Definition for singly-linked list.
     3  * struct ListNode {
     4  *     int val;
     5  *     ListNode *next;
     6  *     ListNode(int x) : val(x), next(NULL) {}
     7  * };
     8  */
     9 class Solution {
    10 public:
    11     ListNode* removeDuplicateNodes(ListNode* head) {
    12         ListNode* p1=head;
    13         ListNode* p2=head;
    14         if(p1==NULL) return head;
    15         while((*p1).next!=NULL){
    16             if(p2==NULL || (*p2).next==NULL){
    17                 p1=(*p1).next;
    18                 p2=p1;
    19             }
    20             else if( (*(*p2).next).val==(*p1).val ){
    21                 (*p2).next= (*(*p2).next).next;
    22             }
    23             else{
    24                 p2=(*p2).next;
    25             }
    26         }
    27         return head;
    28     }
    29 };
    View Code

     6.27

    【手感-链表操作】反转链表-力扣

      题不难,可以学习三种翻转方法。①三个指针交替翻转,其中一个是临时存储用的;②递归,如果当前节点或其下一个节点等于NULL,就直接返回当前节点。否则,新建一个res节点,保存下一层递归的返回值,下一层递归的参数的是当前头节点的下一个节点。在每一层中让当前节点的next节点的next指向自己,之后当前节点的next节点指向NULL,返回res节点;③神奇版双指针法。从头节点开始,向后遍历。把每个节点的next指向节点保存在头节点指针中,然后该节点的next指向前一个节点。这其中用两个指针保存即可(头节点充当了①方法中的第三个临时节点)。

    参考代码(注:②③的思路和代码都是借鉴huwt的)

     1 /**
     2  * Definition for singly-linked list.
     3  * struct ListNode {
     4  *     int val;
     5  *     ListNode *next;
     6  *     ListNode(int x) : val(x), next(NULL) {}
     7  * };
     8  */
     9 
    10 //法①
    11 class Solution {
    12 public:
    13     ListNode* reverseList(ListNode* head) {
    14         if(head==NULL) return NULL;
    15         if(head->next==NULL) return head;
    16         ListNode* pre=NULL;
    17         ListNode* cur=head;
    18         ListNode* tmp=cur->next;
    19         while(cur!=NULL){
    20             tmp=cur->next;
    21             cur->next=pre;
    22             pre=cur;
    23             cur=tmp;
    24         }
    25         return pre;
    26     }
    27 };
    28 
    29 //法②
    30 class Solution {
    31 public:
    32     ListNode* reverseList(ListNode* head) {
    33         if (head == NULL || head->next == NULL) {
    34             return head;
    35         }
    36         ListNode* ret = reverseList(head->next);
    37         head->next->next = head;
    38         head->next = NULL;
    39         return ret;
    40     }
    41 };
    42 
    43 //法③
    44 class Solution {
    45 public:
    46     ListNode* reverseList(ListNode* head) {
    47         if (head == NULL) { return NULL; }
    48         ListNode* cur = head;
    49         while (head->next != NULL) {
    50             ListNode* t = head->next->next;
    51             head->next->next = cur;
    52             cur = head->next;
    53             head->next = t;
    54         }
    55         return cur;
    56     }
    57 };
    View Code

     6.28

    【思维】判断路径是否相交-力扣

      很有意思的题,难在思路,实现很简单,过目不忘。

    参考代码:

     1 //大佬解答,妙!
     2 class Solution {
     3 public:
     4     bool isPathCrossing(string path) {
     5         set<pair<int, int> > st;
     6         st.insert(make_pair(0, 0));
     7         int x = 0, y = 0;
     8         for(char c : path) {
     9             if(c == 'N') {
    10                 x++;
    11             }
    12             else if(c == 'S') x--;
    13             else if(c == 'E') y++;
    14             else y--;
    15             auto tmp = make_pair(x, y);
    16             if(st.count(tmp)) return true;//每点状态唯一,储存状态到set,只需判断是否遇到过该状态
    17             st.insert(tmp);
    18         }
    19         return false;
    20     }
    21 };
    View Code

    【双指针】长度最小的子数组-力扣

      三种做法(为何没有一下看破!面壁去)。①极端的暴力循环法/前缀和暴力循环法;②前缀和+二分查找法;③双指针法。

      看到求“xx最小的子问题”的题就以为是动规,结果自己跳坑了。

    参考代码:

     1 //①自己用了前缀和暴力循环法过的
     2 class Solution {
     3 public:
     4     int minSubArrayLen(int s, vector<int>& nums) {
     5         int n=nums.size();
     6         vector<int> ori=nums;
     7         int sum[33200]={0};
     8         for(int i=0;i<n;++i){
     9             if(i>0) sum[i]=sum[i-1]+nums[i];
    10             else sum[0]=nums[0];
    11         }
    12         sort(nums.begin(),nums.end());
    13         int tmp=0, mmin=0, mmax=0;
    14         for(int i=0;i<n;++i){
    15             tmp+=nums[i];
    16             if(tmp>=s){
    17                 mmax=i+1;
    18                 break;
    19             }
    20         }
    21         tmp=0;
    22         for(int i=n-1;i>=0;--i){
    23             tmp+=nums[i];
    24             if(tmp>=s){
    25                 mmin=n-i;
    26                 break;
    27             }
    28         }
    29         if(mmin==mmax) return mmin;
    30         for(int k=mmin;k<=mmax;++k){
    31             for(int i=0;i<n;++i){
    32                 if(i+k-1>=n) break;
    33                 if(sum[i+k-1]-sum[i]+ori[i]>=s) return k;
    34             }
    35         }
    36         return 0;
    37     }
    38 };
    39 
    40 //②前缀和+二分查找法
    41 class Solution {
    42 public:
    43     int minSubArrayLen(int s, vector<int>& nums) {
    44         int n = nums.size();
    45         if (n == 0) {
    46             return 0;
    47         }
    48         int ans = INT_MAX;
    49         vector<int> sums(n + 1, 0); //可以动态指定vector长度
    50         for (int i = 1; i <= n; i++) {
    51             sums[i] = sums[i - 1] + nums[i - 1];
    52         }
    53         for (int i = 1; i <= n; i++) {
    54             int target = s + sums[i - 1];
    55             auto bound = lower_bound(sums.begin(), sums.end(), target);//啊别忘二分查找
    56             if (bound != sums.end()) {
    57                 ans = min(ans, static_cast<int>((bound - sums.begin()) - (i - 1)));
    58             }
    59         }
    60         return ans == INT_MAX ? 0 : ans;
    61     }
    62 };
    63 
    64 //③双指针法
    65 class Solution {//双指针法。一开始想到要用动规储存,但实际上不用储存,用双指针即可。
    66 public:
    67     int minSubArrayLen(int s, vector<int>& nums) {
    68         int n = nums.size();
    69         if (n == 0) {
    70             return 0;
    71         }
    72         int ans = INT_MAX;
    73         int start = 0, end = 0;
    74         int sum = 0;
    75         while (end < n) {
    76             sum += nums[end];
    77             while (sum >= s) {//满足条件就更新答案
    78                 ans = min(ans, end - start + 1);
    79                 sum -= nums[start];
    80                 start++;
    81             }
    82             end++;//每次迭代end往右移
    83         }
    84         return ans == INT_MAX ? 0 : ans;
    85     }
    86 };
    View Code

     6.29

    【思维-哈希表】罗马数字转整数-力扣

      有、意思的题,不要想复杂了。

    参考代码:

     1 class Solution {
     2 public:
     3     int romanToInt(string s) {
     4         int len=s.length(), ans=0;
     5         int v[22]={0};//类似于哈希的存储方式
     6         v[(int)'I'-67]=1;
     7         v[(int)'V'-67]=5;
     8         v[(int)'X'-67]=10;
     9         v[(int)'L'-67]=50;
    10         v[(int)'C'-67]=100;
    11         v[(int)'D'-67]=500;
    12         v[(int)'M'-67]=1000;
    13         for(int i=0;i<len;++i){
    14             if(i==len-1) ans+=v[(int)s[i]-67];
    15             else{
    16                 int a=v[(int)s[i]-67];
    17                 int b=v[(int)s[i+1]-67];
    18                 if(a<b){
    19                     ans+=b-a;
    20                     i++;
    21                 }
    22                 else ans+=a;
    23             }
    24         }
    25         return ans;
    26     }
    27 };
    View Code

      此题的姊妹篇是 整数转罗马数字-力扣

    虽然这两道题在LeetCode上分别被标为简单、中等难度,我私以为后一道题更容易通过找规律,只需求解一个“类”进制转换问题。

    参考代码:

     1 class Solution {
     2 public:
     3     string digit(int x, string one, string five, string ten){
     4         string d="";
     5         if(x<4){
     6             for(int i=0;i<x;++i){
     7                 d+=one;
     8             }
     9             return d;
    10         }
    11         else if(x==4) return one+five;
    12         else if(x==9) return one+ten;
    13         else{
    14             d=five;
    15             for(int i=0;i<x-5;++i){
    16                 d+=one;
    17             }
    18             return d;
    19         }
    20         return "error";
    21     }
    22     string intToRoman(int num) {
    23         string d1, d2, d3, d4;
    24         d1=digit(num%10,"I","V","X");
    25         num/=10;
    26         d2=digit(num%10,"X","L","C");
    27         num/=10;
    28         d3=digit(num%10,"C","D","M");
    29         num/=10;
    30         d4=digit(num%10,"M","Error1","Error2");
    31         return d4+d3+d2+d1;
    32     }
    33 };
    View Code

     6.30

    【复习-STL】用两个栈实现队列-力扣

      程设作业似乎做过一道类似的,复习一下也好。

    参考代码:

     1 class CQueue {
     2 public:
     3     stack<int> pos;
     4     stack<int> rev;
     5     CQueue() {//栈的函数有push top pop
     6     }
     7     void appendTail(int value) {
     8         rev.push(value);
     9     }
    10     int deleteHead() {
    11         if(!pos.empty()){
    12             int tmp=pos.top();
    13             pos.pop();
    14             return tmp;
    15         }
    16         while(!rev.empty()){
    17             int tmp=rev.top();
    18             pos.push(tmp);
    19             rev.pop();
    20         }
    21         if(!pos.empty()){
    22             int tmp=pos.top();
    23             pos.pop();
    24             return tmp;
    25         }
    26         return -1;
    27     }
    28 };
    29 
    30 /**
    31  * Your CQueue object will be instantiated and called as such:
    32  * CQueue* obj = new CQueue();
    33  * obj->appendTail(value);
    34  * int param_2 = obj->deleteHead();
    35  */
    View Code

     7.1

    【复习-动规】最长重复子数组-力扣

      这题与“最长公共子序列”有点相似,但暗含有连续数组的意思,某种程度上简化了状态转移方程。节省内存可以用一维数组,对数组剩下的那一维度逆序遍历,省略的维度顺序遍历即可。

    参考代码:

     1 class Solution {
     2 public:
     3     int findLength(vector<int>& A, vector<int>& B) {
     4         int la=A.size();
     5         int lb=B.size();
     6         int dp[1100], ans=0;
     7 //dp[i][j]表示A的前i个元素形成的数组与B的前j个元素的数组的最长重复子数组个数,简化为一维dp[j]
     8         memset(dp,0,sizeof(dp));
     9         for(int i=1;i<=la;++i){
    10             for(int j=lb;j>=1;--j){
    11                 if(A[i-1]==B[j-1]) dp[j]=dp[j-1]+1;
    12                 else dp[j]=0;
    13                 ans=max(ans,dp[j]);//因为只有连续才有值,故答案是所有值中的最大者,而不是最后者
    14             }
    15         }
    16         return ans;
    17     }
    18 };
    View Code

     7.2

    【强行广搜】有序矩阵中第K小的元素-力扣

      一眼就能看出这是道排序题,且数据是部分有序的,正常做法是暴力排序、归并排序、二分查找。写题的时候傻了,居然每层循环sort了一次,生生给stl排序的时间复杂度乘了个n[扶额]……当时一拍脑袋,莫非这题不是在考排序查找?于是反手写了一个广搜,把元素从左上角依次加入优先队列,虽然慢点,但过了。有被自己的脑回路笑到,罢,就当复习广搜算了。

    参考代码:

     1 //正常暴力排序(其他复杂度较小的排序用别的题练吧,此题不写了)
     2 class Solution {
     3 public:
     4     int kthSmallest(vector<vector<int>>& matrix, int k) {
     5         int n=matrix.size();
     6         vector<int> my;
     7         for(int i=0;i<n;++i){
     8             for(int j=0;j<n;++j){
     9                 my.push_back(matrix[i][j]);
    10             }
    11         }
    12         sort(my.begin(),my.end());
    13         return my[k-1];
    14     }
    15 };
    16 
    17 //非常规做法—广搜
    18 class Solution {
    19 public:
    20     struct Node{
    21         int x, y, v;
    22         Node(){}
    23         Node(int i, int j, int vv):x(i),y(j),v(vv){}
    24         bool operator < (Node b) const {
    25             return v>b.v;//变成小顶堆
    26         }
    27     };
    28     bool vis[300][300];
    29     int kthSmallest(vector<vector<int>>& matrix, int k) {
    30         int n=matrix.size(), cnt=0;
    31         priority_queue<Node> my;
    32         memset(vis, 0, sizeof(vis));
    33         vis[0][0]=1;
    34         my.push(Node(0,0,matrix[0][0]));
    35         while(!my.empty()){
    36             cnt++;
    37             Node tmp=my.top();
    38             if(cnt==k) return tmp.v;
    39             int tx=tmp.x, ty=tmp.y;
    40             my.pop();
    41             if(tx+1<n && !vis[tx+1][ty]){
    42                 my.push( Node(tx+1, ty, matrix[tx+1][ty]) );
    43                 vis[tx+1][ty]=1;
    44             }
    45             if(ty+1<n && !vis[tx][ty+1]){
    46                 my.push( Node(tx, ty+1, matrix[tx][ty+1]) );
    47                 vis[tx][ty+1]=1;
    48             }
    49         }
    50         return -1;
    51     }
    52 };
    View Code

     刚刚看到Sun君的优先队列解法很棒,mark一下。神奇版代码:

     1 class Solution {
     2 public:
     3     int kthSmallest(vector<vector<int>>& matrix, int k) {
     4         priority_queue<int> pq;
     5         for (int i = 0; i < matrix.size(); ++i) {
     6             for (int j = 0; j < matrix[0].size(); ++j) {
     7                 pq.push(matrix[i][j]);
     8                 if (pq.size() > k) pq.pop();
     9             }
    10         }
    11         return pq.top();
    12     }
    13 };
    View Code

     7.3

    今天事有点多,终于把小组分工定了[呼],明天补上!

    7.4

    【栈】有效的括号-力扣

      思路:A.倒序遍历,先找到最右边的左括号,再二次遍历其右边的紧邻是否匹配;B.遍历,对左括号入栈,遇到匹配的右括号就弹出栈顶元素;栈顶不匹配或未匹配完时栈已经为空,返回false。优化:长度为0直接返回true,长度为奇数直接返回false。

    B思路的参考代码:

     1 class Solution {
     2 public:
     3     bool isValid(string s) {
     4         int n=s.length();
     5         if(n%2) return false;
     6         if(!n) return true;
     7         stack<int> my;
     8         for(int i=0;i<n;++i){
     9             if(s[i]=='('|| s[i]=='['|| s[i]=='{') my.push(s[i]);
    10             else{
    11                 if(my.empty()) return false;
    12                 int tmp=(int)s[i] +(int)my.top();
    13                 if(tmp==81||tmp==184||tmp==248) my.pop();//为了简便损失了可读性,这里实际上就是判断栈顶元素是否是当前元素的左括号
    14                 else return false;
    15             }
    16         }
    17         if(!my.empty()) return false;
    18         return true;
    19     }
    20 };
    View Code

     【动规】最长有效括号-力扣

       dalao的三种思路,太妙了!①动规,状态转移方程极妙;②栈,先入栈虚拟头元素-1;③正序逆序两次遍历,动态储存左右括号的数量与最新答案

    动规思路的参考代码:

     1 class Solution {
     2 public:
     3     int dp[21000];//记录结尾是右括号的 前i个字符中最长有效括号位数
     4     int ans=0;
     5     int longestValidParentheses(string s) {
     6         int n=s.length();
     7         if(n==0) return 0;
     8         memset(dp,0,sizeof(dp));
     9         if(s[0]=='(' && s[1]==')') dp[1]=2;
    10         for(int i=2;i<n;++i){
    11             if(s[i]=='(') continue;//结尾是左括号的直接=0,实际上也阻断了前面连续有效括号对后面计数的影响,这个0值起到了分隔的作用
    12             if(s[i-1]=='(') dp[i]=dp[i-2]+2;//最右边恰好是一对括号
    13             else if(i-1-dp[i-1]>=0 && s[i-1-dp[i-1]]=='('){//最右边是两个右括号,向前找靠右侧的右括号对应的左括号,能找到则分几段计数(理解重点)
    14                 dp[i]=dp[i-1]+2;
    15                 if(i-2-dp[i-1]>=0) dp[i]+=dp[i-2-dp[i-1]];
    16             }
    17         }
    18         for(int i=0;i<n;++i){
    19             ans=max(ans,dp[i]);
    20         }
    21         return ans;
    22     }
    23 };
    View Code

     【递归回溯】括号生成-力扣

    参考代码:

     1 class Solution {
     2 public:
     3     vector<string> my;
     4     int Max=-1;//设一个全局变量记录需要的最大左(右)括号数
     5     void gen(string s, int k, int l, int r){//k是剩余递归层数,l和r分别记录当前左右括号个数。基本原则是在每层递归,Max>=左括号数>=右括号数
     6         if(k==0){
     7             my.push_back(s);
     8             return;
     9         }
    10         if(l==r){
    11             gen(s+"(", k-1, l+1, r);
    12         }
    13         else{
    14             if(l<Max) gen(s+"(", k-1, l+1, r);
    15             gen(s+")", k-1, l, r+1);
    16         }
    17         return;
    18     }
    19     vector<string> generateParenthesis(int n) {
    20         Max=n;
    21         gen("", 2*n, 0, 0);
    22         return my;
    23     }
    24 };
    View Code

     7.5

    【匹配】通配符匹配-力扣

      这题写的真心心累……13次提交终于通过,真的是面向测试样例编程了hhh 歇够了再去看dalao更好的题解。

      自己的两种思路:①递归/去除连续的 '*' 之后的递归,对具体字母和 '?' 匹配一个字符,不匹配return false,对 '*' 递归匹配。但这两种写法都会超时;

      ②对模式串中的具体字符块搜索匹配。先忽略模式串中的'*',对字母和?搜索最近的匹配,不能匹配的/匹配串未匹配完的return false。但对特殊情况的处理超级麻烦,列举几个特殊情况:a.模式串尾部有*,但匹配串未匹配完,不能返回false;b.模式串头部无*,其余部分都能匹配上,但匹配串头部有未匹配部分,要返回false……另外,要非常小心访问越界。

    参考代码:

     1 class Solution {
     2 public:
     3     int myRuntime=0;//用来记录是否是第一个匹配块,处理模式串头部无*,匹配串头部未匹配的特殊情况
     4     int myfind(int from, string ss, string pp){
     5         myRuntime++;
     6         int lst_loc=-1, lss=ss.length();
     7         for(int i=from;i<lss;++i){
     8             for(int j=0;j<pp.length();++j){
     9                 if(i+j>=lss) return -1;
    10                 if(ss[i+j]!=pp[j] && pp[j]!='?') break;
    11                 if(j==pp.length()-1){
    12                     lst_loc=i+j;
    13                     return lst_loc;
    14                 }
    15             }
    16         }
    17         return -1;
    18     }
    19     bool isMatch(string s, string p) {
    20         int ls=s.length(), lp=p.length();
    21         if(ls==0){
    22             for(int i=0;i<lp;++i){
    23                 if(p[i]!='*') return false;
    24             }
    25             return true;
    26         }
    27         if(lp==0) return false;
    28         int ptr=0, lstres=0;
    29         string ptmp="";
    30         for(int i=0;i<lp;++i){
    31             while(p[i]!='*' && i<lp){
    32                 ptmp+=p.substr(i,1);
    33                 i++;
    34             }
    35             if(ptmp!=""){
    36                 int itmp=myfind(ptr, s, ptmp);
    37                 if(myRuntime==1 && p[0]!='*' && itmp+1-ptmp.length()!=0) return false;
    38                 if(itmp==-1) return false;
    39                 else ptr=itmp+1;
    40             }
    41             ptmp="";
    42         }
    43         if(ptr!=ls && p[lp-1]!='*'){
    44             string ptmp2="";
    45             int lpp=ptmp2.length(), i2=0;
    46             for(i2=lp-1;i2>=0;--i2){
    47                 if(p[i2]=='*') break;
    48                 while(i2>=0 && p[i2]!='*'){
    49                     ptmp2=p.substr(i2,1)+ptmp2;
    50                     i2--;
    51                 }
    52                 if(i2<0 || p[i2]=='*') break;
    53             }
    54             lpp=ptmp2.length();
    55             for(int j=0;j<lpp;++j){
    56                 if(ls-1-j<0 || s[ls-1-j]!=ptmp2[lpp-1-j] && ptmp2[lpp-1-j]!='?') return false;
    57             }
    58             if(i2<0 || p[i2]!='*') return false;
    59         }
    60         return true;
    61     }
    62 };
    View Code

     7.6

     7.7

     7.8

    【动规-水题】三步问题-力扣

      优化存储空间!

    参考代码:

     1 class Solution {
     2 public:
     3     int waysToStep(int n) {
     4         int a=1, b=2, c=4, ans=a+b+c;
     5         if(n<3) return n;
     6         if(n==3) return 4;
     7         n=n-3;
     8         while(n--){
     9             ans=((a+b)%1000000007+c)%1000000007;
    10             a=b;
    11             b=c;
    12             c=ans;
    13         }
    14         return ans;
    15     }
    16 };
    View Code

     7.9

    【动规】恢复空格-力扣

      ①动规的思路比较常规,关键是剪枝:1.判断有效的单词长度(直接用set存储所有效的长度即可,也可以再加长度上下界的判断);2.已为0的dp[i]直接continue

      ②另一种思路是“字典树”,稍后整理。

    参考代码:

     1 //法1:动规,根据有效的单词长度进行状态转移
     2 class Solution {
     3 public:
     4     int min_res[1000];//对前下标<=k的k个字符形成的串断句,能达到的最小未识别字符数
     5     int sml=2000, big=0;
     6     int min(int a, int b){
     7         if(a<=b) return a;
     8         return b;
     9     }
    10     int max(int a, int b){
    11         if(a>=b) return a;
    12         return b;
    13     }
    14     int respace(vector<string>& dictionary, string sentence) {
    15         int slen=sentence.length(), dlen=dictionary.size();
    16         if(dlen==0 || slen==0) return slen;
    17         set<string> newdic;
    18         set<int> mylen;
    19         for(string str : dictionary){
    20             newdic.insert(str);
    21             mylen.insert(str.length());
    22         }
    23         for(int i=0;i<dlen;++i){
    24             sml=min(sml,dictionary[i].length());
    25             big=max(big,dictionary[i].length());
    26             newdic.insert(dictionary[i]);
    27         }
    28         for(int k=0;k<slen;++k){
    29             string tmp;
    30             tmp.assign(sentence, 0, k+1);
    31             if(newdic.find(tmp)!=newdic.end()) min_res[k]=0;
    32             else min_res[k]=k+1;
    33             for(int i=sml;i<=k && i<=big;++i){
    34                 if(mylen.find(i)==mylen.end()) continue;
    35                 tmp.assign(sentence, k-i+1, i);
    36                 if(newdic.find(tmp)!=newdic.end()) min_res[k]=min(min_res[k], min_res[k-i]);
    37             }
    38             for(int i=1;;++i){
    39                 if(k-i<0) break;
    40                 if(i>=min_res[k]-min_res[k-i]) break;
    41                 min_res[k]=min(min_res[k], min_res[k-i]+i);
    42             }
    43         }
    44         return min_res[slen-1];
    45     }
    46 };
    View Code

    ……(这几天都有做题,参加了两场周赛,有点疲于总结了,不好不好。赶快补上啊)

     7.13

    【二叉树】重建二叉树-力扣

      复习二叉树构建和遍历的好题。①自己的方法,额外开了内存进行递归,慢且笨拙;②dalao的方法-自己复刻了一遍,巧用STL的find函数,用指针传参也很有魅力hhh

    参考代码:

     1 /**
     2  * Definition for a binary tree node.
     3  * struct TreeNode {
     4  *     int val;
     5  *     TreeNode *left;
     6  *     TreeNode *right;
     7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     8  * };
     9  */
    10 //法①
    11 class Solution {
    12 public:
    13     TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    14         int n=preorder.size();
    15         TreeNode* root= new TreeNode;
    16         root->right=new TreeNode;
    17         root->left =new TreeNode;
    18         if(n<=1){
    19             if(n==0) root=NULL;
    20             else{
    21                 root->val=preorder[0];
    22                 root->right=NULL;
    23                 root->left=NULL;
    24             }
    25             return root;
    26         }
    27         root->val=preorder[0];
    28         vector<int> pretmp1;
    29         vector<int> pretmp2;
    30         vector<int> intmp1;
    31         vector<int> intmp2;
    32         bool find=false;
    33         for(int i=0;i<n;++i){
    34             if(inorder[i]==preorder[0]){
    35                 find=true;
    36                 if(i>0) pretmp1.push_back(preorder[i]);
    37             }
    38             else if(!find){
    39                 if(i>0) pretmp1.push_back(preorder[i]);
    40                 intmp1.push_back(inorder[i]);
    41             }
    42             else{
    43                 pretmp2.push_back(preorder[i]);
    44                 intmp2.push_back(inorder[i]);
    45             }
    46         }
    47         root->left =buildTree(pretmp1, intmp1);
    48         root->right=buildTree(pretmp2, intmp2);
    49         return root;
    50     }
    51 };
    52 
    53 //法②
    54 /*
    55 *注:学习了TheoWu的做法,自己重写的
    56 *链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/solution/cjian-dan-shi-xian-di-gui-by-theowu/
    57 */
    58 class Solution {
    59 public:
    60     TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    61         return Build(preorder.begin(),preorder.end(),inorder.begin(),inorder.end());
    62     }
    63     TreeNode* Build(vector<int>::iterator pre_s, vector<int>::iterator pre_e, vector<int>::iterator in_s, vector<int>::iterator in_e){
    64         if(pre_s==pre_e) return NULL;
    65         TreeNode* cur=new TreeNode(*pre_s);
    66         auto root=find(in_s, in_e, *pre_s);
    67         cur->left =Build(pre_s+1, pre_s+1+(root-in_s), in_s, root);
    68         cur->right=Build(pre_s+1+(root-in_s), pre_e, root+1, in_e);
    69         return cur;  
    70     }
    71 };
    View Code

     7.14

    【位运算复习】二进制中1的个数-力扣

      水题,练手感……今天没做难题,在找实习了555

    参考代码:

     1 class Solution {
     2 public:
     3     int hammingWeight(uint32_t n) {
     4         int cnt=0;
     5         for(int i=0;i<=31;++i){
     6             if(n&(1<<i)) cnt++;
     7         }
     8         return cnt;
     9     }
    10 };
    View Code

     7.15

    【动规-二叉搜索树】不同的二叉搜索树-力扣

      初步解锁二叉搜索树!此题递归方法的思路自己想出来了,可以引申学习一下卡塔兰数。

    参考代码:

     1 /*
     2 二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树) 
     3 它或者是一棵空树,或者是具有下列性质的二叉树: 
     4 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 
     5 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 
     6 它的左、右子树也分别为二叉查找树。
     7 */
     8 //法① 动规
     9 class Solution {
    10 public:
    11     int Max=0;
    12     int dp[1000];
    13     int f(int k){
    14         if(dp[k]>0 || k==0) return dp[k];
    15         dp[k]=f(k-1)*2;
    16         for(int i=1;i<=k-1;++i){
    17             dp[k]+=f(k-i-1)*f(i);
    18         }
    19         return dp[k];
    20     }
    21     int numTrees(int n) {
    22         memset(dp,0,sizeof(dp));
    23         dp[1]=1;
    24         dp[2]=2;
    25         dp[3]=5;
    26         Max=n;
    27         return f(n);
    28     }
    29 };
    30 
    31 //法② 卡塔兰数计算(力扣提供)
    32 
    33 class Solution {
    34 public:
    35     int numTrees(int n) {
    36         long long C = 1;
    37         for (int i = 0; i < n; ++i) {
    38             C = C * 2 * (2 * i + 1) / (i + 2);
    39         }
    40         return (int)C;
    41     }
    42 };
    View Code

     7.16

    【深搜】判断二分图-力扣

      啊我解锁二分图了!顺带学了点并查集的概念(好机智啊这种数据结构)。此题的两种思路是①深搜/广搜;②并查集。

      匈牙利算法(判断是否图里还有交错的路径)

    参考代码:

     1 //深搜C++代码
     2 class Solution {
     3 public:
     4     bool vis[1000];
     5     int col[1000];
     6     int n;
     7     bool ans=true;
     8     void dfs(int k, int c, vector<vector<int>>& graph){
     9         if(!ans) return;
    10         if(graph[k].size()==0) return;
    11         for(int j=0;j<graph[k].size();++j){
    12             int i=graph[k][j];
    13             if(i==k) continue;
    14             if(vis[i]==1){
    15                 if(col[i]==1-c) continue;
    16                 ans=false;
    17                 return;
    18             }
    19             vis[i]=1;
    20             col[i]=1-c;
    21             dfs(i, 1-c, graph);
    22         }
    23         return;
    24     }
    25     bool isBipartite(vector<vector<int>>& graph) {
    26         memset(vis, 0, sizeof(vis));
    27         memset(col, -1, sizeof(col));
    28         n=graph.size();
    29         for(int i=0;i<n;++i){
    30             for(int j=0;j<graph[i].size();++j){
    31                 if(vis[graph[i][j]]) continue;
    32                 dfs(graph[i][j], 0, graph);
    33             }
    34         }
    35         return ans;
    36     }
    37 };
    View Code
     1 //dalao的Java代码,下次补上自己的C++/Python代码
     2 class Solution {
     3     public boolean isBipartite(int[][] graph) {
     4         // 初始化并查集
     5         UnionFind uf = new UnionFind(graph.length);
     6         // 遍历每个顶点,将当前顶点的所有邻接点进行合并
     7         for (int i = 0; i < graph.length; i++) {
     8             int[] adjs = graph[i];
     9             for (int w: adjs) {
    10                 // 若某个邻接点与当前顶点已经在一个集合中了,说明不是二分图,返回 false。
    11                 if (uf.isConnected(i, w)) {
    12                     return false;
    13                 }
    14                 uf.union(adjs[0], w);
    15             }
    16         }
    17         return true;
    18     }
    19 }
    20 
    21 // 并查集
    22 class UnionFind {
    23     int[] roots;
    24     public UnionFind(int n) {
    25         roots = new int[n]; 
    26         for (int i = 0; i < n; i++) {
    27             roots[i] = i;
    28         }
    29     }
    30 
    31     public int find(int i) {
    32         if (roots[i] == i) {
    33             return i;
    34         }
    35         return roots[i] = find(roots[i]);
    36     }
    37 
    38     // 判断 p 和 q 是否在同一个集合中
    39     public boolean isConnected(int p, int q) {
    40         return find(q) == find(p);
    41     }
    42 
    43     // 合并 p 和 q 到一个集合中
    44     public void union(int p, int q) {
    45         roots[find(p)] = find(q);
    46     }
    47 }
    View Code

     7.17

    【复习-二分】第一个错误的版本-力扣

      等hr(!后来发现是老板)开完会打面试电话ing……刷会题好了。这道题的测试点比较强,所以二分的条件一定要卡好,另外注意一下大整数相加除以2防止溢出的问题

    参考代码:

     1 // The API isBadVersion is defined for you.
     2 // bool isBadVersion(int version);
     3 
     4 class Solution {
     5 public:
     6     int firstBadVersion(int n) {
     7         int l=0, r=n, m;
     8         while(l<r){
     9             m=l/2+r/2+1;
    10             if(isBadVersion(m)) r=m-1;
    11             else l=m;
    12         }
    13         if(isBadVersion(l)) return l;
    14         return l+1;
    15     }
    16 };
    View Code

    7.18

    7.19

    7.20

    7.21

    7.22

    【二分】旋转数组的最小值-力扣

      这几天有收到好消息诶。加油!此题是为了考二分而考二分,不许偷偷排序。总体思路是,被“简单洗牌”的数据,前半部分一定大于等于后半部分。

      值得注意的点是,重复元素会非常干扰二分查找边界,可以在遇到时先去重,或对区间再二分搜索判断。

    参考代码:

     1 //没有去重,对重复点的区间查找
     2 class Solution {//类似于简单洗牌
     3 public:
     4     int minArray(vector<int>& numbers) {
     5         int n=numbers.size();
     6         int s=numbers[0], l=0, r=n-1;
     7         while(r-l>2){
     8             int t=l/2+r/2;
     9             if(numbers[t]<s) r=t;
    10             else if(numbers[t]>s) l=t;
    11             else{//重复!查找该点到结尾是否为包含答案的区间,注意1个ans+(N-1)个x的特殊情况
    12                 int p=t;
    13                 while(numbers[p]==s){
    14                     p++;
    15                     if(p==n-1){
    16                         if(numbers[n-1]==s) r=t-1;
    17                         else l=t+1;
    18                         break;
    19                     }
    20                 }
    21                 if(p!=n-1) l=t+1;
    22             }
    23         }
    24         for(int i=l-3;i<=l+3;++i){
    25             if(i<0||i>=n) continue;
    26             if(numbers[i]<s) return numbers[i];
    27         }
    28         return s;
    29     }
    30 }
    View Code

     8.11

    【二叉树】二叉树的层序遍历-力扣

      今天开始要尽快做完二叉树tag!

      这道题看了提示做出来的,多开一个变量num记录下一层放入的节点个数,每次在队列里处理前num个节点。

    参考代码:

     1 /**
     2  * Definition for a binary tree node.
     3  * struct TreeNode {
     4  *     int val;
     5  *     TreeNode *left;
     6  *     TreeNode *right;
     7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     8  * };
     9  */
    10 class Solution {
    11 public:
    12     vector<vector<int> > levelOrder(TreeNode* root) {
    13         vector<vector<int> > ans;
    14         if(!root) return ans;
    15 
    16         queue<TreeNode*> q;
    17         q.push(root);
    18         int num=1;
    19 
    20         while(!q.empty()){
    21             int ntmp=0;
    22             vector<int> vtmp;
    23             for(int i=0;i<num;++i){
    24                 TreeNode* k=q.front();
    25                 q.pop();
    26                 vtmp.push_back(k->val);
    27                 if(k->left){
    28                     q.push(k->left);
    29                     ntmp++;
    30                 }
    31                 if(k->right){
    32                     q.push(k->right);
    33                     ntmp++;                    
    34                 }
    35             }
    36             ans.push_back(vtmp);
    37             num=ntmp;
    38         }
    39 
    40         return ans;
    41     }
    42 };
    View Code

     8.12

    【深搜/图】克隆图-力扣

      这道题迫使人用哈希表标记访问过的点,因此创建了一个unordered_map<Node*, Node*>,还蛮有意思的一题。刚开始没做出来,看了解答后自己又敲了三遍代码↓

    参考代码:

     1 /*
     2 // Definition for a Node.
     3 class Node {
     4 public:
     5     int val;
     6     vector<Node*> neighbors;
     7     
     8     Node() {
     9         val = 0;
    10         neighbors = vector<Node*>();
    11     }
    12     
    13     Node(int _val) {
    14         val = _val;
    15         neighbors = vector<Node*>();
    16     }
    17     
    18     Node(int _val, vector<Node*> _neighbors) {
    19         val = _val;
    20         neighbors = _neighbors;
    21     }
    22 };
    23 */
    24 
    25 class Solution {
    26 public:
    27     unordered_map<Node*, Node*> vis;
    28     Node* cloneGraph(Node* node) {
    29         if(!node) return NULL;
    30         if(vis.find(node)!= vis.end()) return vis[node];//先返回这种情况会比较快
    31 
    32         Node* Cnode = new Node(node->val);//别忘了new
    33         vis[node] = Cnode;//!!
    34 
    35         for(auto x = node->neighbors.begin(); x!=node->neighbors.end(); ++x){
    36             Cnode->neighbors.push_back(cloneGraph(*x));
    37         }
    38         return Cnode;
    39     }
    40 };
    View Code

     8.13——今天是左撇子日喔!祝左撇子朋友们节日快乐啊[跳跳]

    【字符串】字符串相乘-力扣

      复习大整数乘法。注:对两个大整数,每两位直接相乘,先将整数存储到答案数组对应的位置上、再逐位进制会较快,另外要多预留首一位,在不需要进一位时通过判断去掉。

    参考代码:

     1 class Solution {
     2 public:
     3     string Int2String(int x){
     4         string s = "?";
     5         s[0] = '0'+x;
     6         return s;
     7     }
     8     string multiply(string num1, string num2) {
     9         if (num1 == "0" || num2 == "0") {
    10             return "0";
    11         }
    12         int m = num1.size(), n = num2.size();
    13         vector<int> ansArr( m+n, 0 );//声明vector时预设长度的一种方法,长度为m+n,值均为0(0可以换成其他值)
    14         for(int i = m - 1; i >= 0; --i){
    15             for(int j = n - 1; j >= 0; --j){
    16                 ansArr[i+j+1] += (num1[i]-'0') * (num2[j]-'0');
    17             }
    18         }
    19         for(int i = n + m - 1; i > 0; --i){
    20             ansArr[i - 1] += ansArr[i]/10;
    21             ansArr[i] %= 10;
    22         }        
    23     int p = 0;
    24     string ans = "";
    25     if (ansArr[0]==0) p+=1;
    26     while(p < n+m){
    27         ans = ans + Int2String(ansArr[p]);
    28         p++;
    29     }
    30     return ans;
    31     }
    32 };
    View Code

     【二叉树】填充每个节点的下一个右侧节点指针 II-力扣

      题目要求用常数空间O(1)解答,使用的栈空间不计。故不能用广搜/队列(为啥我用了就超时了555),可以用递归。此题的关键是找到next节点,使用bro临时节点在函数中保存并传参。next指针本身就带有方向,所以和队列存储的原理是一致的。

    参考代码:

     1 /*
     2 // Definition for a Node.
     3 class Node {
     4 public:
     5     int val;
     6     Node* left;
     7     Node* right;
     8     Node* next;
     9 
    10     Node() : val(0), left(NULL), right(NULL), next(NULL) {}
    11     Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
    12     Node(int _val, Node* _left, Node* _right, Node* _next)
    13         : val(_val), left(_left), right(_right), next(_next) {}
    14 };
    15 */
    16 
    17 class Solution {
    18 public:
    19     void Run(Node* root, Node* cousin){
    20         if(!root) return;
    21         Node* bro = cousin;//临时变量bro用来记录和root子节点同一层(即root的下一层)的右边最靠近的节点
    22         if(root->right){
    23             while(bro!=NULL){//while循环的意义是往右走,往下走,直到找到下一层第一个节点
    24                 if(bro->left){
    25                     bro = bro->left;
    26                     break;
    27                 }
    28                 if(bro->right){
    29                     bro = bro->right;
    30                     break;
    31                 }
    32                 bro = bro->next;
    33             }
    34             (root->right)->next = bro;
    35             if(root->left) (root->left)->next = (root->right);
    36             Run(root->right, bro);
    37             Run(root->left, root->right);
    38         }
    39         else if(root->left){
    40             while(bro!=NULL){
    41                 if(bro->left){
    42                     bro = bro->left;
    43                     break;
    44                 }
    45                 if(bro->right){
    46                     bro = bro->right;
    47                     break;
    48                 }
    49                 bro = bro->next;
    50             }
    51             if(root->right){
    52                 (root->right)->next = bro;
    53                 (root->left)->next = (root->right);                
    54             }
    55             else{
    56                 (root->left)->next = bro;
    57             }
    58             Run(root->left, (root->left)->next);
    59             Run(root->right, bro);
    60         }
    61         return ;
    62     }
    63     Node* connect(Node* root) {
    64         Run(root, NULL);
    65         return root;
    66     }
    67 };
    View Code

     【二叉树】二叉树的最近公共祖先-力扣

    参考代码:

     1 /**
     2  * Definition for a binary tree node.
     3  * struct TreeNode {
     4  *     int val;
     5  *     TreeNode *left;
     6  *     TreeNode *right;
     7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     8  * };
     9  */
    10 class Solution {
    11 public:
    12     TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    13         if(!root || root == p || root == q) return root;
    14         TreeNode* l = lowestCommonAncestor(root->left, p, q);
    15         TreeNode* r = lowestCommonAncestor(root->right, p, q);
    16         //当l和r都不为NULL时,只有一种可能,说明p, q与root直接相连,在其左右两侧,应返回root;
    17         //l和r不可能都为NULL(跟丢了p和q)
    18         //故,当r、l其中一个为NULL,应返回另一个值
    19         if(!l) return r;
    20         if(!r) return l;
    21         return root;
    22     }
    23 };
    View Code

     【二叉树】二叉树的序列化与反序列化-力扣

      这题也太难了呜呜……下次应该先看看题目难度再做。参考了forgetthing大佬的代码,又加了点注释↓

    参考代码:

     1 /**
     2  * Definition for a binary tree node.
     3  * struct TreeNode {
     4  *     int val;
     5  *     TreeNode *left;
     6  *     TreeNode *right;
     7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     8  * };
     9  */
    10 class Codec {
    11 public:
    12 
    13     // Encodes a tree to a single string.
    14     string serialize(TreeNode* root) {
    15         string res;
    16         dfs_s(root, res);
    17         return res;
    18     }
    19 
    20     // 前序遍历序列转化为字符串               //注:在字符串中加null和空格,就只需要一种遍历方式
    21     void dfs_s(TreeNode* root, string& res) {
    22         if (!root) {
    23             res += "null ";
    24             return;
    25         }
    26         res += to_string(root->val) + ' ';
    27         dfs_s(root->left, res);
    28         dfs_s(root->right, res);
    29     }
    30 
    31     // Decodes your encoded data to tree.
    32     TreeNode* deserialize(string data) {
    33         // 开始遍历索引
    34         int u = 0;
    35         return dfs_d(data, u);
    36     }
    37 
    38     TreeNode* dfs_d(string& data, int& u) { //还要考虑多位整数的计算
    39         if (u >= data.size()) return NULL;
    40         if (data[u] == 'n') {
    41             u = u + 5;
    42             return NULL;
    43         }
    44         int val = 0, sign = 1;
    45         if (data[u] == '-') sign = -1, u ++ ;
    46         while(data[u] != ' '){val = val * 10 + data[u] - '0'; u++;}
    47         val *= sign;
    48         u = u + 1 ;
    49 
    50         auto root = new TreeNode(val);
    51         root->left = dfs_d(data, u);
    52         root->right = dfs_d(data, u);
    53 
    54         return root;
    55     }
    56 };
    57 
    58 // Your Codec object will be instantiated and called as such:
    59 // Codec codec;
    60 // codec.deserialize(codec.serialize(root));
    View Code

     8.14

      今天也非常特殊,因为今天力扣的每日一题终于是一道做过的题了,而且今天进入新的tag啦,开启"二叉搜索树"!以后会有越来越多的题被做过啊

    【二叉搜索树】验证二叉搜索树-力扣

      这道题算是二叉搜索树入门级的练习,注意整数的边界一定要取满,是2147483648(1<<31)和-2147483648

    参考代码:

     1 /**
     2  * Definition for a binary tree node.
     3  * struct TreeNode {
     4  *     int val;
     5  *     TreeNode *left;
     6  *     TreeNode *right;
     7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     8  * };
     9  */
    10 class Solution {
    11 public:
    12     bool judge(TreeNode* root, int up, int down, int upValid, int downValid){
    13         if(!root) return true;
    14         int ov = root->val;
    15         if(root->left){
    16             int lv = root->left->val;
    17             if( lv >= ov || (downValid && lv <= down) || (upValid && lv >= up) ) return false;
    18             if(!judge(root->left, min(ov, up), down, true, downValid) ) return false;
    19         }
    20         if(root->right){
    21             int rv = root->right->val;
    22             if( rv <= ov || (downValid && rv <= down) || (upValid && rv >= up) ) return false;
    23             if(!judge(root->right, up, max(ov, down), upValid, true) ) return false;
    24         }
    25         return true;
    26     }
    27     bool isValidBST(TreeNode* root) {
    28         if(!root) return true;
    29         return judge(root, 2147483647, -2147483648, false, false);
    30     }
    31 };
    View Code

     【二叉搜索树】二叉搜索树迭代器-力扣

      这题不难,主要考中序遍历。AC后看别人的题解,一大半都是用栈模拟,用vector的人好少hhh

     1 /**
     2  * Definition for a binary tree node.
     3  * struct TreeNode {
     4  *     int val;
     5  *     TreeNode *left;
     6  *     TreeNode *right;
     7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     8  * };
     9  */
    10 class BSTIterator {
    11 public:
    12     vector<int> vals;
    13     int p;
    14     int len;
    15     BSTIterator(TreeNode* root){
    16         Inorder(root);
    17         p = 0;
    18         len = vals.size();
    19     }
    20     void Inorder(TreeNode* root){
    21         if(!root) return;
    22         if(root->left){
    23             Inorder(root->left);
    24         }
    25         vals.push_back(root->val);
    26         if(root->right){
    27             Inorder(root->right);
    28         }
    29         return;
    30     }
    31     /** @return the next smallest number */
    32     int next() {
    33         return vals[p++];
    34     }
    35     
    36     /** @return whether we have a next smallest number */
    37     bool hasNext() {
    38         return p < len;
    39     }
    40 };
    41 
    42 /**
    43  * Your BSTIterator object will be instantiated and called as such:
    44  * BSTIterator* obj = new BSTIterator(root);
    45  * int param_1 = obj->next();
    46  * bool param_2 = obj->hasNext();
    47  */
    View Code

     8.17

    【二叉树】平衡二叉树-力扣

      优化递归效率:自顶而下=>自底而上

    参考代码:

     1 /**
     2  * Definition for a binary tree node.
     3  * struct TreeNode {
     4  *     int val;
     5  *     TreeNode *left;
     6  *     TreeNode *right;
     7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     8  * };
     9  */
    10 
    11 //法一:自顶而下
    12 class Solution {
    13 public:
    14     void cal(TreeNode* rt, int depth, int& ans){
    15         if(!rt) return;
    16         if( !(rt->left) && !(rt->right)){
    17             ans = max(ans, depth);
    18             return;
    19         }
    20         if(rt->left) cal(rt->left, depth+1, ans);
    21         if(rt->right) cal(rt->right, depth+1, ans);
    22         return;
    23     }
    24     int maxDepth(TreeNode* root) {
    25         int ans = 0;
    26         if(!root) return 0;
    27         cal(root, 1, ans);
    28         return ans;
    29     }
    30     bool isBalanced(TreeNode* root) {
    31         if(!root) return true;
    32         int t = maxDepth(root->left) - maxDepth(root->right);
    33         if(t<-1 || t>1) return false;
    34         if(!isBalanced(root->left)) return false;
    35         if(!isBalanced(root->right)) return false;
    36         return true;
    37     }
    38 };
    39 
    40 //法二:自底而上
    41 class Solution {
    42 public:
    43     int cal_h(TreeNode* root){
    44         if(!root) return 0;
    45         int rh = cal_h(root->right);
    46         int lh = cal_h(root->left);
    47         if(rh == -1 || lh == -1 || abs(rh-lh)>1 ) return -1; //返回值非常巧妙!
    48         return max(1+rh, 1+lh);
    49     }
    50     bool isBalanced(TreeNode* root) {
    51         return cal_h(root)>=0;
    52     }
    53 };
    View Code

     【二叉树】删除二叉搜索树中的节点-力扣

      这题还真是有那么点意思。分三种情况,①删除叶子节点,直接删;②存在右子树(也就存在中序后继节点),用中序后继节点的值赋值(或交换,注意一下后面待删除节点的值),然后在右子树中递归删除中序后继节点;③存在左子树(也就存在中序前缀节点),用中序前缀节点的值赋值,然后在左子树中递归删除中序前缀节点

      另外,卡住半天的地方是递归删除时,参数应该传root的左(右)子树的根节点,并把返回值赋给左(右)子树的根节点。

    参考代码:

     1 /**
     2  * Definition for a binary tree node.
     3  * struct TreeNode {
     4  *     int val;
     5  *     TreeNode *left;
     6  *     TreeNode *right;
     7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     8  * };
     9  */
    10 class Solution {
    11 public:
    12     TreeNode* findPre(TreeNode* root){//找中序前缀节点
    13         root = root->left;
    14         while(root->right){
    15             root = root->right;
    16         }
    17         return root;
    18     }
    19     TreeNode* findNext(TreeNode* root){//找到中序后继节点
    20         root = root->right;
    21         while(root->left){
    22             root = root->left;
    23         }
    24         return root;
    25     }
    26     TreeNode* deleteNode(TreeNode* root, int key) {
    27         if(!root) return NULL;
    28         if(root->val == key){
    29             if(!root->left && !root->right){//要删除的是叶子节点
    30                 root = NULL;
    31                 return root;
    32             }
    33             else if(root->right){//要删除的节点存在右孩子,用后继节点的值补充
    34             //右子树向下递归删除该后继节点,后继节点一定没有右孩子
    35             TreeNode* tmp = findNext(root);
    36                 root->val = tmp->val;
    37                 root->right = deleteNode(root->right, tmp->val);
    38                 return root;
    39             }
    40             else if(root->left){//要删除的节点存在左孩子,用前缀节点的值补充
    41             //左子树向下递归删除该前缀节点,前缀节点一定没有左孩子
    42             TreeNode* tmp = findPre(root);
    43                 root->val = tmp->val;
    44                 root->left = deleteNode(root->left, tmp->val);
    45                 return root;
    46             }
    47         }
    48         else if(root->val < key){
    49             root->right = deleteNode(root->right, key);
    50             return root;
    51         }
    52         else{
    53             root->left = deleteNode(root->left, key);
    54             return root;
    55         }
    56         return root;
    57     }
    58 };
    View Code

     8.18

    【二叉树】有序链表转换二叉树-力扣

      这道题与二叉平衡树的构造密切相关,注意构造的方式不唯一。用类似于二分的方法构造平衡二叉树,其合理性和效率可以得到证明

    参考代码:

     1 /**
     2  * Definition for singly-linked list.
     3  * struct ListNode {
     4  *     int val;
     5  *     ListNode *next;
     6  *     ListNode() : val(0), next(nullptr) {}
     7  *     ListNode(int x) : val(x), next(nullptr) {}
     8  *     ListNode(int x, ListNode *next) : val(x), next(next) {}
     9  * };
    10  */
    11 /**
    12  * Definition for a binary tree node.
    13  * struct TreeNode {
    14  *     int val;
    15  *     TreeNode *left;
    16  *     TreeNode *right;
    17  *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
    18  *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    19  *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
    20  * };
    21  */
    22 class Solution {
    23 public:
    24     TreeNode* helper(vector<int>& nums, int left, int right) {
    25         if (left > right) {
    26             return nullptr;
    27         }
    28         // 总是选择中间位置左边的数字作为根节点
    29         int mid = (left + right) / 2;
    30         TreeNode* root = new TreeNode(nums[mid]);
    31         root->left = helper(nums, left, mid - 1);
    32         root->right = helper(nums, mid + 1, right);
    33         return root;
    34     }
    35     TreeNode* sortedListToBST(ListNode* head) {
    36         vector<int> nums;
    37         while(head){
    38             nums.push_back(head->val);
    39             head = head->next;
    40         }
    41         return helper(nums, 0, nums.size() - 1);
    42     }
    43 };
    View Code

     8.21

    【二叉树】数据流中的第K大元素-力扣

      第一次用二叉搜索树的形式完成这种题hh,耗时还真是……一言难尽啊。贴一下自己和大佬YouLookDeliciousC 的代码,大佬在构建树时就已经开始计数了,更快,没有对比没有伤害诶

    参考代码:

      1 /**
      2  * Your KthLargest object will be instantiated and called as such:
      3  * KthLargest* obj = new KthLargest(k, nums);
      4  * int param_1 = obj->add(val);
      5  */
      6 
      7 //1.自己的代码,很笨拙,为了处理特殊情况不得不增加全局变量
      8 class KthLargest {
      9 public:
     10     class MyTree{
     11         public:
     12             int val;
     13             MyTree* left = NULL;
     14             MyTree* right = NULL;
     15             MyTree(int x):val(x){}
     16             MyTree(){}
     17     };
     18     MyTree* build(MyTree* root, vector<int>& nums, int s, int e){
     19         if(s>=e) return NULL;
     20         int t = (e+s)/2;
     21         if(root==NULL){
     22             root = new MyTree(nums[t]);
     23         }
     24         else root->val = nums[t];
     25         root->left = build(root->left, nums, s, t);
     26         root->right = build(root->right, nums, t+1, e);
     27         return root;
     28     }
     29 
     30     int cnt = 0, ans = -1, key = -1, len = -1;
     31     MyTree* Myroot = new MyTree(-1);
     32     bool empty = true;
     33 
     34     void help(MyTree* root, int val){
     35         if(!root){
     36             root = new MyTree(val);
     37             return;
     38         }
     39         if(root->val < val){
     40             if(!root->right){
     41                 root->right = new MyTree(val);
     42                 return;
     43             }
     44             help(root->right, val);
     45             return;
     46         }
     47         else{
     48             if(!root->left){
     49                 root->left = new MyTree(val);
     50                 return;
     51             }
     52             help(root->left, val);
     53             return;
     54         }
     55         return;
     56     }
     57 
     58     void Scan(MyTree* s){
     59         if(!s) return;
     60         Scan(s->right);
     61         cnt++;
     62         if(cnt == key){
     63             ans = s->val;
     64             return;
     65         }
     66         else if (cnt>key){
     67             return;
     68         }
     69         Scan(s->left);
     70         return;
     71     }
     72 
     73     KthLargest(int k, vector<int>& nums) {
     74         key = k;
     75         len = nums.size();
     76         if(len>0) empty = false;
     77         sort(nums.begin(), nums.end());
     78         Myroot = build(Myroot, nums, 0, len );
     79     }
     80     
     81     int add(int val) {
     82         cnt = 0;
     83         ans = -1;
     84         if(empty){
     85             Myroot = new MyTree(val);
     86             empty = false;
     87         }
     88         else help(Myroot, val);
     89         Scan(Myroot);
     90         return ans;
     91     }
     92 };
     93 
     94 //2.大佬的代码,简洁!
     95 class KthLargest {
     96     struct TreeNode{
     97         int val;
     98         int count; // 用以记录以包括当前节点,左右子树在内。有多少个节点。
     99         TreeNode* left;
    100         TreeNode* right;
    101         TreeNode(int x): val(x), left(NULL), right(NULL), count(1) {}
    102     };
    103 public:
    104 
    105     int K;
    106     TreeNode* root;
    107     TreeNode* addNode(TreeNode* root, int val){ // 构建二叉搜索树
    108         if(!root)   return new TreeNode(val);
    109         if(val <= root -> val){ //如果子树的点加一,该节点的count值加一
    110             root -> count += 1;
    111             root -> left = addNode(root -> left, val);
    112         }else{
    113             root -> count += 1;
    114             root -> right = addNode(root -> right, val);
    115         }
    116         return root;
    117     }
    118     int findNum(TreeNode* treeRoot, int k){
    119         int m = 1;
    120         if(treeRoot -> right)   m = treeRoot -> right -> count + 1; //m是root加上右子树的所有节点的数量
    121         if(k == m)  return treeRoot -> val;
    122         if(k < m) { // 右子树的节点大于k,目标点在右子树
    123             return findNum(treeRoot -> right, k);
    124         }
    125         else{
    126             return findNum(treeRoot -> left, k - m); //右子树的节点加上根节点小于k个,目标点在左子树
    127         }
    128     }
    129 
    130 
    131     KthLargest(int k, vector<int>& nums): K(k), root(NULL) {
    132         for(auto i : nums){ //创建二叉搜索树
    133             root = addNode(root, i);
    134         }
    135     }
    136     
    137     int add(int val) {
    138         root = addNode(root, val); //更新子树
    139         return findNum(root, K);
    140     }
    141 };
    View Code

     9.16

    【二分法&快慢指针】寻找重复数-力扣

      挺神奇的一道题,在限制了大小为n+1的数组中只含有1~n的数后,有很多解法。挑两种总结一下下:

    ①二分法(有点强行二分的意思)

      “我们定义cnt[i] 表示nums[] 数组中小于等于 i 的数有多少个,假设我们重复的数是 target,那么[1,target−1]里的所有数满足 cnt[i]≤i,[target,n] 里的所有数满足cnt[i]>i,具有单调性。” ——力扣

    ②快慢指针(Floyd 判圈算法)

      此方法真的惊艳。使用条件为,图中存在环。

      操作:

      “我们先设置慢指针slow 和快指针 fast ,慢指针每次走一步,快指针每次走两步,根据「Floyd 判圈算法」两个指针在有环的情况下一定会相遇,此时我们再将slow 放置起点 0,两个指针每次同时移动一步,相遇的点就是答案。”  ——力扣

      原理:

      “假设环长为 L,从起点到环的入口的步数是 a,从环的入口继续走 b 步到达相遇位置,从相遇位置继续走 c 步回到环的入口,则有 b+c=L,其中 L、a、b、c 都是正整数。根据上述定义,慢指针走了 a+b 步,快指针走了 2(a+b) 步。从另一个角度考虑,在相遇位置,快指针比慢指针多走了若干圈,因此快指针走的步数还可以表示成 a+b+kL,其中 k 表示快指针在环上走的圈数。联立等式,可以得到

    2(a+b)=a+b+kL

    解得 a=kL−b,整理可得

    a=(k-1)L+(L-b)=(k-1)L+c

    从上述等式可知,如果慢指针从起点出发,快指针从相遇位置出发,每次两个指针都移动一步,则慢指针走了 a 步之后到达环的入口,快指针在环里走了 k−1 圈之后又走了 c 步,由于从相遇位置继续走 c 步即可回到环的入口,因此快指针也到达环的入口。两个指针在环的入口相遇,相遇点就是答案。”

    ——力扣

    快慢指针法参考代码:

     1 class Solution {
     2 public:
     3     int findDuplicate(vector<int>& nums) {
     4         int slow = 0, fast = 0;
     5         do {
     6             slow = nums[slow];
     7             fast = nums[nums[fast]];
     8         } while (slow != fast);
     9         slow = 0;
    10         while (slow != fast) {
    11             slow = nums[slow];
    12             fast = nums[fast];
    13         }
    14         return slow;
    15     }
    16 };
    View Code

    -------------1个月100道题的flag倒了,抽空继续完成。现在在准备几天后基于HackerRank的1h笔试网测-------------------- 

    9.26

    【数组&排序】New Year Chaos -HackerRank

      容易超时,需要精确优化。自己的两种思路,仅供参考:①逆序对的数量就是答案,但是用两层循环计数、一半测试点会超时;②原题可看做“冒泡打乱”,就用冒泡排序还原,记录交换位置的次数。注意要不断优化冒泡排序的区间。

    参考代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 vector<string> split_string(string);
     4 
     5 // Complete the minimumBribes function below.//只需完成此函数代码
     6 void minimumBribes(vector<int> q){
     7     int ans = 0, len = q.size();
     8     vector<int> pre(len, 0);
     9     for(int i = 0; i < len; ++i){
    10         if(q[i] - (i+1) > 2){
    11             cout<<"Too chaotic"<<endl;
    12             return;
    13         }
    14     }
    15     int s = 0, e = len - 1;
    16     while(s<e){
    17         while(s < e && q[s] == s+1){
    18             s++;
    19         }
    20         if(s >= e) break;
    21         for(int i = s; i < e; ++i){
    22             if(q[i] > q[i+1]){
    23                 ans += 1;
    24                 swap(q[i], q[i+1]);
    25             }
    26         }
    27     }
    28     cout<<ans<<endl;
    29     return;
    30 }
    31 
    32 int main()
    33 {
    34     int t;
    35     cin >> t;
    36     cin.ignore(numeric_limits<streamsize>::max(), '
    ');
    37 
    38     for (int t_itr = 0; t_itr < t; t_itr++) {
    39         int n;
    40         cin >> n;
    41         cin.ignore(numeric_limits<streamsize>::max(), '
    ');
    42 
    43         string q_temp_temp;
    44         getline(cin, q_temp_temp);
    45 
    46         vector<string> q_temp = split_string(q_temp_temp);
    47 
    48         vector<int> q(n);
    49 
    50         for (int i = 0; i < n; i++) {
    51             int q_item = stoi(q_temp[i]);
    52 
    53             q[i] = q_item;
    54         }
    55 
    56         minimumBribes(q);
    57     }
    58 
    59     return 0;
    60 }
    61 
    62 vector<string> split_string(string input_string) {
    63     string::iterator new_end = unique(input_string.begin(), input_string.end(), [] (const char &x, const char &y) {
    64         return x == y and x == ' ';
    65     });
    66 
    67     input_string.erase(new_end, input_string.end());
    68 
    69     while (input_string[input_string.length() - 1] == ' ') {
    70         input_string.pop_back();
    71     }
    72 
    73     vector<string> splits;
    74     char delimiter = ' ';
    75 
    76     size_t i = 0;
    77     size_t pos = input_string.find(delimiter);
    78 
    79     while (pos != string::npos) {
    80         splits.push_back(input_string.substr(i, pos - i));
    81 
    82         i = pos + 1;
    83         pos = input_string.find(delimiter, i);
    84     }
    85 
    86     splits.push_back(input_string.substr(i, min(pos, input_string.length()) - i + 1));
    87 
    88     return splits;
    89 }
    View Code
    用代码改变世界!就是这样,喵!
  • 相关阅读:
    ASP.Net Core -- Logging
    ASP.Net Core -- 文件上传
    ASP.Net Core -- 依赖注入
    ASP.Net Core -- 领域模型与数据库架构保持同步
    Entity Framework Core -- 种子数据
    ASP.Net Core -- Environment TagHelper
    ASP.Net Core -- 为什么要使用TagHelper?
    dotnet ef 无法执行,因为找不到指定的命令或文件
    ASP.Net Core 3.x -- 中间件流程与路由体系
    ASP.Net Core -- View Components
  • 原文地址:https://www.cnblogs.com/Song-Meow/p/13174708.html
Copyright © 2011-2022 走看看