leetcode 字節跳動模擬面試編程題(持續更新~)


第一次模擬

第一題  字符串中的單詞個數(簡單)

統計字符串中的單詞個數,這里的單詞指的是連續的不是空格的字符。

請注意,你可以假定字符串里不包括任何不可打印的字符。

示例:

輸入: "Hello, my name is John"
輸出: 5
 
 題解:注意開頭的空格。然后就一直while判斷就行了,看看有多少個被空格間隔的單詞。
 
參考代碼:
 1 class Solution {
 2 public:
 3     int countSegments(string s) {
 4         int len=s.length();
 5         int ans=0,i=0;
 6         if(len==0) return 0;
 7         while(i<len)
 8         {
 9             while(s[i]==' ' && i<len) ++i;
10             
11             if(s[i]!=' ' && i<len)
12             {
13                 while(s[i]!=' ' && i<len) ++i;
14                 ans++;
15             }
16         }
17         return ans;
18     }
19 };
C++
 

第二題 兩數相加 II(中等)

給定兩個非空鏈表來代表兩個非負整數。數字最高位位於鏈表開始位置。它們的每個節點只存儲單個數字。將這兩數相加會返回一個新的鏈表。

 

你可以假設除了數字 0 之外,這兩個數字都不會以零開頭。

進階:

如果輸入鏈表不能修改該如何處理?換句話說,你不能對列表中的節點進行翻轉。

示例:

輸入: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出: 7 -> 8 -> 0 -> 7


題解:

  將兩個鏈表反轉之后,按照位置加即可,設置一個進位add;

參考代碼:

 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* addTwoNumbers(ListNode *l1, ListNode *l2) {
12         ListNode* tmp = new ListNode(-1), *cur = tmp;
13         int cnt = 0;
14         l1 = reverseList(l1);
15         l2 = reverseList(l2);
16         while (l1 || l2) {
17             int val1 = l1 ? l1 -> val : 0;
18             int val2 = l2 ? l2 -> val : 0;
19             int sum = val1 + val2 + cnt;
20             cnt = sum / 10;
21             cur -> next = new ListNode(sum % 10);
22             cur = cur -> next;
23             if (l1) l1 = l1 -> next;
24             if (l2) l2 = l2 -> next;
25         }
26         if (cnt) cur -> next = new ListNode(1);
27         return reverseList(tmp -> next);
28     }
29 
30     ListNode* reverseList(ListNode *head) {
31         if (!head) return head;
32         ListNode* dummy = new ListNode(-1);
33         dummy -> next = head;
34         ListNode* cur = head;
35         while (cur -> next) {
36             ListNode *tmp = cur -> next;
37             cur -> next = tmp -> next;
38             tmp -> next = dummy -> next;
39             dummy -> next = tmp;
40         }
41         return dummy -> next;
42     }
43 };
C++

 

第三題 最小區間(困難)

你有 k 個升序排列的整數數組。找到一個最小區間,使得 k 個列表中的每個列表至少有一個數包含在其中。

我們定義如果 b-a < d-c 或者在 b-a == d-c 時 a < c,則區間 [a,b] 比 [c,d] 小。

示例 1:

輸入:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
輸出: [20,24]
解釋: 
列表 1:[4, 10, 15, 24, 26],24 在區間 [20,24] 中。
列表 2:[0, 9, 12, 20],20 在區間 [20,24] 中。
列表 3:[5, 18, 22, 30],22 在區間 [20,24] 中。

注意:

  1. 給定的列表可能包含重復元素,所以在這里升序表示 >= 。
  2. 1 <= k <= 3500
  3. -105 <= 元素的值 <= 105
  4. 對於使用Java的用戶,請注意傳入類型已修改為List<List<Integer>>。重置代碼模板后可以看到這項改動。

題解:

使用優先隊列(小頂堆),首先將k個數組的第一個元素加入隊列,並記錄最大值。然后用最大值-堆頂元素(即最小值)去更新答案。然后把堆頂的元素所在數組的指針向后移,如果已經到達數組末尾則跳出循環,輸出答案。

參考代碼:

 1 class node {
 2 public:
 3     int row;
 4     int col;
 5     int val;
 6     node(int ir, int ic, int iv) {
 7         row = ir;
 8         col = ic;
 9         val = iv;
10     }
11 };
12 
13 class cmp {
14 public:
15     bool operator() (const node &lhs, const node &rhs) {
16         return lhs.val > rhs.val;
17     }
18 };
19 
20 class Solution {
21 public:
22     vector<int> smallestRange(vector<vector<int>>& nums) {
23         priority_queue<node, vector<node>, cmp> pqn;
24         const int k = nums.size();
25         int max = -INT_MAX;
26         for (int i = 0; i < k; i++) {
27             if (nums[i][0] > max) {
28                 max = nums[i][0];
29             }
30             pqn.push(node(i, 0, nums[i][0]));            
31         }
32         int lret = 0;
33         int rret = INT_MAX;
34         bool has_next = true;
35         do {
36             auto min = pqn.top();
37             pqn.pop();
38             //cout << min.val << "," << max << endl;
39             if (max - min.val < rret - lret) {
40                 lret = min.val;
41                 rret = max;
42             }
43             min.col++;
44             if (min.col >= nums[min.row].size()) {
45                 has_next = false;
46             } else {
47                 min.val = nums[min.row][min.col];
48                 if (max < min.val)
49                     max = min.val;
50                 pqn.push(min);
51             }
52         } while(has_next);
53         return {lret, rret};
54     }
55 };
C++

 

