前言 : 链表的实现,查询,插入,删除,这些简单的操作。在平时的学习中,经常会遇见,故以单链表为例做个总结。链表的优缺点,以及适用情况在前面的一篇文中就提到过,就不作介绍了。
一、链表的定义
单链表存储结构
typedef struct LNode
{
int data; /*结点的数据域*/
struct LNode *next; /*结点的指针域*/
}LNode,*LinkList; /*LinkList为指向结构体LNode的指针类型*/
LinkList与LNode *同为结构体指针类型,这两种类型定义本质上是等价的,为提高程序的可读性,通常用LinkList定义头指针,LNode*定义指向单链表中任意结点的指针变量。例如,LinkList L,则L为单链表的头指针;使用LNode *p,则p为指向单链表中任意结点的指针,用*p代表该结点。
二、链表的基本操作的实现
1.初始化
[算法思想]
1)生成新的结点作为头结点,用头指针L指向头结点。
2)头结点的指针域置空。
[算法描述]
int InitList_L(LinkList &L)
{
L=new LNode; /*生成新结点作为头结点,用头指针L指向头结点*/
/*L->data=? 头指针的数据域可以不存储任何信息,也可以存储如线性表长度等类似的附加信息*/
L->next=NULL; /*头结点指针域置空*/
return OK;
}
2.查找
2.1按序号查找
[算法思想]
从链表的第一个结点(L->next)开始顺着链域扫描,用指针p指向当前扫描到的结点,p的初值指向第一个结点(p=L->next)。用j做计数器,累计当前扫描过的结点数,j的初值为1,当p指向扫描的下一结点时,计数器j相应加1。当j=i时,p所指的结点就是要找的第i个结点。
[算法描述]
int GetElem_L(LinkList L,int i,int &e)
{
/*在带头结点的单链表L中查找第i个元素*/
LNode *p=new LNode; p=L->next; int j=1; /*初始化,p指向第一个结点,j为计数器*/
while(p && j<i) /*顺链域向后扫描,直到p指向第i个元素或p为空*/
{
p=p->next;
++j;
}
if(!p || j>i) return ERROR; /*第i个元素不存在*/
e=p->data;
return OK;
}
2.2按值查找
[算法思想]
链表中查找其值与给定值e相等的数据元素的过程和顺序表(数组)类似,从第一个结点起,依次和e相比较,如果找到一个其值与e相等的数据元素,则返回其在链表中的“位置”;如果查遍整个链表都没有找到其值和e相等的元素,则返回“NULL”。因此需要设置一个指针变量p顺链扫描,直到p为“NULL”,或者p->data和e相同为止。
[算法描述]
int LocateElem_L(LinkList L,int e)
{
LNode *p=new LNode; p=L->next; int j=1;
while( p&&p->data!=e)
{
p=p->next;
j++;
}
if(!p)return NULL; /*查找失败返回NULL*/
return j;
}
3.插入
[算法思想]
将值为e的新结点插入到表的第i个结点的位置上,即插入到结点a(i-1)与ai之间,分为以下几步:
1)找到结点a(i-1)并由指针p指向该结点。
2)生成一个新结点*s。
3)将新结点*s的数据域置为e。
4)将新结点*s的指针域指向结点ai;
5)令结点a(i-1)的指针域指向新结点*s。
[算法描述]
int ListInsert_L(LinkList &L, int i, int e)
{
/*在带头结点的单链表L中第i个位置之前插入元素e*/
LNode *p=new LNode; p=L; int j=0;
while(p && j<i-1) /*找到第i-1个结点*/
{
p=p->next;
++j;
}
if(!p || j>i-1) return ERROR;
LNode *s=new LNode;
s->data=e; /*将结点s的数据域置为e*/
s->next=p->next; /*将结点s插入L中*/
p->next=s;
return OK;
}
4.删除
[算法思想]
要删除单链表的第i个结点ai,分以下几步:
1)找到结点a(i-1)并由指针p指向该结点。
2)临时保存待删除结点ai的结点在q中,以备释放。
3)令p->next指向ai的直接后继结点。
4)将待删除结点ai的值保留在e中(此步根据实际应用情况可以省略)。
5)释放结点ai的空间。
int ListDelete_L(LinkList &L,int i,int &e)
{
LNode *p=new LNode; p=L; int j=0;
while(p->next && j<i-1)
{
p=p->next;
++j;
}
if(!p||j>i-1) return ERROR;
LNode *q=p->next; /*临时保存被删结点的地址以备释放*/
p->next=q->next; /*改变删除结点前驱结点的指针域*/
e=q->data;
delete q; /*释放删除结点的空间*/
return OK;
}
5.创建链表
链表的创建方法可以分为前插法和后插法。因为后插入比较常用和清晰,故在这里只是实现了后插法。
[算法思想]
后插法是通过将新结点逐个插入到链表的尾部来创建链表。同前插法一样首先要建立一个只有头结点的空链表L。不同的是,为了使新结点能插入到表尾,需要增加一个尾指针r指向链表的尾结点。初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点*r之后,再使r指向新的尾结点。
[算法描述]
void CreateList_L(LinkList &L,int n)
{
/*正位序输入n个元素的值,建立带表头结点的单链表L*/
LNode *r=new LNode; r=L;
for(int i=0;i<n;i++)
{
LNode *p=new LNode;
cin>>p->data;
p->next=NULL; r->next=p; /*插入到表尾*/
r=p; /*r指向新的尾结点*/
}
}
[完整代码]
#include<iostream>
using namespace std;
typedef struct LNode
{
int data; /*结点的数据域*/
struct LNode *next; /*结点的指针域*/
}LNode,*LinkList;
const int OK=1;
const int ERROR = -1;
/*初始化*/
int InitList_L(LinkList &L,int n)
{
L=new LNode; /*生成新结点作为头结点,用头指针L指向头结点*/
/*L->data=? 头指针的数据域可以不存储任何信息,也可以存储如线性表长度等类似的附加信息*/
L->data=n;
L->next=NULL; /*头结点指针域置空*/
cout<<endl<<"初始化成功...\n"<<endl;
return OK;
}
/*按序号查找*/
int GetElem_L(LinkList L,int i,int &e)
{
/*在带头结点的单链表L中查找第i个元素*/
LNode *p=new LNode;
p=L->next; int j=1; /*初始化,p指向第一个结点,j为计数器*/
while(p && j<i) /*顺链域向后扫描,直到p指向第i个元素或p为空*/
{
p=p->next;
++j;
}
if(!p || j>i) /*第i个元素不存在*/
{
cout<<"\n第"<<i<<"个元素不存在或表为空\n"<<endl;
return ERROR;
}
e=p->data;
cout<<"\n第"<<i<<"个元素为"<<e<<"\n"<<endl;
return OK;
}
/*按值查找*/
int LocateElem_L(LinkList L,int e)
{
LNode *p=new LNode ;
p=L->next; int j=1;
while( p&&p->data!=e)
{
p=p->next;
j++;
}
if(!p)
{
cout<<"\n元素"<<e<<"不存在或表为空\n"<<endl;
return ERROR;
}
cout<<"\n元素"<<e<<"在链表的第"<<j<<"位\n"<<endl;
return j;
}
/*插入*/
int ListInsert_L(LinkList &L, int i, int e)
{
/*在带头结点的单链表L中第i个位置之前插入元素e*/
LNode *p=L; int j=0;
while(p && j<i-1) /*找到第i-1个结点*/
{
p=p->next;
++j;
}
if(!p || j>i-1)
{
cout<<"\n第"<<i<<"元素不存在或表为空\n"<<endl;
return ERROR;
}
LNode *s=new LNode;
s->data=e; /*将结点s的数据域置为e*/
s->next=p->next; /*将结点s插入L中*/
p->next=s;
cout<<"\n插入成功...\n\n";
return OK;
}
/*删除*/
int ListDelete_L(LinkList &L,int i,int &e)
{
LNode *p=L;int j=0;
while(p->next && j<i-1)
{
p=p->next;
++j;
}
if(!p||j>i-1)
{
cout<<"\n第"<<i<<"元素不存在或表为空\n"<<endl;
return ERROR;
}
LNode *q=p->next; /*临时保存被删结点的地址以备释放*/
p->next=q->next; /*改变删除结点前驱结点的指针域*/
e=q->data;
delete q; /*释放删除结点的空间*/
cout<<"\n删除成功...\n\n";
return OK;
}
/*后插法创建*/
void CreateList_L(LinkList &L,int n)
{
/*正位序输入n个元素的值,建立带表头结点的单链表L*/
cout<<"依次输入"<<n<<"个元素:";
LNode *r=new LNode;
r=L;
for(int i=0;i<n;i++)
{
LNode *p=new LNode;
cin>>p->data;
p->next=NULL; r->next=p; /*插入到表尾*/
r=p; /*r指向新的尾结点*/
}
cout<<"\n后插法创建链表成功...\n"<<endl;
}
int PrintList_L(LinkList L)
{
LNode *p=new LNode;
p=L; p=p->next;
if(p==NULL)
{
cout<<"\n链表为空!!!\n"<<endl;
return ERROR;
}
cout<<"链表中的元素为:";
while(p)
{
cout<<p->data<<" ";
p=p->next;
}
cout<<endl;
return OK;
}
int main()
{
int n=0;
cout<<"输入链表的长度:";
cin>>n;
LinkList L;
InitList_L(L,n); /*初始化*/
CreateList_L(L,n); /*创建链表*/
PrintList_L(L);
int e=0;
/*删除链表*/
ListDelete_L(L,1,e);
PrintList_L(L);
/*插入链表*/
ListInsert_L(L,1,e);
PrintList_L(L);
/*按值查找*/
LocateElem_L(L,2);
/*按序号查找*/
GetElem_L(L,3,e);
return 0;
}
[运行结果]
后语:链表的反转,双向链表,循环链表这些未实现的有机会再补上。