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. 找出临界点之间的最小和最大距离

思路:

  1. 定义三个指针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 ++;
        }
  1. 遍历完成后,所有的临界点的序号都已存到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 置为空,以确保答案链表不包含原链表中多余的节点。然后返回。

image-20241114190557433

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;
    }
};
posted @ 2024-11-14 21:50  七龙猪  阅读(1)  评论(0)    收藏  举报
-->