您的位置 首页 电源

怎么界说链表结点的数据结构?

1.1.1 数据与p_next分离由于链表只关心p_next指针,因此完全没有必要在链表结点中定义数据域,那么只保留p_next指针就好了。链表结点的数据结构(slist

1.1.1 数据与p_next别离

因为链表只关怀p_next指针,因而彻底没有必要在链表结点中界说数据域,那么只保存p_next指针就好了。链表结点数据结构(slist.h)界说如下:

1 typedef struct _slist_node{

2 struct _slist_node *p_next; // 指向下一个结点的指针

3 }slist_node_t;

因为结点中没有任何数据,因而节省了内存空间,其示意图详见图3.10。


图3.10 链表示意图

当用户需求运用链表办理数据时,仅需相关数据和链表结点,最简略的方法是将数据和链表结点打包在一起。以int类型数据为例,首先将链表结点作为它的一个成员,再增加与用户相关的int类型数据,该结构体界说如下:

1 typedef struct _slist_int{

2 slist_node_t node; // 包括链表结点

3 int data; // int类型数据

4 }slist_int_t;

由此可见,不管是什么数据,链表结点只是用户数据记载的一个成员。当调用链表接口时,仅需将node的地址作为链表接口参数即可。在界说链表结点的数据结构时,因为仅删除了data成员,因而仍是能够直接运用本来的slist_add_tail()函数,办理int型数据的典范程序详见程序清单3.14。

程序清单3.14 办理int型数据的典范程序

1 #include

2 typedef struct _slist_int{

3 slist_node_t node;

4 int data;

5 }slist_int_t;

6

7 int main (void)

8 {

9 slist_node_t head = {NULL};

10 slist_int_t node1, node2, node3;

11 slist_node_t *p_tmp;

12

13 node1.data = 1;

14 slist_add_tail(head, node1.node);

15 node2.data = 2;

16 slist_add_tail(head, node2.node);

17 node3.data = 3;

18 slist_add_tail(head, node3.node);

19 p_tmp = head.p_next;

20 while (p_tmp != NULL){

21 printf(%d , ((slist_int_t *)p_tmp)->data);

22 p_tmp = p_tmp->p_next;

23 }

24 return 0;

25 }

因为用户需求初始化head为NULL,且遍历时需求操作各个结点的p_next指针。而将数据和p_next别离的意图便是使各自的功用责任别离,链表只需求关怀p_next的处理,用户只关怀数据的处理。因而,关于用户来说,链表结点的界说便是一个“黑盒子”,只能经过链表供给的接口拜访链表,不应该拜访链表结点的详细成员。

为了完结头结点的初始赋值,应该供给一个初始化函数,其本质上便是将头结点中的p_next成员设置为NULL。链表初始化函数原型为:

int slist_init (slist_node_t *p_head);

因为头结点的类型与其它一般结点的类型相同,因而很简单让用户误以为,这是初始化一切结点的函数。实际上,头结点与一般结点的意义是不相同的,因为只需获取头结点就能够遍历整个链表,因而头结点往往是被链表的具有者持有,而一般结点只是代表单一的一个结点。为了防止用户将头结点和其它结点混杂,需求再界说一个头结点类型(slist.h):

typedef slist_node_t slist_head_t;

基于此,将链表初始化函数原型(slist.h)修正为:

int slist_init (slist_head_t *p_head);

其间,p_head指向待初始化的链表头结点,slist_init()函数的完成详见程序清单3.15。

程序清单3.15 链表初始化函数

1 int slist_init (slist_head_t *p_head)

2 {

3 if (p_head == NULL){

4 return -1;

5 }

6 p_head -> p_next = NULL;

7 return 0;

8 }

在向链表增加结点前,需求初始化头结点。即:

slist_node_t head;

slist_init(head);

因为从头界说了头结点的类型,因而增加结点的函数原型也应该进行相应的修正。即:

int slist_add_tail (slist_head_t *p_head, slist_node_t *p_node);

其间,p_head指向链表头结点,p_node为新增的结点,slist_add_tail()函数的完成详见程序清单3.16。

程序清单3.16 新增结点典范程序

1 int slist_add_tail (slist_head_t *p_head, slist_node_t *p_node)

2 {

3 slist_node_t *p_tmp;

4

5 if ((p_head == NULL) || (p_node == NULL)){

6 return -1;

7 }

8 p_tmp = p_head;

9 while (p_tmp -> p_next != NULL){

10 p_tmp = p_tmp -> p_next;

11 }

12 p_tmp -> p_next = p_node;

13 p_node -> p_next = NULL;

14 return 0;

15 }

同理,当时链表的遍历选用的仍是直接拜访结点成员的方法,其中心代码如下:

1 slist_node_t *p_tmp = head.p_next;

2 while (p_tmp != NULL){

3 printf(%d , ((slist_int_t *)p_tmp)->data);

4 p_tmp = p_tmp->p_next;

5 }

这儿主要对链表作了三个操作:(1)得到第一个用户结点;(2)得到当时结点的下一个结点;(3)判别链表是否完毕,与完毕符号(NULL)比较。

基于此,将别离供给三个对应的接口来完成这些功用,防止用户直接拜访结点成员。它们的函数原型为(slist.h):

slist_node_t *slist_begin_get (slist_head_t *p_head); // 获取开端方位,第一个用户结点

slist_node_t *slist_next_get (slist_head_t *p_head, slist_node_t *p_pos);// 获取某一结点的后一结点

slist_node_t *slist_end_get (slist_head_t *p_head); // 完毕方位,尾结点下一个结点的方位

其完成代码详见程序清单3.17。

程序清单3.17 遍历相关函数完成

1 slist_node_t *slist_next_get (slist_head_t *p_head, slist_node_t *p_pos)

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/dianyuan/155583.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部