文章目录
- 一、数据结构的概念
- 二、堆栈
- (3)具体eg:用堆栈实现倒序打印
- (4)我们也可以写一个递归函数做倒序打印,利用函数调用的栈帧实现后进先出
- 三、深度优先搜索DFS
- 1.用栈数据结构的DFS解决搜索迷宫问题
- 2.迷宫问题引出的总结
- (6)回溯backtrack的思想
- (7)设计算法和设计数据结构这两件工作是紧密联系的
- 3.习题
- 四、广度优先搜索BFS
- 1.用队列数据结构的BFS解决迷宫问题
- (3)BFS与DFS的区别
- 2.习题
- (1)DFS为什么不能用队列?
- 五、环形队列
- 1.含义
- 2.示意图如下
- 3.BFS用环形队列实现需要分配多少个元素的空间
一、数据结构的概念
- 数据的组织方式包含了存储方式和访问方式这两层意思,二者是紧密联系的
- 数组的各元素是一个挨一个存储的,并且每个元素的大小相同,因此数组可以提供按下标访问的方式,结构体的各成员也是一个挨一个存储的,但是每个成员的大小不同,所以只能用.运算符加成员名来访问,而不能按下标访问。
二、堆栈
(1)堆栈与数组的区别
堆栈是一组元素的集合,类似于数组,不同之处在于,数组可以按下标随机访问,这次访问 a[5] 下次可以访问 a[1]
(2)堆栈的Push和Pop操作
- 但是堆栈的访问规则被限制为Push和Pop两种操作,Push(入栈或压栈) 向栈顶添加元素,Pop(出栈或弹出) 则取出当前栈顶的元素,也就是说,只能访问栈顶元素而不能访问栈中其它元素。
- 如果所有元素的类型相同,堆栈的存储也可以用数组来实现,访问操作可以通过函数接口提供。
(3)具体eg:用堆栈实现倒序打印
解释说明如下:
(a)数组 stack 是堆栈的存储空间,变量 top 总是保存数组中栈顶的下一个元素的下标,我们说“ top 总是指向栈顶的下一个元素”,或者把 top 叫做栈顶指针(Pointer) 。
(b)Pop操作的语义是取出栈顶元素,但上例的实现其实并没有清除原来的栈顶元素,只是把 top 指针移动了一下,原来的栈顶元素仍然存在那里,这就足够了,因为此后通过Push和Pop操作不可能再访问到已经取出的元素,下次Push操作就会覆盖它。 putchar 函数的作用是把一个字符打印到屏幕上,和 printf 的 %c 作用相同
(c)布尔函数 is_empty 的作用是防止Pop操作访问越界
(4)我们也可以写一个递归函数做倒序打印,利用函数调用的栈帧实现后进先出
三、深度优先搜索DFS
1.用栈数据结构的DFS解决搜索迷宫问题
(1)定义如下
(2)代码如下:
运行结果如下:
2.迷宫问题引出的总结
(1)这次堆栈里的元素是结构体类型的,用来表示迷宫中一个点的x和y座标.
(2)我们用一个新的数据结构保存走迷宫的路线,每个走过的点都有一个前趋(Predecessor) 点,表示是从哪儿走到当前点的,比如 predecessor[4][4] 是座标为(3, 4)的点,就表示从(3, 4)走到了(4, 4),一开始 predecessor 的各元素初始化为无效座标(-1, -1)。
(3)在迷宫中探索路线的同时就把路线保存在 predecessor 数组中,已经走过的点在 maze 数组中记为2防止重复走,最后找到终点时就根据 predecessor 数组保存的路线从终点打印到起点
(4)伪代码(Pseudocode)如下
(5)DFS的特点如下:
每次探索完各个方向相邻的点之后,取其中一个相邻的点走下去,一直走到无路可走了再退回来,取另一个相邻的点再走下去。这称为深度优先搜索(DFS,Depth First Search)
(6)回溯backtrack的思想
(7)设计算法和设计数据结构这两件工作是紧密联系的
3.习题
(1)
struct point q={0,0};
for (int q.row=0;q.row<MAX_ROW;q.row++)
for (int q.col=0;q.rcol<MAX_COL;q.col++)
{
if (predecessor[p.row][p.col].row != -1)
printf("(%d, %d)\n", q.row, q.col);
}
(2)
计算出row*MAX_ROW+col,把结果保存到predecessor数组,可节省一半的存储,需要座标
时可以用/MAX_ROW和%MAX_ROW运算把row和col分离出来.
四、广度优先搜索BFS
1.用队列数据结构的BFS解决迷宫问题
(1)队列,即FIFO,其特点如下
- Enqueue(入队) 将元素添加到队尾
- Dequeue(出队) 从队头取出元素并返回
- 先来先服务,先入队的人也是先出队的
(2)程序如下
解释说明如下:
(a)BFS的数据结构如下所示:
(b)BFS的特点
广度优先是一种步步为营的策略,每次都从各个方向探索一步,将前线推进一步,图中的虚线就表示这个前线,队列中的元素总是由前线的点组成的,可见正是队列先进先出的性质使这个算法具有了广度优先的特点。
(3)BFS与DFS的区别
- 广度优先搜索BFS还有一个特点是可以找到从起点到终点的最短路径,而深度优先搜索DFS找到的不一定是最短路径。
- DFS的栈操作的 top 指针在Push时增大而在Pop时减小,因为栈空间是可以重复利用的
- BFS的队列操作的 head 、 tail 指针都在一直增大,虽然前面的元素已经出队了,但它所占的存储空间却不能重复利用。出队的元素仍然有用,保存着走过的路径和每个点的前趋,但大多数程序并不是这样使用队列的,一般情况下出队的元素就不再有保存价值了。
2.习题
(1)DFS为什么不能用队列?
因为出栈的元素被新入栈的元素覆盖了(2)
每个点最多入队一次,迷宫中有多少个点就最多需要多大的队列空间
五、环形队列
1.含义
- 把 queue 数组想像成一个圈, head 和 tail 指针仍然是一直增大的,当指到数组末尾时就自动回到数组开头。
- 就像两个人围着操场赛跑,沿着它们跑的方向看,从 head 到 tail 之间是队列的有效元素,从 tail 到 head 之间是空的存储位置,如果 head 追上 tail 就表示队列空了,如果 tail 追上 head 就表示队列的存储空间满了。
2.示意图如下
3.BFS用环形队列实现需要分配多少个元素的空间