【C语言】链表基础

链表组成

把链表看成是由一个个带数据+指向下一个节点地址指针的数据结构所组成的一串数据,这个中间的数据结构可以称为节点
链接主要要素
链表开始地址+节点+结束标志
开始地址->第一个节点的地址
结束标志(NULL)->最后一个节点的指针值

链表创建

例:创建一个链表保存数组,往数组加入数字,知道遇到-1结束输入
1、定义节点:由一个整型数值和一个自己类型的指针组成。
value用来存储数组数值
next用来存储下一个节点的开始地址或者结束标志
#cat node.h

#ifndef _NODE_H_
#define _NODE_H_

typedef struct _node {
        int value;
        struct _node *next;
} Node;

#endif

2、链表应用

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

/*
typedef struct _node {
        int value;
        struct _node *next;
} Node;
*/

int main(int argc,char const *argv[])
{
        Node * head = NULL;   //开始地址直接保存结束符,表示创建空的linked-list
        int num;
        do{
                scanf("%d",&num);
                if ( num !=-1 ){
                        // add to linked-list
                        Node *p = (Node*)malloc(sizeof(Node));
                        p->value = num;
                        p->next = NULL;   //加到链表的最后面
                        // find the last
                        Node *last = head;
                        if ( last ){   //考虑边界条件
                                while ( last->next ){   //last is not NULL continue running
                                        last = last->next;
                                }

                                // attach
                                last->next = p;   //其他num加到链表的最后面
                        }else{
                                head = p;    //加第一个num
                        }
                }
        } while ( num !=-1 );
}

改进版:
优化1:把链表的添加抽离出来成为子函数方便调用
优化2:加入一个专门保存最后一个节点地址的指针变量tail,省去每次都循环查找最后一个节点指针地址的计算

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

/*
typedef struct _node {
        int value;
        struct _node *next;
} Node;
*/
typedef struct _list{
        Node* head;
        Node* tail;   //用来保存最后一个节点的开始地址,如果只有一个节点则与head相等
} List;
void add_ntl(List *pList,int num);

int main(int argc,char const *argv[])
{
        List list;
        list.head = list.tail = NULL; //创建一个空的linked-list
        int num;
        do{
                scanf("%d",&num);
                if ( num !=-1 ){
                        add_ntl(&list,num);
                }
        } while ( num !=-1 );

        Node* p;
        for (p=list.head;p;p=p->next){
                printf("%d\n",p->value);
        }
        printf("\n");
        return 0;
}

void add_ntl(List *pList,int num){
        // add to linked-list
        Node *p = (Node*)malloc(sizeof(Node));
        p->value = num;
        p->next = NULL;   //加到链表的最后面
        if ( pList->head ){
                pList->tail->next = p;  //二级指针,表示上一个节点的next=p实现链接
                pList->tail = p;        //将tail节点重置为新节点的开始地址,实现追加
        }else{
                pList->head = p;
                pList->tail = p;
                //由于需要修改head指针,
                //所以转入head指针的指针就能通过这种方式修改head,
                //这种方式不需要返回值
        }
}

正常输入运行的结果
在这里插入图片描述
上述程序有个bug,并没有规定用户只能输入int型数据,当用户输入字符再输入-1程序无法正常结束。
在这里插入图片描述
为什么会出现这种现象,应该如何解决?

链表遍历输出

在上面小程序中加一个print_l函数遍历链表中的数据

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

/*
typedef struct _node {
        int value;
        struct _node *next;
} Node;
*/
typedef struct _list{
        Node* head;
        Node* tail;   //用来保存最后一个节点的开始地址,如果只有一个节点则与head相等
} List;
void add_ntl(List *pList,int num);
void print_l(List *pList);

int main(int argc,char const *argv[])
{
        List list;
        list.head = list.tail = NULL; //创建一个空的linked-list
        int num;
        do{
                scanf("%d",&num);
                if ( num !=-1 ){
                        add_ntl(&list,num);
                }
        } while ( num !=-1 );
        print_l(&list);
        return 0;
}

void add_ntl(List *pList,int num){
        // add to linked-list
        Node *p = (Node*)malloc(sizeof(Node));
        p->value = num;
        p->next = NULL;   //加到链表的最后面
        if ( pList->head ){
                pList->tail->next = p;  //二级指针,表示上一个节点的next=p实现链接
                pList->tail = p;        //将tail节点重置为新节点的开始地址,实现追加
        }else{
                pList->head = p;
                pList->tail = p;
                //由于需要修改head指针,
                //所以转入head指针的指针就能通过这种方式修改head,
                //这种方式不需要返回值
        }
}

void print_l(List *pList){
        Node* p;
        for (p=pList->head;p;p=p->next){
                printf("%d\n",p->value);
        }
}

运行结果展示
在这里插入图片描述

链表中节点查找和删除

代码如下

        print_l(&list);

        Node* p;
        Node* q;
        scanf("%d",&num);
        int Founded = 0;
        for ( q=NULL,p=list.head; p;q=p,p=p->next ){
                if ( p->value == num ){
                        if (q){
                                q->next = p->next;
                                //q是p的上一个节点,直接让q链接p的下一个节点实现删除
                        }else{
                                list.head = p->next;
                        }
                        free(p);    //释放p的内存
                        Founded = 1;
                        printf("找到了,干掉他,new_list:\n");
                        print_l(&list);
                        break;   //这里只删除第一个找到的数,当需要删除所有找到的数应该怎么做
                }
        }
        if ( !Founded ){
                printf("sorry,没到了\n");
        }
        return 0;

链表清除

遍历free节点直到遇到NULL

        for (p=list.head;p;q = p->next,p=q){
                free(p);
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值