欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C语言 链表的创建,以及节点的增加和删除

程序员文章站 2022-05-24 17:49:50
第一次写博客,如有错误还请指正………… 今天下午倒腾了一下的链表,感觉链表就是一个小型的,用于简单的小程序的用户信息存储和管理还是很方便的。...

第一次写博客,如有错误还请指正…………
今天下午倒腾了一下的链表,感觉链表就是一个小型的,用于简单的小程序的用户信息存储和管理还是很方便的。
下面为方便起见,以学生信息为例:
链表采用了动态分配的办法为一个结构分配内存空间。每一次分配一块空间可用来存放一个学生的数据,我们可称之为一个结点。有多少个学生就应该申请分配多少块内存空间,也就是说要建立多少个结点。当然用结构数组也可以完成上述工作,但如果预先不能准确把握学生人数,也就无法确定数组大小。而且当学生留级、退学之后也不能把该元素占用的空间从数组中释放出来。用动态存储的方法可以很好地解决这些问题。有一个学生就分配一个结点,无须预先确定学生的准确人数,某学生退学,可删去该结点,并释放该结点占用的存储空间。从而节约了宝贵的内存资源。另一方面,用数组的方法必须占用一块连续的内存区域。而使用动态分配时,每个结点之间可以是不连续的(结点内是连续的)。结点之间的联系可以用指针实现。 即在结点结构中定义一个成员项用来存放下一结点的首地址,这个用于存放地址的成员,常把它称为指针域。
可在第一个结点的指针域内存入第二个结点的首地址,在第二个结点的指针域内又存放第三个结点的首地址,如此串连下去直到最后一个结点。最后一个结点因无后续结点连接,其指针域可赋为0。这样一种连接方式,在数据结构中称为“链表”。
下面给出一个简单的例子,包含链表的创建,

#include
#include


#define len sizeof (struct stu)
/*
head为(伪)头指针,pbefore为指向两相邻结点的前一结点的指针变量。pafter为后一结点的指针变量。
*/

//定义学生个数
int n=4;
//定义链表结构体
typedef struct stu{
    int num;
    int age;
    struct stu *next;
}type;

/*定义删除函数:删除所选中的节点*/
void delete(type *head,int number)
{
    int i;
    type *list=head;
    type *tp=null;
    //这里循环n+1次,是因为包含头节点在内总共有n+1个节点
    for(i=0;i<(n+1);i++){
        printf("the next:%ld\n",(long)list->next);

        if(list->num==number){
            if(list->next==0){
                tp->next=0;
                break;
            }else{
                tp->next=list->next;
                break;
            }
        }
        tp=list;// 临时指针指向的是前一节点
        list=list->next; //继续判断下一节点
    }

}
/*定义查询函数:显示链表中所有信息*/
void query(type *head)
{
    type *list=head;
    while(list)
    {
        printf("%d,%d\n",list->num,list->age);
        printf("the next:%ld\n",(long)list->next);
        list=list->next;
    }

}



int main()
{
    //这是定义的真正的头节点
    struct stu *head;
    head->num=0;
    head->age=0;

    struct stu *thead,*pbefore,*pafter;
    int i;
    for(i=0;inum,&pafter->age);
        if(i==0) pbefore=thead=pafter; //第一次循环,直接设置头节点
        else pbefore->next=pafter;
        //这里的pbefore其实是上一轮循环的after赋予的,所以是 “前一个节点”
        //注意:这里改动的不是pbefore,而是pbefore指向的内存里的数据
        pafter->next=null; // 将最后的指针域清零,赋给pbefore,即前节点
        pbefore=pafter;// 这里pbefore指向了新的内存,也就是下一个节点
                       // 强调一下,pbefore只是一个指针
    }
    head->next=thead;

    query(head);

    int number;
    printf("you want to delete:");
    scanf("%d",&number);

    delete(head,number);
    query(head);


    return 0;
}

以上程序包含了一个节点数为n+1的链表(n个学生节点和1个头节点,因为头节点没有数据域)。如果没有提前定义头节点,则第一个学生的节点则为头节点,这样就导致这个头节点无法删除。C语言 链表的创建,以及节点的增加和删除
上图可以看出,如果直接删除了头节点,会发生段错误。

现在我们在链表的尾部增加一个节点

#include
#include


#define len sizeof (struct stu)



typedef struct stu{
    int num;
    int age;
    struct stu *next;
}type;


int n=4;

/*增加节点函数:返回新的尾部节点地址*/
type *add(type* tail)
{
    type *new;
     new=(type*) malloc(len); 

    printf("input the new num and age\n");
    scanf("%d%d",&new->num,&new->age);

    tail->next=new;
    printf("tail:%ld",(long)tail);
    new->next=null;//缺了这一句,新的节点数据就显示不出了
    tail=new;
    printf("new tail:%ld",(long)tail);

    return tail;


}

/*删除节点函数*/
void delete(type *head,type* tail,int number)
{
    int i;
    type *list=head;
    type *tp=null;
    for(i=0;i<(n+1);i++){
    //  printf("the next:%ld\n",(long)list->next);
        if(list->num==number){
            if(list->next==0){
                tp->next=0;
                tail=tp;//若尾部节点被删除,则更新尾部节点
                break;
            }else{
                tp->next=list->next;
                break;
            }
        }
        tp=list;// 临时指针指向的是前一节点
        list=list->next; //继续判断下一节点
    }

}

/*查询函数:显示所有节点数据*/
void query(type *head)
{
    type *list=head;
    while(list)
    {
        printf("%d,%d\n",list->num,list->age);
        //printf("the next:%ld\n",(long)list->next);
        list=list->next;
    }

}



int main()
{
    //初始化头节点以及尾部节点
    struct stu *head; 
    struct stu *tail; 

    head->num=0;
    head->age=0;

    struct stu *thead,*pbefore,*pafter;
    int i;
    for(i=0;inum,&pafter->age);
        if(i==0) pbefore=thead=pafter; 
        else pbefore->next=pafter; 
        pafter->next=null; 
        pbefore=pafter;
    }

    tail=pafter;
    printf(" tail:%ld",(long)tail);

    head->next=thead;

    query(head);

    int number;
    printf("you want to delete:");
    scanf("%d",&number);

    delete(head,tail,number);
    query(head);

    tail=add(tail);
    query(head);

    tail=add(tail);
    query(head);


    return 0;
}

这里我们需要定义尾部节点,并且增加后要对尾部节点进行更新,方便再次在链表后面插入节点,注意:不要忘了给新的节点分配内存,否则会报段错误,因为你给并没有分配内存的结构体赋值了。
new=(type*) malloc(len);
另外不要忘了给新的尾部节点的指针域设置为null。
new->next=null;
以上是增加了两个节点的例子,运行结果如下
C语言 链表的创建,以及节点的增加和删除
可以看到,增加的节点已经显示出来了。