第二次模擬

第一題 重復的DNA序列(中等)

所有 DNA 都由一系列縮寫為 A,C,G 和 T 的核苷酸組成,例如:“ACGAATTCCG”。在研究 DNA 時,識別 DNA 中的重復序列有時會對研究非常有幫助。

編寫一個函數來查找 DNA 分子中所有出現超過一次的 10 個字母長的序列(子串)。

 

示例:

輸入:s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT"
輸出:["AAAAACCCCC", "CCCCCAAAAA"]

題解:bitset; 因為只有4個字符,所以可以把字符對應為數字。然后兩個bitset,判斷是否出現過,和是否插入到答案集合。

 

參考代碼:

 1 class Solution {
 2 public:
 3    int charToBit(char c){
 4         switch (c){
 5             case 'A': return 0;
 6             case 'G': return 1;
 7             case 'C': return 2;
 8             case 'T': return 3;
 9         }
10         return -1;  // never happened
11     }
12 
13     vector<string> findRepeatedDnaSequences(string s) {
14         vector<string> res;
15         if(s.size() < 10) return res;
16         bitset<1<<20> S1;
17         bitset<1<<20> S2; // to avoid dulplication
18         //init
19         int val = 0;
20         for(int i=0;i<10;i++){
21             val = val << 2 | charToBit(s[i]);
22         }
23         S1.set(val);
24         int mask = (1 << 20) - 1;
25         for(int i=10;i<s.size();i++){
26             val = ((val << 2) & mask) | charToBit(s[i]);
27             if(S1[val]) {
28                 if (!S2[val]) {
29                     res.push_back(s.substr(i - 9, 10));
30                     S2.set(val);
31                 }
32             }
33             else{
34                 S1.set(val);
35             }
36         }
37         return res;
38     }
39 };
C++

 

第二題 分割數組的最大值(困難)

給定一個非負整數數組和一個整數 m,你需要將這個數組分成 個非空的連續子數組。設計一個算法使得這 個子數組各自和的最大值最小。

注意:
數組長度 滿足以下條件:

  • 1 ≤ n ≤ 1000
  • 1 ≤ m ≤ min(50, n)

示例:

輸入:
nums = [7,2,5,10,8]
m = 2

輸出:
18

解釋:
一共有四種方法將nums分割為2個子數組。
其中最好的方式是將其分為[7,2,5] 和 [10,8],
因為此時這兩個子數組各自的和的最大值為18,在所有情況中最小

題解:動態規划。dp[i][j]:表示前i個數分成j個區間所能得到的最大值的最小值。

轉移方程為:dp[i][j]=min(dp[i][j],max(dp[k][j-1],pre[i]-pre[j]));

 

參考代碼:

 1 class Solution {
 2 public:
 3     int splitArray(vector<int>& nums, int m) 
 4     {
 5         int n=nums.size();
 6         unsigned long long dp[n+2][m+2];
 7         memset(dp,127,sizeof(dp));
 8         unsigned long long sum[n+3];
 9         sum[0]=dp[0][0]=0;
10         for(int i=1;i<=n;i++)
11             sum[i]=sum[i-1]+nums[i-1];
12         for(int i=1;i<=n;i++)
13         {
14             for(int j=1;j<=m;j++)
15             {
16                 for(int k=0;k<i;k++)
17                 {
18                     dp[i][j]=min(dp[i][j],max(dp[k][j-1],sum[i]-sum[k]));
19                 }
20             }
21         }
22         return dp[n][m];
23     }
24 };
C++

 

第三題 樹中距離之和(困難)

給定一個無向、連通的樹。樹中有 N 個標記為 0...N-1 的節點以及 N-1 條邊 。

第 i 條邊連接節點 edges[i][0] 和 edges[i][1] 。

返回一個表示節點 i 與其他所有節點距離之和的列表 ans

示例 1:

輸入: N = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
輸出: [8,12,6,10,10,10]
解釋: 
如下為給定的樹的示意圖:
  0
 / \
1   2
   /|\
  3 4 5

我們可以計算出 dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5) 
也就是 1 + 1 + 2 + 2 + 2 = 8。 因此,answer[0] = 8,以此類

題解:兩遍dfs。

第一次dfs出以0節點為根的每個節點到根節點的間距離和每個節點的子節點數量。

第二次dfs,從根開始,它的子節點到所有節點的距離= ans[root] (當前節點的父節點到所有節點的距離) - count[i](當前節點的子節點的數量,包含自己)+ size (所有節點的數量) -count[i];

 

