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]
- 我们一般矩阵如果是从(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];
- 有时候要我们求以(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. 两两交换链表中的节点
思路:模拟
如果定义了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指向pre,p0 = nxt。根据题意需要先统计出链表长度n,每次循环前判断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:
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;
}
};





浙公网安备 33010602011771号