11.20

11/20

304. 二维区域和检索 - 矩阵不可变

思路:二维前缀和

首先,构造sum数组的过程是计算(0,0)到(x , y)围成的矩阵的和。利用周围的点和该坐标点

标准写法:s[i] [j] = s[i-1][j] + s[i][j-1 ] - s[i-1][ j-1] + a[i] [j]

看到有的题解中会把a数组和s数组写到一起省略创建a数组的过程:

s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + s[i][j]; ----> s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];

在遍历矩阵的过程中可以累加a[i][j]来获得前缀和数组s[i][j]

  for (int i = 1; i <= n; i++) {
      for (int j = 1; j <= m; j++) {
            scanf("%d" , &a[i][j]);//一边输入矩阵值a[i][j]一边计算s[i][j]
          s[i][j] = s[i - 1][j] + s[i][j - 1] -s[i - 1][j - 1] + a[i][j];
      }
  }

在最后计算子矩阵和的过程用到另外的公式:Sum(矩阵和) = s[x2][y2] - s[x2][y1- 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1]

  1. 我们一般矩阵如果是从(0,0)开始存的,sum数组是从(1,1)开始的,要将x2 , y2全部加1。公式就变成了:

sum = s[x2 + 1][y2 + 1] - s[x2 + 1][y1] - s[x1][y2 + 1] + s[x1][y1];

  1. 有时候要我们求以(x2 , y2)为终点、边长为r的正方形的矩阵和

这里面x1 = x2 - r , y1 = y2 - r 代入Sum(矩阵和) = s[x2][y2] - s[x2][y1- 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1]

sum = s[x2[y2] - s[x2 - r - 1][y2] - s[x2][y2 - r - 1] + s[x2 - r - 1][y2 - r - 1]

其实本质上都是上面的原始式子。

class NumMatrix {
     vector<vector<int>> sum;
public:
    NumMatrix(vector<vector<int>>& matrix) {//本题这种没返回类型的就不用写return了
      int m = matrix.size();
      int n = matrix[0].size();
      sum.resize(m + 1 , vector<int>(n + 1));
        for (int i = 1; i <= m; i++) {
           for (int j = 1; j <= n; j++) {
              sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + matrix[i - 1][j - 1];
           }
        }
    }
    
    int sumRegion(int row1, int col1, int row2, int col2) {
        return sum[row2 + 1][col2 + 1] - sum[row2 + 1][col1] - sum[row1][col2 + 1] + sum[row1][col1];
    }
};

/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix* obj = new NumMatrix(matrix);
 * int param_1 = obj->sumRegion(row1,col1,row2,col2);
 */

24. 两两交换链表中的节点

思路:模拟

lc24-c.png

如果定义了dummyHead就不用单独考虑链表为空或为1的情况了。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
      ListNode* dummyHead = new ListNode(0);          
      dummyHead->next = head;
      ListNode* pre = dummyHead;
      ListNode* cur = head; 
      ListNode* nxt;
      ListNode* tmp;
      while(cur && cur->next){
        nxt = cur->next;
        tmp = cur->next->next;
        pre->next = nxt;
        nxt->next = cur;
        cur->next = tmp;

        pre = cur;
        cur = cur->next;
      }
      return dummyHead->next;
    }
};

25. K 个一组翻转链表

思路:92. 反转链表 II的变式。

每次翻转长度为k的部分肯定要记下l的前一个节点r的后一个节点

同时由于可能从头开始翻转,定义虚拟头结点dummyHead,令p0 = dummyHead,运用头插法翻转链表,最终pre停在翻转后的头结点,cur指向下一组的第一个节点。此时p0->next的链还没断,p0->next仍然指向初始头结点。由于p0->next即为下一组的前一个节点,即下一个p0,故需要记下来:ListNode* nxt = p0->next,然后将p0->next->next 指向cur,再将p0->next 指向prep0 = nxt

根据题意需要先统计出链表长度n,每次循环前判断n>=k决定是否翻转。

image-20241117223328808

image-20241117223428508

image-20241117223450407

/**
 * 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:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* dummyHead = new ListNode(0,head);
        ListNode* p0 = dummyHead;
        
        ListNode* cur1 = head;
        int n = 0;//统计链表长度
        while(cur1){
          cur1 = cur1->next;
          n ++;
        }

        ListNode* pre = nullptr;
        ListNode* cur = p0->next;
        ListNode* nxt;
        //翻转k次,最后pre停在反转后的头结点上,cur停在下一个节点。
        while(n >= k){
          n -= k;
          for (int i = 0; i < k; i++) {
           ListNode* nxt = cur->next;
           cur->next = pre;
           pre = cur;
           cur = nxt;
          }

        nxt = p0->next;
        p0->next->next = cur;
        p0->next = pre;
        p0 = nxt;
        }
        //此时已经翻转完毕一组,l->next = nullptr但是p0->next并没有断

        return dummyHead->next;
    }
};

2074. 反转偶数长度组的节点

思路:链表直接操作or放到vector中操作

反转 每个 偶数 长度组中的节点,并返回修改后链表的头节点 head。

上面的栗子是这么分组的:

第一组:0 (奇数个元素,不反转)
第二组:4,2 (偶数个元素,反转)
第三组:1,3 (偶数个元素,反转)

这里反转是根据每组元素个数的奇偶来判断的,而非组数的奇偶,实际就是说如果最后余下的一组元素个数为偶,也要反转。

法一:将链表值存入数组中,利用vector反转,最后再放回链表中。

class Solution{
public:
    ListNode* reverseEvenLengthGroups(ListNode* head) {
    vector<int> a;
    ListNode* cur = head;
    while(cur){
      a.push_back(cur->val);
      cur = cur->next;
    }

    int i = 0 , flag = 1;//i为每组第一个下标,flag为当前组的元素个数
    int n = a.size();
    for (; i + flag < n ; ){
      if(flag % 2 == 0){
        reverse(a.begin() + i , a.begin() + i + flag);
      }
      i += flag;
      flag ++;
    }
    //退出循环时 i + flag >= n 说明最后一组不足flag个,单独判断奇偶
    if((n - i) % 2 == 0)  reverse(a.begin() + i , a.end());

    cur = head;
    for (int i = 0; i < n; i++) {
       cur->val = a[i];
       cur = cur->next;
    }
    return head;
    }
};

法二:直接将节点存入vector,依次翻转。这种方法直接改变了节点对应的值,最后不用存回链表。

class Solution{
public:
    ListNode* reverseEvenLengthGroups(ListNode* head) {
    vector<ListNode*> v;
    ListNode* p = head;
    int num = 1;
    while(p){
      v.emplace_back(p);//emplace_back() 函数在原理上比 push_back() 有了一定的改进,包括在内存优化方面和运行效率方面。作用同push_back()
      if(v.size() == num || p->next == nullptr){//每次处理num个节点,或者走到最后一个节点时
        int n = v.size();
        if(n % 2 == 0){
          for (int i = 0; i < n / 2; i++)  swap(v[i]->val , v[n - 1 - i]->val);            
        }
        v.clear();
        num ++;
      }
      p = p->next;
    }
    return head;
    }
};
posted @ 2024-11-20 22:55  七龙猪  阅读(1)  评论(0)    收藏  举报
-->