1 移除链表结构
1.1 题目介绍
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
题目来源:https://leetcode-cn.com/problems/remove-linked-list-elements/description/
1.2 题目分析
1.2.1 基本思路
- 步骤一:定义一个指针变量cur用来遍历整个链表,指针变量prev表示链表中当前节点的上一个节点;
- 步骤二:判断
cur->data == val
是否成立,不成立则prev = cur ;cur = cur->next ;
- 步骤三:若不成立,则删除cur指向的节点 。
1.2.2 关键变量
- cur 指向当前访问的节点
- prev 表示链表中当前节点的上一个节点
1.3 程序代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* cur , *prev;
cur = head ;
prev = NULL;
while(cur){
//数据等于val,则删除该节点
if(cur->val == val){
struct ListNode* next = cur->next ;
if(prev == NULL){
head = next ;
}else{
prev->next = next ;
}
free(cur);
cur = next ;
}else{
prev = cur ;
cur = cur->next ;
}
}
return head ;
}
1.4 代码运行
2 反转链表
2.1 题目介绍
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
题目来源:https://leetcode-cn.com/problems/reverse-linked-list/submissions/
2.2 题目分析
2.2.1 基本思路
方法一:
- 步骤一:创建一个链表head2;
- 步骤二:将原有链表的首个节点取下,并头插入head2链表中;
- 步骤三:不断重复步骤二,直至原有链表为空。
方法二:
- 步骤一:使用三个指针变量n1、n2、n3来辅助链表的反转;
- 步骤二:使
n2->next = n1;
; - 步骤三:使
n1 = n2 ; n2 = n3 ; n3 = n3->next;
; - 步骤四:不断重复上述操作,直至n2的值为空;
- 步骤五:使
head = n1
。
2.2.2 关键变量
- n2表示需要反转的节点
- n1表示需要反转节点应指向的节点
- n3表示需要反转的下一个节点
2.3 程序代码
方法一:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* head2 = NULL;
//当原有节点不为空时进行循环
while(head != NULL){
//从原有节点上取出第一个节点
struct ListNode* cur = head ;
head = head->next ;
//将取出的节点头插入head2链表中
cur->next = head2;
head2 = cur ;
}
return head2 ;
}
方法二:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
if(head == NULL || head->next == NULL)
return head ;
//定义三个辅助指针变量并进行初始赋值
struct ListNode* n1 , *n2 , *n3 ;
n1 = head ;
n2 = head->next ;
n3 = n2->next ;
n1->next = NULL;
//当n2不为空时进行循环
while(n2){
//将n2节点指向反转
n2->next = n1 ;
//将三个辅助指针变量向后移动
n1 = n2 ;
n2 = n3 ;
if(n3 != NULL){
n3 = n3->next ;
}
}
head = n1 ;
return head ;
}
2.4 代码运行
方法一:
方法二:
3 链表的中间节点
3.1 题目介绍
给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
示例 :
输入:[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.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/middle-of-the-linked-list
3.2 题目分析
3.2.1 基本思路
方法一:
- 步骤一:遍历链表,统计链表节点的个数size;
- 步骤二:找到第size/2+1个节点,该节点即为中间节点。
方法二:
- 步骤一:设置快慢指针
fast = slow = head
; - 步骤二:fast指针每次向前推进2个节点,slow指针每次向前推进1个节点;
- 步骤三:当fast指针指向最后一个节点或fast指针为空时,slow指针指向链表的中间节点。
3.2.2 关键变量
- fast表示快指针
- slow表示慢指针
3.3 程序代码
方法一:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head){
if(head == NULL )
return head ;
int size = 0 ;
struct ListNode* cur = head ;
//统计链表节点数
while(cur){
size++;
cur = cur->next ;
}
//找到第len/2+1个节点
int middle = size/2+1;
cur = head ;
while(--middle){
cur = cur->next ;
}
return cur;
}
方法二:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head){
//设置快慢指针
struct ListNode* fast , *slow ;
fast = slow = head ;
//当快指针为最后一个节点或者为空是结束循环
while(fast && fast->next){
//快指针步长为2,慢指针步长为1
fast = fast->next->next;
slow = slow->next;
}
return slow ;
}
3.4 代码运行
方法一:
方法二:
4 倒数第k个节点
4.1 题目介绍
输入一个链表,输出该链表中倒数第k个节点。
示例
输入:1,{1,2,3,4,5}
返回值:{5}
题目链接:剑指offer找到倒数第k个节点
4.2 题目分析
4.2.1 基本思路
方法一:
步骤一:遍历链表,统计链表结点的个数size;
步骤二:返回第size-k个节点的下一个节点。
方法二:
步骤一:设置快慢指针fast = slow = head
;
步骤二:先让快指针先走k步;
步骤三:快慢指针同时向后移动,当快指针为空时,慢指针指向返回结点。
4.2.2 关键变量
- fast表示快指针
- slow表示慢指针
4.3 程序代码
方法一:
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
*
* @param pListHead ListNode类
* @param k int整型
* @return ListNode类
*/
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
// write code here
int size = 0;
struct ListNode* cur = pListHead;
while(cur){
size++;
cur = cur->next ;
}
cur = pListHead ;
int ret = size - k ;
while(ret--&& cur){
cur = cur->next ;
}
return cur ;
}
方法二:
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
*
* @param pListHead ListNode类
* @param k int整型
* @return ListNode类
*/
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
// write code here
//设置快慢指针`fast = slow = head`
struct ListNode* fast , *slow ;
fast = slow = pListHead ;
//先让快指针先走k步
while(k && fast){
k--;
fast = fast->next ;
}
if(k > 0)
return NULL;
//快慢指针同时向后移动,当快指针为空时,慢指针指向返回结点
while(fast){
fast = fast->next ;
slow = slow->next ;
}
return slow ;
}
4.4 代码运行
方法一:
方法二:
5 有序链表的合并
5.1 题目介绍
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists
5.2 题目分析
5.2.1 基本思路
方法一:
申请一个链表,将两个链表中元素按升序赋给新链表节点的val中,销毁两个链表并返回新链表。
方法二:
步骤一:申请一个新的表头,并设置指向当前节点的指针cur = NULL
;
步骤二:设置指向两个有序链表当前节点的指针cur1、cur2;
步骤三:比较cur1->val
与cur2->val
的值,将值较小的节点插入新链表中,即if(cur) cur->next = cur1(或cur2);
步骤四:当cur1 != NULL && cur2 != NULL
时,不断重复步骤三;
步骤五:将剩余的链表元素直接插入新的链表中;
步骤六:返回新的链表。
方法一的具体实现与合并两个数组的方法类似,故这里不详细讲述。
5.2.2 关键变量
- cur指向新链表的当前节点
- cur1指向链表l1的当前节点
- cur2指向链表l2的当前节点
5.3 程序代码2
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
//创建新的链表
struct ListNode* newList , * cur , *cur1 , *cur2 ;
cur1 = l1 ;
cur2 = l2 ;
//创建头节点
struct ListNode* head =(struct ListNode*)malloc(sizeof(struct ListNode));
head->next = NULL;
newList = cur = head ;
//将较小者插入新链表中
while(cur1 && cur2){
struct ListNode* inst;
if(cur1->val <= cur2->val){
inst = cur1 ;
cur1 = cur1->next ;
}else{
inst = cur2 ;
cur2 = cur2->next ;
}
cur->next = inst ;
cur = inst ;
}
//将剩余元素加入新链表
if(cur1){
cur->next = cur1;
}
if(cur2){
cur->next = cur2;
}
newList = head->next;
free(head);
return newList ;
}