单向链表的获取元素,删除元素,插入元素看似很难,但是当抓住要点后,一切问题都迎刃而解。我来发表一下我学习链表时的一些思路与见解,仅供学习交流之用,因水平问题,如有错误短视之处,恳请谅解。

单向链表的获取元素,删除元素,插入元素看似很难,但是当抓住要点后,一切问题都迎刃而解。我来发表一下我学习链表时的一些思路与见解,仅供学习交流之用,因水平问题,如有错误短视之处,恳请谅解。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

拿昨天的代码举例,链表节点定义为

struct note{

    char name[20];

    int heartlevel;

    struct note *next;

};


1.链表的获取
形参为:
@struct note *head 头节点
@int n 需要获取的元素的位置
返回值为:
@无
功能为:
@如果第n个元素存在,则返回第n个元素的内容,否则输出There are no data.

定义函数

void get(struct note *head,int n){

......

}

定义
 操作指针struct note *p=head;
 是否读取成功的标志flag=0;
 计数器step=0;

我们的目的是让p指向第n个节点,这里我们用一个while循环

while(step<n){

        step++;

        p=p->next;

扫描二维码关注公众号,回复: 2353727 查看本文章

        if(p==NULL){

            flag=1;

            printf("There are no data.\n");

            break;

        }

    }

当n在链表内时
 当step=n-1时,会执行
 step++;
 p=p->next;
 然后step=n,while循环终止,p即指向了节点n.
----------------------------------------->>>示意图<<<-----------------------------------------
如果n超出了链表长度,即n位置处无数据,我们可以这么考虑
 首先,p与step是严格一致的,p指向哪个节点的头指针,step就记录这个节点的位置。假如我们要获取最后一个节点的数据,假设  最后一个节点位置是m,当step=m-1时执行
   step++;
   p=p->next;
 p指向了最有一个节点的位置,step等于m,跳出循环。
 以此看来,如果n<=m,那么p就不可能是NULL。一旦p=NULL,那么即可判断输入超出,记flag标志位位1。

当flag不为1时,可以进行读取数据的操作。

printf("n=%d,creature=%s,hrearlevel=%d\n",n,p->name,p->heartlevel);

当flag为1时,数据不存在。

附上本函数全部代码

void get(struct note *head,int n){//输入一个n,返回其对应的元素,如果没有,则输出There are no creatures.

    struct note *p=head;

    int flag=0;

    int step=1;

    while(step<n){

        step++;

        p=p->next;

        if(p==NULL){

            flag=1;

            printf("There are no data.\n");

            break;

        }

    }

    if(flag==0){

        printf("n=%d,creature=%s,hrearlevel=%d\n",n,p->name,p->heartlevel);

    }

}



2.链表的删除
形参为:
@struct note *head 头节点
@int n 需要删除的元素的位置
返回值为:
@修改过后的链表头指针
功能为:
@如果第n个元素存在,则删除第n个节点,否则输出There are no data.

我们需要从何下手?我建议应该把问题分割成如下部分:


首先定义

    struct note *p=head;

    struct note *q;

    int step=1;

    int flag=0;


从链表数据方面考虑,如果链表是空表,即head==NULL,则直接输出数据不存在:

if(head==NULL){

        printf("There are no data.\n");

    }




从删除节点位置方面考虑
@如果要删除表头,即n==1,那么把第一个节点释放,然后接上第二个节点:

if(n==1){

            struct note *q=head;

            head=head->next;

            free(q);

            printf("delete OK\n");

        }


@如果删除表中或者表尾,最关键的一步是把p移动到n-1节点的位置。
这里有一个思维难点,我们继续把这个难点拆分:
==step1:先不考虑n是否超出链表长度,我们先假设n是符合要求的,那么将p移动到n-1节点的操作是:

while(step<n-1){

                p=p->next;

                step++;

            }

==step2:如果p->next是空,那么下一轮循环中p->next肯定会出错,因为NULL->next是错误的,所以我们要加上“如p->next==NULL,则循环终止”。

while(p->next&&step<n-1){

                p=p->next;

                step++;

            }

==step3:我们假设,输入的n超过了链表最大长度m,那么在step超出n前,会发生什么呢?
假设,n=m+1,这是一种临界情况;
当step==m时,p->next==NULL,上面的循环停止,step永远定格在m上,若n=m+1或者比m+1还大,则以下式子恒成立
  p->next&&step<=n-1为真
于是判断n是否在链表内的语句可以写成:

if((p->next==NULL)&&step<=n-1){

                flag=1;

                printf("There are no data.\n");

            }

flag=1标明n超过链表长度;
flag=0标明n没有超过链表长度;
----------------------------->>>下面的操作是删除中间或尾节点的过程<<<------------------------------
q=p->next;
p指向的是节点n-1的位置,q指向节点n的位置。
==若q->next!=NULL(这意味着q不是尾节点)

