链表算法练习
1. 移除链表元素
删除链表中等于给定值 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
输出:[]
代码如下:
点击查看代码
#include<iostream>
using namespace std;
struct Node
{
int val;
Node* next;
Node(int val) : val(val), next(nullptr) {}
};
Node* removeElement(Node* head, const int val)
{
Node* dum = new Node(0);
dum->next = head;
Node* cur = dum;
while (cur->next)
{
if (cur->next->val == val)
{
cur->next = cur->next->next;
}
else
{
cur = cur->next;
}
}
return dum->next;
}
void showList(Node* head)
{
Node* cur = head;
cout << "[";
while (cur)
{
cout << cur->val << ", ";
cur = cur->next;
}
cout << "]" << endl;
}
int main()
{
Node* a = new Node(1);
Node* b = new Node(2);
Node* c = new Node(6);
Node* d = new Node(3);
Node* e = new Node(4);
Node* f = new Node(5);
Node* g = new Node(6);
a->next = b;
b->next = c;
c->next = d;
d->next = e;
e->next = f;
f->next = g;
g->next = nullptr;
int val = 6;
showList(removeElement(a, 6));
return 0;
}
2. 设计链表
在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
用的带访问权限和函数的 struct:
bool isValid(int index):判断索引 index 是否有效。
ListNode* findPosition(bool bFront, int index):寻找操作位置;标志 bFront 标识是在 index 节点之前还是自身操作。
get(int index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtIndex(int index,int val):
- 指定位置 index,在其前加值为 val 的节点;
- 如果 index=count,则附加到尾,
- 如果 index>count,则不插入;
- 如果index<0,则插入到头。
addAtHead(int val):在链表的头元素前加值为 val 的节点。
addAtTail(int val):在链表的尾元素后加值为 val 的节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
代码如下:
点击查看代码
#include<iostream>
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int val) : val(val), next(nullptr) {}
};
struct MyList
{
public:
MyList()
{
this->count = 0;
this->dum = new ListNode(0);
}
bool isValid(int index);
ListNode* findPosition(bool bFront, int index);
int get(int index);
void addAtIndex(int index, int val);
void addAtHead(int val);
void addAtTail(int val);
void deleteAtIndex(int index);
void showList();
private:
ListNode* dum;
int count;
};
bool MyList::isValid(int index)
{
// 非法索引
if (index < 0 || index > count)
{
cout << "index invalid" << endl;
return false;
}
return true;
}
ListNode* MyList::findPosition(bool bFront, int index)
{
ListNode* cur = nullptr;
// 需要前一个节点就从 dum 开始数,否则从 真实数据 开始
if (bFront)
cur = this->dum;
else
cur = this->dum->next;
while (index--)
cur = cur->next;
return cur;
}
int MyList::get(int index)
{
// 非法索引
if (!isValid(index) || index == count) return -1;
// 获取自身索引 故为 false
ListNode* cur = findPosition(false, index);
return cur->val;
}
void MyList::addAtIndex(int index, int val)
{
// 非法索引
if (!isValid(index)) return;
// 尾节点之后
if (index == count && count != 0)
{
// count = true_idx + 1,所以需要 count - 1 = true_idx,
// 尾结点索引自身 故为 false
ListNode* cur = findPosition(false, count - 1);
cur->next = new ListNode(val);
}
// 所有节点之前
else
{
// 索引前 为 true
ListNode* cur = findPosition(true, index);
ListNode* tmp = cur->next;
cur->next = new ListNode(val);
cur->next->next = tmp;
}
++count;
}
void MyList::addAtHead(int val)
{
addAtIndex(0, val);
}
void MyList::addAtTail(int val)
{
addAtIndex(this->count, val);
}
void MyList::deleteAtIndex(int index)
{
// 非法索引
if (!isValid(index) || index == count) return;
// 获取删除节点索引的前一个 为 true
ListNode* cur = findPosition(true, index);
ListNode* tmp = cur->next->next;
delete cur->next;
cur->next = tmp;
--count;
}
void MyList::showList()
{
ListNode* cur = this->dum->next;
cout << "[";
while (cur)
{
cout << cur->val << ", ";
cur = cur->next;
}
cout << "]" << endl;
}
int main()
{
MyList* list = new MyList();
list->addAtHead(7);
list->addAtHead(2);
list->addAtHead(1);
list->addAtIndex(3, 0);
list->deleteAtIndex(2);
list->addAtHead(6);
list->addAtTail(4);
list->showList();
cout << list->get(4) << endl;
list->addAtHead(4);
list->addAtIndex(5, 0);
list->addAtHead(6);
list->showList();
return 0;
}
3. 反转链表
反转一个单链表
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
代码如下:
点击查看代码
#include<iostream>
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int val) : val(val), next(nullptr) {}
};
ListNode* reverseList(ListNode* head)
{
ListNode* pre = nullptr;
ListNode* cur = head;
while (cur)
{
ListNode* tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
void showList(ListNode* head)
{
ListNode* cur = head;
while (cur)
{
cout << cur->val << "->";
cur = cur->next;
}
cout << "NULL" << endl;
}
int main()
{
ListNode* a0 = new ListNode(1);
ListNode* a1 = new ListNode(2);
ListNode* a2 = new ListNode(3);
ListNode* a3 = new ListNode(4);
ListNode* a4 = new ListNode(5);
a0->next = a1;
a1->next = a2;
a2->next = a3;
a3->next = a4;
showList(a0);
a0 = reverseList(a0);
showList(a0);
return 0;
}
4. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换
示例 1:
输入:head =[1,2,3,4]
输出:[2,1,4,3]
示例 2:
输入:head =[]
输出:[]
示例 3:
输入:head =[1]
输出:[1]
代码如下:
点击查看代码
#include<iostream>
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int val) : val(val), next(nullptr) {}
};
void showList(ListNode* head)
{
ListNode* cur = head;
cout << "[";
while (cur)
{
cout << cur->val;
if (cur->next)
cout << ", ";
cur = cur->next;
}
cout << "]" << endl;
}
ListNode* exchangeDouble(ListNode* head)
{
ListNode* dum = new ListNode(-1);
dum->next = head;
ListNode* cur = dum;
while (cur->next && cur->next->next)
{
ListNode* tmp = cur->next;
cur->next = cur->next->next;
tmp->next = cur->next->next;
cur->next->next = tmp;
cur = tmp;
}
return dum->next;
}
int main()
{
ListNode* a0 = new ListNode(1);
ListNode* a1 = new ListNode(2);
ListNode* a2 = new ListNode(3);
ListNode* a3 = new ListNode(4);
a0->next = a1;
a1->next = a2;
a2->next = a3;
showList(a0);
a0 = exchangeDouble(a0);
showList(a0);
return 0;
}
5. 删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
尝试使用一趟扫描实现。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
代码如下:
点击查看代码
#include<iostream>
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int val) : val(val), next(nullptr) {}
};
void showList(ListNode* head)
{
ListNode* cur = head;
cout << "[";
while (cur)
{
cout << cur->val;
if (cur->next)
cout << ", ";
cur = cur->next;
}
cout << "]" << endl;
}
ListNode* removeIndexElem(ListNode* head, int index)
{
ListNode* dum = new ListNode(0);
dum->next = head;
ListNode* pre = dum;
ListNode* tail = dum;
// 先用 index 定位尾巴(因为要删除,要提前一位,所以 index + 1)
++index;
while (index--)
{
tail = tail->next;
}
while (tail)
{
pre = pre->next;
tail = tail->next;
}
ListNode* tmp = pre->next;
pre->next = tmp->next;
delete tmp;
return dum->next;
}
int main()
{
ListNode* a0 = new ListNode(1);
ListNode* a1 = new ListNode(2);
ListNode* a2 = new ListNode(3);
ListNode* a3 = new ListNode(4);
ListNode* a4 = new ListNode(5);
a0->next = a1;
a1->next = a2;
a2->next = a3;
a3->next = a4;
showList(a0);
removeIndexElem(a0, 2);
showList(a0);
return 0;
}
6. 链表相交
在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点head1和head2,这两个链表可能相交,也可能不相交。
请实现一个函数,如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null即可,
要求:如果链表1的长度为N,链表2的长度为M,时间复杂度请达到0(N+M),额外空间复杂度请达到0(1)。
相交分情况:
1. 先判断有无环;
快慢指针寻找,有环会相遇。
2. 其次判断交点:
(1)均无环:
1)不相交,返回 null;
2)相交,长短统一后,寻找相交一个节点。
(2)有环:
1)一个有,一个无,必不相交,返回 null;
2)两个都有,看入环点:
相同:
去除环后,找寻相交节点:
有,则返回;
无,则在环上相交,返回切入点。
不同:
选择一个入环点,寻找相交节点:
有,返回任意一个入环点;
无,返回 null。
画图示意:

代码如下:
点击查看代码
#include<iostream>
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int val) : val(val), next(nullptr) {}
};
// 双环的入环点匹配
ListNode* cricleEntryMatch(ListNode* head1, ListNode* head2)
{
ListNode* idx1 = head1, *idx2 = head2;
ListNode* stop = head2;
while (idx1 && idx2)
{
if (idx1 == idx2)
return idx1;
idx2 = idx2->next;
if (idx2 == stop)
return nullptr;
}
}
ListNode* cricleDetect(ListNode* head)
{
ListNode* slow = head, *fast = head;
// ----- 快慢匹配 -----
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
{
// ----- 入环点匹配 -----
ListNode* idx1 = head, *idx2 = fast;
while (idx1 != idx2)
{
idx1 = idx1->next;
idx2 = idx2->next;
}
return idx1;
// ----- 入环点匹配 -----
}
}
// ----- 快慢匹配 -----
return nullptr;
}
ListNode* crossDetect(ListNode* head1, ListNode* head2)
{
ListNode* cur1 = head1, *cur2 = head2;
// ----- 统计长度 -----
int len1 = 0, len2 = 0;
while (cur1)
{
++len1;
cur1 = cur1->next;
}
while (cur2)
{
++len2;
cur2 = cur2->next;
}
// ----- 统计长度 -----
// ----- 长短统一 -----
bool bShort1 = len1 > len2 ? true : false;
int gap = bShort1 ? len1 - len2 : len2 - len1;
cur1 = head1, cur2 = head2;
while (gap--)
{
if (bShort1)
cur1 = cur1->next;
else
cur2 = cur2->next;
}
// ----- 长短统一 -----
// ----- 寻找相交一个节点 -----
while (cur1)
{
if (cur1 == cur2)
{
return cur1;
}
cur1 = cur1->next;
cur2 = cur2->next;
}
// ----- 寻找相交一个节点 -----
return nullptr;
}
ListNode* Detector(ListNode* head1, ListNode* head2)
{
// 先判断有无环
int count = 0;
ListNode* cricleEntry1 = cricleDetect(head1);
if (cricleEntry1) ++count;
ListNode* cricleEntry2 = cricleDetect(head2);
if (cricleEntry2) ++count;
// 均无环
if (count == 0)
{
// 1)不相交,返回 null;
// 2)相交,长短统一后,寻找相交一个节点。
return crossDetect(head1, head2);
}
// 有 1 环 必不相交
else if (count == 1)
{
return nullptr;
}
// 有 2 环 看入环点
else
{
// 相同
if (cricleEntry1 == cricleEntry2)
{
/*
去除环后,找寻相交节点(记得恢复):
有,则返回;
无,则在环上相交,返回切入点
*/
ListNode* tmp1 = cricleEntry1->next;
ListNode* tmp2 = cricleEntry2->next;
cricleEntry1->next = nullptr;
cricleEntry2->next = nullptr;
ListNode* ans = crossDetect(head1, head2);
cricleEntry1->next = tmp1;
cricleEntry2->next = tmp2;
return ans ? ans : cricleEntry1;
}
// 不同
else
{
/*
选择一个入环点,寻找相交节点:
有,返回任意一个入环点;
无,返回 null。
*/
ListNode* tmp = cricleEntry1->next;
cricleEntry1->next = nullptr;
ListNode* ans = cricleEntryMatch(cricleEntry1, cricleEntry2);
cricleEntry1->next = tmp;
return ans;
}
}
}
void showResult(ListNode* ans)
{
if (!ans)
cout << "不相交" << endl;
else
{
cout << "交点为:" << ans << ", ";
cout << "交点值为:" << ans->val << endl;
}
}
int main()
{
// ----- 无环 -----
// ~ 不相交 ~
ListNode* a0 = new ListNode(1);
ListNode* a1 = new ListNode(2);
ListNode* a2 = new ListNode(3);
ListNode* b0 = new ListNode(4);
ListNode* b1 = new ListNode(5);
a0->next = a1;
a1->next = a2;
b0->next = b1;
// [1,2,3]; [4,5]
cout << "无环";
showResult(Detector(a0, b0));
// ~ 不相交 ~
// ~ 相交 ~
ListNode* ca0 = new ListNode(1);
ListNode* ca1 = new ListNode(2);
ListNode* ca2 = new ListNode(3);
ListNode* ca3 = new ListNode(8);
ListNode* cb0 = new ListNode(4);
ListNode* cb1 = new ListNode(5);
ca0->next = ca1;
ca1->next = ca2;
ca2->next = ca3;
cb0->next = ca3;
ca3->next = cb1;
// [1,2,3,8,5]; [4,8,5]; 交点是8
cout << "无环相交, ";
showResult(Detector(ca0, cb0));
// ~ 相交 ~
// ----- 无环 -----
// ----- 有环 -----
// ~ 1 环 ~
ListNode* rca0 = new ListNode(1);
ListNode* rca1 = new ListNode(2);
ListNode* rca2 = new ListNode(3);
ListNode* rca3 = new ListNode(8);
ListNode* rcb0 = new ListNode(4);
ListNode* rcb1 = new ListNode(5);
rca0->next = rca1;
rca1->next = rca2;
rca2->next = rca3;
rca3->next = rca1;
rcb0->next = rcb1;
// [1,2,3,8,2,3,8,.....] 有环点 2 ; [4,5]
cout << "有 1 环";
showResult(Detector(rca0, rcb0));
// ~ 1 环 ~
// ~ 2 环 环点不同 ~
ListNode* rca011 = new ListNode(1);
ListNode* rca111 = new ListNode(2);
ListNode* rca211 = new ListNode(3);
ListNode* rca311 = new ListNode(8);
ListNode* rcb011 = new ListNode(4);
ListNode* rcb111 = new ListNode(5);
rca011->next = rca111;
rca111->next = rca211;
rca211->next = rca311;
rca311->next = rca111;
rcb011->next = rcb111;
rcb111->next = rca311;
// [1,2,3,8,2,3,8,.....]; [4,5,8,2,3,8,...]; 有环点 2,8
cout << "有 2 环, 相交但环点不同, 任意一";
showResult(Detector(rca011, rcb011));
ListNode* da01 = new ListNode(1);
ListNode* da11 = new ListNode(2);
ListNode* da21 = new ListNode(3);
ListNode* da31 = new ListNode(7);
ListNode* da41 = new ListNode(8);
da01->next = da11;
da11->next = da21;
da21->next = da31;
da31->next = da41;
da41->next = da21;
ListNode* db01 = new ListNode(4);
ListNode* db11 = new ListNode(5);
ListNode* db21 = new ListNode(6);
ListNode* db31 = new ListNode(9);
db01->next = db11;
db11->next = db21;
db21->next = db31;
db31->next = db11;
// [1,2,3,7,8,3,7,8,.....]; [4,5,6,9,5,6,9,...]; 有环点 3,5
cout << "有 2 环, 相交但环点不同, 且是自身有环, ";
showResult(Detector(da01, db01));
// ~ 2 环 坏点不同 ~
// ~ 2 环 环点相同 ~
ListNode* rca01 = new ListNode(1);
ListNode* rca11 = new ListNode(2);
ListNode* rca21 = new ListNode(3);
ListNode* rca31 = new ListNode(8);
ListNode* rcb01 = new ListNode(4);
ListNode* rcb11 = new ListNode(5);
rca01->next = rca11;
rca11->next = rca21;
rca21->next = rca31;
rca31->next = rca11;
rcb01->next = rcb11;
rcb11->next = rca11;
// [1,2,3,8,2,3,8,.....]; [4,5,2,3,8,2,...]; 有环点 2
cout << "有 2 环, 相交且环点相同, ";
showResult(Detector(rca01, rcb01));
ListNode* da0 = new ListNode(1);
ListNode* da1 = new ListNode(2);
ListNode* da2 = new ListNode(3);
ListNode* da3 = new ListNode(7);
ListNode* da4 = new ListNode(8);
ListNode* db0 = new ListNode(4);
ListNode* db1 = new ListNode(5);
ListNode* db2 = new ListNode(6);
da0->next = da1;
da1->next = da2;
da2->next = da3;
da3->next = da4;
da4->next = da2;
db0->next = db1;
db1->next = db2;
db2->next = da1;
// [1,2,3,7,8,3,7,8,.....]; [4,5,6,2,3,7,8,3,7,8,...]; 有交点 2 环点 3
cout << "有 2 环, 相交且环点相同, 但前面有交点, ";
showResult(Detector(da0, db0));
// ~ 2 环 环点相同 ~
// ----- 有环 -----
return 0;
}
结果截图:



浙公网安备 33010602011771号