目录
前言
1.学习两两交换链表中节点的算法
2.学习删除节点中倒数第N个节点算法
3.学习如何判断两个链表是否相交
4.学习判断链表是否是循环链表
一、两两交换链表中的节点(力扣24)
实现思想:给整个链表添加一个虚拟头节点,从虚拟头节点开始每次操纵后两个点,因此每一次移动cur指针,我们都必须确保cur指针在要操作的两个节点之前
1.力扣写法
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummy_head = new ListNode(-1);
dummy_head->next = head;
ListNode* cur = dummy_head;
while(cur->next != nullptr && cur->next->next != nullptr){
ListNode* tmp = cur->next;
ListNode* tmp1 = cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = tmp;
cur->next->next->next = tmp1;
cur = cur->next->next;
}
return dummy_head->next;
}
};
#include <iostream>
using namespace std;
struct ListNode{
int val;
ListNode* next;
ListNode(int val):val(val), next(nullptr){}
};
//创建一个链表
void create(ListNode* &head){
ListNode* p = head;
for (int i = 4; i > 0; i--){
ListNode* newNode = new ListNode(i);
newNode->next = p->next;
p->next = newNode;
}
}
//遍历整个链表,并输出每一个节点的值
void printlist(ListNode* head){
ListNode* p = head;
while(p){
cout << p->val << ' ';
p = p->next;
}
cout << endl;
}
void change_node(ListNode* &head){
//给整个链表创建一个虚拟头节点
//虚拟头(_dummy_head)->0(head)->1->2->3->4->null
ListNode* _dummy_head = new ListNode(-1);
//将虚拟头节点指向整个链表的头节点,并创建一个cur指针指向虚拟头节点
//因为如果想交换一个节点的指针指向,那么就要找到这个节点的前一个节点
_dummy_head->next = head;
//虚拟头(cur)->0(head)->1->2->3->4->null
ListNode* cur = _dummy_head;
//节点的个数可能是偶数个或者奇数个,这两种情况我们都要考虑到
while(cur->next != nullptr && cur->next->next != nullptr){
ListNode* tmp = cur->next;
//虚拟头(cur)->0(head)->1(tmp)->2->3->4->null
ListNode* tmp1 = cur->next->next->next;
//虚拟头(cur)->0(head)->1(tmp)->2(tmp1)->3->4->null
cur->next = cur->next->next;
//虚拟头(cur)->1(tmp)->2(tmp1)->3->4->null
// 0(tmp)
cur->next->next = tmp;
//虚拟头(cur)->1(tmp)->0(tmp)
// 2(tmp1)->3->4->null
cur->next->next->next = tmp1;
//虚拟头(cur)->1(tmp)->0(tmp)->2(tmp1)->3->4->null
cur = cur->next->next;
//虚拟头->1(tmp)->0(cur)->2(tmp1)->3->4->null
}
head = _dummy_head->next;
}
int main(void){
ListNode *head = new ListNode(0);
//创建链表
create(head);
//打印没有交换之前的链表
printlist(head);
//两两交换链表当中的节点
change_node(head);
//打印交换之后的链表
printlist(head);
return 0;
}
二、删除链表中的倒数第N个节点(力扣19)
实现思想:使用一个fast指针和一个slow指针,先让fast指针向前移动N+1个位置,因为要删除一个节点,就要找到其前一个节点,因此删除倒数第N个节点,我们要找到第N-1个节点,这样fast指针和slow指针中间形成的空间正好可以在fast指向null的时候使得slow指向第N个节点的前一个节点
1.力扣实现
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy_head = new ListNode(-1);
dummy_head->next = head;
ListNode* fast = dummy_head;
ListNode* slow = dummy_head;
while(n-- && fast != nullptr){
fast = fast->next;
}
fast = fast->next;
while(fast != nullptr && slow != nullptr){
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummy_head->next;
}
};
2.自行创建链表并实现删除倒数第N个节点
#include <iostream>
using namespace std;
struct ListNode{
int val;
ListNode* next;
ListNode(int val):val(val), next(nullptr){}
};
//创建一个链表
void create(ListNode* &head){
ListNode* p = head;
for (int i = 4; i > 0; i--){
ListNode* newNode = new ListNode(i);
newNode->next = p->next;
p->next = newNode;
}
}
//遍历整个链表,并输出每一个节点的值
void printlist(ListNode* head){
ListNode* p = head;
while(p){
cout << p->val << ' ';
p = p->next;
}
cout << endl;
}
void delete_n(ListNode* &head, int n){
//创建一个虚拟头节点指向链表的head节点
ListNode* _dummy_head = new ListNode(-1);
_dummy_head->next = head;
//创建两个指针,快指针用来在前面寻找链表尽头
//slow指针用来指向需要删除节点的前一个节点
ListNode* fast = _dummy_head;
ListNode* slow = _dummy_head;
//先让fast指针前进
while(n-- && fast != nullptr){
fast = fast->next;
}
//fast指针需要多走一步才能指向需要删除节点的前一个结点
fast = fast->next;
while(fast != nullptr){
slow = slow->next;
fast = fast->next;
}
//删除节点
slow->next = slow->next->next;
//因为头节点也有可能被删除,所以需要重新赋值头节点
head = _dummy_head->next;
}
int main(void){
ListNode *head = new ListNode(0);
//创建链表
create(head);
//打印没有删除之前的链表
printlist(head);
//删除链表中倒数第N个元素
delete_n(head, 5);
//打印删除之后的链表
printlist(head);
return 0;
}
三、链表相交(力扣面试题02.07)
实现思想:这里观察链表相交的特点可以发现,两个链表相交之后的节点都是相同的,因此不论两个链表谁长谁短,将他们两个尾部对齐,其中一个指针从短的链表的第一个节点位置开始遍历,指向长的链表的指针,从短链表对应的长链表的相应位置开始遍历,如果遍历过程中指针相同就说明链表相交,否则就不相交。
1.力扣实现
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* pa = headA;
ListNode* pb = headB;
int lenA = 0, lenB = 0;
while(pa!=nullptr){
lenA++;
pa = pa->next;
}
while(pb!=nullptr){
lenB++;
pb = pb->next;
}
pa = headA;
pb = headB;
if (lenA < lenB){
swap(lenA, lenB);
swap(pa, pb);
}
int gap = lenA - lenB;
while(gap--){
pa = pa->next;
}
while(pa != nullptr && pb != nullptr){
if (pa == pb) return pa;
pa = pa->next;
pb = pb->next;
}
return NULL;
}
};
2.自行创建相交链表实现查找链表相交
#include <iostream>
#include <vector>
using namespace std;
struct ListNode{
int val;
ListNode* next;
ListNode(int val):val(val), next(nullptr){}
};
//创建链表1:
// 4->1->8->4->5
void create_1(ListNode* &head1){
ListNode* p = head1;
vector<int> nums = {1, 8, 4, 5};
for (int i = 0; i < nums.size(); i++){
ListNode* newNode = new ListNode(nums[i]);
newNode->next = p->next;
p->next = newNode;
p = p->next;
}
}
//创建链表2并连接到链表1上(两个链表相交在8处):
// 4->1->8->4->5
//5->0->1-^
void create_2(ListNode* &head2, ListNode* &head1){
ListNode* p1 = head1;
ListNode* p2 = head2;
vector<int> nums = {0, 1};
for (int i = 0; i < nums.size(); i++){
ListNode* newNode = new ListNode(nums[i]);
newNode->next = p2->next;
p2->next = newNode;
p2 = p2->next;
}
//此处的操作就是将5->0->1这段链表衔接在8上
p1 = p1->next;
p2->next = p1->next;
}
//遍历整个链表,并输出每一个节点的值
void printlist(ListNode* head){
ListNode* p = head;
while(p){
cout << p->val << ' ';
p = p->next;
}
cout << endl;
}
//找出两个链表相交的节点
ListNode* find(ListNode* head1, ListNode* head2){
//定义两个指针,分别从头开始遍历两个链表
ListNode* p1 = head1;
ListNode* p2 = head2;
//记录两个链表的长度
int size1 = 0, size2 = 0;
//先计算一下两个链表的长度
while(p1){
p1 = p1->next;
size1++;
}
while(p2){
p2 = p2->next;
size2++;
}
//计算过后,两个指针已经指到最末尾
//此时要将p1和p2指针在指回两个链表的头节点处
p1 = head1;
p2 = head2;
//链表1长度>链表2长度
if (size1 >= size2){
//两个链表长度相差多少
int gap = size1 - size2;
//因为如果两个链表有相交的地方,那么他们相交后的部分是完全相同的
//那么要将这两个的末端对齐
//while(gap--) p1 = p1->next;这部操作就相当于末端对齐
//短的链表从头部的位置开始遍历
//长的链表在短链表头部对应位置开始遍历
while(gap--) p1 = p1->next;
while(p1 != nullptr && p2 != nullptr){
//相同则返回
if (p1 == p2) return p1;
p1 = p1->next;
p2 = p2->next;
}
//循环结束说明没有相同节点,返回NULL
return nullptr;
}
//与上面同理
else{
int gap = size2 - size1;
while(gap--) p2 = p2->next;
while(p1 != nullptr && p2 != nullptr){
if (p1 == p2) return p2;
p1 = p1->next;
p2 = p2->next;
}
return nullptr;
}
}
int main(void){
//两个链表的头节点
ListNode* head1 = new ListNode(4);
ListNode* head2 = new ListNode(5);
//创建两个链表并使他们相交
create_1(head1);
create_2(head2, head1);
//打印两个链表
printlist(head1);
printlist(head2);
//寻找两链表相交的地方
ListNode* result = find(head1, head2);
cout << "这两个链表的交汇点是: "<<result->val;
return 0;
}
四、环形链表II(力扣142)
实现思量:使用双指针算法,如果链表有环则fast指针一定会在环中追上slow指针,具体证明过程不做具体解释
1.力扣实现
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != nullptr && fast->next != nullptr){
fast = fast->next->next;
slow = slow->next;
if(fast == slow){
ListNode* index1 = fast;
ListNode* index2 = head;
while(index1 != index2){
index1 = index1->next;
index2 = index2->next;
}
return index2;
}
}
return nullptr;
}
};