0
点赞
收藏
分享

微信扫一扫

LeetCode 有关链表的算法题目(C++)


1、反转链表

问题描述

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

代码实现

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL)
return NULL;
if(head->next == NULL)
return head;
ListNode* p = reverseList(head->next); // p最终指向了尾结点
head->next->next = head; // 向回指向
head->next = NULL; // 原来顺序的指针断掉
return p; // 返回最为顺序指向尾结点指针 此时作为头指针的p指针
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_代码实现

非递归(迭代)写法

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL)
return NULL;
ListNode* pcur = head, *pnext = pcur->next;
pcur->next = nullptr;
while(pnext){
ListNode* ptmp = pcur;
pcur = pnext;
pnext = pnext->next;
pcur->next = ptmp;
}
return pcur;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_结点_02

2、反转链表 II

问题描述

反转从位置​​m​​​ 到​​n​​ 的链表。请使用一趟扫描完成反转。

说明:
​​​1 ≤ m ≤ n ≤ 链表长度​​。

示例:

输入: ​​1->2->3->4->5->NULL, m = 2, n = 4​​​ 输出:​​1->4->3->2->5->NULL​

解题思路

用善意题目迭代的方法,但要注意分两种情况讨论:(1)​​m == 1​​​时,需要改动头指针;(2)​​m != 1​​时,无需改动头指针。

