刺破数据结构-数据结构学习总结(第二章)b站p13(源码实现)

线性表逻辑:唯一前驱唯一后继.

#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;
typedef int Status;
/*
typedef struct{
    char num[8];
    char name[8];
    int score;
}ElemType;
*/
#define OK 1
#define ERROR 0

typedef struct Lnode{
    ElemType data;
    struct Londe *next;
}Lnode,*Linklist;

//自己定义自己,嵌套定义。

void printLnode(Linklist L){
    Lnode *p = L->next;
    if(!p) return printf("kong");
    while(p){
        printf("%d ",p->data);
        p = p->next;
    }
    
}


Status InitList_L(Linklist *L)
{
     *L = (Linklist)malloc(sizeof(Lnode));
     (*L)->next =NULL;
     return OK;
}

Status void_L(Linklist L)
{
    if (L->next)
     {
     return ERROR;
    }
     else return OK;
}


Status Destroy_L(Linklist *L)
{
     Lnode *p;
     while ((*L))
     {
     p = *L;
    (*L) = (*L)->next;
     free(p);
     } 
}

//销毁整个链表的时候,需要将整个链表包括头结点全部删除,清空时需要进行保留。

Status Clear_L(Linklist *L)
{
    Lnode* p,*q;
    p =(*L)->next;
    q = p->next;
    while (q)
    {
        free(p);
        p = q;
        q = q->next;     
    }
    (*L)->next = NULL;
    return OK;
}

int  ListLength(Linklist L)
{
    Lnode *p;
    p = L->next;
    int  i = 0;
    while (p)
    {
        i++;
        p = p->next;
    }
    return i;
}

Status GetElem_L(Linklist L,int i,ElemType *e)
{
    Lnode *p;int j=1;
    p = L->next;
    while(p && j<i){
        p = p->next;
        j++;
    }
    if(!p||j>i) return ERROR;
    *e = p->data;
    return OK;
}

Lnode * LocateElem_L(Linklist L,ElemType e)
{
    Lnode* p = L->next;
    while(p && p->data!=e)
    {
        p = p->next;
    }
    return p;
}

int LocateElem_L2(Linklist L,ElemType e)
{
    Lnode* p = L->next;int j=1;
    while(p && p->data!=e){
        p = p->data;
        j++;
    }
    if(p) return j;
    else return ERROR;
}

Status ListInsert_L(Linklist*L,int i,ElemType e)
{
    Lnode * p;
    p = (*L);
    int j = 0;
    while (p && j<i-1){
        p = p->next;j++;
    }
    if(!p||j>i-1) return ERROR;
    Lnode* s = (Lnode*)malloc(sizeof(Lnode));
    s->data = e;
    s->next = p->next;
    p->next = s;
}

Status ListDelete(Linklist *L,int i,ElemType *e)
{
    Lnode *p = L;
    Lnode *q = L;
    int j =0;
    while (p->next && j<i-1)
    {
        p = p->next;
        j++;
    }
    if(!p->next||j>i-1) return ERROR;
    q = p->next;
    p->next = q->next;
    e = q->data;
    free(q);
    return OK;
}

//头插法:

void CreateList_H(Linklist *L,int n)
{
    *L = (Lnode*)malloc(sizeof(Lnode));
    (*L)->next = NULL;
    for(int i=n;i>0;i--)
    {
        Lnode *p = (Lnode*)malloc(sizeof(Lnode));
        printf("请输入第%d个元素",n-i+1);
        scanf("%d",&(p->data));
        p->next = (*L)->next;
        (*L)->next = p;
    }
}

//尾插法:

void CreateList_R(Linklist *L,int n)
{
    (*L) = (Lnode*)malloc(sizeof(Lnode));
    (*L)->next = NULL;
    //初始化
    Lnode *r = *L;
    for(int i = 0; i < n; ++i)
    {
        Lnode *p = (Lnode*)malloc(sizeof(Lnode));
        p->next = NULL;
        r->next = p;
        r = p;
        printf("请输入第%d个元素",i+1);
        scanf("%d",&(p->data));
    }
}




int main()
{
    Linklist L;
    int i;
    ElemType e=0;
    //头插法初始化链表
    printf("请输入需要初始化的元素数量:");
    scanf("%d",&i);
    CreateList_H(&L,i);
    printLnode(L);

    //输入长度
    printf("%d",ListLength(L));

    //查找需要的长度
    printf("请输入需要查找的元素:");
    scanf("%d",&i); 
    GetElem_L(L,i,&e);
    printf("\ne=%d",e);
    //删除所需要的元素
    printf("请输入需要删除的元素位置:");
    scanf("%d",&i); 
    ListDelete(L,i,&e);
    printf("\ne=%d",e);
    printLnode(L);

    //清除链表
    Clear_L(&L);

    //实现尾插操作
    printf("请输入需要初始化的元素数量:");
    scanf("%d",&i);
    CreateList_R(&L,i);
    printf("\n");
    printLnode(L);

    //删除所需要的元素
    printf("请输入需要插入的元素位置:");
    scanf("%d",&i); 
    printf("请输入需要插入的元素:");
    scanf("%d",&e); 
    ListInsert_L(&L,i,e);
    printLnode(L);

    return 0;
}

