小肥柴慢慢手写数据结构(C篇)(2-4 单链表 严版教材实现浅析)
目录
我在网上传播的答案中,找了一个相对简单的实现,方便手写。
2-11 单链表完整代码
2-11-1 头文件c2-5.h
这是严版教材中较为实用的一个实现方案,抱佛脚可用,有明显的的OOP思维。
typedef struct LNode { //这里的typedef将多句合并为一句
ElemType data;
LNode *next;
}*Link,*Position;
struct LinkList { // 已经有面对对象的思想了,维护一个len记录长度,还有头尾
Link head,tail; // 分别指向线性链表中的头结点和最后一个结点
int len; // 指示线性链表中数据元素的个数
};
2-11-2 具体实现bo2-6.cpp
具有实用意义的线性链表,共24个基本操作,注意函数指针和引用显示调用
Status MakeNode(Link &p,ElemType e){
p=(Link)malloc(sizeof(LNode));
if(!p)
return ERROR;
p->data=e;
return OK;
}
void FreeNode(Link &p){
free(p);
p=NULL;
}
Status InitList(LinkList &L){
Link p;
p=(Link)malloc(sizeof(LNode));
if(p){
p->next=NULL;
L.head=L.tail=p;
L.len=0;
return OK;
} else
return ERROR;
}
Status ClearList(LinkList &L){
Link p,q;
if(L.head!=L.tail){
p=q=L.head->next;
L.head->next=NULL;
while(p!=L.tail){
p=q->next;
free(q);
q=p;
}
free(q);
L.tail=L.head;
L.len=0;
}
return OK;
}
Status DestroyList(LinkList &L){
ClearList(L);
FreeNode(L.head);
L.tail=NULL;
L.len=0;
return OK;
}
Status InsFirst(LinkList &L,Link h,Link s){
s->next=h->next;
h->next=s;
if(h==L.tail)
L.tail=h->next;
L.len++;
return OK;
}
Status DelFirst(LinkList &L,Link h,Link &q){
q=h->next;
if(q){
h->next=q->next;
if(!h->next)
L.tail=h;
L.len--;
return OK;
} else
return FALSE;
}
Status Append(LinkList &L,Link s){
int i=1;
L.tail->next=s;
while(s->next){
s=s->next;
i++;
}
L.tail=s;
L.len+=i;
return OK;
}
Position PriorPos(LinkList L,Link p){
Link q;
q=L.head->next;
if(q==p)
return NULL;
else{
while(q->next!=p)
q=q->next;
return q;
}
}
Status Remove(LinkList &L,Link &q){
Link p=L.head;
if(L.len==0){
q=NULL;
return FALSE;
}
while(p->next!=L.tail)
p=p->next;
q=L.tail;
p->next=NULL;
L.tail=p;
L.len--;
return OK;
}
Status InsBefore(LinkList &L,Link &p,Link s){
Link q;
q=PriorPos(L,p);
if(!q)
q=L.head;
s->next=p;
q->next=s;
p=s;
L.len++;
return OK;
}
Status InsAfter(LinkList &L,Link &p,Link s){
if(p==L.tail)
L.tail=s;
s->next=p->next;
p->next=s;
p=s;
L.len++;
return OK;
}
Status SetCurElem(Link p,ElemType e) {
p->data=e;
return OK;
}
ElemType GetCurElem(Link p){
return p->data;
}
Status ListEmpty(LinkList L){
return L.len ? FALSE : TRUE;
}
int ListLength(LinkList L){
return L.len;
}
Position GetHead(LinkList L){
return L.head;
}
Position GetLast(LinkList L){
return L.tail;
}
Position NextPos(Link p){
return p->next;
}
Status LocatePos(LinkList L,int i,Link &p){
int j;
if(i<0||i>L.len)
return ERROR;
else{
p=L.head;
for(j=1;j<=i;j++)
p=p->next;
return OK;
}
}
Position LocateElem(LinkList L,ElemType e,Status (*compare)(ElemType,ElemType)){
Link p=L.head;
do
p=p->next;
while(p&&!(compare(p->data,e)));
return p;
}
Status ListTraverse(LinkList L,void(*visit)(ElemType)){
Link p=L.head->next;
int j;
for(j=1;j<=L.len;j++){
visit(p->data);
p=p->next;
}
printf("\n");
return OK;
}
Status OrderInsert(LinkList &L,ElemType e,int (*comp)(ElemType,ElemType)){
Link o,p,q;
q=L.head;
p=q->next;
while(p!=NULL&&comp(p->data,e)<0){
q=p;
p=p->next;
}
o=(Link)malloc(sizeof(LNode));
o->data=e;
q->next=o;
o->next=p;
L.len++;
if(!p)
L.tail=o;
return OK;
}
Status LocateElem(LinkList L,ElemType e,Position &q,int(*compare)(ElemType,ElemType)){
Link p=L.head,pp;
do{
pp=p;
p=p->next;
}while(p&&(compare(p->data,e)<0));
if(!p||compare(p->data,e)>0){
q=pp;
return FALSE;
} else {
q=p;
return TRUE;
}
}
2-11-3 测试和调用main2-6.cpp
#include"c1.h" //定义各种状态
typedef int ElemType;
#include"c2-5.h"
#include"bo2-6.cpp"
Status compare(ElemType c1,ElemType c2){
return c1==c2 ? TRUE : FALSE;
}
int cmp(ElemType a,ElemType b){
return a==b? 0 : (a-b)/abs(a-b);
}
void visit(ElemType e){
printf("%d ",e);
}
void main(){ //多年不见的C++写法,void main()
Link p,h;
LinkList L;
int j,k;
if(!InitList(L))
exit(FALSE);
for(j=1;j<=2;j++){
MakeNode(p,j);
InsFirst(L,L.tail,p);
}
OrderInsert(L,0,cmp);
for(j=0;j<=3;j++){
if(LocateElem(L,j,p,cmp))
printf("链表中有值为%d的元素。\n",p->data);
else
printf("链表中没有值为%d的元素。\n",j);
}
printf("输出链表:");
ListTraverse(L,visit);
for(j=1;j<=4;j++){
printf("删除表头结点:");
DelFirst(L,L.head,p); // 删除L的首结点,并以p返回
if(p)
printf("%d\n",GetCurElem(p));
else
printf("表空,无法删除 p=%u\n",p);
}
printf("L中结点个数=%d L是否空 %d(1:空 0:否)\n",ListLength(L),ListEmpty(L));
MakeNode(p,10);
p->next=NULL;
for(j=4;j>=1;j--){ // h指向一串5个结点,其值依次是2 4 6 8 10
MakeNode(h,j*2);
h->next=p;
p=h;
}
Append(L,h); // 把结点h链接在线性链表L的最后一个结点之后
OrderInsert(L,12,cmp);
OrderInsert(L,7,cmp);
printf("输出链表:");
ListTraverse(L,visit);
for(j=1;j<=2;j++){
p=LocateElem(L,j*5,compare);
if(p)
printf("L中存在值为%d的结点。\n",j*5);
else
printf("L中不存在值为%d的结点。\n",j*5);
}
for(j=1;j<=2;j++){
LocatePos(L,j,p); // p指向L的第j个结点
h=PriorPos(L,p); // h指向p的前驱
if(h)
printf("%d的前驱是%d。\n",p->data,h->data);
else
printf("%d没前驱。\n",p->data);
}
k=ListLength(L);
for(j=k-1;j<=k;j++){
LocatePos(L,j,p); // p指向L的第j个结点
h=NextPos(p); // h指向p的后继
if(h)
printf("%d的后继是%d。\n",p->data,h->data);
else
printf("%d没后继。\n",p->data);
}
printf("L中结点个数=%d L是否空 %d(1:空 0:否)\n",ListLength(L),ListEmpty(L));
p=GetLast(L); // p指向最后一个结点
SetCurElem(p,15); // 将最后一个结点的值变为15
printf("第1个元素为%d 最后1个元素为%d\n",GetCurElem(GetHead(L)->next),GetCurElem(p));
MakeNode(h,10);
InsBefore(L,p,h); // 将10插到尾结点之前,p指向新结点
p=p->next; // p恢复为尾结点
MakeNode(h,20);
InsAfter(L,p,h); // 将20插到尾结点之后
k=ListLength(L);
printf("依次删除表尾结点并输出其值:");
for(j=0;j<=k;j++)
if(!(i=Remove(L,p))) // 删除不成功
printf("删除不成功 p=%u\n",p);
else
printf("%d ",p->data);
MakeNode(p,29); // 重建具有1个结点(29)的链表
InsFirst(L,L.head,p);
DestroyList(L); // 销毁线性链表L
printf("销毁线性链表L之后: L.head=%u L.tail=%u L.len=%d\n",L.head,L.tail,L.len);
}
2-12 循环单链表完整代码
2-12-1 头文件c2-2.h
struct LNode {
ElemType data;
LNode *next;
};
typedef LNode *LinkList;
2-12-2 具体实现bo2-4.cpp
共12个基本操作,还有虚拟头结点!
Status InitList_CL(LinkList &L){
L=(LinkList)malloc(sizeof(LNode));
if(!L)
exit(OVERFLOW);
L->next=L;
return OK;
}
Status DestroyList_CL(LinkList &L){
LinkList q,p=L->next;
while(p!=L){
q=p->next;
free(p);
p=q;
}
free(L);
L=NULL;
return OK;
}
Status ClearList_CL(LinkList &L){
LinkList p,q;
L=L->next;
p=L->next;
while(p!=L){
q=p->next;
free(p);
p=q;
}
L->next=L;
return OK;
}
Status ListEmpty_CL(LinkList L){
return L->next==L ? TRUE : FALSE;
}
int ListLength_CL(LinkList L){
int i=0;
LinkList p=L->next;
while(p!=L){
i++;
p=p->next;
}
return i;
}
Status GetElem_CL(LinkList L,int i,ElemType &e){
int j=1;
LinkList p=L->next->next;
if(i<=0||i>ListLength_CL(L))
return ERROR;
while(j<i){
p=p->next;
j++;
}
e=p->data;
return OK;
}
int LocateElem_CL(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType)){
int i=0;
LinkList p=L->next->next; // p指向第一个结点
while(p!=L->next){
i++;
if(compare(p->data,e))
return i;
p=p->next;
}
return 0;
}
Status PriorElem_CL(LinkList L,ElemType cur_e,ElemType &pre_e){
LinkList q,p=L->next->next;
q=p->next;
while(q!=L->next){
if(q->data==cur_e){
pre_e=p->data;
return TRUE;
}
p=q;
q=q->next;
}
return FALSE;
}
Status NextElem_CL(LinkList L,ElemType cur_e,ElemType &next_e){
LinkList p=L->next->next;
while(p!=L){
if(p->data==cur_e){
next_e=p->next->data;
return TRUE;
}
p=p->next;
}
return FALSE;
}
Status ListInsert_CL(LinkList &L,int i,ElemType e){
LinkList p=L->next,s;
int j=0;
if(i<=0||i>ListLength_CL(L)+1)
return ERROR;
while(j<i-1){
p=p->next;
j++;
}
s=(LinkList)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
if(p==L)
L=s;
return OK;
}
Status ListDelete_CL(LinkList &L,int i,ElemType &e){
LinkList p=L->next,q;
int j=0;
if(i<=0||i>ListLength_CL(L))
return ERROR;
while(j<i-1){
p=p->next;
j++;
}
q=p->next;
p->next=q->next;
e=q->data;
if(L==q)
L=p;
free(q);
return OK;
}
Status ListTraverse_CL(LinkList L,void(*vi)(ElemType)){
LinkList p=L->next->next;
while(p!=L->next){
vi(p->data);
p=p->next;
}
printf("\n");
return OK;
}
2-13 静态链表
这种方法也成为“链表的游标实现”,我第一次看到是在《数据结构与算法分析:C语言描述》Page42页
其实回想下我们已经熟悉的指针节点结构的链表,不就是*next存储了一个整数嘛,只不过这个整数是一个地址而已,都是相通的,对比严版教材的描述:
嘿嘿嘿,是不是一样哒,阔能还是严版教材借鉴人家的呢?
但实际上这个部分看看就行,实用性不是很强,主要是锻炼自己的思维。
2-13 小结
- 单链表部分的内容,self版本不如严版实现那么全面,但基本功能还是有的;
- self版本使用了虚拟头结点DummyHead,严版一般单链表用了两个标记(head和tail),严版在循环链表中用了虚拟头结点,多学多看,但操作的核心是不变的;
- 严版教材忽略了两个重要的内容“反转”和“快慢指针”,其实还是放在课后习题供大家思考和讨论的。
这一帖就是水完了,相比之前self版本实现,看严版教材还是比较轻松的,毕竟已经有了自己的认知和一定的基础嘛;下一贴,我们准备讨论一个好玩的数据结构“跳表”,在面试中也经常会被问到,敬请期待。