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++

 

 

 

 

 

 

 

 

posted @ 2020-02-11 17:34  StarHai  阅读(2953)  评论(0编辑  收藏  举报