表 (list)

表(list)是常见的数据结构。从数学上来说,表是一个有序的元素集合。在C语言的内存中,表储存为分散的节点(node)。每个节点包含有一个元素,以及一个指向下一个(或者上一个)元素的指针。如下图所示:

表: 橙色储存数据,蓝色储存指针

图中的表中有四个节点。第一个节点是头节点(head
node),这个节点不用于储存元素,只用于标明表的起始。头节点可以让我们方便的插入或者删除表的第一个元素。整个表中包含有三个元素(5, 2,
15)。每个节点都有一个指针,指向下一个节点。最后一个节点的指针为NULL,我们用“接地”来图示该指针。

表的功能与数组(array)很类似,数组也是有序的元素集合,但数组在内存中为一段连续内存,而表的每个节点占据的内存可以是离散的。在数组中,我们通过跳过固定的内存长度来寻找某个编号的元素。但在表中,我们必须沿着指针联系起的长链,遍历查询元素。此外,数组有固定的大小,表可以根据运行情况插入或者删除节点,动态的更改大小。表插入节点时需要从进程空间的堆中开辟内存空间,用以储存节点。删除节点可以将节点占据的内存归还给进程空间。

删除节点, free释放内存

插入节点,malloc开辟内存


有多种变种。上面的表中,指针指向是从前向后的,称为单向链表(linked list)。还有双向链表(double-linked
list),即每个节点增加一个指向前面一个元素的指针。以及循环链表(tabular
list),最后一个元素的指针并不为NULL,而是指向头节点。不同类型的链表有不同的应用场景。

双向链表

循环链表

双向循环链表

单向链表的C实现


一个数据结构的实现有两方面: 1. 数据结构的内存表达方式; 2.
定义在该数据结构上的操作。我们这里实现最简单的单向链表。表所支持的操作很灵活多样,我们这里定义一些最常见的操作。每个操作都写成一个函数。


/* By Vamei */
#include <stdio.h>
#include <stdlib.h>

typedef struct node *LIST;
typedef struct node *position;

/* node,节点 */
struct node {
int element;
position next;
};

/*
* operations (stereotype)
* 操作
*/
LIST init_list(void);
void delete_list(LIST);
int is_null(LIST);
void insert_node(position, int);
void delete_node(LIST, position);
position find_last(LIST);
position find_value(LIST, int);
position find_previous(LIST, position);
void print_list(LIST);

/* for testing purpose */
void main()
{
LIST L;
position np;

int i;
/* elements to be put into the list */
int a[] = {1, 3, 5, 7, 9};

/* initiate a list */
L = init_list();
print_list(L);

/* insert nodes. Insert just after head node */
for (i=4; i>=0; i--) {
insert_node(L, a[i]);
}
print_list(L);

/* delete first node with value 5 */
np = find_value(L, 5);
delete_node(L, np);
print_list(L);

/* delete list */
delete_list(L);

/* initiate a list */
L = init_list();
print_list(L);

/* insert nodes. Insert just after head node */
for (i=4; i>=0; i--) {
insert_node(L, a[i]);
}
print_list(L);

/* delete list */
delete_list(L);
}

/*
* Traverse the list and print each element
* 打印表
*/
void print_list(LIST L)
{
position np;
if(is_null(L)) {
printf("Empty List\n\n");
return;
}

np = L;
while(np->next != NULL) {
np = np->next;
printf("%p: %d \n", np, np->element);
}
printf("\n");

}

/*
* Initialize a linked list. This list has a head node
* head node doesn‘t store valid element value
* 创建表
*/
LIST init_list(void)
{
LIST L;
L = (position) malloc(sizeof(struct node));
L->next = NULL;
return L;
}

/*
* Delete all nodes in a list
* 删除表
*/
void delete_list(LIST L)
{
position np, next;

np = L;
do {
next = np->next;
free(np);
np = next;
} while(next != NULL);
}

/*
* if a list only has head node, then the list is null.
* 判断表是否为空
*/
int is_null(LIST L)
{
return ((L->next)==NULL);
}

/*
* insert a node after position np
* 在np节点之后,插入节点
*/
void insert_node(position np, int value)
{
position nodeAddr;

nodeAddr = (position) malloc(sizeof(struct node));
nodeAddr->element = value;
nodeAddr->next = np->next;
np->next = nodeAddr;
}

/*
* delete node at position np
* 删除np节点
*/
void delete_node(LIST L, position np)
{
position previous, next;
next = np->next;
previous = find_previous(L, np);
if(previous != NULL) {
previous->next = next;
free(np);
}
else {
printf("Error: np not in the list");
}
}

/*
 * find the last node of the list
* 寻找表的最后一个节点
 */
position find_last(LIST L)
{
position np;
np = L;
while(np->next != NULL) {
np = np->next;
}
return np;
}

/*
* This function serves for 2 purposes:
* 1. find previous node
* 2. return NULL if the position isn‘t in the list
* 寻找npTarget节点前面的节点
*/
position find_previous(LIST L, position npTarget)
{
position np;
np = L;
while (np->next != NULL) {
if (np->next == npTarget) return np;
np = np->next;
}
return NULL;
}

/*
* find the first node with specific value
* 查询
*/
position find_value(LIST L, int value)
{
position np;
np = L;
while (np->next != NULL) {
np = np->next;
if (np->element == value) return np;
}
return NULL;
}

在main()函数中,我们初始化表,然后插入(1, 3,
5, 7, 9)。又删除元素5。可以看到,节点零散的分布在内存中。删除节点操作不会影响其他节点的存储位置。

我们随后删除表,又重新创建表。可以看到,这次表占据内存的位置与第一次不同。

