目录
1.单链表
(1)定义
1.采用链式存储结构的线性表称为线性链表。
2.结点包含数据域和指针域两部分。
3.元素节点前通常加头结点,链表名指向头结点(头指针),尾结点指针域值为NULL。
4.头指针标识整个链表, 链表名即头指针;
(2)链表的创建
typedef struct LNode{//LNode类似user
ElemType data;//数据域
struct LNode *next;//指针域
}LNode,*Linklist;
//这里的LNode等于struct LNode,结构体名称,类似xiaoming
//命名方式:表名创建LNode T=>T.data T.next
//这里的*Linklist等于struct LNode*,指向整个结构体的指针
//命名方式:指针创建Linklist T=>T->data T->next
(3)链表的初始化
Status Listcreate_(Linklist &L,int n)
{
LNode *rearptr,*curptr;//一个尾指针,一个指向结点的指针
L=(LNode*)malloc(sizeof(LNode));
//开辟头结点,其数据域一般不做赋值。
if(!L)
exit(OVERFLOW);
L->next=NULL;//指针域指向空,先建立一个头结点的单链表
rearptr=L;//初始头结点为尾结点
for(int i=1;i<n;i++)
{//每次循环一次开辟一个新节点,并把新节点拼到尾结点
curptr=(LNode*)malloc(sizeof(LNode));//开辟一个新结点
if(!curptr)
exit(OVERFLOW);
scanf("%d",&curptr->data);//输入元素值
curptr->next=NULL;
rearptr->next=curptr;
rearptr=curptr;
}
}
(4)链表的查找
查找位置
//查找位置
Status ListLocate_L(Linklist &L,ElemType e)
{
LNode *temp=L->next;//临时指针指向第一个结点
int pos=0;
while(temp!=NULL)
{
pos++;
if(temp->data==e)
return pos;//当指向e时,返回pos并结束循环
else temp=temp->next;
}
return 0;
}
查找元素
Status GetElem_L(Linklist &L,int pos,ElemType &e)
{
//L为带头结点的单链表的头指针。
//第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
//设遍历指针和相应计数器,从头开始后移至计数器为i停.i异常
LNode *p=L;
int j=0;//p指向”第0个”元素结点,j是该结点的位序
while(p&&j<pos) //顺序查找,只要p不空且未到第i结点就前进
{
p=p->next;
++j;
}
if(!p||pos<1)return ERROR; //第i个元素不存在 或 i<1,
e=p->data; //取第i个元素
return OK;
}
(5)链表的插入
Status ListInsert_L(Linklist &L, int i, ElemType e){
//定位到第i-1个结点,在其后插入一个新开辟的结点.注意i异常
LNode *p=L; int j=0; /*p始终指向第j个结点*/
//找到插入位置
while(p&&j<i-1)
{p=p->next; ++j;}//只要不空且未到第i-1个
if(!p||i<1) return ERROR; //第i-1个节点不存在或者i<1
//连接插入结点
LNode *q;
q= (LNode *)malloc(sizeof(LNode)); //或q=new LNode;
if(!q)exit(OVERFLOW);
q->data=e;
q->next=p->next;
p->next=q;
return(OK);
}
(6)链表的删除
Status ListDelete_L(Linklist &L, int i, ElemType &e){
//寻找第i-1个结点 ,若其后继结点存在则带回其data并删除
LNode *p=L,*q; int j=0; /*p始终指向第j个结点*/
while(p&&j<i-1){p=p->next; ++j;}//寻找第i-1个结点
// j 最终为i-1,除非p空
if(!p||!p->next||i<1) return ERROR;//第i个或更前结点不存在,
q=p->next;
e=q->data;
p->next=q->next;
free(q);
return(OK);
}
(8)链表的输出
void printfLinklist(Linklist &L){
LNode *p=L->next;
while (p!= NULL)
{
printf("%d ",p->data) ;
p=p->next;
}
printf("%d",p->data) ;
}
//库函数头文件包含
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
//函数状态码定义
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int ElemType;
typedef int Status;
#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10
typedef struct LNode //LNode类似user
{
ElemType data;//数据域
struct LNode *next;//指针域
//数据域用来存放数据元素,指针域用来存放下一个数据结点的地址
} LNode,*Linklist;
//这里的LNode等于struct LNode,结构体名称,类似xiaoming
//命名方式:表名创建LNode T=>T.data
//这里的*Linklist等于struct LNode*,指向整个结构体的指针
//命名方式:指针创建Linklist T=>LinkList->data
Status Listcreate_L(Linklist &L,int n)
{
LNode *rearptr,*curptr;//一个尾指针,一个指向结点的指针
L=(LNode*)malloc(sizeof(LNode));
//开辟头结点,其数据域一般不做赋值。
if(!L)
exit(OVERFLOW);
L->next=NULL;//指针域指向空,先建立一个头结点的单链表
rearptr=L;//初始头结点为尾结点
for(int i=1; i<=n; i++)
{
//每次循环一次开辟一个新节点,并把新节点拼到尾结点
curptr=(LNode*)malloc(sizeof(LNode));//开辟一个新结点
if(!curptr)
exit(OVERFLOW);
scanf("%d",&curptr->data);//输入元素值
curptr->next=NULL;
rearptr->next=curptr;
rearptr=curptr;
}
return OK;
}
//查找位置
Status ListLocate_L(Linklist &L,ElemType e)
{
LNode *temp=L->next;//临时指针指向第一个结点
int pos=0;
while(temp!=NULL)
{
pos++;
if(temp->data==e)
return pos;//当指向e时,返回pos并结束循环
else temp=temp->next;
}
return 0;
}
Status GetElem_L(Linklist &L,int pos,ElemType &e)
{
//L为带头结点的单链表的头指针。
//第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
//设遍历指针和相应计数器,从头开始后移至计数器为i停.i异常
LNode *p=L;
int j=0;//p指向”第0个”元素结点,j是该结点的位序
while(p&&j<pos) //顺序查找,只要p不空且未到第i结点就前进
{
p=p->next;
++j;
}
if(!p||pos<1)return ERROR; //第i个元素不存在 或 i<1,
e=p->data; //取第i个元素
return OK;
}
Status ListInsert_L(Linklist &L, int i, ElemType e){
//定位到第i-1个结点,在其后插入一个新开辟的结点.注意i异常
LNode *p=L; int j=0; /*p始终指向第j个结点*/
while(p&&j<i-1)
{p=p->next; ++j;}//只要不空且未到第i-1个
if(!p||i<1) return ERROR; //第i-1个节点不存在或者i<1
LNode *q;
q= (LNode *)malloc(sizeof(LNode)); //或q=new LNode;
if(!q)exit(OVERFLOW);
q->data=e;
q->next=p->next;
p->next=q;
return(OK);
}
Status ListDelete_L(Linklist &L, int i, ElemType &e){
//寻找第i-1个结点 ,若其后继结点存在则带回其data并删除
LNode *p=L,*q; int j=0; /*p始终指向第j个结点*/
while(p&&j<i-1){p=p->next; ++j;}//寻找第i-1个结点
// j 最终为i-1,除非p空
if(!p||!p->next||i<1) return ERROR;//第i个或更前结点不存在,
q=p->next;
e=q->data;
p->next=q->next;
free(q);
return(OK);
}
void printfLinklist(Linklist &L){
LNode *p=L->next;
while (p!= NULL)
{
printf("%d ",p->data) ;
p=p->next;
}
printf("%d",p->data) ;
}
int main()
{
Linklist L;
int n;
scanf("%d",&n);
Listcreate_L(L,n);
// printfLinklist(L);
int pos;
scanf("%d",&pos);
//ListInsert_L(L,pos,e);
ElemType e;
GetElem_L(L,pos,e);
printf("%d",e);
//printfLinklist(L);
}
补充:
链表的指针
#include<stdio.h>
typedef struct LNode
{
int data;
LNode *p;
} Node;
int main()
{
Node a,b;
a.data=123;
b.data=124;
a.p=&b;//指向b的头结点
a.p->data=1212;
b.p=&a;//地址=地址
b.p->data=807;//等于a.data
printf("%d\n",b.data);
printf("%d",a.data);
}
(7)链表顺序表的优缺点
优点:空间利用好,插入删除不需移动数据,表头操作快。
缺点:顺序访问,位序概念淡化, 表尾操作、求前驱、求表长慢,存储密度低。例如,为方便求前驱可引入双向链表,为方便表尾操作可引入尾指针和循环链表,为实用改进结构定义如添加表长成员和尾指针的改进单链表等
(9)小结
2.双向链表
(1)定义
单向链表特点:
1.我们可以轻松的到达下一个节点, 但是回到前一个节点是很难的.
2.只能从头遍历到尾或者从尾遍历到头(一般从头到尾)
双向链表特点
1.每次在插入或删除某个节点时, 需要处理四个节点的引用, 而不是两个. 实现起来要困难一些
2.相对于单向链表, 必然占用内存空间更大一些.
3.既可以从头遍历到尾, 又可以从尾遍历到头
双向链表的定义:
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。下图为双向链表的结构图。
从上中可以看到,双向链表中各节点包含以下 3 部分信息:
指针域:用于指向当前节点的直接前驱节点;
数据域:用于存储数据元素。
指针域:用于指向当前节点的直接后继节点;
双向循环链表的定义:
双向链表也可以进行首尾连接,构成双向循环链表,如下图所示
在创建链表时,只需要在最后将收尾相连即可(创建链表代码中已经标出)。其他代码稍加改动即可。
(2)创建
//---线性表的双向(循环)链表存储结构---
typedef struct DuLNode{
ElemType data;
struct DuLNode *prior;
struct DuLNode *next;
}DuLNode,*DuLinkList;
//双向链表有利于访问前驱结点,复杂度多少?
(3)插入
p=GetElemP_DuL(L,i-1); //确定插入位置
s=new DuLNode;
s->data=e;
s->prior=p;
s->next=p->next;
p->next->prior=s;
p->next=s;
Status ListInsert_DuL(DuLinkList &L,int i,ElemType &e){
DuLNode *p=L; int j=0;
while(p&&j<i-1){p=p->next;++j;}
if(!p||i<1) return ERROR;
s=new DuLNode; if(!s)exit(OVERFLOW);
s->data=e;
s->prior=p; s->next=p->next; //注意分析指针改变次序
p->next->prior =s; p->next=s;
return OK;
}//ListDelete_DuL
(4)删除
(1)p->prior->next=p->next; (2)p->next->prior=p->prior; (3) free(p); p=NULL
Status ListDelete_DuL(DuLinkList &L,int i,ElemType &e){
//删除带头结点的双向链表L的第i个元素,1≤i≤ListLength(L)
if(!(p=GetElemP_Dul(L,i))) return ERROR;
e=p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);p=NULL; //教材未写p=NULL,容易引起内存泄露;
return OK;
}//ListDelete_DuL