华为面试题库刷题第四次整理。
给你这棵「无向树」,请你测算并返回它的「直径」:这棵树上最长简单路径的 边数。
我们用一个由所有「边」组成的数组 edges 来表示一棵无向树,其中 edges[i] = [u, v] 表示节点 u 和 v 之间的双向边。
树上的节点都已经用 {0, 1, …, edges.length} 中的数做了标记,每个节点上的标记都是独一无二的。
提示:
- 0 <= edges.length < 10^4
- edges[i][0] != edges[i][1]
- 0 <= edges[i][j] <= edges.length
- edges 会形成一棵无向树
解法:很明显的dfs题目,但是最关键的是如何dfs。
我写了一个很傻的遍历dfs,对所有只有一边相连的点进行一次dfs,其实很明显就可以发现有很多冗余的搜索。
代码:
class Solution {
private:
int res = 0;
int l = 0;
public:
void dfs(vector>& b, int k, int d){
bool f = false;
for(int i=0; i>& edges) {
if(edges.empty()) return 0;
l = edges.size();
vector a(l+2,0);
vector> b(l+1,vector(l+1,0));
for(int i=0; i
代码:
class Solution {
private:
vector> graph;
vector visited;
public:
int treeDiameter(vector>& edges) {
// build graph
int l = edges.size();
graph.resize(l+2);
visited.resize(l+2);
for(auto e: edges) {
graph[e[0]].push_back(e[1]);
graph[e[1]].push_back(e[0]);
}
pair p1, p2;
p1 = { -1, 0 };
dfs(0, 0, p1);
visited.assign(l+2, false);
p2 = { -1, 0 };
dfs(p1.first, 0, p2);
return p2.second;
}
void dfs(int u, int depth, pair& p) {
visited[u] = true;
if(depth > p.second) {
p.second = depth;
p.first = u;
}
for(int v: graph[u])
if(!visited[v])
dfs(v, depth + 1, p);
}
};
给出一个二进制数组 data,你需要通过交换位置,将数组中 任何位置 上的 1 组合到一起,并返回所有可能中所需 最少的交换次数。
提示:
- 1 <= data.length <= 10^5
- 0 <= data[i] <= 1
解法:滑动窗口很适合这题,算出1的个数为k,那么窗口的大小就是p,滑动过程中更新窗口内的1的个数,记录下最大的个数q,p-q就是答案。
代码:
class Solution {
public:
int minSwaps(vector& data) {
if(data.empty()) return 0;
int sum = 0;
for(int i=0; i
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
上一题就是一道很好的滑动窗口题,我们趁热打铁,再练一道滑动窗口题(其实是因为刷题群里有个妹子问了这题 雾)。
解法:一开始群里一个热心大佬说这是一道优先队列,我一想,很有道理,然后做了半天发现有问题,窗口移动,需要删除,就不行。这道题还是用多种解法来练习一下。
代码:
class Solution {
public:
vector maxSlidingWindow(vector& nums, int k) {
if(nums.empty() || k==0) return {};
vector res;
for(int i=0; i+k<=nums.size(); i++){
int m = nums[i];
for(int j=i+1; j<=i+k-1; j++)
m = max(m,nums[j]);
res.push_back(m);
}
return res;
}
};
这是这题的正解,用双端队列可以同时维护队头和队尾,队列里面保存索引,队头永远是最大的,超出窗口大小就弹出队头,每次有新元素就从队尾删掉比新元素小的数。
代码:
class Solution {
public:
vector maxSlidingWindow(vector& nums, int k) {
if(nums.empty() || k==0) return {};
deque q;
vector res;
for(int i=0; inums[q.back()])
q.pop_back();
q.push_back(i);
}
res.push_back(nums[q.front()]);
for(int i=k; inums[q.back()])//删除队尾小的索引
q.pop_back();
q.push_back(i);
res.push_back(nums[q.front()]);
}
return res;
}
};
这道题动态规划的思路很巧,这里我贴一下官方的解释,讲的很清楚,我就不多赘述了。 https://leetcode-cn.com/problems/sliding-window-maximum/solution/hua-dong-chuang-kou-zui-da-zhi-by-leetcode-3/
代码:
class Solution {
public:
vector maxSlidingWindow(vector& nums, int k) {
if(nums.empty() || k==0) return {};
vector res,left;
vector right(nums.size(),0);
int t = 0;
for(int i=0; i=0; i--){
if((i+1) % k==0){
t = nums[i];
}
t = max(t,nums[i]);
right[i] = t;
}
for(int i=0; i+k<=nums.size(); i++)
res.push_back(max(left[i+k-1],right[i]));
return res;
}
};
整数可以被看作是其因子的乘积。
例如:
8 = 2 x 2 x 2;
= 2 x 4.
请实现一个函数,该函数接收一个整数 n 并返回该整数所有的因子组合。
注意:
- 你可以假定 n 为永远为正数。
- 因子必须大于 1 并且小于 n。
示例:
输入: 32
输出:
[
[2, 16],
[2, 2, 8],
[2, 2, 2, 4],
[2, 2, 2, 2, 2],
[2, 4, 4],
[4, 8]
]
解法:很明显的dfs,不过难点是存储,根据示例给出的存储顺序,可以找到dfs的思路,每次拆解最后一位就行了,然后保证序列不下降。
代码:
class Solution {
private:
vector> res;//存储答案
public:
void dfs(vector& m){
res.push_back(m);
for(int i=m[m.size()-2]; i<=sqrt(m[m.size()-1]); i++){//拆解最后一位数字,范围:[倒数第二位数,最后一位数的平方根]
if(m[m.size()-1]%i==0){
vector temp(m.begin(),m.end()-1);
temp.push_back(i);
temp.push_back(m[m.size()-1]/i);
dfs(temp);
}
}
}
vector> getFactors(int n) {
for(int i=2; i<=sqrt(n); i++){
if(n%i==0){
vector temp = {i , n/i};
dfs(temp);
}
}
return res;
}
};
本题小结:这题从思路和算法上来看不难,但是递归最怕的其实就是存储,我这里参考的题解里面一位大佬的思路,用一个临时的一维vector来操作,就比较方便了。
你的面前有一堵方形的、由多行砖块组成的砖墙。 这些砖块高度相同但是宽度不同。你现在要画一条自顶向下的、穿过最少砖块的垂线。
砖墙由行的列表表示。 每一行都是一个代表从左至右每块砖的宽度的整数列表。
如果你画的线只是从砖块的边缘经过,就不算穿过这块砖。你需要找出怎样画才能使这条线穿过的砖块数量最少,并且返回穿过的砖块数量。
你不能沿着墙的两个垂直边缘之一画线,这样显然是没有穿过一块砖的。
示例:
输入:
[1,2,2,1],
[3,1,2],
[1,3,2],
[2,4],
[3,1,2],
[1,3,1,1]]
输出: 2
解法:
思路不太难,每一行的砖块加上前面所有的宽度,然后因为这些宽度都是线性递增的,所以加完之后,用hashmap记录一下相同宽度的个数,最大的就是答案的位置。
代码:
class Solution {
public:
int leastBricks(vector>& wall) {
int l = wall.size();
for(int i=0; i a;
for(int i=0; isecond);
}
return l-res;
}
};
还是先求和,然后用一维数组存下所有的宽度,然后sort,比上一种方法快一点点。
代码:
class Solution {
public:
int leastBricks(vector>& wall) {
int l = wall.size();
for(int i=0; i a;
for(int i=0; i
给定一个01矩阵 M,找到矩阵中最长的连续1线段。这条线段可以是水平的、垂直的、对角线的或者反对角线的。
示例:
输入:
[[0,1,1,0],
[0,1,1,0],
[0,0,0,1]]
输出: 3
提示: 给定矩阵中的元素数量不会超过 10,000。
解法:dp题目,只不过得四个方向分别做,代码很长,得注意边界问题。
代码:
class Solution {
public:
int longestLine(vector>& a) {
if(a.empty() || a[0].empty()) return 0;
int m = a.size();
int n = a[0].size();
int res = 0;
for(int i=0; i> dp(m+1,vector(n+1,0));
res = 0;
//横向
dp[0][0] = a[0][0];
for(int i=1; i