LeetCode 203. 移除链表元素
题目链接:
这是一道关于链表的很基础的题,只要理解了链表这个数据结构,这道题做起来就很轻松了。这道需要注意的地方就是关于目标值在头节点的情况了。可以参考代码随想录的文章。
文章链接:
关于这道题的特殊情况,有两种处理方式。
第一种:用原来的链表来执行删除操作,也就是和常规状态的删除分开多写一个while。
第二种:给原来的链表创建一个虚拟头节点,使它指向真实的链表。然后就可以和常规方法放一起了。
第一种方式代码如下:
/**
* 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, *r;
while (head != NULL && head->val == val) {
r = head;
head = r->next;
delete r;
}
for (p = head; p != NULL && p->next != NULL;) {
if (p->next->val == val) {
r = p->next;
p->next = r->next;
delete r;
}
else {
p = p->next;
}
}
return head;
}
};
第二种方式代码如下:
/**
* 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, *r, *q;
q = new ListNode(0);
q->next = head;
for (p = q; p->next != NULL;) {
if (p->next->val == val) {
r = p->next;
p->next = r->next;
delete r;
}
else {
p = p->next;
}
}
head = q->next;
delete q;
return head;
}
};
第一种方式的头节点处理就是,让head->next做新的头节点,然后删除掉旧的head节点,使用while不用if的原因是有可能新的头节点的val仍然是目标值,所以用while才可以删除干净。
第二种方式就相当于在链表的头节点前再插入一个节点当作虚拟头节点,那么原本的头节点就可以当作常规节点来处理了。
至于常规情况的处理方法,就是遍历至目标值所在节点的前一个节点,然后让一个临时的节点保存要删除的节点,代码表现就是r = p->next,其中r是临时的节点,p->next就是要删除的节点。然后让p->next = r->next,其中r->next是删除节点的下一个节点,要使其与p节点连接起来。最后删除掉r节点就完成了这一次删除了。
LeetCode 707. 设计链表
题目链接:
这道题考察到了几乎所有对于链表的操作,由于我的C++还在学习中,所以这道题是使用的C语言写的。这道题主打就是一个链表基本功,只要掌握了链表的基本功,慢慢细心的打,很轻松就出来了。
文章链接:
这道题要注意的就是链表操作中的各种边界问题,要在心里有一个清楚的规划。
C语言代码如下:
typedef struct MyLinkedList{
int val;
struct MyLinkedList *next;
} MyLinkedList;
MyLinkedList* myLinkedListCreate() {
MyLinkedList *L;
L = (MyLinkedList*)malloc(sizeof(MyLinkedList));
L->next = NULL;
return L;
}
int myLinkedListGet(MyLinkedList* obj, int index) {
MyLinkedList *p;
int i = 0;
for(p = obj->next; p != NULL; i++) {
if (i == index) {
return p->val;
}
else {
p = p->next;
}
}
return -1;
}
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
MyLinkedList *p;
p = (MyLinkedList*)malloc(sizeof(MyLinkedList));
p->val = val;
p->next = obj->next;
obj->next = p;
}
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
MyLinkedList *p = obj, *q;
for (; p->next != NULL; p = p->next);
q = (MyLinkedList*)malloc(sizeof(MyLinkedList));
q->val = val;
q->next = NULL;
p->next = q;
}
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
MyLinkedList *p;
int i = 1;
if (index == 0) {
myLinkedListAddAtHead(obj, val);
return ;
}
for (p = obj->next; p != NULL; i++) {
if (i == index) {
MyLinkedList *q = (MyLinkedList*)malloc(sizeof(MyLinkedList));
q->val = val;
q->next = p->next;
p->next = q;
return ;
}
else {
p = p->next;
}
}
}
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
if (index == 0) {
MyLinkedList *tmp = obj->next;
if(tmp != NULL) {
obj->next = tmp->next;
free(tmp);
}
return ;
}
MyLinkedList *p;
int i = 1;
for (p = obj->next; p != NULL && p->next != NULL; i++) {
if (i == index) {
MyLinkedList *r = (MyLinkedList*)malloc(sizeof(MyLinkedList));
if (r != NULL) {
r = p->next;
p->next = r->next;
free(r);
}
return ;
}
else {
p = p->next;
}
}
}
void myLinkedListFree(MyLinkedList* obj) {
while (obj != NULL) {
MyLinkedList *p;
p = obj;
obj = obj->next;
free(p);
}
}
/**
* Your MyLinkedList struct will be instantiated and called as such:
* MyLinkedList* obj = myLinkedListCreate();
* int param_1 = myLinkedListGet(obj, index);
* myLinkedListAddAtHead(obj, val);
* myLinkedListAddAtTail(obj, val);
* myLinkedListAddAtIndex(obj, index, val);
* myLinkedListDeleteAtIndex(obj, index);
* myLinkedListFree(obj);
*/
这道题也许光看代码会觉得没什么难点,但是要自己写又可能会修修补补很久才可以打出来,所以就更需要自己心中有清晰的关于边界问题的想法。只要想清楚了,这道题也就不难了。
LeetCode 206. 反转链表
题目链接:
这道题可谓是老朋友了,在我学习链表的时候,听我哥说他面试遇到了这道题,结果当天老师也布置了这道题,然后今天又在代码随想录上做到了。在我第一次碰到这道题的时候我是使用了,一个数组的方法,就是左右指针互换值的方法,但是数组的方法不太适用于链表,因为每换一次值,都要重新遍历链表。然后代码随想录的文章给我打开了新思路。
文章链接:
思路是定义两个指针,一快一慢,慢指针紧跟快指针,通过操作这两个链表来实现链表的反转。其本质也就是改变节点的next的指向。
C++代码如下:
/**
* 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 *slow, *fast, *tmp;
slow = NULL;
fast = head;
while (fast != NULL) {
tmp = fast->next;
fast->next = slow;
slow = fast;
fast = tmp;
}
return slow;
}
};
初始化好两个指针后就可以开始遍历反转链表了,用一个临时的节点来存储fast->next的地址,然后将slow赋给fast->next,这一步就是反转链表的操作了,可以理解为将他们链接的节点更换为他们的前驱节点。最后为下一次的反转更新快慢指针。这个程序打出来并不难,难就难在思路上。思考清楚再开始打。
Ps:这道题也可以用递归算法,但是我大一才疏学浅,还不能写出来。
今日学习5小时收获
今天进入了新的一章链表,前两道题让我又回忆起了关于链表操作的各种细节,以后如果要复习链表就先做这两道题巩固基本功,第三道题让我对于链表的算法有了一点模糊的感觉,后面还得多刷链表的题才可以摸索出链表的常用算法。最后多多重复,百炼成钢!!