认识数据结构之【链表】
认识数据结构之【链表】
认识数据结构系列往期文章:
回顾数组元素的访问
name
的第5个元素,我们可以使用下标来获取,例如 name[4]
。#include <stdio.h>
int main()
{
unsigned int numbers[] = {100, 200, 300, 400, 500};
printf("单个unsigned int所占字节数:%d\n", sizeof(unsigned int));
printf("numbers数组所占字节数:%d\n", sizeof(numbers));
printf("数组numbers的元素个数:%d\n\n", sizeof(numbers) / sizeof(unsigned int));
printf("numbers的指针地址:0x%x\n\n", &numbers);
printf("元素numbers[0]的指针地址:0x%x\n", &numbers[0]);
printf("元素numbers[1]的指针地址:0x%x\n", &numbers[1]);
printf("元素numbers[2]的指针地址:0x%x\n", &numbers[2]);
printf("元素numbers[3]的指针地址:0x%x\n", &numbers[3]);
printf("元素numbers[4]的指针地址:0x%x\n", &numbers[4]);
return 0;
}
单个unsigned int所占字节数:4
numbers数组所占字节数:20
数组numbers的元素个数:5
numbers的指针地址:0xc48df320
元素numbers[0]的指针地址:0xc48df320
元素numbers[1]的指针地址:0xc48df324
元素numbers[2]的指针地址:0xc48df328
元素numbers[3]的指针地址:0xc48df32c
元素numbers[4]的指针地址:0xc48df330
注:您自己运行的结果地址应该和上面是不一样的。
•每个 unsigned int
占四个字节•numbers
数组所占字节数 = 单个 unsigned int
所占字节 * numbers
数组元素个数•数组 numbers
的内存地址和 其第一个元素 numbers[0]
的地址相同•numbers
每两个相邻元素的地址相差一个 unsigned int
的长度
numbers
数组在内存中应该是如下图的样子:numbers[2]
的值,则可以通过如下方式获取:numbers指针地址0xc48df320 + 单个unsigned int的长度4 * 2 = 0xc48df320 + 8 = 0xc48df328
内存 [0xc48df328 , 0xc48df32c) 之间的数据就是 number[2] 的值
链表
链表的定义
链表的特点
链表的分类
线性链表(单链表)和非线性链表
•由分别表示 结点1,结点2,…,结点N的N个结点依次相链构成的链表,称为线性表的链式存储表示。由于此类链表的每个结点中只包含一个指针域,故又称单链表或线性链表。•非线性的链表,可以参见相关的其他数据结构,例如树、图。对比线性链表的定义可知,非线性链表的每个结点可能会包含多个指针域。
双向链表
循环链表
1.在建立一个循环链表时,必须使其最后一个结点的指针指向表头结点,而不是象单链表那样置为NULL。此种情况还使用于在最后一个结点后插入一个新的结点。2.在判断是否到表尾时,是判断该结点链域的值是否是表头结点,当链域值等于表头指针时,说明已到表尾。而非象单链表那样判断链域值是否为NULL。
循环链表的举例:约瑟夫还问题,感兴趣的朋友可以自己查找相关资料了解一下。
问题来源:
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
链表的操作
遍历
•对于线性链表的遍历很容易。因为每个结点上有指向下一个结点的指针域,通过指针可以很容易的遍历完整个链表。•对于非线性链表的遍历稍微复杂一些,因为每个结点指向下一个结点的指针域有多个,这些多个指针域的遍历顺序不一样则遍历出来的结果也不一样。此处不多说了,关于树的遍历可以点击文末关于树的文章链接查看•对于循环链表来说,需要注意的是如何决定终止条件,而使遍历不会变成一个死循环
插入结点
结点A
和 结点C
之间插入 结点B
,则仅仅需要把 结点A
的指针域指向 结点B
,并且把 结点B
的指针域指向 结点C
即可。删除结点
A -> B -> C
这样的链表中的 结点B
删除仅仅需要将 结点A
的指针域指向 结点C
即可。结点B
指向 结点C
的指针域可以根据你的需要来决定是否删除,不管删除与否这都不影响在原来的链表中已经移除了结点B。最后
大家下期见~(●ˇ∀ˇ●)
往期文章:
欢迎关注我的公众号“须弥零一”,原创技术文章第一时间推送。