小肥柴慢慢手写数据结构(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 小结

  1. 单链表部分的内容,self版本不如严版实现那么全面,但基本功能还是有的;
  2. self版本使用了虚拟头结点DummyHead,严版一般单链表用了两个标记(head和tail),严版在循环链表中用了虚拟头结点,多学多看,但操作的核心是不变的;
  3. 严版教材忽略了两个重要的内容“反转”和“快慢指针”,其实还是放在课后习题供大家思考和讨论的。

这一帖就是水完了,相比之前self版本实现,看严版教材还是比较轻松的,毕竟已经有了自己的认知和一定的基础嘛;下一贴,我们准备讨论一个好玩的数据结构“跳表”,在面试中也经常会被问到,敬请期待。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值