參考代碼:

 1 class Solution {
 2 public:
 3      vector<unordered_set<int>> tree;
 4     vector<int> res, count;
 5 
 6     vector<int> sumOfDistancesInTree(int N, vector<vector<int>>& edges) {
 7         tree.resize(N);
 8         res.assign(N, 0);
 9         count.assign(N, 1);
10         for (auto e : edges) {
11             tree[e[0]].insert(e[1]);
12             tree[e[1]].insert(e[0]);
13         }
14         dfs(0, -1);
15         dfs2(0, -1);
16         return res;
17 
18     }
19 
20     void dfs(int root, int pre) {
21         for (auto i : tree[root]) {
22             if (i == pre) continue;
23             dfs(i, root);
24             count[root] += count[i];
25             res[root] += res[i] + count[i];
26         }
27     }
28 
29     void dfs2(int root, int pre) {
30         for (auto i : tree[root]) {
31             if (i == pre) continue;
32             res[i] = res[root] - count[i] + count.size() - count[i];
33             dfs2(i, root);
34         }
35     }
36 };
C++

 

 第三次模擬

 第一題 最大連續1的個數 III

給定一個由若干 0 和 1 組成的數組 A,我們最多可以將 K 個值從 0 變成 1 。

返回僅包含 1 的最長(連續)子數組的長度。

 

示例 1:

輸入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2
輸出:6
解釋: 
[1,1,1,0,0,1,1,1,1,1,1]
粗體數字從 0 翻轉到 1,最長的子數組長度為 6。

示例 2:

輸入:A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
輸出:10
解釋:
[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗體數字從 0 翻轉到 1,最長的子數組長度為 10。

 

提示:

  1. 1 <= A.length <= 20000
  2. 0 <= K <= A.length
  3. A[i] 為 0 或 1 

 

 題解:雙指針。保證l到r之間的0的數量為k即可,每次移動r和l記錄r-l的最大值。

 

參考代碼:

 1 class Solution {
 2 public:
 3     int longestOnes(vector<int>& A, int K) {
 4        int left = 0;
 5         int right = 0;
 6         int count = 0;
 7         int count_all = 0;
 8         while(right < A.size())
 9         {
10             while(right < A.size())
11             {
12                 if(K == 0)
13                 {
14                     while(right < A.size() && left == right && A[left] == 0)
15                     {
16                         left++;
17                         right++;
18                     }
19                     if(right == A.size())
20                         break;
21                 }
22                 if(A[right] == 0)
23                     count++;
24                 right++;
25                 while(right < A.size() && A[right] == 1)
26                     right++;
27                 if(count >= K)
28                     break;
29             }
30             count_all = max(count_all,right-left);
31             while(left < A.size()&&count >=K)
32             {
33                 if(A[left] == 0)
34                     count--;
35                 left++;
36             }
37         }
38         return count_all;
39     }
40 
41 };
C++

 

第二題 島嶼的最大面積

給定一個包含了一些 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 class Solution {
 2 public:
 3     int dfs(vector<vector<int>>& grid,int ii,int j)
 4     {
 5         int n=grid.size();int m=grid[0].size();
 6         int dx[4]={0,0,1,-1};
 7         int dy[4]={1,-1,0,0};
 8         grid[ii][j]=0;//把訪問過的1變為0
 9         int sum=1;
10         
11         for(int i=0;i<4;i++)
12         {
13             int x=ii+dx[i];
14             int y=j+dy[i];
15             if(x>=0&&x<n&&y>=0&&y<m&&grid[x][y]==1)
16                 sum+=dfs(grid,x,y);
17         }
18         return sum;
19     }
20     int maxAreaOfIsland(vector<vector<int>>& grid) 
21     {
22         int n=grid.size();
23         if(n==0) return 0;
24         int m=grid[0].size();
25         int ans=0;
26         for(int i=0;i<n;i++)
27         {
28             for(int j=0;j<m;j++)
29             {
30                 if(grid[i][j]==1)
31                     ans=max(dfs(grid,i,j),ans);
32             }
33         }
34         return ans;
35     }
36 };
C++

第三題 求根到葉子節點數字之和

給定一個二叉樹,它的每個結點都存放一個 0-9 的數字,每條從根到葉子節點的路徑都代表一個數字。

例如,從根到葉子節點路徑 1->2->3 代表數字 123

計算從根到葉子節點生成的所有數字之和。

說明: 葉子節點是指沒有子節點的節點。

示例 1:

輸入: [1,2,3]
    1
   / \
  2   3
輸出: 25
解釋:
從根到葉子節點路徑 1->2 代表數字 12.
從根到葉子節點路徑 1->3 代表數字 13.
因此,數字總和 = 12 + 13 = 25.

示例 2:

輸入: [4,9,0,5,1]
    4
   / \
  9   0
 / \
5   1
輸出: 1026
解釋:
從根到葉子節點路徑 4->9->5 代表數字 495.
從根到葉子節點路徑 4->9->1 代表數字 491.
從根到葉子節點路徑 4->0 代表數字 40.
因此,數字總和 = 495 + 491 + 40 = 1026.

 

題解:dfs即可。

 

參考代碼:

 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 private:
12     int ans=0;
13 public:
14 
15     void dfs(TreeNode* root,int num)
16     {
17         if(root->left==NULL && root->right==NULL)
18             ans=ans+num;
19         else
20         {
21             if(root->left) dfs(root->left,num*10+root->left->val);
22             if(root->right) dfs(root->right,num*10+root->right->val);
23         }
24     }
25     int sumNumbers(TreeNode* root) 
26     {
27         if(root==NULL) return 0;
28         dfs(root,root->val);
29         return ans;
30     }
31 };
C++

第四次模擬

第一題 用戶分組

