2_线性表之链式存储
存储结构
通过“链”建立数据元素间的逻辑关系,使物理位置上不相邻的元素逻辑上也能相邻,操作不需要移动元素,只需修改指针。
1.单链
表示方法
typedef struct LNode{ ElemType data; //数据域 struct LNode *next; //指针域 } LNode,*LinkList
基本操作1:建立
头插法
实现步骤:从创建头结点开始,生成新结点,将读取到的数据放到新结点的数据域中,然后将新结点插入到头结点之后,读取顺序与生成顺序相反
{ s = (LNode*)malloc(sizeof(LNode)); s—>data=x; s—>next=L—>next; //将L—>next(初值Null)所指赋给s—>next L—>next = s; scanf("%d",&x); }
*指针所代表的是指向
尾插法
生成顺序与读取顺序相同
s—>data=x; r—>next=s; //r所指向 r = s;
基本操作2:查找
按位
LNode *GetElem(LinkList L,int i){ int j=1; //计数,初始为1 LNode *p=L—>Next; if(i==0) //若i等于0,返回头结点 return L; if(i<1) //若i无效,返回Null return Null; while(p&&j<i){ p=p—>next; j++; } return p; }
*单链表长度不包含头结点
按值
LNode *LocateElem(LinkList L,ElemType e){ LNode *p=L—>Next; while(p!=Null&&p—>data!=e){ p=p—>Next; return p; }
*操作时先检验操作合法性,再执行操作
基本操作3:插入
1.p=GetElem(L,i-1); //查找插入位置前驱结点 2.s—>next=p—>next; //步骤1 3.p—>next=s; //步骤2
基本操作4:删除
按位
实现步骤:将单链表第i个结点删除,先检查合法性,然后找出第i-1它的前驱结点,便可删除i结点,复杂度O(n)
p=GetElem(L,i-1); //查找删除位置的前驱结点 q=p—>next; //令q指向被删除结点 p—>next=q—>next; //断开 free(q);
删给定结点*p
实现步骤:将其后继结点赋予给定结点,再删除后继,复杂度O(1)
q=p—>next; p—>next=q—>next—>data; //和后继结点交换数据域 p—>next=q—>next; free(q);
2.双链表
双链表节点中有两个指针prior和next,查找与单链表相同,插入和删除有较大不同
表示方法:
typedef struct DNode{ ElemType data; //数据域 struct DNode *prior,*next; //前驱、后驱指针 }DNode,*DLinkList
基本操作1:插入
1.s—>next=p—>next; //建立后继与s联系 2.p—>next—>prior=s; //P的下个结点的前驱指向 3.s—>prior=p; //建立与前驱结点联系 4.p—>next=s; //P的上个结点的后继指向
*方法不唯一,但1.2.必须在4.前面
基本操作2:删除
p—>next=q—>next; //步骤1. q—>next—>prior=p; //步骤2. free(q);
3.循环链表
只需在终端结点和头结点建立联系即可,判断P走到表尾的条件是P—>next=head
算法1:递归删除无头结点单链表所有某值
void Del_x_3(LinkList,&L,ElemType x){ LNode *p; //p指向待删除结点 if(L==NULL) //递归终止条件 return; if(L—>data==x){ //若L所指结点值为x,删除*L指向 p=L; L—>next; //并让L指向下一结点 free(p); Del_x_3(L,x); } //跳回函数本体 else Del_x_3(L—>data,x) }
算法2:删除带头结点单链表所有某值
void Del_x_4(LinkList &L,ElemType x){ LNode *p=L—>next,*pre=L,*q; //置*P和*pre初始值 while(p!=NULL){ if(p—>data==x){ q=p; //q指向该结点 p=p—>next; //通过操作删除该结点 pre—>next=p; free(q); } else{ //不等,pre和p同时后移 pre=p; p=p—>next; }}}
算法3:从尾到头递归遍历带头结点单链表
用栈的思想递归实现,也可以逆置改变方向后从头到尾遍历
void Reverse_Print(LinkList L){ if(L—>next!=NULL){ Reverse_Print(L—>next); } //递归 print(L—>next); } //整体输出函数
算法4:删除带头结点单链表最值
void Del_Min(LinkList &L){ LNode *pre=L,*p=pre—>next; //p为扫描指针,pre为p的前驱 LNode *minpre=pre,*minp=p; //边扫描边比较,将较小者存入minp和premin,将前驱考虑进来是为了便于删除一个结点 while(p!=NULL) { if(p—>data<minp—>data){ minp=p; minpre=pre; } p=p—>next; //继续扫描下一个结点 pre=p; } minpre—>next=minp—>next; //删除最小值 free(minp); return L; }
算法5:带头结点单链表就地逆置
LinkList Reverse_1(LinkList &L){ LNode *p,*r; //p是扫描指针,r暂存p后继 p=L—>next; //从头结点开始 L—>next=NULL; //先将头结点指针域置NULL while(p!=NULL){ r=p—>next; // 暂存p的后继防止断链 p—>next=L—>next; //以下循环头插法,这里是插入后继 L—>next=p; //插入前驱 p=r; } //扫描下一个 return L; }
算法6:无序——>有序
使带头结点的单链表有序递增
void Sort(LinkList &L){ LNode *p=L—>next,*pre; LNode *r=p—>next; //r防止断链