什么是链表?---链表是一种常见的重要的数据结构。它是动态地进行存储分配的一种结构。链表和数组比较,不用事先确定存储空间,而是根据需要开辟内存单元。
单链表的结点结构
struct node* next是结构体指针,指向下一个结构体
typedef struct node
{
int data;//数据域
struct node* next;//指针域
}Node,*Link;//Node为struct node的别名,*Link为指向结构体的指针
初始化单链表
1.创建一个头结点,如果内存分配失败,则返回NULL,如果创建成功,则让head指向NULL
(此时没有下一个结点)
2.返回一个头结点
Link creatList()
{
Link head = new Node;//分配头结点
if (head == NULL)//内存不足,返回失败
{
return NULL;
}
else
{
head->next = NULL;//头结点的下一结点暂时不存在,置空
}
return head;
}
单链表的定向插入
为了保证头指针不变,每次实行操作时,都要把head指针复制一份,作为此函数的工作指针(来遍历链表);
i为插入的位置(从0开始),x为插入的数据;
1.为了找到正确的位置,需要设置一个count来计数,直到工作指针移动到i-1位(因为从0开始,所以是i-1位)停止,进行插入操作;同时要保证p指针不为空(若为空无法插入,因为没有结点存在)
2.如果p从头结点移动到NULL也没有找到第i-1位,则返回0,插入失败;
3.如果找到了第i-1位,则为了插入目标数据,新创建一个结点来存放该数据(node->data=x);
并且让工作指针p的下一个结点与新建立结点node相联系node->next=p->next;
再让p与新建立的结点node相联系p->next=node;
bool insertNode(Link head,int i,int x)
{
Link p = head;//工作指针指向头结点
int count = 0;
if (i < 1)
{
return 0;
}
while (p != NULL && count < i - 1)//p移动到第i-1个结点
{
p = p->next;
count++;
}
if (p == NULL)//没有找到第i-1个结点
return 0;
else
{
Link node1 = new Node;//建立新的结点
node1->data = x;//插入的值
node1->next = p->next;
p->next = node1;
return 1;
}
}
头插法
无需遍历链表,所以不需要设置工作指针;
直接新创建一个结点,让头结点的下一个结点与新结点进行联系,并将x赋给新结点
bool HaddNode(Link head, int x)
{
Link p = new Node;//创建新的结点
if (head == NULL)
{
return 0;
}
else
{
Link p = new Node;
p->next = head->next;
head->next = p;
p->data = x;
return 1;
}
}
尾插法
因为要遍历链表到最后一个结点,为了保证头指针不变,需要设立工作指针tmp来遍历链表;
让tmp移动到最后一个结点上,只有到下一个结点为空(不存在),才可以实现尾插;
即创建一个新的结点p,让工作指针tmp(此时已经移动到最后一个结点上了)指向新创建的结点p,并将数据赋给p->data,再将p->next=NULL;使之成为(新的)最后一个结点。
bool addNode(Link head, int x)
{
Link p = new Node;//创建新的结点
Link tmp = head;
if (head == NULL)
{
return 0;
}
while (tmp->next!= NULL)//下一个为空,才能成为结点
{
tmp = tmp->next;
}
tmp->next = p;
p->data = x;
p->next = NULL;
return 1;
}
单链表的定向删除
建立两个工作指针来遍历链表(一前一后)
只要后指针p不为NULL,并且没有找到要删除的结点,则前后指针都往后移一位
若找到要删除的结点,则改变要删除结点前后结点的指针指向关系
让前指针q指向被删除的结点p的下一个,建立起链接关系,再delete要删除的结点p
bool deleteNode(Link head, int x)
{
if (head == NULL || head->next == NULL)//若链表为空表
{
return false;
}
Link q = head;
Link p = head->next;//使得q在前,p在后
while (p != NULL)
{
if (p->data == x)
{
q->next = p->next;//找到对应的结点,删除并且提返回
delete p;
return true;
}
else//没有找到相应的结点,q往后移,p往后移
{
q = p;
p = p->next;
}
}
return false;//循环结束了,说明没有找到相应的结点
}
顺序删除
bool HdeleteNode(Link head)
{
if (head == NULL)
{
return 0;
}
else
{
Link p = head->next;//获得第一个结点
head->next = p->next;//调整头结点与第二个结点的联系,使之成为第一个结点(删除后)
delete p;//删除第一个结点
return 1;
}
}
逆序删除
bool WdeleteNode(Link head)
{
if (head == NULL)
{
return 0;
}
else
{
Link p = head;
Link q = head->next;
while (q->next != NULL)//让q移动到最后一个结点,p移动到倒数第二个结点,再删除最后一个结点q
{
p = p->next;
q = q->next;
}
delete q;
p->next = NULL;//重新定义最后一个结点
return 1;
}
}
单链表的释放
若头指针head=NULL则为空链表,无法释放;否则只要head结点指向的下一个结点不为NULL,就进行释放结点
从第一个结点开始释放,为了将链表内所有结点都释放完,需要设置工作指针q=head来遍历链表;并且头结点head移动到下一个结点上(head->next),然后将工作指针q(原始头结点)删除,达到释放结点的目的
bool clearLink(Link head)
{
if (head == NULL)
{
return 0;
}
while (head->next != NULL)
{
Link q = head;//先让q指向head
head = head->next;//head指向下一个结点
delete q;
}
head->next=NULL;
return 1;
}
单链表数据的查询
保证头结点不改变,需要设立工作指针p来遍历链表
bool queryNode(Link head, int x)//x为待查找的数据
{
Link p = head->next;//从第一个数据结点开始
while (p != NULL)
{
if (p->data == x)
{
return 1;
}
else
p = p->next;
}
return 0;
}
单链表的长度
也可以在头结点里的data域设置一个len,每创建一个结点就head->len++
为了保证头指针不变,需要设立一个工作指针p来遍历链表
int length(Link head)
{
if (head == NULL)
{
return 0;
}
Link p = head->next;
int count = 0;
while (p != NULL)
{
count++;
p = p->next;
}
return count;
}
单链表的显示
为了保证头指针不变,需要设立工作指针p来遍历链表,只有p不为NULL,就输出数据域,并将p指向p的下一个结点
void displayNode(Link head)//传入头指针
{
Link p = head->next;//获得第一个数据结点
if(head==NULL||head->next==NULL)
{
cout<<"该链表不存在"<<endl;
}
while (p != NULL)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
完整的单链表
#include<iostream>
using namespace std;
typedef struct node
{
int data;
struct node* next;
}Node,*Link;
//初始化链表
Link creatList()
{
Link head = new Node;
head->next = NULL;
//head->data = 0;
return head;
}
//头插法
bool HaddNode(Link head, int x)
{
if (head == NULL)
{
return 0;
}
else
{
Link p = new Node;
p->next = head->next;
head->next = p;
p->data = x;
return 1;
}
}
//尾插法
bool WaddNode(Link head, int x)
{
Link p = new Node;
Link tmp = head;
if (head == NULL)
{
return 0;
}
else
{
while (tmp->next != NULL)
{
tmp = tmp->next;
}
tmp->next = p;
p->next = NULL;
p->data = x;
return 1;
}
}
//定向插入
bool insertNode(Link head, int i, int x)
{
if (head == NULL)
{
return 0;
}
else
{
Link p = head;
int count = 0;
while (p != NULL && count < i - 1)
{
Link Xnode = new Node;
Xnode->next = p->next;
p->next = Xnode;
Xnode->data = x;
return 1;
}
}
}
//顺序删除
bool HdeleteNode(Link head)
{
if (head == NULL)
{
return 0;
}
else
{
Link p = head->next;//获得第一个结点
head->next = p->next;//调整头结点与第二个结点的联系,使之成为第一个结点(删除后)
delete p;//删除第一个结点
return 1;
}
}
//逆序删除
bool WdeleteNode(Link head)
{
if (head == NULL)
{
return 0;
}
else
{
Link p = head;
Link q = head->next;
while (q->next != NULL)//让q移动到最后一个结点,p移动到倒数第二个结点,再删除最后一个结点q
{
p = p->next;
q = q->next;
}
delete q;
p->next = NULL;//重新定义最后一个结点
return 1;
}
}
//定向删除
bool deleteNode(Link head,int i)
{
if (head == NULL)
{
return 0;
}
else
{
int count = 0;
Link p = head;
Link q = head->next;
while (q!=NULL)
{
if (count < i - 1)// 没有找到相应的位置,两个工作指针同时往后移
{
p = p->next;
q = q->next;
count++;
}
else//如果找到相应的位置
{
p->next = q->next;
delete q;
return 1;
}
}
return 0;//循环结束了,但是没有找到相应的结点
}
}
//链表长度
int getLenth(Link head)
{
int len = 0;
Link p = head;
while (p != NULL)
{
len++;
p = p->next;
}
return len;
}
//打印链表
void print(Link head)
{
if (head == NULL)
{
cout << "空链表" << endl;
}
else
{
Link p = head;
while (p->next != NULL)
{
cout << p->next->data << " ";
p = p->next;
}
cout << endl;
}
}
//链表查询
bool querry(Link head,int x)
{
if (head == NULL)
{
return 0;
}
else
{
Link p = head;
while (p != NULL)
{
if (p->data == x)
return 1;
else
p = p->next;
}
return 0;
}
}
//链表释放
void clearList(Link head)
{
if (head == NULL)
{
cout << "链表已经为空,无需释放" << endl;
}
else
{
while (head->next != NULL)
{
Link p = head;
head = p->next;
delete p;
}
head->next = NULL;
}
}
int main()
{
Link head = creatList();
int n;
cin >> n;
cout << "头插法---逆序" << endl;
for (int i = 0; i < n; i++)
{
HaddNode(head, i);
}
print(head);
cout << "尾插法---顺序" << endl;
for (int i = 0; i < n; i++)
{
WaddNode(head, i);
}
print(head);
cout << "顺序删除(删除第一个)" << endl;
HdeleteNode(head);
print(head);
cout << "逆序删除(删除最后一个)" << endl;
WdeleteNode(head);
print(head);
cout << "定向删除" << endl;
cout << "请输入要删除的序号:";
int x;
cin >> x;
deleteNode(head, x);
print(head);
cout << "请输入要查询的数据:";
int a;
cin >> a;
if (querry(head, a))
{
cout << 1 << endl;
}
else
{
cout << 0 << endl;
}
clearList(head);
}
双链表
在处理时间方面:双向链表的插入与删除操作比单链表的时间要快很多。在最末尾插入的时候,单链表需要找到需要插入位置的前一个节点,需要遍历整个链表,时间复杂度为O(n),而双向链表只需要head->tail,就可以得到最后一个节点的位置,然后就可以进行插入操作,时间复杂度为O(1)。在删除操作方面,单链表需要遍历到需要删除的节点的前一个节点,双向链表需要遍历到需要删除的节点,时间复杂度同为O(n)
双链表结点结构
typedef struct node
{
int data;
struct node* pre;//前指针
struct node* next;//后指针
}Node, * Link;
双链表的创建
pre为前指针,next为后指针
data记录链表的长度即元素的个数
Link creatList()
{
Link head = new Node;//创建头结点
head->pre = NULL;
head->next = NULL;
head->data = 0;//头结点的data域来记录链表中有多少个元素
return head;
}
头插法
若链表为空,则新创建的结点node的next就指向NULL;新结点node的pre前指针指向head,
head的next指向node;head的data++,结点增加即长度增加
若链表不为空,则新创建的结点node的pre前指针指向head;新结点node的next后指针指向头结点的下一个即head->next;再让head->next的前指针pre指向新结点node;再让头指针head指向新结点node
void headInsert(Link head, int x)//头结点的data域来记录有多少个元素
{
Link node = new Node;
node->data = x;
if (head->data == 0 )
{
//链表为空
node->pre = head;//前指针指向头结点
node->next = NULL;//因为是空表,所以node->next=head->next=NULL
head->next = node;
head->data++;//头结点的data域来记录链表中有多少个元素
}
else
{
node->pre = head;//前指针指向头结点
node->next = head->next;//后指针指向头结点的next
head->next->pre = node;//头结点的next指向下一个,下一个的pre指向新结点
head->next = node;//头结点的next指向新结点
head->data++;//头结点的data域来记录链表中有多少个元素
}
}
尾插法
设置工作指针,找到最后一个结点
再改变pre和next指针之间的指向关系
即让最后一个结点p赋给新结点node的pre前指针;p的next赋给新结点node的next;再让node赋给p的next
void tailInsert(Link head, int x)
{
Link p = head;//设置工作指针
Link node = new Node;//创建新结点
node->data = x;
while (p->next != NULL)//找到最后一个结点,next指向NULL
{
p = p->next;
}
node->pre = p;//node的前指针指向p
node->next = p->next;//node的后指针指向p的next即为NULL
p->next = node;//p的后指针指向node(新建立在最后的结点)
head->data++;
}
删除结点(以值来删除)
bool deleteNode(Link head, int x)
{
Link node = head->next;//设置工作指针
while (node != NULL)
{
if (node->data == x)//找到要删除的node结点
{
node->pre->next = node->next;//前一个结点的next指向node的next
node->next->pre = node->pre;//后一个结点的pre指向前一个结点
delete node;
return 1;
}
node = node->next;
}
return 0;
}
删除结点(以下标删除)
bool deleteNodeX(Link head, int i)
{
Link p = head->next;//设置工作指针
int count = 0;//计数
while (p != NULL)
{
if (count == i)
{
p->pre->next = p->next;//p的后指针赋给p前指针的next
p->next->pre = p->pre;//p的前指针赋给p->next的前指针
delete p;
return 1;
}
else
{
p = p->next;
count++;
}
}
return 0;
}
打印双链表
void printList(Link head)
{
Link node = head->next;//设置工作指针
while (node != NULL)
{
cout << node->data << " ";
node = node->next;
}
cout << endl;
}
完整的双链表
#include<iostream>
using namespace std;
typedef struct node
{
int data;
struct node* pre;//前指针
struct node* next;//后指针
}Node, * Link;
Link creatList()
{
Link head = new Node;//创建头结点
head->pre = NULL;
head->next = NULL;
head->data = 0;//头结点的data域来记录链表中有多少个元素
return head;
}
void headInsert(Link head, int x)//头结点的data域来记录有多少个元素
{
Link node = new Node;
node->data = x;
if (head->data == 0)
{
//链表为空
node->pre = head;//前指针指向头结点
node->next = NULL;//因为是空表,所以node->next=head->next=NULL
head->next = node;
head->data++;//头结点的data域来记录链表中有多少个元素
}
else
{
node->pre = head;//前指针指向头结点
node->next = head->next;//后指针指向头结点的next
head->next->pre = node;//头结点下一个的前指针指向新结点
head->next = node;//头结点指向新结点
head->data++;//头结点的data域来记录链表中有多少个元素
}
}
void printList(Link head)
{
Link node = head->next;//设置工作指针
while (node != NULL)
{
cout << node->data << " ";
node = node->next;
}
cout << endl;
}
void tailInsert(Link head, int x)
{
Link p = head;//设置工作指针
Link node = new Node;//创建新结点
node->data = x;
while (p->next != NULL)//找到最后一个结点,next指向NULL
{
p = p->next;
}
node->pre = p;//node的前指针指向p
node->next = p->next;//node的后指针指向p的next即为NULL
p->next = node;//p的后指针指向node(新建立在最后的结点)
head->data++;
}
//删除值
bool deleteNodeZ(Link head, int x)
{
Link p = head->next;//设置工作指针
while (p != NULL)
{
if (p->data == x)//找到要删除的p结点
{
p->pre->next = p->next;//前一个结点的next指向p的next
p->next->pre = p->pre;//后一个结点的pre指向前一个结点
delete p;
return 1;
}
p = p->next;
}
return 0;
}
//删除下标
bool deleteNodeX(Link head, int i)
{
Link p = head->next;//设置工作指针
int count = 0;//计数
while (p != NULL)
{
if (count == i)
{
p->pre->next = p->next;//p的后指针赋给p前指针的next
p->next->pre = p->pre;//p的前指针赋给p->next的前指针
delete p;
return 1;
}
else
{
p = p->next;
count++;
}
}
return 0;
}
int main()
{
Link head = creatList();
for (int i = 0; i < 5; i++)
{
headInsert(head, i);
}
printList(head);
for (int i = 0; i < 5; i++)
{
tailInsert(head, i);
}
printList(head);
int x;
cin >> x;
deleteNodeZ(head, x);
printList(head);
int n;
cin >> n;
deleteNodeX(head, n);
printList(head);
return 0;
}