算法刷题 | Leetcode 链表典题解题笔记
摆烂人智商康复刷题第3天
战绩:8题
Leetcode 59. 螺旋矩阵 II
小练一道数组(其实是昨天的尾巴)
代码随想录给出的解法有些麻烦,还要算循环次数,然而只需要加个判断即可代替循环圈数。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> a(n, vector<int>(n)); // 学习初始化
int cur = 1, tar = n*n;
int left=0, rig=n-1, top=0, bot=n-1;
while(cur<=tar){
if(left==rig && bot==top) a[left][bot]=cur++; // 到最中间一个元素,进入不了下面的循环
for(int i=left; cur<=tar && i<rig; i++){a[top][i]=cur++;} //上边 从左往右
for(int i=top; cur<=tar && i<bot; i++){a[i][rig]=cur++;} //右边 从上往下
for(int i=rig; cur<=tar && i>left; i--){a[bot][i]=cur++;} //底边 从右往左
for(int i=bot; cur<=tar && i>top; i--){a[i][left]=cur++;} //左边 从下往上
left++;top++; rig--;bot--; // 一圈结束,坍缩
}
return a;
}
};
链表前置知识回顾
结构体指针
-
定义
注意点:带指针结构体的定义、构造函数的定义。
struct ListNode{ int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数 ListNode(int x, ListNode *next) : val(x), next(next){} // 构造函数2 }
-
声明
老忘记格式,做个笔记
// 法一 ListNode *p = new ListNode(5); // 初始化赋值为5 // 法二 ListNode *q = new ListNode(); q->val = 5; // 法三:先声明一个结构体变量,再将地址赋值给指针 ListNode nd = new ListNode(5); ListNode *p = &nd;
链表典题
Leetcode 203. 移除链表元素
-
法一:啰嗦版
遍历循环的条件为p->next!=null,不用虚拟节点和pre指针辅助,不过需要在最开始判断头指针是否为空,在最后判断头结点存的数是否为要删除的数。
/**
* 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 *p = head, *q;
if(head==nullptr) return head;
while(p->next!=nullptr){
if(p->next->val == val){
q = p->next;
p->next = p->next->next;
delete q; // 释放空间,养成好习惯
}
else p = p->next;
}
if(head->val==val) head = head->next; // 头结点也存数,最后判断一下
return head;
}
};
-
法二:看起来稍简洁版,挑战用pre指针。
循环条件为p!=null;
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode *vhead = new ListNode(0, head);// 添加一个不存数据的头结点
head = vhead;
ListNode *p = head->next, *q, *pre=head;
while(p!=nullptr){
if(p->val == val){
q = p;
p = p->next;
pre->next = p;
delete q;
}
else p = p->next, pre = pre->next;
}
return head->next;
}
};
Leetcode 707. 设计链表
原始模板
class MyLinkedList {
public:
MyLinkedList() {
}
int get(int index) {
}
void addAtHead(int val) {
}
void addAtTail(int val) {
}
void addAtIndex(int index, int val) {
}
void deleteAtIndex(int index) {
}
};
/**
* 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);
*/
-
基础补弱:类里面可以定义结构体
显然,给的模板没有结点定义,我们需要定义结点结构体,看代码随想录发现可以把结构体写进类中,又学到了!同时也发现自己一些基础语法的薄弱,得补补了。
一些测试用例
["MyLinkedList","addAtIndex","addAtIndex","addAtIndex","get"]
[[],[0,10],[0,20],[1,30],[0]]
["MyLinkedList","addAtHead","addAtIndex","get","addAtHead","addAtTail","get","addAtTail","get","addAtHead","get","addAtHead"]
[[],[5],[1,2],[1],[6],[2],[3],[1],[5],[2],[2],[6]]
["MyLinkedList","addAtIndex","addAtIndex","addAtIndex","get"]
[[],[0,10],[0,20],[1,30],[0]]
代码
class MyLinkedList {
public:
struct Node{
int val;
Node *next;
Node(int x):val(x),next(nullptr){}
Node(int x, Node *p):val(x),next(p){}
};
MyLinkedList() {
head = new Node(0, nullptr);
length = 0;
}
int get(int index) {
if( index<0 || index > (length-1)) return -1;
Node *p=head->next;
while(index--){
p = p->next;
}
return p->val;
}
void addAtHead(int val) {
Node *newNode = new Node(val);
newNode->next = head->next;
head->next = newNode;
this->length++;
}
void addAtTail(int val) {
Node *p=head;
while(p->next!=nullptr){ p = p->next;}
p->next = new Node(val);
this->length++;
}
void addAtIndex(int index, int val) {
if(index>length) return;
Node *p=head; // 带头结点
while(index--){
p = p->next;
}
Node *newNode = new Node(val);
newNode->next = p->next;
p->next = newNode;
this->length++;
}
void deleteAtIndex(int index) {
if(index<0 || index>=length) return;
int i=0;
Node *p=head->next, *pre = head;
while(i<index){
p = p->next;
pre = pre->next;
i++;
}
pre->next= p->next;
delete p;
length--;
}
private:
Node *head;
int length;
};
Leetcode 206. 反转链表
- 法一:没什么好说了
/**
* 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* reverseList(ListNode* head) {
ListNode *p= new ListNode(0, head), *rtn=nullptr, *q;
while(p->next!=nullptr){ //
q = p->next->next;
p->next->next = rtn;
rtn = p->next;
if(q!=nullptr)p->next = q;
else break;
}
return rtn;
}
};
- 法二:递归(暂时还没练到)
Leetcode 24. 两两交换链表中的节点
掉坑里了,循环条件一开始图方便用 !pre->next && !pre->next->next
,压根没进循环。。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode *vhead = new ListNode(0, head);
head = vhead;
ListNode *p, *q, *pre = head;
while(pre->next!=nullptr && pre->next->next!=nullptr){ // 都不为空
p = pre->next;
q = pre->next->next;
pre->next = q;
p->next = q->next;
q->next = p;
pre = pre->next->next;
}
return head->next;
}
};
Leetcode 19. 删除链表的倒数第 N 个结点
快慢指针。
临界情况分析:添加了头结点,p始终在q前面,不会指向同一个元素,p指针先向后挪n次,然后p、q指针一起挪,q挪到最后一个元素位置停止,则p指向元素的下一个是倒数第n个元素,将被删除,让p->next = p->next->next即可,若p恰好指向倒数第n个元素,删除起来跟麻烦些,还需要pre指针。
代码随想录给出的参考q是到Null停止,初始化快慢指针要挪N+1次。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *vhead = new ListNode(0, head);
head = vhead;
ListNode *p=head, *q=head;
while(q->next!=nullptr && n--){
q = q->next;
}
while(q->next!=nullptr){
p=p->next; q=q->next;
}
p->next = p->next->next;
return head->next;
}
};
Leetcode面试题 02.07. 链表相交
又一道考研真题。
- f法一:先上暴力
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *p = headA, *q = headB;
while(p!=NULL && q!=NULL){
ListNode *t = q;
while(t!=NULL){
if(t == p) return t;
t = t->next;
}
p = p->next;
}
return NULL;
}
};
- 法二:不会
Leetcode 142. 环形链表 II
链表最后一题!
-
确认有环:快慢指针,这一步并不难
-
个人难点:找到环入口
思路及数学推理:代码随想录 (programmercarl.com)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *p=head, *q=head;
while(q!=NULL){
if(q->next!=NULL)q = q->next->next;
else break;
p = p->next;
if(p == q){// 说明有环, 找环入口
q = head;
while(p!=q){
p=p->next;
q=q->next;
}
return q;
}
}
return NULL;
}
};
总结
- 循环条件判断p->next是否为空。
- 没有头结点(不存数据)就创一个,比较方便操作。
- 链表入环入口寻找:相遇点距离环入口距离等于第一个元素到环入口距离。