上面的单链表代码是按照王卓老师的一步一步实现的。以下是对其中的一些实现的解释说明。

定义

首先是对链表的结构怎么定义的:

其实就是抽象的 数据域与代码域怎么用c表达

typedef int ElemType;
typedef int Status;
/*
typedef struct{
    char num[8];
    char name[8];
    int score;
}ElemType;
*/

typedef struct Lnode{
    ElemType data;
    struct Londe *next;
}Lnode,*Linklist;

这里我们可以看到其实就是一个 结构体。结构体中的第一个元素表示数据域,可以任意定义,第二个元素表示下一个元素的指针链条),两者相组合构成了一个链表。

初始化头结点

Status InitList_L(Linklist *L)
{
     *L = (Linklist)malloc(sizeof(Lnode));
     (*L)->next =NULL;
     return OK;
}

初始化第一步分配空间。没有空间资源就如巧妇无米,炊不起来。所以第一步我们分配给L一个空间。此处我们要用Linklist * 的原因在于我们在c中无引用参数,所以只能用传地址的方式进行平替。

需要注意的是(*L)这个地方要用到*L。我在之前的文章中有提到关于指针的一些知识,这里就不再赘述,其目的是为了让L的真身得到地址的分配以及真身的next指针设置为空。

初始化第二步设置我们所构成的数据结构的参数

因为此处我们设置的是头结点,所以其实也就是设置next指针为NULL。

判断表是否为空?

Status void_L(Linklist L)
{
    if (L->next)
     {
     return ERROR;
    }
     else return OK;
}

思想:看一个链表是否为空,就看头结点的next是否为即可。

删除一个链表(头都不剩)

Status Destroy_L(Linklist *L)
{
     Lnode *p;
     while ((*L))
     {
     p = *L;
    (*L) = (*L)->next;
     free(p);
     } 
}

思想:我们要是想将链表删除,那么我们需要从头结点开始一个一个删除。

这是一个基本思路。那我们咋删呢?只用一个头指针能不能删呢?我们可以发现,如果只用一个头指针删除,我们只能删除一项,因为我们删了一个后,没有可以指向下一个链表结构的指针了。所以我们需要定义一个指针,替我们擦屁股(或者替我们带路)。

此处就是,等L存好下一个指针的时候,p将前一个指针释放。

清空指针

//销毁整个链表的时候,需要将整个链表包括头结点全部删除,清空时需要进行保留。

Status Clear_L(Linklist *L)
{
    Lnode* p,*q;
    p =(*L)->next;
    q = p->next;
    while (q)
    {
        free(p);
        p = q;
        q = q->next;     
    }
    free(p);
    (*L)->next = NULL;
    return OK;
}

此处是一个清空指针的操作。此处我们需要保留头节点。所以说之前我们头结点动,现在不能动了。那怎么办呢?我们再定义一个指针就行了嘛。

此处需要注意的是停止条件。我试过停止条件是p的时候,下面的代码会有报错,我猜的可能是因为q =NULL。此时让q = q->next查无此词所以就失败了。

 

返回链表长度

int  ListLength(Linklist L)
{
    Lnode *p;
    p = L->next;
    int  i = 0;
    while (p)
    {
        i++;
        p = p->next;
    }
    return i;
}

就是数数,但要注意条件,捋一捋逻辑:首先将p指向首元结点,若首元结点存在即让j++,再将p指向下一个节点。如果说下一个节点非空再(此处循环)执行j++ 。。。。。

头插法plus尾插法

//头插法:

void CreateList_H(Linklist *L,int n)
{
    *L = (Lnode*)malloc(sizeof(Lnode));
    (*L)->next = NULL;
    for(int i=n;i>0;i--)
    {
        Lnode *p = (Lnode*)malloc(sizeof(Lnode));
        printf("请输入第%d个元素",n-i+1);
        scanf("%d",&(p->data));
        p->next = (*L)->next;
        (*L)->next = p;
    }
}

//尾插法:

void CreateList_R(Linklist *L,int n)
{
    (*L) = (Lnode*)malloc(sizeof(Lnode));
    (*L)->next = NULL;
    //初始化
    Lnode *r = *L;
    for(int i = 0; i < n; ++i)
    {
        Lnode *p = (Lnode*)malloc(sizeof(Lnode));
        p->next = NULL;
        r->next = p;
        r = p;
        printf("请输入第%d个元素",i+1);
        scanf("%d",&(p->data));
    }
}

其实这两种方法都包含了对链表L的初始化。前两行便是对链表的初始化。

头插法就是在头结点处进行插入。反正输入新元素都先记住!!!先创造新元素,即先分给新元素空间。后面再管指针。

头插法:

先将头结点的指针给新节点的next,再将头结点的next等于最新的节点。

打个不太适合的比喻:本来是(老板)直接管理(员工),现在多了(经理),那老板先将权利下放给经理们,然后再掌控经理。

尾插法:

一直在最尾部插入节点,我们需要一个指向尾部的指针 r 。该指针一开始跟 L 一样指向头结点。

r->next 其实就等于下一个新元素的位置。我们需要将此位置信息给新的值的地址,再将r移到新的元素的位置。所以需要对指针的操作只有两个,一个是链接上,第二个是将 r 移到最新元素的地址处。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值