一、移除链表元素
(Leetcode 203)
1.1 题目


1.2 解题思路
思路一:可以遍历原来的链表,将值为val的节点释放掉。

可以定义一个指针变量pcur,让它从头节点开始遍历链表,如果节点存储的数据值不为val,就将这个值尾插到新链表中。由于我们要把删除的节点的前后两个节点连起来,所以我们还要定义变量prev来标记pcur前面的那个节点。
思路二:可以找值不为val的节点,尾插到新链表中。
由于思路二比较简洁,我们按照思路二来进行代码实现。
1.3 代码实现
首先我们创建两个结构体指针newHead和newTail,分别代表新链表的头节点和尾结点。把它们先都置为空,就相当于创建好了一个新的链表。
接着我们创建一个指针变量pcur来遍历原来链表中的所有节点,判断该节点存储的数据不为空时就将这个节点插入新链表中。
注意此时新链表可能为空,为空的情况需要拿出来特别处理一下:只需让newHead和newTail都指向pcur所在的这个节点即可。
不为空,我们就让newtail的next指向pcur所在的节点,相当于把这个节点尾插到了新链表中。
完成判断和尾插之后我们让pcur沿着原链表往后走一个节点。遍历了所有元素后,即pcur为空的时候,循环结束。
但此时还需要注意,如果原链表的最后一个节点存储的数据恰好是需要被删除的,那么newTail的next仍然指向这个节点,也就是新链表中包括了这个节点。为了避免这种情况,我们需要在遍历结束后把newTail的next置为空。
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val)
{
//创建一个空链表
ListNode* newHead,*newTail;
newHead = newTail = NULL;
//遍历原链表
ListNode* pcur = head;
while(pcur)
{
//找值不为value的节点,尾插到新链表中
if(pcur->val != val)
{
//链表为空
if(newHead == NULL)
{
newHead = newTail = pcur;
}
else
{
//链表不为空
newTail->next = pcur;
newTail = newTail->next;
}
}
pcur = pcur->next;
}
if(newTail)
newTail->next = NULL;
return newHead;
}
二、反转链表
(Leetcode 206)
2.1 题目


2.2 解题思路
思路一: 创建新链表,将原链表中的节点拿过来头插。比较麻烦。
思路二:用三个指针变量解决问题。
首先创建三个指针n1、n2和n3,n1先置为空,n2指向头结点,n3指向第二个节点。

此时我们让n2的next指针不再指向n3,而是指向n1。
然后我们让n1、n2、n3都往后走一个节点。

此后再重复以上的步骤,直到完成链表的反转。
2.3 代码实现
按照上述思路,代码写起来顺理成章。需要注意的是,如果原链表为空,定义n3的过程会涉及对空指针的解引用。为了避免,我们要特别处理链表为空的情况:此时直接返回head即可。
以及,我们让n3向后移动的时候,如果n3已经走到链表末尾,也会导致对空指针的解引用。那我们就加个判断,确保n3的next指针不为空再让n3向后移动。
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
//判空
if(head == NULL)
{
return head;
}
//创建三个指针
ListNode*n1,*n2,*n3;
n1 = NULL,n2 = head,n3 = n2->next;
while(n2)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if(n3)
n3 = n3->next;
}
return n1;
}
三、链表的中间节点
(Leetcode 876)
3.1 题目

3.2 解题思路
思路一:遍历节点,创建变量count来记节点数,返回count/2节点的next节点。
思路二(快慢指针):创建两个指针变量slow和fast,刚开始让两个指针都指向头节点,让后让两个指针以不同的速度沿着链表向前移动,slow每移动一个节点,fast移动两个节点。当fast指针为空(链表节点个数为偶数)或者fast->next为空(链表节点个数为奇数)时,slow指针指向的节点就恰好是所要求的中间节点。
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head)
{
//创建快慢指针
ListNode* slow = head;
ListNode* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
//此时slow正好指向中间节点
}
return slow;
}
此处注意,while括号中的两个条件顺序不能交换。因为如果链表节点个数为偶数,最后一次fast会直接指向空,此时对fast进行解引用会报错。目前顺序下,如果fast为空,fast->next会被短路掉,不会执行,也就不会报错。
END