下面是main()函数的运行结果。

Empty
List

0x154d0b0:
1
0x154d090:
3
0x154d070:
5
0x154d050:
7
0x154d030:
9

0x154d0b0:
1
0x154d090:
3
0x154d050:
7
0x154d030:
9

Empty
List

0x154d070:
1
0x154d010:
3
0x154d0b0:
5
0x154d090:
7
0x154d050:
9

总结


表: 内存中离散分布的有序节点

插入,删除节点

时间: 2024-08-24 23:18:10

表 (list)的相关文章

Oracle 10g通过创建物化视图实现不同数据库间表级别的数据同步

摘自:http://blog.csdn.net/javaee_sunny/article/details/53439980 目录(?)[-] Oracle 10g 物化视图语法如下 实例演示 主要步骤 在A节点创建原表和物化视图日志 在B节点创建连接A节点的远程链接 在B节点处创建目标表和与目标表名称相同的物化视图 在B节点处刷新物化视图 升级采用存储过程定时任务JOB方式定时刷新物化视图 进一步优化 文章更新记录 参考文章 Oracle 10g 物化视图语法如下: create materia

SqlServer给一个表增加多个字段语法

添加字段语法 alter table table_name add column_name +字段类型+ 约束条件 给一个表增加多个字段: use NatureData go alter table XunHu add MaleCount varchar(50) null, FemaleCount varchar(50) null, SubadultCount varchar(50) null, LarvaeCount varchar(50) null, TraceType varchar(50

凯撒密码、GDP格式化输出、99乘法表

1凯撒密码加密plaincode=input('请输入明文:')print('密文:',end='')for i in plaincode:print(chr(ord(i)+3),end='') 2.国家名称 GDP总量(人民币亿元) 中国 ¥765,873.4375澳大利亚 ¥ 78,312.4375 print('国家名称 GDP总量(人民币亿元)')print('{0:''<12}¥{1:''>10,.2f}'.format('中国',765873.4375))print('{0:''&

c打印9*9乘法表

c打印9*9乘法表(这里提供了两种打印方法).乘号两边有规律可寻,其左边递减,右边递增. 1 #include <stdio.h> 2 int main() 3 { 4 int i, j, result; 5 for (i = 9; i >= 1; i--) //外层循环,从9开始,每次循环自减1 6 { 7 for (j = 1; j <= i; j++) //内层循环,从1开始,每次循环自加1 8 { 9 result = i*j; 10 printf("%d*%d=

MySQL(九)之数据表的查询详解(SELECT语法)二

上一篇讲了比较简单的单表查询以及MySQL的组函数,这一篇给大家分享一点比较难得知识了,关于多表查询,子查询,左连接,外连接等等.希望大家能都得到帮助! 在开始之前因为要多表查询,所以搭建好环境: 1)创建数据表suppliers 前面已经有一张表是book表,我们在建立一张suppliers(供应商)表和前面的book表对应. 也就是说 让book中s_id字段值指向suppliers的主键值,创建一个外键约束关系. 其实这里并没有达到真正的外键约束关系,只是模拟,让fruits中的s_id中

C# 远程服务器 安装、卸载 Windows 服务,读取远程注册表,关闭杀掉远程进程

这里安装windows服务我们用sc命令,这里需要远程服务器IP,服务名称.显示名称.描述以及执行文件,安装后需要验证服务是否安装成功,验证方法可以直接调用ServiceController来查询服务,也可以通过远程注册表来查找服务的执行文件:那么卸载文件我们也就用SC命令了,卸载后需要检测是否卸载成功,修改显示名称和描述也用sc命令.至于停止和启动Windows服务我们可以用sc命令也可以用ServiceController的API,当停止失败的时候我们会强制杀掉远程进程,在卸载windows

数据库表:区域表system_district

区域表[system_district] 数据模型 建表脚本 -- ---------------------------- -- Table structure for system_district -- ---------------------------- DROP TABLE IF EXISTS `system_district`; CREATE TABLE `system_district` (   `district_id` bigint(20) unsigned NOT NUL

SQL Server 里面sql语句备份表

select * into 新表名字  from XXXX 新表名字中最好加入日期  提醒自己哪天创建 var code = $("#editWin input[name=Code]").val();        var re = new RegExp(/^[0-9a-zA-Z]*$/g);        if (!re.test(code)) {            Msg.show("请输入英文或数字", 9);            return fals

SQL基础:数据表的创建

1. 先选择创建表所在的数据库 2. 创建表 3. 查看表是否创建成功 4. 主键:要求主键列的数据唯一,且不允许为空.主键能够唯一的标识表中的每一条记录,可以结合外键来定义不同数据表之间的关系,并且可以加快数据库查询的速度. 主键分为:单字段主键和多字段联合主键. 4.1 单字段主键 4.1.1 定义方式一:在定义列的同时指定主键 4.1.2 定义方式二:在定义完所有的列之后指定主键 4.2 多字段联合主键 5. 使用外键约束 外键用来在两个表之间建立连接,可以是一列或多列.一个表的外键可以是

MySQL分库分表方案

1. MySQL分库分表方案 1.1. 问题: 1.2. 回答: 1.2.1. 最好的切分MySQL的方式就是:除非万不得已,否则不要去干它. 1.2.2. 你的SQL语句不再是声明式的(declarative) 1.2.3. 你招致了大量的网络延时 1.2.4. 你失去了SQL的许多强大能力 1.2.5. MySQL没有API保证异步查询返回顺序结果 1.2.6. 总结 MySQL分库分表方案 翻译一个stackoverflow上的问答,关于分库分表的缺点的,原文链接: MySQL shard