先把p->next跳过q指向的节点,直接指向下一个节点。然后free(q),即释放q所占的空间。

p->next=NULL;

free(q);

printf("delete OK\n");

==若q==NULL(q是尾节点)
那么q->next就根本不存在,直接将p->next赋予NULL即可。

p->next=NULL;

free(q);


完整函数代码如下:

struct note *delete(struct note *head,int n){

    struct note *p=head;

    struct note *q;

    int step=1;

    int flag=0;

    if(head==NULL){

        printf("There are no data.\n");

    }else{

        if(n==1){

            struct note *q=head;

            head=head->next;

            free(q);

            printf("delete OK\n");

        }else{

            while(p->next&&step<n-1){

                p=p->next;

                step++;

            }

            if((p->next==NULL)&&step<=n-1){

                flag=1;

                printf("There are no data.\n");

            }

            if(flag==0){

                q=p->next;

                if(q->next==NULL){

                    p->next=NULL;

                    free(q);

                    printf("delete OK\n");

                }else{

                    p->next=q->next;

                    free(q);

                    printf("delete OK\n");

                }

            }

        }

    }

    return head;

}



3.链表的添加(这里实现的是向前添加)
形参为:
@struct note *head 头节点
@int n 在n位置前添加一个节点
返回值为:
@修改过的链表头指针
功能为:
@如果第n个元素存在,则添加一个节点,否则输出There are no data.

这里我们核心思想还是先让p指向n-1的位置,但同时也要考虑n超出链表位置,空数据等情况。解决方法与链表节点的删除类似,这里不再累述;
定义:

struct note *inster(struct note *head,int n){

    struct note *p,*q,*temp;

处理空数据:

    if(head==NULL){

        printf("There are no data.\n");

把p指向n-1的位置(先不考虑在头节点前添加的情况):

int step=1;

        int flag=0;

        p=head;

        while(p->next&&step<n-1){

            p=p->next;

            step++;

        }

        if(p->next==NULL&&step<=n-1){

            flag=1;

            printf("There are no data.\n");

        }

我们可以注意到这些代码与删除链表节点的操作极其类似;

----------------------------------->>>然后我们就可以开始处理了<<<--------------------------------
 我们先用malloc函数申请一个节点,并把这个节点的头指针赋值给temp:

temp=(struct note*)malloc(sizeof(struct note));


==如果n==1(即我们要在头节点前再添加一个节点)
我们应该把temp设置为新的头节点,temp->next指向原来的头节点,然后将数据输入到这个新的节点内即可:

temp=(struct note*)malloc(sizeof(struct note));

                head=temp;

                temp->next=p;

                setbuf(stdinNULL);

                scanf("%s %d",temp->name,&temp->heartlevel);


如果n!=1
我们定义q=p->next。p指向的是n-1的位置,q指向的即为n的位置。我们现申请一个新的节点,并把这个节点的头指针赋值给temp。然后p->next=temp,temp->next=q。

代码如下:

temp=(struct note*)malloc(sizeof(struct note));

                head=temp;

                temp->next=p;

                setbuf(stdinNULL);

                scanf("%s %d",temp->name,&temp->heartlevel);


附上整个函数的代码:

struct note *inster(struct note *head,int n){

    struct note *p,*q,*temp;

    if(head==NULL){

        printf("There are no data.\n");

    }else{

        int step=1;

        int flag=0;

        p=head;

        while(p->next&&step<n-1){

            p=p->next;

            step++;

        }

        if(p->next==NULL&&step<=n-1){

            flag=1;

            printf("There are no data.\n");

        }

        if(flag==0){

            if(n!=1){

                temp=(struct note*)malloc(sizeof(struct note));

                q=p->next;

                p->next=temp;

                temp->next=q;

                setbuf(stdinNULL);

                scanf("%s %d",temp->name,&temp->heartlevel);

            }else{

                temp=(struct note*)malloc(sizeof(struct note));

                head=temp;

                temp->next=p;

                setbuf(stdinNULL);

                scanf("%s %d",temp->name,&temp->heartlevel);

            }

        }

    }

    return head;

}


ps:我开始学链表的时候发现在插入元素时scanf一直会被跳过执行,我猜是键盘缓存区的问题,于是加了个linux下的清楚键盘缓冲区代码「setbuf(stdio,NULL);」,问题果然解决。windows系统下可以使用

fflush(stdin); 或rewind(stdin);

清空输入缓存区。
-----------------------------------------------------------------------------------------------

我的见解很浅显,以上教程仅供参考。如果您发现任何错误或者有什么更好想法,欢迎与我联系。
GJK

猜你喜欢

转载自blog.csdn.net/shadowfox_/article/details/78943923