leetcode 字节跳动模拟面试编程题(持续更新~)
第一次模拟
第一题 字符串中的单词个数(简单)
统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。
请注意,你可以假定字符串里不包括任何不可打印的字符。
示例:
输入: "Hello, my name is John" 输出: 5
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 };
第二题 两数相加 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 };
第三题 最小区间(困难)
你有 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 <=
k
<= 3500 - -105 <=
元素的值
<= 105 - 对于使用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 };
第二次模拟
第一题 重复的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 };
第二题 分割数组的最大值(困难)
给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
注意:
数组长度 n 满足以下条件:
- 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 };
第三题 树中距离之和(困难)
给定一个无向、连通的树。树中有 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 };
第三次模拟
第一题 最大连续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 <= A.length <= 20000
0 <= K <= A.length
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 };
第二题 岛屿的最大面积
给定一个包含了一些 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 };
第三题 求根到叶子节点数字之和
给定一个二叉树,它的每个结点都存放一个 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 };
第四次模拟
第一题 用户分组
有 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 };
第二题 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 };
第三题 旋转链表
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 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 };
第五次模拟
第一题 二叉树的层平均值
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组.
示例 1:
输入: 3 / \ 9 20 / \ 15 7 输出: [3, 14.5, 11] 解释: 第0层的平均值是 3, 第1层是 14.5, 第2层是 11. 因此返回 [3, 14.5, 11].
注意:
- 节点值的范围在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 };
第二题 同构字符串
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
示例 1:
输入: s ="egg",
t ="add"
输出: true
示例 2:
输入: s ="foo",
t ="bar"
输出: false
示例 3:
输入: s ="paper",
t ="title"
输出: true
说明:
你可以假设 s 和 t 具有相同的长度。
题解:
用两个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 };
第三题 括号生成
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 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 };
第六次模拟
第一题 最长字符串链
给出一个单词列表,其中每个单词都由小写英文字母组成。
如果我们可以在 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 <= words.length <= 1000
1 <= words[i].length <= 16
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 };
第二题 分数到小数
给定两个整数,分别表示分数的分子 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 };
第三题 缺失的第一个正数(困难)
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
示例 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 };
第七次模拟
第一题 长度最小的子数组
给定一个含有 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 };
第二题 交换字符使得字符串相同
有两个长度相同的字符串 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 };
第三题 二叉树最大宽度
给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(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 };
第八次模拟
第一题 课程表
现在你总共有 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。这是不可能的。
说明:
- 输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵。详情请参见图的表示法。
- 你可以假定输入的先决条件中没有重复的边。
提示:
- 这个问题相当于查找一个循环是否存在于有向图中。如果存在循环,则不存在拓扑排序,因此不可能选取所有课程进行学习。
- 通过 DFS 进行拓扑排序 - 一个关于Coursera的精彩视频教程(21分钟),介绍拓扑排序的基本概念。
-
拓扑排序也可以通过 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 };
第二题 循环码排列
给你两个整数 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 };
第三题 和至少为 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 <= A.length <= 50000
-10 ^ 5 <= A[i] <= 10 ^ 5
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 };