59螺旋矩阵Ⅱ
54螺旋矩阵
剑指Offer 29.顺时针打印矩阵
54题和29题其实是一模一样的题
螺旋矩阵这种题考的不是什么算法,就是一种
思路,一种模拟过程,对代码的掌控能力。
从上图可以看出来坚持左闭右开的政策,就是每次拐角处留给新的一边来画,一直坚持用左闭右开的政策,这样代码不会乱,不要一会左开右闭,一会左闭右闭的,这些容易把自己思路搞乱。
就顺时针画矩阵:
1.上行从左到右
2.右列从上到下
3.下行从右到左
4.左列从下到上
下面就是59题代码
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>>res(n,vector<int>(n,0));
int startx = 0,starty = 0; //循环的起始位置
int loop = n/2; //循环的圈数
int mid = n/2; //判断矩阵是否有中间位置,奇数矩阵是有的
int count = 1; //需要填充的数字
int offset = 1;
int i,j;
while(loop--)
{
i = startx;
j = starty;
//填充上行从左到右
for(;j<starty + n - offset;j++)
{
res[i][j] = count++;
}
//填充右列从上到下
for(;i<startx + n -offset;i++)
{
res[i][j] = count++;
}
//填充下行从右到左
for(;j>starty;j--)
{
res[i][j] = count++;
}
//填充左列从下到上
for(;i>startx;i--)
{
res[i][j] = count++;
}
//第二圈的起始位置
startx++;
starty++;
offset += 2;
}
if(n%2) //如果这个矩阵是奇数矩阵是有中心位置的
{
res[mid][mid] = count;
}
return res;
}
};
对于54题我刚开始也想用59题这样的思想,但是我发现出现了各种各样的小bug,因为54题他没有说行列相等所以要考虑一些问题,比如就一行的时候我们要考虑,就不能把拐角处留个另外一边了等等之类的因素吧,所以就换了一种思路,就是我先每次读取一行然后再判断读取完这一行,剩下来的首行是否大于低,如果大于证明读取完毕,如果没有继续,读右边这一列的所有元素,读取判断是否小于左边这列,如果没有继续,如果小于就跳出while,然后再读取底那一行,判断是否小于重新定义的首行如果小于就跳出,不小就继续读取左边一列,这样一次反复直到跳出循环为止。
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector <int> ans;
if(matrix.empty()) return ans; //若数组为空,直接返回答案
int u = 0; //赋值上下左右边界
int d = matrix.size() - 1;
int l = 0;
int r = matrix[0].size() - 1;
while(true)
{
for(int i = l; i <= r; i++) ans.push_back(matrix[u][i]); //向右移动直到最右
if(++ u > d) break; //重新设定上边界,若上边界大于下边界,则遍历遍历完成,下同
for(int i = u; i <= d; i++) ans.push_back(matrix[i][r]); //向下
if(-- r < l) break; //重新设定有边界
for(int i = r; i >= l; i--) ans.push_back(matrix[d][i]); //向左
if(-- d < u) break; //重新设定下边界
for(int i = d; i >= u; i--) ans.push_back(matrix[i][l]); //向上
if(++ l > r) break; //重新设定左边界
}
return ans;
}
};
29与54题是一样的题,这里就不再详细描述了。
链表的知识
我们平时在刷leetcode题的时候,链表节点都定义好了,直接用就行,这样我们会漏掉一个知识点就是链表节点的定义
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
如果我们写第四行节点的构造函数也可以,因为C++默认生成一个构造函数,但是不能初始化成员变量,自己定义节点构造函数:
ListNode* head = new ListNode(5);
使用默认构造函数初始化节点:
ListNode* head = new ListNode();
head->val = 5;
经典对比
数组在定义的时候,长度是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。
链表长度可以不是固定的,并且可以动态增删,适合数量不固定,频繁增删,减少查询的场景。
203移除链表元素
707设计链表
对于203这题属于简单题,最终想学习的一个思想就是添加一个虚拟的头节点,这个思想来源于链表上加一个头节点(不存数,只存链表长度,和指向第一个节点的指针)这样会让整个链表的增删操作一体化,如果不增加虚拟头节点这题的代码为(代码中那段注释应该注意):
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 删除头结点
while (head != NULL && head->val == val) { // 注意这里不是if
ListNode* tmp = head;
head = head->next;
delete tmp;
}
// 删除非头结点
ListNode* cur = head;
//应该先判断p是否为空而不能先判断p->next是否为空,如果先判断后者会出现直接给你一个空链表,这样就会报错!
while (cur != NULL && cur->next!= NULL) {
if (cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
return head;
}
};
上面这段代码分了一下头节点和非头节点,下面是带虚拟节点的:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
ListNode* cur = dummyHead;
while (cur->next != NULL) {
if(cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
显然操作方便很多,而且记得最后把你设置的虚拟头节点删除。
对于707题,个人觉得就是考察你对链表的一系列操作吧,考察你头插,尾插,删除,这些各种操作吧,这个你初始化一个虚拟头节点很方便,再定义一个你的链表长度,这样在你按位置查找节点内元素val值为多少更好判断一些。在类中你需要用到一些什么属性的时候需要定义一下私有属性,然后再去操作。
class MyLinkedList {
public:
struct ListNode{
int val;
ListNode *next;
ListNode(int x):val(x),next(NULL){}
};
MyLinkedList() {
dummyHead = new ListNode(0);
size = 0;
}
int get(int index) {
if(index > (size-1) || index < 0)
{
return -1;
}
ListNode *p = dummyHead->next;
while(index--) //如果--index就会陷入死循环 如果index为0的时候 --index=-1,C++规定while语句任意非0值都为真,所以会陷入死循环。
{
p = p->next;
}
return p->val;
}
void addAtHead(int val) {
ListNode *addHead = new ListNode(val);
addHead->next = dummyHead->next;
dummyHead->next = addHead;
size++;
}
void addAtTail(int val) {
ListNode *addTail = new ListNode(val);
ListNode *p = dummyHead;
int index = size;
while(index--) //这个判断条件也可以写完cur->next != NULL
{
p = p->next;
}
p->next = addTail;
addTail->next = NULL;
size++;
}
void addAtIndex(int index, int val) {
ListNode *p = dummyHead;
ListNode *addIndex = new ListNode(val);
// if(index < 0)
// {
// addAtHead(val);
// }
// if(index == size)
// {
// addAtTail(val);
// }
if(index > size)
{
return;
}
while(index--)
{
p = p->next;
}
addIndex->next = p->next;
p->next = addIndex;
size++;
}
void deleteAtIndex(int index) {
ListNode *p = dummyHead;
if(index <0 || index >=size)
{
return;
}
while(index--)
{
p = p->next;
}
ListNode *tmp;
tmp = p->next;
p->next = tmp->next;
delete tmp;
size--;
}
private:
ListNode *dummyHead;
int size;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/