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