有 n 位用戶參加活動,他們的 ID 從 0 到 n - 1,每位用戶都 恰好 屬於某一用戶組。給你一個長度為 n 的數組 groupSizes,其中包含每位用戶所處的用戶組的大小,請你返回用戶分組情況(存在的用戶組以及每個組中用戶的 ID)。

你可以任何順序返回解決方案,ID 的順序也不受限制。此外,題目給出的數據保證至少存在一種解決方案。

 

示例 1:

輸入:groupSizes = [3,3,3,3,3,1,3]
輸出:[[5],[0,1,2],[3,4,6]]
解釋: 
其他可能的解決方案有 [[2,1,6],[5],[0,4,3]] 和 [[5],[0,6,2],[4,3,1]]。

示例 2:

輸入:groupSizes = [2,1,3,3,3,2]
輸出:[[1],[0,5],[2,3,4]]

 

提示:

  • groupSizes.length == n
  • 1 <= n <= 500
  • 1 <= groupSizes[i] <= n

題解:

把數字和下標存到pair里面,然后按照數字大小排序。然后每次取第一個數字的大小為長度len,從該數字向后的len個數字分為一個組。

參考代碼:

 1 class Solution {
 2 public:
 3     vector<vector<int>> groupThePeople(vector<int>& g) 
 4     {
 5         vector<vector<int>> ans;
 6         int n=g.size();
 7         if(n==0) return ans;
 8         pair<int,int> pi[n];
 9         for(int i=0;i<n;++i)
10             pi[i].first=g[i],pi[i].second=i;
11         sort(pi,pi+n);
12         int len=0;
13         while(len<n)
14         {
15             vector<int> res;
16             int num=pi[len].first;
17             while(num--)
18             {
19                 res.push_back(pi[len].second);
20                 len++;
21             }
22             ans.push_back(res);
23         }
24 
25         return ans;
26     }
27 };
C++

第二題 Excel表列名稱

 

給定一個正整數,返回它在 Excel 表中相對應的列名稱。

例如,

    1 -> A
    2 -> B
    3 -> C
    ...
    26 -> Z
    27 -> AA
    28 -> AB 
    ...

示例 1:

輸入: 1
輸出: "A"

示例 2:

輸入: 28
輸出: "AB"

示例 3:

輸入: 701
輸出: "ZY"

 題解:

十進制轉化為26進制,每次(num-1)%26即可得到一個字符。

參考代碼:

 1 class Solution {
 2 public:
 3     string convertToTitle(int n) {
 4         string res = "";
 5         while(n > 0){
 6             int mod = (n-1) % 26;
 7             res += ('A' + mod);
 8             n = (n-1) / 26;
 9         }
10         reverse(res.begin(), res.end());
11         return res;
12     }
13 };
C++

第三題 旋轉鏈表

 

給定一個鏈表,旋轉鏈表,將鏈表每個節點向右移動 個位置,其中 是非負數。

示例 1:

輸入: 1->2->3->4->5->NULL, k = 2
輸出: 4->5->1->2->3->NULL
解釋:
向右旋轉 1 步: 5->1->2->3->4->NULL
向右旋轉 2 步: 4->5->1->2->3->NULL

示例 2:

輸入: 0->1->2->NULL, k = 4
輸出: 2->0->1->NULL
解釋:
向右旋轉 1 步: 2->0->1->NULL
向右旋轉 2 步: 1->2->0->NULL
向右旋轉 3 步: 0->1->2->NULL
向右旋轉 4 步: 2->0->1->NULL

題解:

先頭尾相連,然后再斷開。

參考代碼:

 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* rotateRight(ListNode* head, int k) 
12     {
13         if(!head||k==0)return head;
14 
15         ListNode *tail=head;
16         int size=1;
17         while(tail->next)
18         {
19             size++;
20             tail=tail->next;
21         }
22         if(k%size==0) return head;
23 
24         tail->next=head;
25         int m=size-k%size;
26         while(m--) tail=tail->next;
27         ListNode *res=tail->next;
28         tail->next=nullptr;
29         return res;
30     }
31 };
C++

 

第五次模擬

第一題 二叉樹的層平均值

給定一個非空二叉樹, 返回一個由每層節點平均值組成的數組.

示例 1:

輸入:
    3
   / \
  9  20
    /  \
   15   7
輸出: [3, 14.5, 11]
解釋:
第0層的平均值是 3,  第1層是 14.5, 第2層是 11. 因此返回 [3, 14.5, 11].

注意:

  1. 節點值的范圍在32位有符號整數范圍內。

題解:

用隊列存儲,每次取一層求平均值。

參考代碼:

 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<double> averageOfLevels(TreeNode* root) {
13         vector<double> res;
14         queue<TreeNode*> que;
15         TreeNode* p,*last=root;
16         double sum=0;
17         int count=0;
18         que.push(root);
19         while(!que.empty())
20         {
21             p=que.front();
22             sum+=(double)p->val;
23             count++;
24             que.pop();
25             if(p->left) que.push(p->left);
26             if(p->right) que.push(p->right);
27             if(p==last)
28             {
29                 res.push_back(sum/(double)count);
30                 sum=count=0;
31                 last=que.back();
32             }
33         }
34         return res;
35     }
36 };
C++

第二題  同構字符串

 

給定兩個字符串 和 t,判斷它們是否是同構的。

