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

原创 2018年04月16日 15:01:43
#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。


Linux应用与ubuntu基本操作

-
  • 1970年01月01日 08:00

对链表的操作为什么要使用二级指针

拿上面的链表的插入函数举例,它使用了二级指针是因为:如果使用一级指针,当我们向空链表中插入节点时,新节点就是头节点,我们会改动头指针,但函数调用完后,head还是一个空指针。 用一级指针和二级指针的区...
  • cmj1994
  • cmj1994
  • 2015-09-29 16:26:20
  • 357

单链表的基本操作-----图形解析

首先我们需要思考的是为什么需要单链表呢? 单链表和顺序表相比较,又有什么优点呢? 在顺序表中,当我们需要头插,或者在顺序表的中间位置插入元素时,就必须将后面的元素一一后移,再将需要插入的元...
  • qq_34992845
  • qq_34992845
  • 2016-12-31 01:12:28
  • 4495

从单链表创建中的内存变化来理解结构体指针与二级指针的用法

先要有堆区和栈区的概念,然后开始: 首先我们定义一个结构体和该结构体指针 typedef struct Lnod{ //定义节点 int data;//数据 ...
  • sontaze
  • sontaze
  • 2015-07-28 00:36:40
  • 408

二级指针实现单链表的插入、删除及 linux内核源码双向链表之奇技

今天看了coolshell上关于二级指针删除单链表节点的文章。 文章中Linus 举例: 例如,我见过很多人在删除一个单项链表的时候,维护了一个”prev”表项指针,然后删除当前表项,就像这样: if...
  • gfsfg8545
  • gfsfg8545
  • 2013-12-03 13:54:27
  • 7183

错误总结:无头结点链表的创建(二级指针)

错误总结:无头结点链表的创建(二级指针)无头结点链表的创建(二级指针)这是上次关于无头链表的操作的所有操作,这些是通过传链表头指针本身(一级指针)所做的操作(其实忘记写释放链表的操作,这是个大问题,不...
  • men_wen
  • men_wen
  • 2016-10-24 22:28:32
  • 467

巧妙利用二级指针删除单链表

代码#include #include /* 单链表 */ typedef struct linkNode { int val; struct linkNode...
  • csubxb
  • csubxb
  • 2016-10-03 16:45:47
  • 558

单向链表(二级指针实现)

#include #include typedef struct newlist { int data; struct newlist *next;//next指针存储在当前的结构...
  • somestill
  • somestill
  • 2013-02-02 19:08:02
  • 2806

今天对二级指针的深入理解

#include using namespace std; int b = 100; void func(int *p) { p = &b; } int main() { int a...
  • qq_27201577
  • qq_27201577
  • 2016-05-27 17:41:22
  • 678

彻底理解链表中为何使用二级指针或者一级指针的引用

在用c/c++写数据结构程序时,链表和二叉树中经常需要用到二级指针或者一级指针的引用,那么什么时候用什么时候不用呢? 先看一个简单的c++链表操作程序: (虽然风格有点像c,不过这个是cp...
  • u012234115
  • u012234115
  • 2014-10-01 11:01:31
  • 4410
收藏助手
不良信息举报
您举报文章:单链表基本操作(可执行程序),二级指针使用必要性的初步理解
举报原因:
原因补充:

(最多只允许输入30个字)