给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
示例 1:
输入:n = 3 输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入:n = 1 输出:[[1]]
要画出一个螺旋形状的矩阵,看着就脑壳痛,完全没有想法。这里我们要想到顺时针画矩阵的过程,填充上行从左到右,填充右列从上到下,填充下行从左到右,填充左列从下到上 ,这样像海螺一样,一圈一圈的处理。
这里的每个颜色代表着一条边,所要遍历的规则也是不一样的,尤其是拐角这里,挺难处理的。我们设想大循环套小循环,一个一圈大循环套上四个边的小循环,这样就能够遍历。
注意一点,每个边的遍历到底怎么取边呢?一整个边?还是左开右闭左闭右开之类的?都可以!但是要确保全程要有一个规则--循环不变量原则,这里我的处理方式统一的左闭右开。
首先定义一个二维数组存放所要return的答案,定义一个loop来表示每个圈循环几次,也就是大的循环变量,startx和starty表示的是每一圈最开始循环的时候的起点,offset是来计算每次小循环时的边长,count用来记录放入ans中的数值,每次加1。此时用while语句进入大循环,循环次数其实就是n/2(圈数),此时,进入四条边的赋值循环,i代表行,j代表列,首先j从starty开始遍历,遍历到本行的倒数第二个元素,i也是同理,第三次j要遍历到starty处,i也是同理,每次都要让count++,每次while都要让start++,loop--。最后处理一下比较特殊的情况,就是如果n是个奇数,最后循环很多圈以后,中间会剩下一个元素,让这个元素等于n²即可,本题代码如下,时间复杂度为O(n²)。
class Solution {
public:
vector> generateMatrix(int n) {
vector>ans(n,vector(n,0));
int startx=0,starty=0,offset=1,count=1;
int i,j;
int loop = n/2;
while(loop)
{
for(j=starty;jstarty;j--)
ans[i][j]=count++;
for(;i>startx;i--)
ans[i][j]=count++;
startx++;
starty++;
offset++;
loop--;
}
if(n%2==1)
ans[n/2][n/2]=n*n;
return ans;
}
};
当然,本题代码还可以继续优化,不需要两个start,直接用offset来处理,因为这三个量其实是有一定的关系的,简单计算一下,其他的同上。
class Solution {
public:
vector> generateMatrix(int n) {
vector>ans(n,vector(n,0));
int i,j;
int offset = 0;
int loop = n/2;
int count =1;
while(loop)
{
for(j=offset;joffset;j--)
ans[i][j]=count++;
for(;i>offset;i--)
ans[i][j]=count++;
offset++;
loop--;
}
if(n%2==1)
ans[n/2][n/2]=n*n;
return ans;
}
};
这样就完成了螺旋矩阵的设计,本题主要要注意以下几点:循环不变量,循环条件,没有什么算法,只是一个特殊的案例而已,暴力求解。
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1 输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7 输出:[]
终于进入了新的篇章---链表。移除元素的原理,让这个元素的上一个节点连接到这个元素的下一个结点上,然后再delete掉这个删除的结点即可,其实就是让结点next指针直接指向下一个结点,这里我们设计一个虚拟头结点,对后面的操作会比较方便。本题比较简单,时间复杂度为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:
ListNode* removeElements(ListNode* head, int val) {
ListNode*dummyHead=new ListNode(0, head);
ListNode*current=dummyHead;
while(current->next!=nullptr)
{
if(current->next->val==val)
{
ListNode*tmp = current->next;
current->next=current->next->next;
delete(tmp);
}
else
{
current = current->next;
}
}
head = dummyHead->next;
delete dummyHead;
dummyHead = nullptr;
return head;
}
};
要注意的几点就是:不能直接用虚拟头结点进行后面的操作,都则最后要返回的时候就找不到真正的head了,所以建立一个current;其次在寻找val时,也要创建一个tmp来指向current,是为了释放删除元素的空间;最后不要忘记把虚拟头结点dummyhead也给释放了,同时释放内存以后最好让他指向nullptr。
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList
类:
MyLinkedList()
初始化 MyLinkedList
对象。int get(int index)
获取链表中下标为 index
的节点的值。如果下标无效,则返回 -1
。void addAtHead(int val)
将一个值为 val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)
将一个值为 val
的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)
将一个值为 val
的节点插入到链表中下标为 index
的节点之前。如果 index
等于链表的长度,那么该节点会被追加到链表的末尾。如果 index
比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为 index
的节点。本题涵盖了链表设计的大部分操作,首先一定要先创建一个链表,一般都要有值,下一个地址的指针,和一个构造函数列表,代码如下:
struct LinkedNode{
int val;
LinkedNode*next;
LinkedNode(int val):val(val),next(nullptr){}
};
之后时这个类的构造函数,我们采用虚拟头结点的话,需要定义一个虚拟头结点,同时,因为后面还要进行一些函数的操作,所以要设定一个链表长度size,代码如下:
MyLinkedList() {
dummyhead = new LinkedNode(0);
size = 0;
}
private:
int size;
LinkedNode* dummyhead;
接下来就是各种内置函数的实现了:
首先是获取下标index结点的值,一定要先判断下标是否有效,如果下标小于0或者大于size-1,均为非法下标,此时return-1,其他的时候,定义一个cur指向dummyhead的下一个元素,遍历index,一直往下指,直到最后找到index元素,返回它的value,代码如下:
int get(int index) {
if(index<0||index>(size-1))
return -1;
LinkedNode*cur=dummyhead->next;
while(index)
{
cur=cur->next;
index--;
}
return cur->val;
}
头插法:先要new出来一个要插入的元素,因为我们有虚拟头结点,所以让新元素的next指向dummydhead的next,再让dummy的next指向新元素,最后别忘了要让size++。
void addAtHead(int val) {
LinkedNode*NewNode=new LinkedNode(val);
NewNode->next=dummyhead->next;
dummyhead->next=NewNode;
size++;
}
尾插法:与头插法类似,但是要先找到尾巴,什么时候能遍历到尾巴呢,就是cur的next为空指针的时候,找到最后一个cur让其指向新元素即可。
void addAtTail(int val) {
LinkedNode*NewNode=new LinkedNode(val);
LinkedNode*cur=dummyhead;
while(cur->next!=nullptr)
{
cur=cur->next;
}
cur->next=NewNode;
size++;
}
按照下标增加元素:判断如果index大于size了,那么就直接return,如果index=0,那么采用头插法函数,如果index=size,那就采用尾差法函数,对于其余的情况,只好正常的来设计了,既然要在index的前一个位置插入元素,那么就要找到cur和cur的next,在这两个中间插入元素才行,不要忘记size++。
void addAtIndex(int index, int val) {
LinkedNode*NewNode=new LinkedNode(val);
LinkedNode*cur=dummyhead;
if(index>size)
return;
if(index==0)
addAtHead(val);
else if(index==size)
addAtTail(val);
else
{
while(index)
{
cur=cur->next;
index--;
}
NewNode->next=cur->next;
cur->next=NewNode;
size++;
}
}
删除指定元素:先要判断index是否非法,之后正常删除即可,同上一题,注意最后delete以后还要将指针设置成nullptr,还有别忘了size--。
void deleteAtIndex(int index) {
if(index<0||index>=size)
return;
LinkedNode*cur=dummyhead;
while(index)
{
cur=cur->next;
index--;
}
LinkedNode*tmp=cur->next;
cur->next=cur->next->next;
delete tmp;
tmp=nullptr;
size--;
}
至此设计完成,本题的完整代码如下所示:
class MyLinkedList {
public:
struct LinkedNode{
int val;
LinkedNode*next;
LinkedNode(int val):val(val),next(nullptr){}
};
MyLinkedList() {
dummyhead = new LinkedNode(0);
size = 0;
}
int get(int index) {
if(index<0||index>(size-1))
return -1;
LinkedNode*cur=dummyhead->next;
while(index)
{
cur=cur->next;
index--;
}
return cur->val;
}
void addAtHead(int val) {
LinkedNode*NewNode=new LinkedNode(val);
NewNode->next=dummyhead->next;
dummyhead->next=NewNode;
size++;
}
void addAtTail(int val) {
LinkedNode*NewNode=new LinkedNode(val);
LinkedNode*cur=dummyhead;
while(cur->next!=nullptr)
{
cur=cur->next;
}
cur->next=NewNode;
size++;
}
void addAtIndex(int index, int val) {
LinkedNode*NewNode=new LinkedNode(val);
LinkedNode*cur=dummyhead;
if(index>size)
return;
if(index==0)
addAtHead(val);
else if(index==size)
addAtTail(val);
else
{
while(index)
{
cur=cur->next;
index--;
}
NewNode->next=cur->next;
cur->next=NewNode;
size++;
}
}
void deleteAtIndex(int index) {
if(index<0||index>=size)
return;
LinkedNode*cur=dummyhead;
while(index)
{
cur=cur->next;
index--;
}
LinkedNode*tmp=cur->next;
cur->next=cur->next->next;
delete tmp;
tmp=nullptr;
size--;
}
private:
int size;
LinkedNode* dummyhead;
};
/**
* 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);
*/