一.链表节点
1.链表节点的定义
#include<iostream>
#include<algorithm>
using namespace std;
class LinkNode
{
public:
int Data;
LinkNode* Prev;
LinkNode* Next;
LinkNode(int data);
LinkNode();
};
2.链表节点的实现
将新节点的next指针和prev指针设为null
#include "LinkNode.h"
LinkNode::LinkNode(int data)
:Data(data),Prev(nullptr),Next(nullptr)
{
}
LinkNode::LinkNode()
:Prev(nullptr),Next(nullptr)
{
}
二.双链表实现
1.链表接口
#include<iostream>
#include<algorithm>
#include"LinkNode.h"
using namespace std;
class LinkList
{
private:
LinkNode* Head;//头节点
LinkNode* Curr;//当前节点
LinkNode* Tail;//尾节点
int Size;
public:
LinkList();
bool isEmpty();//判空
void BuildListByHeadInsert(int ele);//头插法建表
void BuildListByTailInsert(int ele);//尾插法建表同时在尾节点处插入节点
void BackwardInsert(int ele);// 在当前节点(位于中间)的后一个节点插入一个新节点
void ForwardInsert(int ele);// 在当前节点(位于中间)的前一个节点插入一个新节点
void BackwardInsertByNode(int ele, LinkNode* Node);//在已知节点(位于中间)的后一个节点插入一个新节点
void ForwardInsertByNode(int ele, LinkNode* Node);//在已知节点(位于中间)的前一个节点插入一个新节点
int getSize();//获取表长
void Delete();//删除当前节点的下一个节点
void DeleteByNode(LinkNode* Node);//删除已知节点的下一个节点
int getCurr();//获取当前节点的数据
void SequentialPrint();//顺序打印链表
void ReversePrint();//序打印链表
LinkNode* GetHead();//获取头节点
void Prev();//将当前节点后移一位
int Find(int ele);//按值查找节点
int At(int pos);//按位置查找节点
void Clear();//清空链表内元素但不删除
void Destory();//销毁链表
LinkNode* GetElemByPos(int pos);//按位置查找节点
LinkNode* GetElemByValue(int ele);//按值查找节点
void setTail();//设置尾节点
int CaculateSize();//计算链表长
int getTail();
};
2.初始化
1.创建一个新节点设为头节点
2.将尾节点设为头节点
LinkList::LinkList()
:Size(0)
{
Head = new LinkNode;
Curr = Head;
Tail = Head;
}
3.建表操作
Ⅰ.头插法
每次将节点插入到链表的表头的前一个节点,生成的节点次序和输入数据相反
一.若头节点后无节点 新节点和头节点双向联系
1.将头节点的next和新节点相连
2.将新节点的prev和头节点联系
二.若头节点后有节点 新节点和头节点后驱双向联系
1.新节点的next指向头节点的后驱
2.头节点后驱的prev指向新节点
新节点和头节点双向联系
1.将头节点的next和新节点相连
2.将新节点的prev和头节点联系
三.若新节点的下一个节点为空节点则将新节点设为尾节点
时间复杂度:O(N)
void LinkList::BuildListByHeadInsert(int ele)
{
Size++;
LinkNode* Temp = new LinkNode(ele);
if (Head->Next == NULL)
{
//新节点和头节点建立双向联系
Temp->Prev = Head;
Head->Next = Temp;
}
else
{
//新节点和头节点的后驱建立双向联系
Head->Next->Prev = Temp;//将当前节点的后一个节点的上一个节点设为新节点
Temp->Next = Head->Next;//将新节点的后驱节点为当前节点的后驱节点
//新节点和头节点建立双向联系
Temp->Prev = Head;
Head->Next = Temp;
}
//setTail();
if (Temp->Next == nullptr)
{
Tail = Temp;
}
}
Ⅱ.尾插法
每次将节点插入到链表的表头的后一个节点,生成的节点次序和输入数据相同
1.将新节点和尾节点双向联系
将尾节点的next和新节点相连
将新节点的prev和尾节点联系
2.将新节点设为尾节点
3.将尾节点的下一个节点设为空节点
void LinkList::BuildListByTailInsert(int ele)
{
Size++;
LinkNode* Temp = new LinkNode(ele);
//和尾节点建立双向联系
if (Tail->Next == nullptr)
{
Tail->Next = Temp;
Temp->Prev = Tail;
}
Tail = Temp;
}
4.插入操作
Ⅰ.后插法
一.和插入位置的后驱节点建立双向联系
1.将新节点的next为插入位置的next相连
2.将插入位置的next的prev和新节点相连
二.和插入位置建立双向联系
1..将新节点的prev设为插入位置
2.将插入位置的next设为新节点
三.若新节点的下一节点为空则设新节点为尾节点
void LinkList::BackwardInsertByNode(int ele, LinkNode* Node)
{
Size++;
LinkNode* Temp = new LinkNode(ele);
//和当前节点的后一个节点建立双向联系
Temp->Next = Node->Next;//将新节点的后驱节点为当前节点的后驱节点
Node->Next->Prev = Temp;//将当前节点的后一个节点的上一个节点设为新节点
//和当前节点建立双向联系
Temp->Prev = Node;//将新节点的上一个节点设为当前节点
Node->Next = Temp;//将当前节点的后驱节点设为新节点
if (Temp->Next == nullptr)Tail = Temp;
}
Ⅱ.前插法
在后插法的基础上交换元素
void LinkList::ForwardInsertByNode(int ele, LinkNode* Node)
{
Size++;
LinkNode* Temp = new LinkNode(ele);
//和当前节点的后一个节点建立双向联系
Temp->Next = Node->Next;//将新节点的后驱节点为当前节点的后驱节点
Node->Next->Prev = Temp;//将当前节点的后一个节点的上一个节点设为新节点
//和当前节点建立双向联系
Temp->Prev = Node;//将新节点的上一个节点设为当前节点
Node->Next = Temp;//将当前节点的后驱节点设为新节点
swap(Temp->Data, Node->Data);
}
5.删除操作
一.设被删除节点为删除位置的后驱节点
二.将删除位置和被删除节点的后驱节点相连
1.删除位置的next和被删除节点的后驱节点相连
2.被删除节点的后驱节点的prev指向删除位置
三.将节点删除
四.若删除位置的后一个节点为空,将删除位置设置为尾节点
void LinkList::DeleteByNode(LinkNode* Node)
{
Size--;
LinkNode* Temp = Node->Next;//被删除节点
//和被删除的后驱节点进行双向关联
Node->Next = Temp->Next;
Temp->Next->Prev = Node;
delete Temp;
if (Node->Next == NULL)Tail = Node;
}
6.查找操作
1.按值查找
LinkNode* LinkList::GetElemByValue(int ele)
{
LinkNode* Temp = Head->Next;
while (Temp)
{
if (Temp->Data == ele)
{
return Temp;
}
Temp = Temp->Next;
}
}
2.按位置查找
LinkNode* LinkList::GetElemByPos(int pos)
{
LinkNode* Temp = Head;
for (int i = 1; i <= pos; i++)
{
Temp = Temp->Next;
}
return Temp;
}
7.其他操作
1.判空
bool LinkList::isEmpty()
{
//return Size==0;
return Head->Next == nullptr;
}
2.清空链表
1.头节点的下一节点视为数组下标为1
2.遍历链表,但循环中要保存要删除节点中的后驱节点
3.移动节点
4.将头节点的下一节点设为空指针
void LinkList::Clear()
{
if (Head->Next == nullptr)//若为空表
return;
else
{
LinkNode* Temp = Head->Next;//头节点的下一节点视为数组下标为1
while (Temp != nullptr)
{
LinkNode* Temp_Next = Temp->Next;//保存要删除节点的后驱节点
delete Temp;//删除节点
Temp = Temp_Next;//移动被删除节点
}
Head->Next = nullptr;//
Size = 0;
}
}
3.销毁链表
1.判断是否为空表
2.清空链表
3.删除头节点,将头节点设为空节点
void LinkList::Destory()
{
if (Head->Next == nullptr)//若为空表
{
return;
}
else
{
this->Clear();
delete Head;
Head = nullptr;
cout << "Successful Destory" << endl;
}
}
4.计算链表长
int LinkList::CaculateSize()
{
int length = 0;
LinkNode* Temp = Head->Next;
while (Temp)
{
length++;
Temp = Temp->Next;
}
return length;
}
5.遍历输出链表
1.顺序输出
void LinkList::SequentialPrint()
{
if (Head->Next == nullptr)
{
cout << "空链表" << endl;
}
int i = 1;
LinkNode* Temp = Head->Next;
while (Temp)
{
cout << "节点" << i << "为:" << Temp->Data << endl;
Temp = Temp->Next;
i++;
}
}
2.逆序输出
void LinkList::ReversePrint()
{
if (Head->Next == nullptr)
{
cout << "空链表" << endl;
}
int i = 0;
LinkNode* Temp = Tail;
while (Temp->Prev)
{
cout << "节点" << Size-i << "为:" << Temp->Data << endl;
Temp = Temp->Prev;
i++;
}
}
三.测试函数
#include"LinkList.h"
#include<iostream>
#include<random>
using namespace std;
int main()
{
LinkList Test;
cout << (Test.isEmpty() ? "Test表为空" : "Test表不为空") << endl;
int time;
cout << "输入元素个数为:";
cin >> time;
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(1, 10);
for (int i = 0; i < time; i++)
{
int ele;
ele = dis(gen);
Test.BuildListByHeadInsert(ele);
}
Test.SequentialPrint();
cout << endl;
Test.ReversePrint();
cout << endl;
cout << Test.getTail() << endl;
LinkNode* Temp = Test.GetElemByPos(4);
Test.BackwardInsertByNode(10, Temp);
Test.SequentialPrint();
cout << Test.getTail() << endl;
}