如果 中的字符可以被替換得到 ,那么這兩個字符串是同構的。

所有出現的字符都必須用另一個字符替換,同時保留字符的順序。兩個字符不能映射到同一個字符上,但字符可以映射自己本身。

示例 1:

輸入: s = "egg", t = "add"
輸出: true

示例 2:

輸入: s = "foo", t = "bar"
輸出: false

示例 3:

輸入: s = "paper", t = "title"
輸出: true

說明:
你可以假設 和 具有相同的長度。

 

題解:

用兩個umordered_map記錄兩邊的對應關系即可。

參考代碼:

 1 class Solution {
 2 public:
 3     bool isIsomorphic(string s, string t) 
 4     {
 5         unordered_map<char,char> ump,ump2;
 6         int len=s.length();
 7         if(len==0) return true;
 8 
 9         for(int i=0;i<len;++i)
10         {
11             if(ump.count(s[i]))
12             {
13                 if(ump[s[i]]!=t[i])
14                     return false;
15                 continue;
16             }
17             if(ump2.count(t[i]))
18             {
19                 if(ump2.count(t[i])!=s[i])
20                     return false;
21                 continue;
22             }
23             ump[s[i]]=t[i];
24             ump2[t[i]]=s[i];
25             
26         }
27         return true;    
28     }
29 };
C++

 

第三題  括號生成

 

給出 n 代表生成括號的對數,請你寫出一個函數,使其能夠生成所有可能的並且有效的括號組合。

例如,給出 = 3,生成結果為:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

題解:

遞歸生成,中間加一些判斷是否滿足正確的括號匹配規則即可。

參考代碼:

 1 class Solution {
 2 private:
 3     vector<string> ans;
 4 public:
 5 
 6     void dfs(string s,int l,int r,int n)
 7     {
 8         if(l==n && r==n)
 9         {
10             ans.push_back(s);
11             return ;
12         }
13         if(l==r) dfs(s+'(',l+1,r,n);
14         else if(l>r && l<=n && r<=n)
15         {
16             if(l<n) dfs(s+'(',l+1,r,n),dfs(s+')',l,r+1,n);
17             else dfs(s+')',l,r+1,n);
18         }
19     }
20 
21     vector<string> generateParenthesis(int n) 
22     {
23         if(n==0) return ans;
24         dfs("",0,0,n);
25         return ans;
26     }
27 };
C++

第六次模擬

第一題 最長字符串鏈

 

給出一個單詞列表,其中每個單詞都由小寫英文字母組成。

如果我們可以在 word1 的任何地方添加一個字母使其變成 word2,那么我們認為 word1 是 word2 的前身。例如,"abc" 是 "abac" 的前身。

詞鏈是單詞 [word_1, word_2, ..., word_k] 組成的序列,k >= 1,其中 word_1 是 word_2 的前身,word_2 是 word_3 的前身,依此類推。

從給定單詞列表 words 中選擇單詞組成詞鏈,返回詞鏈的最長可能長度。
 

示例:

輸入:["a","b","ba","bca","bda","bdca"]
輸出:4
解釋:最長單詞鏈之一為 "a","ba","bda","bdca"。

 

提示:

  1. 1 <= words.length <= 1000
  2. 1 <= words[i].length <= 16
  3. words[i] 僅由小寫英文字母組成。

 題解:

先按長度排序,然后判斷是否滿足包含關系,然后dp即可。

參考代碼:

 1 class Solution {
 2 
 3     // a是否是b的前身
 4     bool isFor(string& a, string& b) {
 5         if(b.size() - a.size() == 1) {
 6             int i = 0, j = 0;
 7             while(i < a.size() && j < b.size()) {
 8                 if(a[i] == b[j]) i++;
 9                 j++;
10             }
11             if(i == a.size()) return true;
12         }
13         return false;
14     }
15 
16 public:
17 
18     int longestStrChain(vector<string>& words) {
19         if(words.size() < 2)
20             return words.size();
21         
22         vector<int> dp(words.size(), 1);
23         int res = 1;
24         
25         // 按字符串長度遞增排序
26         sort(words.begin(), words.end(), 
27              [](string a, string b) {return a.size() < b.size();});
28         
29         for(int i = 0; i < words.size(); i++) {
30             for(int j = i - 1; j >= 0; j--) {
31                 if(isFor(words[j], words[i])) {
32                     dp[i] = max(dp[i], dp[j] + 1);
33                 }
34             }
35             res = max(res, dp[i]);
36         }
37 
38         return res;
39     }
40 };
C++

 

第二題 分數到小數

 

給定兩個整數,分別表示分數的分子 numerator 和分母 denominator,以字符串形式返回小數。

如果小數部分為循環小數,則將循環的部分括在括號內。

示例 1:

輸入: numerator = 1, denominator = 2
輸出: "0.5"

示例 2:

輸入: numerator = 2, denominator = 1
輸出: "2"

示例 3:

輸入: numerator = 2, denominator = 3
輸出: "0.(6)"

題解:

這里有一個技巧,就是我們可以每次把被除數*10,然后去除以除數,如果出現了被除數重復的情況,就是出現了循環節。

