11.14灵神题单补充
11/14
每日一题:3249. 统计好节点的数目
思路:看不懂,码住后边看
建树,然后从根节点 0 开始 DFS 这棵树。
DFS 返回子树大小。
对于节点 x,如果其是叶子节点,或者其所有儿子子树大小都一样,那么答案加一。
class Solution {
public:
int countGoodNodes(vector<vector<int>>& edges) {
int n = edges.size() + 1;
vector<vector<int>> g(n);
for (auto& e: edges){
int x = e[0] , y = e[1];
g[x].push_back(y);
g[y].push_back(x);
}
int ans = 0;
auto dfs =[&](auto&& dfs , int x , int fa) -> int {
int size = 1 , sz0 = 0;
bool ok = true;
for (int y : g[x]){
if(y == fa){
continue;
}
int sz = dfs(dfs , y , x);
if (sz0 == 0) sz0 = sz;
else if(sz != sz0) ok = false;
size += sz;
}
ans += ok;
return size;
};
dfs(dfs , 0 , -1);
return ans;
}
};
1290. 二进制链表转整数
思路:遍历链表,每次ans*2 然后加上当前val。
while(cur){ ans = 2 * ans + cur->val; cur = cur->next; }
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
int getDecimalValue(ListNode* head) {
ListNode* cur = head;
int ans = 0;
while(cur){
ans = 2 * ans + cur->val;
cur = cur->next;
}
return ans;
}
};
2058. 找出临界点之间的最小和最大距离
思路:
- 定义三个指针
pre=head、cur、tmp分别指向连续的三个节点,起始cur指向的节点编号n = 2,每次让三个节点依次后移,直到tmp移到链表末。同时判断当前三个节点是否满足最大||最小临界点,满足的话记下cur的编号n,放到vec数组里。while(tmp){ if((cur->val > pre->val && cur->val > tmp->val) || (cur->val < pre->val && cur->val < tmp->val)) vec.push_back(n); pre = pre->next; cur = cur->next; tmp = tmp->next; n ++; }
- 遍历完成后,所有的临界点的序号都已存到vec数组里。对vec数组的大小进行讨论,记
vec.size() = m,如果m为0或1,直接返回{-1 , -1};m>2时,遍历数组找相邻元素的最小差值minD(初始化minD为最大值INT_MAX);由于vec数组有序,最大差值maxD肯定为vec[m - 1] - vec[0]。int m = vec.size(); if(m == 0 || m == 1) return {-1 , -1}; int minD = INT_MAX; for (int i = 1 ; i < m ; i ++){ minD = min(vec[i] - vec[i - 1] ,minD); } int maxD = vec[m - 1] - vec[0];
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
vector<int> nodesBetweenCriticalPoints(ListNode* head) {
if(head->next->next == NULL) return {-1 , -1};
vector<int> res;
vector<int> vec;
int n = 2;
ListNode* pre = head;
ListNode* cur = head->next;
ListNode* tmp = head->next->next;
while(tmp){
if((cur->val > pre->val && cur->val > tmp->val) || (cur->val < pre->val && cur->val < tmp->val)) vec.push_back(n);
pre = pre->next;
cur = cur->next;
tmp = tmp->next;
n ++;
}
int m = vec.size();
if(m == 0 || m == 1) return {-1 , -1};
int minD = INT_MAX;
for (int i = 1 ; i < m ; i ++){
minD = min(vec[i] - vec[i - 1] ,minD);
}
int maxD = vec[m - 1] - vec[0];
res.push_back(minD);
res.push_back(maxD);
return res;
}
};
- 时间复杂度:O(n),其中 n 是给定的链表的长度,vec数组的长度m<n,可忽略。
- 空间复杂度:O(n)。额外开辟数组。
一次遍历链表的写法:只需维护第一个和上一个临界点即可。因为
- 最小距离一定出现在两个相邻的临界点之间;
- 最大距离一定出现在第一个和最后一个临界点之间。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
vector<int> nodesBetweenCriticalPoints(ListNode* head) {
int minDist = -1 , maxDist = -1;
//第一个临界点first、上一个临界点last,pos记录cur当前位置,临界点的差值=pos差值
int first = -1 , last = -1 , pos = 0;
ListNode* cur = head;
while(cur->next->next){
int x = cur->val;
int y = cur->next->val;
int z = cur->next->next->val;
//如果y是临界点
if(y > max(x , z) || y < min(x , z)){
//当不是第一个临界点时
if(last != -1) {
minDist = (minDist == -1 ? pos - last : min(minDist , pos - last));
maxDist = max(maxDist , pos - first);
}
//找到第一个临界点时,把first更新为pos
if(first == -1) first = pos;
last = pos;
}
cur = cur->next;
pos ++;
}
return {minDist , maxDist};
}
};
- 时间复杂度:O(n),其中 n 是给定的链表的长度。
- 空间复杂度:O(1)。。
2181. 合并零之间的节点
思路:没A出来
法一:原地改值。初始设tail = head,用cur遍历整个链表,如果val非0,把值累加存到tail里;碰到0,把tail后移,值赋为0。相当于顺序利用链表的前面的节点原地存和。最后把
tail.next置为空,以确保答案链表不包含原链表中多余的节点。然后返回。
class Solution {
public:
ListNode* mergeNodes(ListNode* head) {
auto tail = head;
for (auto cur = head->next; cur->next; cur = cur->next) {
if (cur->val) {
tail->val += cur->val;
} else {
tail = tail->next;
tail->val = 0;
}
}
// 注:这里没有 delete 剩余节点,可以自行补充
tail->next = nullptr;
return head;
}
};
- 时间复杂度:O(n),其中 n 是链表长度。
- 空间复杂度:O(1)。没有创建新的节点。
递归法:
class Solution {
public:
ListNode* mergeNodes(ListNode* head) {
int sum = 0;
while(1){
head = head->next; //向下移动
sum += head->val;
if(head->val == 0 && head->next){
//返回了下一个0的地址后,修改这层0的数值为sum,结束循环返回head
head->next = mergeNodes(head);
head->val = sum;
break;
}
if(!head->val) {
//如果是最后一个0,直接更改数值即可
head->val = sum;
break;
}
}
return head;
}
};
725. 分隔链表
思路:模拟
题目要求将给定的链表分隔成 k 个连续的部分,分隔成的每个部分的长度和原始链表的长度有关,因此需要首先遍历链表,得到链表的长度 n。
得到链表的长度 n 之后,记
q = ⌊k/n⌋(下取整),r = n mod k,则在分隔成的 k 个部分中,前r个部分的长度各为q+1,其余每个部分的长度各为q。 分隔链表时,从链表的头结点开始遍历,记当前结点为 curr,对于每个部分,进行如下操作:将 curr 作为当前部分的头结点;计算当前部分的长度
Size;将 curr 向后移动Size - 1步,则 curr 为当前部分的尾结点; 当 curr 到达当前部分的尾结点时,需要拆分 curr 和后面一个结点之间的连接关系,在拆分之前需要存储 curr 的后一个结点
cur1;令 curr 的 next 指针指向 null,完成 curr 和 next 的拆分;再将 cur1 赋值给 curr。完成上述操作之后,即得到分隔链表后的一个部分。 重复上述操作,直到分隔出 k 个部分,或者链表遍历结束,curr 指向 null(n < k的情况)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
vector<ListNode*> splitListToParts(ListNode* head, int k) {
ListNode* cur = head;
int n = 0;
while(cur){
n++;
cur = cur->next;
}
int q = n / k , r = n % k;
vector<ListNode*> res(k);
ListNode* curr = head;
for (int i = 0; i < k && curr; i++) {
res[i] = curr;
int size = q + (i < r ? 1 : 0);
for (int j = 0; j < size - 1; j++) {
curr = curr->next;
}
ListNode* cur2 = curr->next;
curr->next = nullptr;
curr = cur2;
}
return res;
}
};
817. 链表组件
思路:遍历模拟
本题就是要找 值存在于数组nums中的节点,有连续的多少段。
因为本题要在遍历链表的过程中判断val是否存在数组中,所以最好先遍历一次nums构造一个哈希表,后边每次找都是o(1)。
另外的技巧是设置
标志位r,链表初始或每次连续间断时把r置1,连续时置0,每次让res+=r。int r = 1; if(check(nums , cur->val)) { res += r; r = 0; } else r = 1;
自己写的,没用哈希表,每次用check()函数判断,效率较低:
class Solution {
private:
bool check(vector<int>& nums , int k){
for (int i = 0; i < nums.size(); i++) {
if(nums[i] == k) return true;
}
return false;
}
public:
int numComponents(ListNode* head, vector<int>& nums) {
ListNode* cur = head;
int res = 0 , r = 1;
while(cur){
if(check(nums , cur->val)) {
res += r;
r = 0;
}
else r = 1;
cur = cur->next;
}
return res;
}
};
构造哈希表:
class Solution {
public:
int numComponents(ListNode* head, vector<int>& nums) {
unordered_set<int> s(nums.begin(), nums.end());//很简单的一行
ListNode* cur = head;
int res = 0 , r = 1;
while(cur){
if(s.count(cur->val)) { //这里注意是cur->val不是head->val
res += r;
r = 0;
}
else r = 1;
cur = cur->next;
}
return res;
}
};


浙公网安备 33010602011771号