1.移除链表元素
链接:
https://leetcode.cn/problems/remove-linked-list-elements/
题目描述:
给你一个链表的头节点 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
输出:[]
思路1:
用一个cur指针去找到要删除的节点,用prev指针记录cur指针的上一个位置,然后让prev指向的节点链接上cur的下一个节点,最后删除cur位置的节点
代码1:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* prev = NULL; // 记录保存cur的上一个节点
struct ListNode* cur = head; // 删除指定节点
while (cur)
{
if (cur->val == val)
{
if (cur == head) // 第一个节点就是要删除的节点
{
head = cur->next;
free(cur);
cur = head;
}
else
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
else // 寻找要删除的节点
{
prev = cur;
cur = cur->next;
}
}
return head;
}
思路2:
先让cur把头节点的位置给记录下来,再把头节点head指向NULL,让cur往后走,把不是val的节点尾插到head上,把是val的给删除,tail用来记录尾节点的位置
思路:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* cur = head; // 遍历链表
head = NULL;
struct ListNode* tail = head; // 记录尾节点
while (cur)
{
if (cur->val != val) // 尾插
{
if (tail == NULL) // 第一次尾插
{
head = tail = cur;
cur = cur->next;
}
else
{
tail->next = cur;
tail = cur;
cur = cur->next;
}
}
else // 删除
{
struct ListNode* del = cur;
cur = cur->next;
free(del);
}
}
if (tail) // 处理尾节点的next
tail->next = NULL;
return head;
}
2.反转链表
链接:
https://leetcode.cn/problems/reverse-linked-list/
题目描述:
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2]
输出:[2,1]
示例 3:
输入:head = []
输出:[]
思路1:
拿cur去遍历整个链表,prev记录cur的前一个位置,next记录cur的后一个位置,让cur的节点指向prev的节点,三个指针同时往后移动,不断重复这个过程,最终反转整个链表
代码1:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
if (head == NULL)
{
return head;
}
struct ListNode* prev = NULL;
struct ListNode* cur = head;
struct ListNode* next = cur->next;
while (cur) // cur去遍历整个链表
{
cur->next = prev;
prev = cur;
cur = next;
if (next) // 避免野指针
next = next->next;
}
return prev; // 注意最后返回的不是cur
}
思路2:
cur记录head的位置并遍历整个链表,next记录cur的下一个位置,将head置空,让cur位置的节点指向head,同时更新head的指向为cur,cur回到next的位置,不断重复
代码2:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* cur = head;
head = NULL;
while (cur)
{
struct ListNode* next = cur->next;
cur->next = head;
head = cur;
cur = next;
}
return head;
}
3.返回链表的中间节点
链接:
https://leetcode.cn/problems/middle-of-the-linked-list/description/
题目描述:
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
思路:
给定两个指针fast和slow同时指向头节点,fast每走两步,slow只走一步
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head){
struct ListNode* fast = head;
struct ListNode* slow = head;
while (fast && fast->next) // 单数走到尾节点,双数走到NULL
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
4.链表中倒数第k个结点
链接:
题目描述:
描述
输入一个链表,输出该链表中倒数第k个结点。
示例1
输入:1,{1,2,3,4,5}
返回值:{5}
思路:
计算链表节点个数,将一些特殊情况筛选掉,然后用一个cur指针先走k+1步,slow指针指向头节点,让cur和slow同时往后走,当cur走到NULL时,slow恰好到倒数k个节点的位置
代码:
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
// write code here
if (pListHead == NULL) // 空链表直接返回
{
return pListHead;
}
int i = 0; // 计算节点个数
struct ListNode* newhead = pListHead;
while (newhead)
{
newhead = newhead->next;
i++;
}
if (k <= 0 || k > i) // 筛选其他情况
{
return NULL;
}
struct ListNode* cur = pListHead;
struct ListNode* slow = pListHead;
while (k) // cur走到第k+1个节点的位置
{
cur = cur->next;
k--;
}
while (cur) // slow跟着cur走
{
cur = cur->next;
slow = slow->next;
}
return slow;
}
5.合并两个有序链表
链接:
https://leetcode.cn/problems/merge-two-sorted-lists/submissions/
题目描述:
思路:
分为头插,中间插入和尾插三种插入方式,对其中一个链表的元素一个一个的插入到另一个链表当中,若其中一个链表为空链表,那么另外一个链表可以直接返回了。
代码:
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
if (list1 == NULL && list2 == NULL)
{
return NULL;
}
if (list1 == NULL)
{
return list2;
}
if (list2 == NULL)
{
return list1;
}
struct ListNode* cur1 = list1; // 遍历list1链表
struct ListNode* next1 = cur1->next; // cur1的下一个节点
struct ListNode* cur2 = list2; // 遍历list2链表
struct ListNode* next2 = cur2->next; // cur2的下一个节点
while (cur2)
{
if (cur2->val <= list1->val) // 头插
{
cur2->next = list1;
cur1 = list1 = cur2;
next1 = cur1->next;
cur2 = next2;
if (next2)
next2 = next2->next;
}
else if (cur2->val > cur1->val && next1 == NULL) // 尾插
{
cur1->next = cur2;
return list1;
}
else if (cur2->val >= cur1->val && cur2->val <= next1->val) // 中间插入
{
cur1->next = cur2;
cur2->next = next1;
cur1 = cur1->next;
cur2 = next2;
if (next2)
next2 = next2->next;
}
else
{
cur1 = cur1->next;
next1 = next1->next;
}
}
return list1;
}