參考代碼:

 1 class Solution {
 2 public:
 3      string fractionToDecimal(int numerator, int denominator) 
 4     {
 5         if(numerator==INT_MIN&&denominator==-1)//邊界條件,沒法直接除,因為除完結果溢出
 6         return "2147483648";
 7         if(numerator==-1&&denominator==INT_MIN)//邊界條件,都是int類型,沒法除
 8         return "0.0000000004656612873077392578125";
 9         int shang=numerator/denominator,yushu=numerator%denominator;//記錄商和余數
10         string res;//最終要返回的string
11         if(double(numerator)/double(denominator)<0)//如果兩個數一正一負
12         {
13             if(shang==0)//如果商為0
14                 res='-'+to_string(abs(shang));//可能有的同學疑惑為什么要這樣處理,比如7/-12,除完shang為0,但是我們要的是-0
15             else
16                 res=to_string(shang);//如果不為0,那么直接處理
17         }
18         else//如果都是正數或者都是負數
19             res=to_string(shang);//直接處理
20         if(yushu==0)//如果余數為0,那么到此為止,返回res就可以了
21             return res;
22         res+='.';//如果還有余數,那么要加個小數點
23         unordered_map<int,int>record;//記錄出現過的余數和余數除以除數得到的商的位置
24         while(yushu!=0)
25         {
26             yushu=abs(yushu);//余數有可能是負的,全都轉為正數
27             denominator=abs(denominator);//除數也轉為正數
28             yushu*=10;//余數乘10,作為新的被除數
29             if(record.count(yushu))//如果之前出現過了這個余數,那么可以取出循環體了
30             {
31                 int start=record[yushu],end=res.size()-1;//start和end表示循環體的開端和末尾
32                 res=res.substr(0,start)+'('+res.substr(start,end-start+1)+')';//加一下括號
33                 return res;//直接返回
34             }
35             record[yushu]=res.size();//如果沒出現過,那么記錄在record中,value是這個余數除以除數得到的商應該放的位置
36             shang=yushu/denominator;//更新商
37             yushu=yushu%denominator;//更新余數
38             res+=to_string(shang);//加入最新的商
39         }
40         return res;//如果一直沒有出現重復的余數,那么最終跳出循環后直接返回res
41     }
42 };
C++

 

第三題  缺失的第一個正數(困難)

 

給定一個未排序的整數數組,找出其中沒有出現的最小的正整數。

示例 1:

輸入: [1,2,0]
輸出: 3

示例 2:

輸入: [3,4,-1,1]
輸出: 2

示例 3:

輸入: [7,8,9,11,12]
輸出: 1

說明:

你的算法的時間復雜度應為O(n),並且只能使用常數級別的空間

題解:

我們把對應的數字放在對應的位置,eg 1放在nums[0]這里,最后去找第一個不對應的位置,即為答案。

參考代碼:

 1 class Solution {
 2 public:
 3     int firstMissingPositive(vector<int>& nums) 
 4     {
 5         int n=nums.size();
 6         if(n==0) return 1;
 7         int flag;
 8         for(int i=0;i<n;++i)
 9         {
10             if(nums[i]>=1&&nums[i]<=n && nums[i]!=(i+1))
11             {
12                 flag=nums[nums[i]-1];
13                 nums[nums[i]-1]=nums[i];
14                 while(flag>=1&&flag<=n && nums[flag-1]!=flag)
15                     swap(flag,nums[flag-1]);
16             }
17         }
18         int ans=n+1;
19         for(int i=0;i<nums.size();++i)
20         {
21             if(nums[i]!=i+1)
22             {
23                 ans=i+1;
24                 break;
25             }
26         }
27         return ans;
28     }
29 };
C++

 

第七次模擬

第一題 長度最小的子數組

 

給定一個含有 n 個正整數的數組和一個正整數 s ,找出該數組中滿足其和 ≥ s 的長度最小的連續子數組。如果不存在符合條件的連續子數組,返回 0。

示例: 

輸入: s = 7, nums = [2,3,1,2,4,3]
輸出: 2
解釋: 子數組 [4,3] 是該條件下的長度最小的連續子數組。

進階:

如果你已經完成了O(n) 時間復雜度的解法, 請嘗試 O(n log n) 時間復雜度的解法。

 

題解:

  雙指針做法。

參考代碼:

 1 class Solution {
 2 public:
 3     int minSubArrayLen(int s, vector<int>& nums)
 4     {
 5         int n = nums.size();
 6         int ans = INT_MAX;
 7         int left = 0;
 8         int sum = 0;
 9         for (int i = 0; i < n; i++) {
10             sum += nums[i];
11             while (sum >= s) {
12                 ans = min(ans, i + 1 - left);
13                 sum -= nums[left++];
14             }
15         }
16         return (ans != INT_MAX) ? ans : 0;
17     }
18 };
C++

 

第二題 交換字符使得字符串相同

有兩個長度相同的字符串 s1 和 s2,且它們其中 只含有 字符 "x" 和 "y",你需要通過「交換字符」的方式使這兩個字符串相同。

每次「交換字符」的時候,你都可以在兩個字符串中各選一個字符進行交換。

交換只能發生在兩個不同的字符串之間,絕對不能發生在同一個字符串內部。也就是說,我們可以交換 s1[i] 和 s2[j],但不能交換 s1[i] 和 s1[j]

最后,請你返回使 s1 和 s2 相同的最小交換次數,如果沒有方法能夠使得這兩個字符串相同,則返回 -1 。

 