算法实现

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution { //建立伪头结点
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(head->next == nullptr)
return head;
ListNode* h = new ListNode(0);
h->next = head;
ListNode* p = h;
head = nullptr;
int i = 1;
for(; i < m; i++)
p = p->next; // 让p指向m的前一个结点
ListNode* pbegin = p->next, *pcur = pbegin, *pend = pcur->next;
for(; i < n; i++){
ListNode* ptmp = pcur;
pcur = pend;
pend = pend->next;
pcur->next = ptmp;
}
p->next = pcur;
pbegin->next = pend;
return h->next;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_代码实现_03

3、合并两个有序链表

问题描述

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:​​1->2->4, 1->3->4​​​ 输出:​​1->1->2->3->4->4​

解题思路

  • 链表的兼并是老生常谈的问题,却总是费时间完成。对于不带头结点的链表来说,需要建立一个伪头结点作为合并链表后的头结点。
  • 考虑其中一个链表为空的情况,直接返回另一个链表。
  • 定义两个动态指针指向两个链表表头,设置​​tail​​指针作为尾插法的尾指针。
  • 比较两个动态指针所指向结点值的大小,较小的值是待插入结点,方法就是尾插法。

代码实现

/**
* 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* mergeTwoLists(ListNode *a, ListNode *b) {
if (a == nullptr or b == nullptr)
return a ? a : b;
ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
while (aPtr and bPtr) {
if (aPtr->val < bPtr->val) {
tail->next = aPtr;
aPtr = aPtr->next;
}
else{
tail->next = bPtr;
bPtr = bPtr->next;
}
tail = tail->next;
}
tail->next = (aPtr ? aPtr : bPtr);
return head.next;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_代码实现_04

兼并链表的递归写法

/**
* 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* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == nullptr or l2 == nullptr)
return l1 ? l1 : l2;
ListNode* head = nullptr;
if(l1->val <= l2->val){
head = l1;
head->next = mergeTwoLists(l1->next, l2);
}
else{
head = l2;
head->next = mergeTwoLists(l1, l2->next);
}
return head;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_链表_05

4、环形链表(判断链表中是否有环)

问题描述

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 ​​pos​​​来表示链表尾连接到链表中的位置(索引从​​0​​​ 开始)。 如果​​pos​​​ 是​​-1​​,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

LeetCode 有关链表的算法题目(C++)_代码实现_06

代码实现

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == nullptr or head->next == nullptr)
return false;
ListNode* p = head, *q = head->next;
while(p != q){
if(q == nullptr or q->next == nullptr) // 说明快指针走到了尽头
return false;
p = p->next;
q = q->next->next;
}
return true;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_结点_07

5、环形链表 II(返回环的起始结点)

问题描述

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 ​​null​​。

为了表示给定链表中的环,我们使用整数 ​​pos​​​ 来表示链表尾连接到链表中的位置(索引从 ​​0​​​开始)。 如果 ​​pos​​​ 是 ​​-1​​,则在该链表中没有环。

说明:不允许修改给定的链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。

LeetCode 有关链表的算法题目(C++)_代码实现_06

代码实现(借用辅助空间,时间复杂度低)

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head == nullptr or head->next == nullptr)
return nullptr;
ListNode* p = head;
vector<ListNode*> v;
for(; p; p = p->next){
v.push_back(p);
if(count(v.begin(), v.end(), p) > 1)
return *find(v.begin(), v.end(), p);
}
return nullptr;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_代码实现_09

6、 删除排序链表中的重复元素

题目描述

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 1:

输入: 1->1->2
输出: 1->2

示例 2:

输入: 1->1->2->3->3
输出: 1->2->3

代码实现

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head == nullptr or head->next == nullptr)
return head;
ListNode* p = head->next, *tmp = head;
for(; p; p = p->next){
if(p->val == tmp->val){
tmp->next = p->next;
continue;
}
tmp = tmp->next;
}
return head;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_链表_10

7、删除排序链表中的重复元素 II

问题描述

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

示例 1:

输入: 1->2->3->3->4->4->5
输出: 1->2->5

示例 2:

输入: 1->1->1->2->3
输出: 2->3

解题思路

  • 头结点有可能发生改变,所以新建一伪头结点,方便头结点的删除操作。
  • 如果重复的数字在链表结尾,for循环结束后,需要在此判断一下。

代码实现

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head == nullptr or head->next == nullptr)
return head;
ListNode* h = new ListNode(0);
h->next = head;
ListNode* q = head, *p = head->next, *tmp = nullptr;
ListNode* pre = h;
head = nullptr;
for(; p; p = p->next){
if(p->val != q->val){
q = p;
if(tmp){
for(; pre->next != tmp; pre = pre->next);
pre->next = p;
tmp = nullptr;
}
}
else{
tmp = q;
}
}
if(tmp){
for(; pre->next != tmp; pre = pre->next);
pre->next = nullptr;
}
return h->next;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_链表_11

8、返回倒数第 k 个节点

实现一种算法,找出单向链表中倒数第 ​​k​​个节点。返回该节点的值。

注意:本题相对原题稍作改动

示例:

输入: 1->2->3->4->5 和 k = 2
输出: 4

说明:

给定的 k 保证是有效的。

解题思路

  • 两种方法:
  • (1)快慢指针。让快慢指针分别指向头结点后,快指针向前移动​​k​​​个结点,这样快慢指针就拉开​​k​​​个结点的距离,待快指针指向​​nullptr​​​后,慢指针自然指向倒数第​​k​​个结点。
  • (2)引入额外空间,用容器存储结点值,返回倒数第​​k​​个结点值即可。

代码实现(快慢指针)

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
int kthToLast(ListNode* head, int k) {
ListNode* fast = head, *low = head;
for(; k > 0; k--)
fast = fast->next;
for(; fast; fast = fast->next)
low = low->next;
return low->val;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_链表_12

代码实现(使用容器)

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
int kthToLast(ListNode* head, int k) {
vector<int> v;
for(ListNode* p = head; p; p = p->next)
v.push_back(p->val);
reverse(v.begin(), v.end());
return v[k - 1];

}
};

运行截图

LeetCode 有关链表的算法题目(C++)_代码实现_13

9、排序链表

问题描述

在​​O(n log n)​​时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4

示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5

解题思路

  • 使用容器。
  • 归并排序。

代码实现(引入容器)

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(head == nullptr or head->next == nullptr)
return head;
vector<int> v;
for(ListNode* p = head; p; p = p->next){
v.push_back(p->val);
}
sort(v.begin(), v.end());
int i = 0;
for(ListNode* p = head; p; p = p->next){
p->val = v[i++];
}
return head;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_结点_14

代码实现(归并排序)

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
return mergesort(head);
}
ListNode* mergesort(ListNode* head){
if(head == nullptr or head->next == nullptr)
return head;
ListNode *fast = head, *slow = head, *brek = head; // 截断
while(fast and fast->next){
fast = fast->next->next;
brek = slow;
slow = slow->next;
}
brek->next = nullptr;
ListNode *l1 = mergesort(node);
ListNode *l2 = mergesort(slow);
return merge(l1, l2);
}
ListNode* merge(ListNode* l1, ListNode* l2){
if(l1 == nullptr)
return l2;

if(l2 == nullptr)
return l1;
if(l1->val < l2->val){
l1->next = merge(l1->next, l2);
return l1;
}
else{
l2->next = merge(l2->next, l1);
return l2;
}
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_结点_15

10、对链表进行插入排序

问题描述

对链表进行插入排序。

LeetCode 有关链表的算法题目(C++)_结点_16

插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

插入排序算法:

插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4

示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5

解题思路

  • 链表中,遇到需要在头指针前面插入数据的情况,可以添加一个伪头指针​​h​​​,方便对头结点的更新,最后返回​​h->next​​即可。
  • 算法思路就是直接插入排序。

代码实现

class Solution {
public:
ListNode* insertionSortList(ListNode* head) {
if (head == nullptr or head->next == nullptr)
return head;
ListNode *h = new ListNode(-1);//伪头指针
h->next = head;
ListNode *p = head;
ListNode *q = head->next;
head == nullptr;
while (q){
if (q->val < p->val){
ListNode* temp = h;//!!temp要等于h,这样才可以比较第一个元素
for(; temp->next->val < q->val; temp = temp->next); // 找到待插入点
p->next = q->next;
q->next = temp->next;
temp->next = q;
q = p->next;//此时不用改变p指向!因为p没有变,只是待排序元素变了位置。
}
else{
p = p->next;
q = q->next;
}
}
return h->next;//!!!不能返回head!!因为后面会改变head所指向内存的位置!
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_链表_17

11、合并K个排序链表

合并​​k​​个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6

解题思路(1 顺序合并)

  • 定义一个归并两个链表的算法。再次求解容器中所有链表的归并。

代码实现

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergetwoLists(ListNode* l1, ListNode* l2){
if(l1 == nullptr or l2 == nullptr)
return l1 ? l1 : l2;
ListNode head, * tail = &head, *p1 = l1, *p2 = l2;
while(p1 and p2){
if(p1->val < p2->val){
tail->next = p1;
p1 = p1->next;
}else{
tail->next = p2;
p2 = p2->next;
}
tail = tail->next;
}
tail->next = (p1 ? p1 : p2);
return head.next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode* head = nullptr;
for(auto& i : lists)
head = mergetwoLists(head, i);
return head;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_代码实现_18

解题思路(2 分治合并)

两两归并的方法,如下图。

LeetCode 有关链表的算法题目(C++)_链表_19

代码实现

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergetwoLists(ListNode* l1, ListNode* l2){
if(l1 == nullptr or l2 == nullptr)
return l1 ? l1 : l2;
ListNode head, * tail = &head, *p1 = l1, *p2 = l2;
while(p1 and p2){
if(p1->val < p2->val){
tail->next = p1;
p1 = p1->next;
}else{
tail->next = p2;
p2 = p2->next;
}
tail = tail->next;
}
tail->next = (p1 ? p1 : p2);
return head.next;
}
ListNode* merge(vector<ListNode*>& lists, int l ,int r){
if(l == r)
return lists[l];
if(l > r)
return nullptr;
int mid = (l + r) >> 1;
return mergetwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
int n = lists.size();
return merge(lists, 0, n - 1);
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_链表_20

解题思路(3 使用优先队列合并)

维护当前每个链表没有被合并的元素的最前面一个,​​k​​​个链表就最多有 ​​k​​​ 个满足这样条件的元素,每次在这些元素里面选取​​val​​属性最小的元素合并到答案中。在选取最小元素的时候,我们可以用优先队列来优化这个过程。

代码实现

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
struct Status {
int val;
ListNode *ptr;
bool operator < (const Status &rhs) const {
return val > rhs.val;
}
};

priority_queue <Status> q;

ListNode* mergeKLists(vector<ListNode*>& lists) {
for (auto& node : lists) {
if (node)
q.push({node->val, node});
}
ListNode head, *tail = &head;
while (q.size()) {
auto f = q.top();
q.pop();
tail->next = f.ptr;
tail = tail->next;
if (f.ptr->next)
q.push({f.ptr->next->val, f.ptr->next});
}
return head.next;
}
};

运行截图

LeetCode 有关链表的算法题目(C++)_代码实现_21


举报

相关推荐

0 条评论