目录:
一、线性表的链式存储结构
1、用一组物理位置任意的存储单元来存放线性表的数据元素
2、相关术语:
1)结点:数据元素的存储映像,由数据域与指针域两部分组成。
2)链表:n个结点由c指针链组成一个链表
3)头指针:是指向链表中第一个结点的指针
首元结点:是指链表中存储第一个元素的结点
头结点:是在链表的首元结点之前附设的一个结点
3、单链表的存储结点:
typedef struct Lnode{ /*声明结点类型和指向结点的指针类型*/
ElemType data; /*结点的数据域*/
struct Lnode *next; /*结点的指针域*/
}Lnode,*LinkList; /*Lnode为结点类型,*LinkList为指针类型*/
eg.储存学生学号、姓名、成绩的单链表结点类型定义操作如下:
Typedef struct{
char num[8];
char name[8];
int score;
}ElemType;
typedef struct Lnode{
ElemType data; /*数据域*/
struct Lnode *next; /*指针域*/
}Lnode,*LinkList
二、单链表基本操作的实现
1、单链表的初始化
1)生成新结点作头结点,用头指针L指向头结点。
2)将头结点的指针域置空。
Status InitList_L(LinkList &L){
L=new LNode; /*或L=(LinkList)malloc(sizeof(LNode));*/
L->next=NULL;
return OK;
}
2、判断链表是否为空(即判断头结点指针域是否为空)
int ListEmpty(LinkList L){ /*若L为空表,则返回1,否则返回0*/
if(L->next) /*非空*/
return 0;
else
return 1;
}
3、单链表的销毁:链表销毁后不存在(从头指针开始,依次释放所有结点)
1)p=L
2) L=L->next; delete p(free(p));
3) 结束条件:L==NULL
循环条件:L!=NULL 或 L
Status DestroyList_L(LinkList &L){ /*销毁单链表*/
Lnode *p; /*或LinkList p*/
while(L){
p=L;
L=L->next;
delete p;
}
return OK;
}
4、清空单链表(依次释放所有结点,并将头结点指针域设置为空)
1)p=L->next
2)q=p->next
delete p;
3)反复执行:p=q;q=q->next
4)L->next=NULL
5)结束条件:p==NULL
循环条件:p!=NULL
Status ClearList(ListList &L){
Lnode *p,*q; /*或LinkList p,q*/
p=L->next;
while(p){ /*没到表尾*/
q=p->next;
delete p;
p=q;}
L->next=NULL; /*头结点指针域为空*?
return OK;
}
5、求单链表的表长(从首元结点开始,依次计数所有的结点)
int ListLength_L(LinkList L){ /*返回L中元素的个数*/
LinkList p;
p=L->next; /*p指向首元结点*/
i=0;
while(p){ /*遍历L,统计结点数*/
i++;
p=p->next;
}
return i;
}
6、取值—取单链表中第i个元素
1)从第1个结点(L->next )顺链扫描,用指针p指向当前扫描到的结点值,p初值p=L->next。
2)j为计数器,累计扫描的结点数,j的初值为1。
3)当p指向下一个结点时,j加1
4)当j等于i时,p所指的结点就是要找的第i个结点
Status GetElem_L(LinkList L,int i,ElemType &e){ /*获取线性表中第i个元素的内容,通过变量e返回*/
p=L->next;j=1; /*初始化*/
while(p&&j<i){ /*向后扫描,直到p指向第i个元素或p为空*/
p=p->next;++j;
}
if(!p||j>i) /*第i个元素不存在*/
return ERROR;
e=p->data; /*取第i个元素*/
return OK;
}//GetElem _L
7、按值查找
1)从第一个结点起,依次和e比较
2)如果找到一个其值与e相等的元素,则返回其在链表中的位置或地址
3)如果查遍整个链表都没有与e相等的元素,则返回0或error
Lnode *LocateElem_L(LinkList L,Elemtype e){
/*在线性表中查找值为e的数据元素,找到则返回值为e的数据元素的地址,查找失败则返回NULL*/
while(p&&p->data!=e)
p=p->next;
return p;
}
8、插入操作—在第i个结点插入值为e的新结点
1)首先找到ai-1的存储位置p
2)生成一个数据为e的新结点s
3)插入新结点:1、新结点时指针域指向结点ai
2、结点ai-1的指针域指向新结点(两步不可以调换位置,因为会丢失ai的地址)
Status LinkInsert_L(LinkList &L,int i,ElemType e){
p=L;j=0;
while (p&&j<i-1){ /*寻找第i-1个结点,p指向第i-1个结点*/
p=p->next;++j;
if(!p||j>i-1) return ERROR; /*非法情况*/
s=new LNode;s->data =e; /*建立新结点,将结点s的数据域设置为e*/
s->next=p->next; /*将s结点的指针域指向ai*/
p->next =s; /*将p结点的指针域指向s*/
return OK;
}//LinkInsert _L
9、删除—删除第i个结点
1)首先找到ai-1的存储位置p,保存要删除ai的值
2)令p->next指向ai+1
3)释放ai的空间
Status ListDelete_L(LinkList &L,int i,ElemType &e){
p=L;j=0;
while(p->next!=NULL&&j<i-1){ /*寻找第i个结点*/
p=p->next;++j;}
if(p->next==NULL||j>i-1) /*删除位置不合理*/
return ERROR;
q=p->next; /*临时保存被删结点的地址以备释放*/
p->next=q->next; /*改变删除结点的前驱结点的指针域*/
e=q->data; /*存储删除结点的数据域*/
delete q;
return OK;
}//ListDelete_L
10、建立单链表:头插法——元素插在链表头部,也叫前插法
1)从一个空表开始,重复读入数据。
2)建立一个新结点,将读入的数据存放在新结点的数据域中
3)从最后一个结点开始,依次将各结点插入到链表的前端
4) 算法的时间复杂度为:O(n)
5)重要操作:p->next=L->next;
L->next=p; (不可调换位置,会失去L的后继结点的地址)
void CreateList_L(LinkList &L,int n){
L=newLNode; /*生成新结点*/
L->next=NULL; /*将头结点的指针域置空*/
for(i=n;i>0;--i){ /*从最后一个结点开始,依次将各结点插入到链表的前端*/
p=newLNode;
cin>>p->data;
p->next=L->next;
L->next=p;
}
}//CreateList_H
11、建立单链表:尾插法——元素插入在链表的尾部,也叫做后插法
1)从一个空表开始,将新结点插入到链表的尾部,尾指针r指向链表L的尾结点。
2)初始时,r与L均指向头结点,每读入一个新数据则申请一个新结点,当新结点插入到尾结点后
人指向新结点。
3)算法复杂度为O(n)。
void CreateList_R(LinkList &L,int n)
{
L=new LNode; /*生成新结点*/
L->next=NULL; /*将新结点的指针域置为空*/
r=l; /*尾指针L指向头结点*/
for(i=0;i<n;i++)
{
p=new LNode;cin>>p->data;/*生成新结点,并为结点的数据域赋值*/
p->next=NULL; /*将新结点的指针域赋值为空*/
r->next=p; /*将p插入表尾*/
r=p; /*r指向新的尾结点*/
}
}//CreateList_R
12、带尾指针循环链表的合并
1)p存储表头结点
2)Tb的表头连接到Ta的表尾
3)释放Tb的表头结点
4)Tb的表尾接到Ta的表头
5)时间复杂度为O(1)
LinkList Connect(LinkList Ta,LinkList Tb){ //假设Ta,Tb都是非空的单循环链表
p=Ta->next; //p存储表头结点
Ta->next=Tb->next->next; //Tb表头连接Ta的表尾
delete Tb->next; //释放Tb的头结点
Tb->next=p; //修改指针
return Tb;
}
13、双向链表的定义
1)在单链表的每个结点里再增加一个指向其直接前驱的指针域prior,这样链表中就形成了有两个方向不同的链,故称为双向链表
2)让头结点的前驱指针指向链表的最后一个结点,让最后一个结点的后继结点指向头结点
3)在双向链表中有些操作(如:ListLength、GetElem等),因仅涉及一个方向的指针,故它们的算法与线性链表的相同。但在插入,删除的时候,则需要同时修改两个方向上的指针,两者操作的时间复杂度均为O(n)
typedef struct DuLNode{
Elemtype data;
struct DuLNode *prior,*next;
}DuLNode,*DuLinkList;
14、双向链表的插入
void ListInsert_DuL(DuLinkList &L,int i,ElemType e){
if(!(p=GetElemP_DuL(L,i))) return ERROR; //非法情况
s=new DuLNode; //建立新结点
s->data=e;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
return OK;
}//LinkInsert_DuL
15、双向链表的删除
void ListDelete_DuL(DuLink &L,int i,ElemType &e){
//删除带头结点的双向循环链表的第i个元素,并用e返回
if(!(p=GetElemP_DuL(L,i))) return ERROR;
e=p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
return OK;
} //ListDelete_DuL