c语言结构体链表

原文链接:http://zhina123.blog.163.com/blog/static/417895782012106036289/

引用自身的结构体,一个结构体中有一个或多个成员的基类型就是本结构体类型时,说明这个结构体可以引用自己,所以称作引用自身的结构体。

例如下面的结构体:

 

struct link{ char ch; struct link *p} a;

 

p是一个可以指向struct link类型变量的指针成员,这样,a.p=&a就是合法的表达式。那么,这有什么意义呢?

这样的意义就是我们可以把分散存储的数据项,用一个结构体成员链接起来(当然这也耗费了那个存储指针的内存空间)看下面的程序

 

#include <stdio.h>
struct node{
 int data;
 struct node *next;
};
main(){
 struct node a,b,c,*p;//我们假设,这样声明的结构体变量a 、b、c在内存中并不是相临的
 a.data=10;  b.data=20; c.data=30; 

 a.next=&b; b.next=&c; c.next='\0';
 p=&a;
 //结构体变量a地址赋给p 
 while(p){
 printf(" %d ",p->data);
 //每输出一个值后,p自动指向本项的链接项 
/*这样有了一个单独保持链接的成员就把不相临的存储单元在逻辑上存储在了一起*/ 

p=p->next;
 }
 printf("\n");
}

 

这样的相链的数据存储形式称为链表!上面形成链表的方法是人为定义的,在程序执行过程中,不会生成新的存储单元,所以也称为“静态链表”

下面看一种更方法使用的“动态链表”

前面的日志中提到了C语言动态存储分配提供了“按需分配内存”的实现方法,有一个问题是,如果多次动态分配存储单元,各存储单元的地址不是一定是连续的,而所需要处理的批量数据往往是一个整体,各数据之间存在着顺序关系,这样,我们可以利用上面的链表把动态得到的存储单元在逻辑上(而不是在物理上)连接起来,就可以实现我们需要的顺序关系了。这时,是把链表技术与动态存储分配结合了起来,所以这里我们给了链表一个新的名词叫做“动态链表”

很自然,我们上面例子中的链表只有一个指向后面数据项的指针,如果有两个呢?一个指后,一个指前,这样就会出现“双向链表”与“单向链表”的区别

下面我们主要看单向链表

事实上,单身链表中的每个存储单元的数据(也就是结构体)的格式类型是相同的(包括数据成员和指针成员)

如下:

 

struct abc{int data,……struct abc *next;};

 

与单向链表有关的算法有以下几类:

建立链表  输出结点数据   插入结点数据 删除结点数据

下面这个程序是示例

 

#include <stdio.h>
#include <stdlib.h>
struct slist{ int data; struct slist *next;};
typedef struct slist SLIST;

SLIST *creat_slist(){
/*该函数的作用是创建动态链表,函数的返回值是结构体指针变量,也就是新创建的动态链表的头结点,需要注意的是,这里的头结点没有数据data,只有指针next指向动态链表的第一个结点*/
 int c;

/*用来临时存储结构体中的data*/ 
 SLIST *h,*s,*r;

/*声明工作指针*/
 h=(SLIST *)malloc(sizeof(SLIST));
 /*动态获取一个结构体的存储空间*/
 r=h;

/*结构体指针r用来在下面的循环中使用h指针不变*/
 scanf("%d",&c);

/*得到一个结构体成员int data数据*/ 
 while(c!=-1){
 /*如果上面得到的int data数据不为-1就进入循环 */
  s=(SLIST *)malloc(sizeof(SLIST));
 /*循环中用结构体指针s获取结点*/
  s->data=c;
 /*s成员data为c注意第一次进入循环时,这个c是在循环外部上面输入的*/
     r->next=s;
   /*r指针本来与h相同,r的成员next在进入循环前是没有指向的现在进入了循环得到了一个结点,就把r的next指向新的结点,这样就使h头结点指向了s*/
  r=s;
 /*r依次与最新的结点相同,为了依次用最新的存储空间的next指向下一个获得的结点*/
  scanf("%d",&c);
 }
 r->next='\0';
 /*退出循环后,r指向最后一个结点,而这个最后结点的成员next要指向'\0'*/
 return h;
 /*返回动态链表的头结点指针*/
}
void print_slist(SLIST *head){
/*该函数是依次输出动态链表的结点,参数是结构体指针变量,也就是
动态链表的头结点*/ 
 SLIST *p;
 /*声明一个工作指针,因为head不能自己往后依次移动,所以用指针
 p实现*/
 p=head->next;
 if(p=='\0')printf("Linklist is null!\n");/*空链表*/ 
 else{printf("head");/*非空链表*/ 
        do{ printf("->%d",p->data);
        p=p->next;}while(p!='\0');
 printf("->end\n");
 }
 
}

SLIST *insert_snode(SLIST *head,int x,int y){
/*该函数实现在链表值为x的结点前面插入一个结点,值为y参数有三个链表头结点,值x,y*/ 
 SLIST *s,*p,*q;
 /*定义工作指针*/
 s=(SLIST *)malloc(sizeof(SLIST));
 s->data=y;
 /*上面这两句先获取了一个结构体动态存储空间,并给其成员data赋值为y,但此时该空间并未成为链表的一个结点*/
 q=head;
 p=head->next;
 /*上面两句初始化工作指针,就是把工作指针q与head相同,p指向head的next*/
 while((p!='\0')&&(p->data!=x)){
  /*这个循环是供查找到值为x的结点所在位置的,需要注意的是这里的并的两个条件位置不能变,因为只有在p指向不为空的时候才能讨论其data成员值是否为x,否则如果p的指向是空,程序确要先判断p指向的data是不是x这样会发生地址访问错误,因为p本不指向结点,也就无从谈成员data,所以判断p指向的data是不是x是不对的*/
  q=p;p=p->next;
  /*满足循环条件的话,p与q依次后移直到找到值为x的结点或到了链表的尾部*/
  }
  s->next=p;
  /*在p指向的结点前面插入,所以新的结点的next指向p*/
  q->next=s;
  /*q-next本指向p结点,现在令其指向s结点,实现了插入*/
  
 return head;/*头指针并未变化,返回即可*/
}

SLIST *insert_bnode(SLIST *head,int x,int y){
/*该函数实现在链表值为x的结点后面插入一个结点,值为y参数有三个链表头结点,值x,y*/ 
 SLIST *s,*p,*q;
 /*定义工作指针*/
 s=(SLIST *)malloc(sizeof(SLIST));
 s->data=y;
 /*上面这两句先获取了一个结构体动态存储空间,并给其成员data赋值为y,但此时该空间并未成为链表的一个结点*/
 q=head;
 p=head->next;
 /*上面两句初始化工作指针,就是把工作指针q与head相同,p指向head的next*/
 while((p!='\0')&&(p->data!=x)){
  /*这个循环是供查找到值为x的结点所在位置的,需要注意的是这里的并的两个条件位置不能变,因为只有在p指向不为空的时候才能讨论其data成员值是否为x,否则如果p的指向是空,程序确要先判断p指向的data是不是x这样会发生地址访问错误,因为p本不指向结点,也就无从谈成员data,所以判断p指向的data是不是x是不对的*/
  q=p;p=p->next;
  /*满足循环条件的话,p与q依次后移直到找到值为x的结点或到了链表的尾部*/
  }
  s->next=p->next;
  /*在p指向的结点后面插入,所以新的结点的next指向p*/
  p->next=s;
  /*p-next本指向p后面的结点,现在令其指向s结点,实现了后插入*/
  
 return head;/*头指针并未变化,返回即可*/
}

SLIST *del_node(SLIST *head,int x){
/*该函数实现删除链表值为x的结点,参数有两个链表头结点,值x */ 
 SLIST *s,*p,*q;
 /*定义工作指针*/
 q=head;
 p=head->next;
 /*上面两句初始化工作指针,就是把工作指针q与head相同, p指向head的next*/
 while((p!='\0')&&(p->data!=x)){
  /*这个循环是供查找到值为x的结点所在位置的,需要注意的是这里的并的两个条件位置不能变,因为只有在p指向不为空的时候才能讨论其data成员值是否为x,否则如果p的指向是空,程序确要先判断p指向的data是不是x这样会发生地址访问错误,因为p本不指向结点,也就无从谈成员data,所以判断p指向的data是不是x是不对的*/
  q=p;p=p->next;
  /*满足循环条件的话,p与q依次后移直到找到值为x的结点或到了链表的尾部*/
  }
  q->next=p->next;
  /*把q->next置成p->next,*/
  free(p);
  /*释放p的存储空间,实现删除*/
 return head;

/*头指针并未变化,返回即可*/
}

main(){
 SLIST *head; int x,y;
 head=creat_slist();//创建链表函数
 print_slist(head);
 printf("please input x\n");  scanf("%d",&x);
 printf("please input y\n");  scanf("%d",&y);
 head=insert_snode(head,x,y);//结点前插入函数
 print_slist(head);
 printf("please input x\n");  scanf("%d",&x);
 printf("please input y\n");   scanf("%d",&y);
 head=insert_bnode(head,x,y);//结点后插入函数
 print_slist(head);
 printf("please input x\n"); scanf("%d",&x);
 head=del_node(head,x);//删除结点函数

 print_slist(head);
}

 

转载于:https://www.cnblogs.com/hubery/p/4494229.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值