示例 1:

輸入:s1 = "xx", s2 = "yy"
輸出:1
解釋:
交換 s1[0] 和 s2[1],得到 s1 = "yx",s2 = "yx"。

示例 2:

輸入:s1 = "xy", s2 = "yx"
輸出:2
解釋:
交換 s1[0] 和 s2[0],得到 s1 = "yy",s2 = "xx" 。
交換 s1[0] 和 s2[1],得到 s1 = "xy",s2 = "xy" 。
注意,你不能交換 s1[0] 和 s1[1] 使得 s1 變成 "yx",因為我們只能交換屬於兩個不同字符串的字符。

示例 3:

輸入:s1 = "xx", s2 = "xy"
輸出:-1

示例 4:

輸入:s1 = "xxyyxyxyxx", s2 = "xyyxyxxxyx"
輸出:4

 

提示:

  • 1 <= s1.length, s2.length <= 1000
  • s1, s2 只包含 'x' 或 'y'

 

題解:

我們可以找出對應位置不同的,並記錄兩種不同的數量即:x : y和y : x的數量,然后對於x : y的,每兩個則需要一次交換,對於y : x也一樣,如果兩則的數量有剩余的話,對於一個x : y和一個y : x則需要兩次交換才行。

參考代碼:

 1 class Solution {
 2 public:
 3     int minimumSwap(string s1, string s2) 
 4     {
 5          int n=s1.length(),m=s2.length();
 6          if(n==0) return 0;
 7          int cnt1=0,cnt2=0,ans=0;
 8 
 9          for(int i=0;i<n;++i)
10          {
11             if(s1[i]=='x'&&s2[i]=='y') cnt1++;
12             if(s1[i]=='y'&&s2[i]=='x') cnt2++; 
13          }
14          ans=ans+cnt1/2+cnt2/2;
15          cnt1%=2;cnt2%=2;
16         ans=ans+cnt1*2;
17          if(cnt1!=cnt2) return -1;
18          else return ans;
19     }
20 };
C++

 

第三題 二叉樹最大寬度

給定一個二叉樹,編寫一個函數來獲取這個樹的最大寬度。樹的寬度是所有層中的最大寬度。這個二叉樹與滿二叉樹(full binary tree)結構相同,但一些節點為空。

每一層的寬度被定義為兩個端點(該層最左和最右的非空節點,兩端點間的null節點也計入長度)之間的長度。

示例 1:

輸入: 

           1
         /   \
        3     2
       / \     \  
      5   3     9 

輸出: 4
解釋: 最大值出現在樹的第 3 層,寬度為 4 (5,3,null,9)。

示例 2:

輸入: 

          1
         /  
        3    
       / \       
      5   3     

輸出: 2
解釋: 最大值出現在樹的第 3 層,寬度為 2 (5,3)。

示例 3:

輸入: 

          1
         / \
        3   2 
       /        
      5      

輸出: 2
解釋: 最大值出現在樹的第 2 層,寬度為 2 (3,2)。

示例 4:

輸入: 

          1
         / \
        3   2
       /     \  
      5       9 
     /         \
    6           7
輸出: 8
解釋: 最大值出現在樹的第 4 層,寬度為 8 (6,null,null,null,null,null,null,7)。

注意: 答案在32位有符號整數的表示范圍內。

題解:

分層記錄即可。(不知道為啥力扣的指針老是編譯錯誤。。。)

參考代碼:

 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     int widthOfBinaryTree(TreeNode* root) 
13     {
14         if(!root) return 0;
15         queue<TreeNode*> Q;
16         Q.push(root);
17         int ans = 0;
18         while(Q.size())
19         {
20             int cnt = Q.size();
21             int f = -1;
22             int r = -1;
23             for(int i = 0;i < cnt;++i)
24             {
25                 TreeNode* cur = Q.front();Q.pop();
26                 if(cur && f < 0) r = f=i;
27                 if(cur) r=i;
28                 if(!cur) 
29                 {
30                     if(f > -1)
31                     {
32                         Q.push(NULL);
33                         Q.push(NULL);
34                     }
35                     continue;
36                 }
37                 
38                 Q.push(cur->left);
39                 Q.push(cur->right);
40             }
41             if(f > -1){
42                 ans = max(ans,r-f+1);
43             }else break;
44             
45         }
46         return ans;
47     }
48 };
C++

 

第八次模擬

第一題 課程表

現在你總共有 n 門課需要選,記為 0 到 n-1

在選修某些課程之前需要一些先修課程。 例如,想要學習課程 0 ,你需要先完成課程 1 ,我們用一個匹配來表示他們: [0,1]

給定課程總量以及它們的先決條件,判斷是否可能完成所有課程的學習?

示例 1:

輸入: 2, [[1,0]] 
輸出: true
解釋: 總共有 2 門課程。學習課程 1 之前,你需要完成課程 0。所以這是可能的。

示例 2:

輸入: 2, [[1,0],[0,1]]
輸出: false
解釋: 總共有 2 門課程。學習課程 1 之前,你需要先完成​課程 0;並且學習課程 0 之前,你還應先完成課程 1。這是不可能的。

說明:

  1. 輸入的先決條件是由邊緣列表表示的圖形,而不是鄰接矩陣。詳情請參見圖的表示法
  2. 你可以假定輸入的先決條件中沒有重復的邊。

