前言
人生如逆旅,我亦是行人。
一、链表的定义
-
链表是有一系列结点组成的,每个结点包括两个部分:
1、存储数据元素的数据域;
2、存储下一个节点地址的指针域。
struct ListNode
{
int val;
struct ListNode* next;
ListNode(int x):
val(x),next(NUL){
}
};
二、基本操作
1、创建单链表
步骤:
- 创建头节点 head ,并且将当前节点p指向头结点 ( p = head )
- 创建下一个节点 q ,当前节点 p 的下一个结点为 q ( p->next = q )
- 结点 p 后移一位 ( p = p -> next)
#include <iostream>
#include<vector>
using namespace std;
struct ListNode{
int val;
struct ListNode* next;
ListNode(int x) :
val(x), next(NULL){
}
};
int main(){
int num;
cin >> num;
ListNode* head = new ListNode(num);
ListNode* p = head;
//利用尾插法创建一个链表
while (cin >> num){
ListNode* q = new ListNode(num);
p->next = q;
p = p->next;
}
//遍历这个链表,并输出每个结点的元素
ListNode* m = head;
while (m != nullptr){
cout << m->val << endl;
m = m->next;
}
return 0;
}
2、插入节点
- 判断原链表是否为空,if 为空,将head(头结点指针) 指向新增结点
- if 不为空,向链表尾部插入新节点
ListNode* insertNode(ListNode* head, int data)
{
//将新插入的数据定义到一个新节点中
ListNode* newNode = new ListNode(data);
//定义一个新节点存储头节点
ListNode* p = head;
if(p == nullptr)
head = newNode;
else
{
//一直遍历链表结点,直至遍历到最后一个
while(p->next != nullptr)
p = p->next;
p->next = newNode;
}
//最后返回链表
return head;
}
3、删除节点
- 空链表
- 非空链表
- 删除节点存在
- 头节点
- 非头节点
- 删除节点不存在
- 删除节点存在
ListNode* deleteNode(ListNode* head, int data)
{
ListNode* p = head;
//首先判断是不是空链表
if(p == nullptr)
return head;
else
{
//判断是不是删除头结点
//如果头结点的值等于删除的值
if(p->val == data)
{
head = p->next;
delete p;
return head;
}
else
{
//如果有该结点且不是头结点,遍历到待删除节点的前一节点
while(p->next != nullptr && p->next->val != data)
p = p->next;
//遍历完整链表都没有待删除的结点
if(p->next == nullptr)
return head;
else
{
ListNode* deleteNode = p->next;
p->next = deleteNode->next;
delete deleteNode;
return head;
}
}
}
}
4、反转链表
-
假设在 c 开始反转,需要知道 c 前面的 b,还要保留 c 后面的 d。
-
if pNode 为当前的节点,pPrev 是 pNode 前面的节点,pNext 是 pNode 后面的节点,那么:
while(pNode != nullptr && pNext != nullptr)
- 将 pNode 指向 pPrev (pNode->next = pPrev)
- 将 pNode 给 pPrev (pPrev = pNode)
- 将 pNext 给 pNode(pNode = pNext)
while(pNode != nullptr && pNext == nullptr)
-
把反转后的头部指向 pNode
(边界条件)
#include<iostream>
#include<vector>
using namespace std;
struct ListNode
{
int val;
struct ListNode* next;
ListNode(int x):
val(x), next(NULL){
}
};
//反转链表
ListNode* reverse(ListNode* head)
{
ListNode* pPrev = nullptr;
ListNode* pNode = head;
ListNode* pReverseHead = nullptr;
while(pNode != nullptr)
{
ListNode* pNext = pNode->next;
if(pNext == nullptr)
pReverseHead = pNode;
pNode->next = pPrev;
pPrev = pNode;
pNode = pNext;
}
return pReverseHead;
}
int main()
{
int num;
cin >> num;
ListNode* head = new ListNode(num);
ListNode* p = head;
while(cin >> num)
{
ListNode* q = new ListNode(num);
p->next = q;
p = p->next;
}
//停止输入后
p->next = nullptr;
ListNode* result = reverse(head);
ListNode* node = result;
//将结果打印输出
while(node != nullptr)
{
cout << node->val << endl;
node = node->next;
}
return 0;
}
5、倒数第 X 个节点
- 快慢指针:快指针比慢指针会多走 (X-1) 步,当快指针到达终点时,慢指针到达倒数第 X 个节点。(X与链表节点个数的关系)
ListNode* FindKthToTail(ListNode* pListHead, unsigned int x)
{
if(pListHead == nullptr || x == 0)
return nullptr;
ListNode* pHead = pListHead;
//判断x是不是超出了链表的长度范围
for(int i=0; i<x-1; i++)
{
if(pHead->next != nullptr)
pHead = pHead->next;
else
return nullptr;
}
ListNode* pNext = pListHead;
while(pHead->next != nullptr)
{
pHead = pHead->next;
pNext = pNext->next;
}
return pNext;
}
6、是否有环 (快慢指针法)
- 如果有环,找出环的入口节点
- 判断是否有环:
- 思想:设置一个快指针和一个慢指针,快指针一次走一步,慢指针一次走两步,如果快指针追上了慢指针,说明链表有环,如果走到链表尾部都没有追上,说明链表无环。(注:快慢指针是否为nullptr的判断)
- 如果有环,找出入口节点:
- 思想:返回的节点一定在环内,如果找出环中节点的个数count,快指针比慢指针多走count步,那么两个指针相遇时,就是入口节点。
//判断快慢指针是否相遇
ListNode* MeetNode(ListNode* pHead)
{
ListNode* pNode = pHead;
//判断链表是否为空
if(pNode == nullptr)
return nullptr;
//设置慢指针,不能为nullptr
ListNode* slowNode = pNode->next;
if(slowNode == nullptr)
return nullptr;
//设置快指针
ListNode* fastNode = slowNode->next;
while(fastNode != nullptr && slowNode != nullptr)
{
//相遇返回快慢指针,找出入口位置
if(fastNode == slowNode)
return fastNode;
slowNode = slow->next;//走一步
fastNode = fastNode->next;//走两步
if(fastNode->next != nullptr)
{
fastNode = fastNode->next;
}
}
return nullptr;
}
//计算环中结点个数
int Count(ListNode* pMeet)
{
int count = 0;
ListNode* pNode = pMeet;
while(pNode->next != pMeet)
{
count++;
pNode = pNode->next;
}
count++;
return count;
}
//计算环的入口节点
LisNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode* meetNode = MeetNode(pHead);
if(meetNode == nullptr)
return nullptr;
int count = Count(meetNode);
ListNode* pPrev = pHead;
ListNode* pNext = pHead;
for(int i=0; i<count; i++)
pPrev = pPrev->next;
while(pPrev != pNext)
{
pPrev = pPrev->next;
pNext = pNext->next;
}
ListNode* result = pPrev;
}