单链表基本操作(可执行程序),二级指针使用必要性的初步理解

#include <stdio.h>
#include <stdlib.h>
#include<time.h>
#define ERROR 0;
#define OK 1;

//typedef
typedef int Status;
typedef int ElemType;
typedef struct Node//定义带头结点的单链表的节点类型
{
    ElemType data;
    struct Node *next;
}Node;
typedef struct Node *LinkList;

//操作函数声明
Status CreateListHead(LinkList*L,int n);
Status CreateListTail(LinkList *L,int n);
Status GetElem(LinkList L,int i,ElemType *e);
Status ListInsert(LinkList *L,int i,ElemType e);
Status ListDelete(LinkList*L,int i,ElemType *e);
Status DestroyList(LinkList *L);
Status ListTraverse(LinkList L);

int main()
{
    LinkList L;
    int n=5;
    int e=0;
    int i=0;
    printf("请输入需要产生的单链表长度(>=1):");
    scanf("%d",&n);
    CreateListTail(&L,n);
    printf("产生的单链表是:");
    ListTraverse(L);

    printf("请输入你想要插入的位置和数:");
    scanf("%d %d",&i,&e);
    ListInsert(&L,i,e);
    printf("插入后得到的新链表是:");
    ListTraverse(L);

    printf("你想要查看哪个位置的数:");
    scanf("%d",&i);
    printf("该数是:");
    GetElem(L,i,&e);
    printf("\n");

    printf("你想要删除哪个位置的数:");
    scanf("%d",&i);
    ListDelete(&L,i,&e);
    printf("删除以后的新链表是:");
    ListTraverse(L);

    printf("现在要销毁链表释放空间啦!\n");
    DestroyList(&L);

}




//单链表的整表创建,头插法
Status CreateListHead(LinkList*L,int n)
{
    LinkList p;
    int i;
    srand(time(0));
    *L=(LinkList)malloc(sizeof(Node));
   ( *L)->next=NULL;
   if(n<1)
        return ERROR;
    for(i=0;i<n;i++)
    {
        p=(LinkList)malloc(sizeof(Node));
        p->data=rand()%100+1;
        p->next=(*L)->next;
        (*L)->next=p;
    }
    return OK;

}
//单链表的整表创建,尾插法
Status CreateListTail(LinkList *L,int n)
{
    LinkList p,r;
    int i;
    *L=(LinkList)malloc(sizeof(Node));
   ( *L)->next=NULL;
    r=*L;
    srand(time(0));
    for (i=0;i<n;i++)
    {
        p=(LinkList)malloc(sizeof(Node));
        p->data=rand()%100+1;
        r->next=p;
        r=p;
    }
    r->next=NULL;
    return OK;
}

//读取单链表中的第i个元素
Status GetElem(LinkList L,int i,ElemType *e)
{
    int j=1;
    LinkList p;
    p=L->next;
    while(p&&j<i)
    {
        p=p->next;
        ++j;
    }
    if(j>i||!p)
        return ERROR;
    *e=p->data;
    printf("%d",*e);
    return OK;
}

//在单链表中第i个位置之前插入一个新节点e
Status ListInsert(LinkList *L,int i,ElemType e)
{
    int j=1;
    LinkList p,s;
    p=*L;
    while(p&&j<i)
    {
        p=p->next;
        ++j;
    }
    if (!p||j>i)
        return ERROR;
    s=(LinkList)malloc(sizeof(Node));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return OK;
}

//删除单链表的第i个元素,并用e返回其值
Status ListDelete(LinkList*L,int i,ElemType *e)
{
    int j=1;
    LinkList p,q;
    p=*L;
    while(p->next&&j<i)
    {
        p=p->next;
        ++j;
    }
    if (!(p->next)||j>i)
        return ERROR;
    q=p->next;
    p->next=q->next;
   * e=q->data;
   free(q);
    return OK;
}

//销毁单链表
Status DestroyList(LinkList *L)
{
    LinkList p,q;
    p=(*L)->next;
    while(p)
    {
        q=p->next;
        free(p);
        p=q;
    }
    free(*L);
    *L=NULL;
    printf("链表销毁成功!");
    return OK;
}
//打印单链表
Status ListTraverse(LinkList L)
{
    LinkList p;
    p=L->next;
    while(p)
    {
        printf("%d ",p->data);
        p=p->next;
    }
    printf("\n");
    return OK;
}

程序测试结果:


关于二级指针使用的必要性,我是这样理解的:

很多操作其实使用一级指针也可以实现,但是在需要改变一级指针的值是就必须使用二级指针,例如,需要删除头结点或者生成链表时,L的值就需要变化,那么此时只有传入二级指针才能达到修改L的值的目的,这种情况下传入&L也是可行的。

下面举两个例子说明一下:

(1)

在链表已经生成的情况下,修改ListInsert函数为:

Status ListInsert(LinkList L,int i,ElemType e)
{
    int j=1;
    LinkList p,s;
    p=L;
    while(p&&j<i)
    {
        p=p->next;
        ++j;
    }
    if (!p||j>i)
        return ERROR;
    s=(LinkList)malloc(sizeof(Node));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return OK;
}

程序仍然可以得到正确的结果。

(2)

但是在生成链表时,若只传入一级指针,即修改CreateListTail为:

Status CreateListTail(LinkList  L,int n)
{
    LinkList p,r;
    int i;
    L=(LinkList)malloc(sizeof(Node));
    L->next=NULL;
    r=L;
    srand(time(0));
    for (i=0;i<n;i++)
    {
        p=(LinkList)malloc(sizeof(Node));
        p->data=rand()%100+1;
        r->next=p;
        r=p;
    }
    r->next=NULL;
    return OK;
}

则运行程序时,CodeBlocks会停止工作,这是因为在生成链表阶段,需要将L的值传出到主函数,供后面的链表打印函数ListTraverse调用。若此处只传入一级指针,则CreateListTail函数调用结束以后,L的值并没有改变,相当于L仍指向主函数中定义时所指向的位置(野指针或者指向NULL,暂时还不是很清楚如果不初始化指向哪里),则后续调用打印函数会出错。

总结:需要修改L的值时,必须传入二级指针即func(LinkList *L),其他情况下传一级指针即func(LinkList L)就ok。


阅读更多
个人分类: 数据结构
上一篇关于类的tips
下一篇函数调用时栈的相关变化
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