提示:

  1. 這個問題相當於查找一個循環是否存在於有向圖中。如果存在循環,則不存在拓撲排序,因此不可能選取所有課程進行學習。
  2. 通過 DFS 進行拓撲排序 - 一個關於Coursera的精彩視頻教程(21分鍾),介紹拓撲排序的基本概念。
  3. 拓撲排序也可以通過 BFS 完成。

 

 題解:

拓撲排序板題。

參考代碼:

 1 class Solution {
 2 public:
 3     bool canFinish(int n, vector<vector<int>>& p) 
 4     {
 5         if(n==0) return true;
 6         vector<int> w[n+1];
 7         int m=p.size(),cnt[n]={0};
 8 
 9         for(int i=0;i<m;++i)
10         {
11             w[p[i][0]].push_back(p[i][1]);
12             cnt[p[i][1]]++;
13         }
14 
15         queue<int> q;
16         for(int i=0;i<n;++i)
17             if(cnt[i]==0) q.push(i);
18         
19         while(!q.empty())
20         {
21             int u=q.front();q.pop();
22             for(int i=0;i<w[u].size();++i)
23             {
24                 int v=w[u][i];
25                 --cnt[v];
26                 if(cnt[v]==0) q.push(v);
27             }
28         }
29         bool flag=true;
30         for(int i=0;i<n;++i)
31             if(cnt[i])
32             {
33                 flag=false;
34                 break;
35             }
36 
37         return flag;
38     }
39 };
C++

 

 

第二題 循環碼排列

給你兩個整數 n 和 start。你的任務是返回任意 (0,1,2,,...,2^n-1) 的排列 p,並且滿足:

  • p[0] = start
  • p[i] 和 p[i+1] 的二進制表示形式只有一位不同
  • p[0] 和 p[2^n -1] 的二進制表示形式也只有一位不同

 

示例 1:

輸入:n = 2, start = 3
輸出:[3,2,0,1]
解釋:這個排列的二進制表示是 (11,10,00,01)
     所有的相鄰元素都有一位是不同的,另一個有效的排列是 [3,1,0,2]

示例 2:

輸出:n = 3, start = 2
輸出:[2,6,7,5,4,0,1,3]
解釋:這個排列的二進制表示是 (010,110,111,101,100,000,001,011)

 

提示:

  • 1 <= n <= 16
  • 0 <= start < 2^n

 

 題解:

格雷碼。

參考代碼:

 1 int gray[70000];
 2 class Solution {
 3 public:
 4     vector<int> circularPermutation(int n, int start) 
 5     {
 6         vector<int> ans;
 7         for (int i = 0; i < (1 << n); ++i)
 8             gray[i] = (i ^ (i >> 1));
 9         int pos = 0;
10         for (int i = 0; i < (1 << n); ++i)
11             if (gray[i] == start) {
12                 pos = i;
13                 break;
14             }
15         for (int i = pos; i < (1 << n); ++i)
16             ans.push_back(gray[i]);
17         for (int i = 0; i < pos; ++i)
18             ans.push_back(gray[i]);
19         return ans;
20     }
21 };
C++

 

 

第三題 和至少為 K 的最短子數組

返回 A 的最短的非空連續子數組的長度,該子數組的和至少為 K 。

如果沒有和至少為 K 的非空子數組,返回 -1 。

 

示例 1:

輸入:A = [1], K = 1
輸出:1

示例 2:

輸入:A = [1,2], K = 4
輸出:-1

示例 3:

輸入:A = [2,-1,2], K = 3
輸出:3

 

提示:

  1. 1 <= A.length <= 50000
  2. -10 ^ 5 <= A[i] <= 10 ^ 5
  3. 1 <= K <= 10 ^ 9

 題解:

單調棧維護前綴和遞增。然后去二分查詢距離最近的滿足條件的數所在位置。

參考代碼:

 1 class Solution {
 2 public:
 3     int shortestSubarray(vector<int>& A, int K) {
 4         if(A.size() == 0)
 5             return -1;
 6         int ans = A.size() + 1;
 7         vector<vector<int>> s; //s中的每一個元素都是一個長度為2的數組{到地址為止的count值,地址}
 8         vector<int> leftBound = {0,-1};
 9         s.push_back(leftBound);
10         int count = 0;
11         for(int i = 0;i<A.size();i++)
12         {
13             if(A[i] >= K)
14                 return 1;
15             //維護到i為止的累加和count
16             count += A[i];
17             //更新ans,需要用二分查找降低時間復雜度
18             int left = 0;
19             int right = s.size() - 1;
20             while(left < right)
21             {
22                 int mid = (left + right) / 2 + 1;
23                 if(count - s[mid][0] < K)
24                     right = mid - 1;
25                 else
26                     left = mid;
27             }
28             if(count - s[left][0] >= K)
29                 ans = min(ans,i-s[left][1]);
30             //維護單調遞增棧s
31             while(s.size() > 0 && s.back()[0] >= count)
32                 s.pop_back();
33             vector<int> temp = {count,i};
34             s.push_back(temp);
35         }
36         return ans <= A.size() ? ans : -1;//檢查是否存在滿足題意的子數組
37     }
38 };
C++

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM