链表(1)
链表(1)
链表是一组数据项的集合,其中每个数据项都是一个节点的一部分,每个节点都包含指向下一个节点的链接。在链表中各个对象按线性顺序排序。与数组不同的是,数组的线性顺序是由数组的下标决定的,而链表中的顺序是由各个对象中的指针决定的。链表可以用来简单而灵活的表示动态集合。链表是最基本的数据结构之一,也是我们经常会用到的数据结构。链表又分为单向链表,双向链表,循环链表。
单向链表
顾名思义,其链接方向是单向的,对链表的访问要通过顺序的读取从头部开始。
下面是一个简单的单向链表结构体定义
typedef struct struct_node{
int value; //数据
struct struct_node * next; //指向下一个节点的指针
}node_t;
双向链表
相对于单向链表,其多了一个指向前一个节点的指针,使其可以向前和向后访问节点,所以双向链表可以从任意一个节点开始读取链表的数据。
下面是一个简单的双向链表结构
typedef struct struct_node{
int value; //数据
struct struct_node * prev; //指向前一个节点的指针
struct struct_node * next; //指向下一个节点的指针
}node_t;
循环链表
与双向链表相比,循环链表是双向链表的头尾相连,即双向链表的最后一个节点的next指针指向头节点,头节点的prev指针指向最后一个节点。由于是一个循环链表,这个时候表头和表尾的边界条件就很重要了,
哨兵节点的引入可以简化边界条件,哨兵节点是一个哑对象,其在表头和表尾之间,即哨兵节点的next指向头节点,哨兵节点的prev指向尾节点。这样一个空的循环链表就仅包含哨兵节点,其next和prev指针都指向自己本身.
循环链表的定义同双向链表一样,同上不在赘述。
链表的基本操作
链表的基本操作包括插入操作,删除操作,搜索操作等。这里以循环链表为例来讲链表的基本操作。
#include <stdio.h>
typedef struct struct_node_s{
int value;
struct struct_node_s * prev;
struct struct_node_s * next;
}node_t;
//初始化哨兵节点
void
node_init(node_t * h){
h->prev = h;
h->next = h;
}
//创建一个节点
node_t *
node_new(int v){
node_t * p = calloc(sizeof(node_t),1);
p->value = v;
p->next = p;
p->prev = p;
return p;
}
//在添加一个节点到链表头
void
node_insert_head(node_t * h,node_t * p){
node_t * q = h->next;
h->next = p;
p->prev = h;
p->next = q;
q->prev = p;
}
//添加一个节点到表尾
void
node_insert_tail(node_t * h,node_t * p){
node_t * q = h->prev;
h->prev = p;
p->next = h;
q->next = p;
p->prev = q;
}
//删除一个节点
void
node_remove(node_t * p){
node_t * h = p->prev;
node_t * q = p->next;
h->next = q;
q->prev = h;
}
//释放链表中所有节点(哨兵节点除外)
void
node_free(node_t * h){
node_t * p = h->next;
while(p != h){
node_remove(p);
free(p);
p = h->next;
}
}
//从表头开始打印链表
void
node_print(node_t * h){
printf("list:");
node_t * p = h->next;
while(p != h){
printf("%d ",p->value);
p=p->next;
}
printf("\n");
}
int main(int argc, char *argv[])
{
printf("node_init:\n");
node_t h;
node_init(&h);
node_print(&h);
printf("node_insert_tail:\n");
for (int i = 0; i < 10; i++) {
node_t * p = node_new(i);
node_insert_tail(&h,p);
}
node_print(&h);
printf("node_insert_head value 10 :\n");
node_t * p = node_new(10);
node_insert_head(&h,p);
node_print(&h);
printf("node_remove value 10 :\n");
node_remove(p);
node_print(&h);
node_free(&h);
printf("node_free:\n");
node_print(&h);
printf("----------------end---------------\n");
return 0;
}
输出结果:
node_init: list: node_insert_tail: list:0 1 2 3 4 5 6 7 8 9 node_insert_head value 10 : list:10 0 1 2 3 4 5 6 7 8 9 node_remove value 10 : list:0 1 2 3 4 5 6 7 8 9 node_free: list: ----------------end---------------
上面的例子只是链表的一个基本用法,这里着重将循环链表拿出来举例,是因为它是我们在实际编程中用的最多的一种数据结构。但是通常在实际的项目中我们用的是另一种通用的链表结构,见链表(2)
参考文献:
- <<算法导论>>第10章,基本数据结构
程序运行环境:Ubuntu 12.04 LTS
generated by haroopad