数据结构之链表操作详解与示例(反转链表,合并链表,旋转链表,对链表排序)
创作时间:
作者:
@小白创作中心
数据结构之链表操作详解与示例(反转链表,合并链表,旋转链表,对链表排序)
引用
CSDN
1.
https://m.blog.csdn.net/qq_35320456/article/details/140416351
链表是一种常见的基础数据结构,它在内存中的存储方式非常灵活。本文将详细介绍反转链表、合并链表、旋转链表以及对链表排序这四种操作,并提供C和C++的实现示例。
1. 反转链表
反转链表意味着我们需要改变链表中每个节点的指针方向。例如,给定一个链表 1 -> 2 -> 3 -> 4 -> 5,反转后变为 5 -> 4 -> 3 -> 2 -> 1。
算法步骤
- 初始化三个指针:prev(前一个节点)、curr(当前节点)和next(下一个节点)。
- 将prev初始化为NULL,curr初始化为链表的头节点,next初始化为None。
- 遍历链表,对于每个节点,执行以下操作:
- 将next设置为curr的下一个节点。
- 将curr的下一个节点设置为prev。
- 将prev设置为curr。
- 将curr设置为next。
- 最后,prev将是新的头节点,curr将是新的尾节点。
C++实现:
ListNode* reverseList(ListNode* head) {
ListNode* prev = NULL;
ListNode* curr = head;
while (curr != NULL) {
ListNode* nextTemp = curr->next;
curr->next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
C实现:
ListNode* reverseList(ListNode* head) {
ListNode* prev = NULL;
ListNode* curr = head;
while (curr != NULL) {
ListNode* nextTemp = curr->next;
curr->next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
2. 合并链表
合并链表意味着将两个或多个链表合并为一个有序链表。这里我们以合并两个升序链表为例。
算法步骤
- 初始化一个哨兵节点dummy,其下一个节点指向第一个链表的头节点。
- 使用两个指针分别遍历两个链表,比较当前两个链表的节点的值,将值较小的节点添加到dummy后面,并移动该指针到下一个节点。
- 当一个链表遍历完成后,将另一个链表的剩余部分直接连接到dummy后面。
C++实现:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == NULL) return l2;
if (l2 == NULL) return l1;
if (l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
C实现:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == NULL) return l2;
if (l2 == NULL) return l1;
if (l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
3. 旋转链表
旋转链表意味着将链表中的节点进行重新排列,例如,对于一个具有n个节点的链表,我们可以将链表的每个节点移动k个位置(k < n)。如果链表的最后一个节点需要移动到链表的头部,我们可以简单地将链表的头节点和尾节点连接起来。
算法步骤
- 计算链表中的元素个数n。
- 计算需要旋转的次数k(k可以是通过给定的步数或数组来确定)。
- 如果链表长度小于2,直接返回链表。
- 将链表的尾节点与头节点连接起来。
- 将新的头节点设置为当前尾节点的下一个节点。
C++实现:
ListNode* rotateRight(ListNode* head, int k) {
if (head == NULL || head->next == NULL || k == 0) return head;
ListNode* old_tail = head;
int n;
for (n = 1; old_tail->next != NULL; n++)
old_tail = old_tail->next;
old_tail->next = head; // 成环
ListNode* new_tail = head;
for (int i = 0; i < n - k % n - 1; i++)
new_tail = new_tail->next;
ListNode* new_head = new_tail->next;
new_tail->next = NULL;
return new_head;
}
C实现:
ListNode* rotateRight(ListNode* head, int k) {
if (head == NULL || head->next == NULL || k == 0) return head;
ListNode* old_tail = head;
int n;
for (n = 1; old_tail->next != NULL; n++)
old_tail = old_tail->next;
old_tail->next = head; // 成环
ListNode* new_tail = head;
for (int i = 0; i < n - k % n - 1; i++)
new_tail = new_tail->next;
ListNode* new_head = new_tail->next;
new_tail->next = NULL;
return new_head;
}
4. 对链表排序
对链表排序通常指的是对链表中的元素进行排序,以得到一个有序的链表。有多种方法可以实现链表排序,这里我们介绍两种常见的方法:归并排序和快速排序。
归并排序
归并排序是一种分治算法,它将链表分成两半,对每一半递归地进行排序,然后将排序好的两半合并起来。
C++实现:
ListNode* sortList(ListNode* head) {
if (head == NULL || head->next == NULL) return head;
ListNode* slow = head, *fast = head, *prev = NULL;
while (fast != NULL && fast->next != NULL) {
prev = slow;
slow = slow->next;
fast = fast->next->next;
}
prev->next = NULL; // 断开链表
ListNode* l1 = sortList(head);
ListNode* l2 = sortList(slow);
return mergeTwoLists(l1, l2);
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == NULL) return l2;
if (l2 == NULL) return l1;
if (l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
C实现:
ListNode* sortList(ListNode* head) {
if (head == NULL || head->next == NULL) return head;
ListNode* slow = head, *fast = head, *prev = NULL;
while (fast != NULL && fast->next != NULL) {
prev = slow;
slow = slow->next;
fast = fast->next->next;
}
prev->next = NULL; // 断开链表
ListNode* l1 = sortList(head);
ListNode* l2 = sortList(slow);
return mergeTwoLists(l1, l2);
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == NULL) return l2;
if (l2 == NULL) return l1;
if (l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
总结
本文介绍了链表的四种常见操作:反转链表、合并链表、旋转链表和对链表排序。每种操作都有其特定的应用场景和算法步骤,通过示例代码展示了如何实现这些操作。理解和掌握这些链表操作对于深入理解数据结构和算法至关重要。
热门推荐
书荐丨《八十本书环游地球》:哈佛大学教授的80堂文学课,不可错过的各大洲经典之作
变速箱与油耗关系:揭秘变速箱如何影响爱车油耗?
溢油事件应急预案
王鸿鹭:让人工智能更好赋能新质生产力
张雪峰谈统计学专业就业前景、考研方向、优势与劣势
麻州荣登2024年全美最佳居住地榜首,综合评分稳居第一
80后生二胎政策:回顾与思考
中医治疗小儿急性喉炎:辨证论治与中西医结合方案
隆鼻术后鼻塞与消肿:恢复过程与调适方法
深度剖析币圈四大诈骗伎俩与防范策略
如何批量注册抖音号?实用技巧大揭秘!
什么是入声字?其在古代汉语中的独特地位与现代演变
小学生数学学习动机激发的策略研究
黄金套利的操作方法有哪些?这些操作方法的风险如何控制?
国新办房地产会议落幕,地产股的表现反应了怎样的预期差?
尼泊尔的民族情况和民族政策
“减肥神药”有“毒”!已有人受害!警方提醒大家↓
全身发麻警示健康隐患?揭示背后的五大潜在原因!
2025年开运指南:迎接新年好运的全方位策略!
欢迎光临筑地
帕金森病流口水现象可控!这4个动作效果显著,逐渐帮你改善!
谁是第一?浪琴表世界马匹最新排名出炉!
《九龙城寨之围城》:黑帮、动作与乡愁
如何理解金融行业的相关利率问题?这种利率如何进行计算和分析?
撂荒地变身“希望的田野”!惠东有序推进撂荒耕地复耕复种
时隔九年,《巫师3》被删除的结局剧情重见天日
埃弗顿 2-2 利物浦:一场平局的多重意义与后续影响
天鹅绒和锦纶袜子的区别:柔软奢华 vs 耐用多功能
撂荒地变身“希望的田野”!惠东有序推进撂荒耕地复耕复种
适合年轻人摆地摊十大商品 年轻人适合摆什么地摊 年轻人摆摊卖什么