【程序人生】数据结构杂记(三)

说在前面

个人读书笔记

栈(stack)是存放数据对象的一种特殊容器,其中的数据元素按线性的逻辑次序排列,故也可定义首、末元素。不过,尽管栈结构也支持对象的插入和删除操作,但其操作的范围仅限于栈的某一特定端。也就是说,若约定新的元素只能从某一端插入其中,则反过来也只能从这一端删除已有的元素。禁止操作的另一端,称作盲端

栈中元素接受操作的次序必然始终遵循所谓“后进先出”(last-in-first-out, LIFO)的规律:
从栈结构的整个生命期来看,更晚(早)出栈的元素,应为更早(晚)入栈者;反之,更晚(早)入栈者应更早(晚)出栈。

栈中可操作的一端更多地称作栈顶(stack top),而另一无法直接操作的盲端则更多地称作栈底(stack bottom)。

栈的典型应用——逆序输出

在栈所擅长解决的典型问题中,有一类具有以下共同特征:

  • 首先,虽有明确的算法,但其解答却以线性序列的形式给出;
  • 其次,无论是递归还是迭代实现,该序列都是依逆序计算输出的;
  • 最后,输入和输出规模不确定,难以事先确定盛放输出数据的容器大小。因其特有的“后进先出”特性及其在容量方面的自适应性,使用栈来解决此类问题可谓恰到好处。

示例:进制转换

在这里插入图片描述
递归实现:
在这里插入图片描述迭代实现:
在这里插入图片描述

栈的典型应用—— 递归嵌套

示例:括号匹配

在这里插入图片描述
实际上,只要将push、pop操作分别与左、右括号相对应,则长度为n的栈混洗,必然与由n对括号组成的合法表达式彼此对应。比如,栈混洗{ 3, 2, 4, 1 }对应于表达式"( ( ( ) ) ( ) )"。按照这一理解,借助栈结构,只需扫描一趟表达式,即可在线性时间内,判定其中的括号是否匹配。

实例:
在这里插入图片描述
算法:
在这里插入图片描述

栈的典型应用——逆波兰表达式

逆波兰表达式(reverse Polish notation,,RPN)是数学表达式的一种,其语法规则可概括为:
操作符紧邻于对应的(最后一个)操作数之后。比如12+“1 2 +”即通常习惯的1+2“1 + 2”
按此规则,可递归地得到更复杂的表达式,比如RPN表达式
1 2 + 3 4 ^ *

即对应于常规的表达式
( 1 + 2 ) * 3 ^ 4

波兰表达式求值算法:
在这里插入图片描述示例:
在这里插入图片描述
在这里插入图片描述

试探回溯法

八皇后问题

在这里插入图片描述

如图,国际象棋中皇后的势力范围覆盖其所在的水平线、垂直线以及两条对角线。现考查如下问题:
nnn * n的棋盘上放置nn个皇后,如何使得她们彼此互不攻击——此时称她们构成一个可行的棋局。
对于任何整数n>=4n >= 4,这就是nn皇后问题。
由鸽巢原理可知,在nnnn列的棋盘上至多只能放置nn个皇后。反之,nn个皇后在nnn * n棋盘上的可行棋局通常也存在。

皇后类:
在这里插入图片描述
既然每行能且仅能放置一个皇后,故不妨首先将各皇后分配至每一行。然后,从空棋盘开始,逐个尝试着将她们放置到无冲突的某列。每放置好一个皇后,才继续试探下一个。若当前皇后在任何列都会造成冲突,则后续皇后的试探都必将是徒劳的,故此时应该回溯到上一皇后。

示例:
在这里插入图片描述

迷宫寻径问题

间区域限定为由nnn * n个方格组成的迷宫,除了四周的围墙,还有分布其间的若干障碍物;只能水平或垂直移动。我们的任务是,在任意指定的起始格点与目标格点之间,找出一条通路(如果的确存在)。

格点是迷宫的基本组成单位,故首先需要实现Cell类
在这里插入图片描述
可见,除了记录其位置座标外,格点还需记录其所处的状态。共有四种可能的状态:
原始可用的(AVAILABLE)、在当前路径上的(ROUTE)、所有方向均尝试失败后回溯过的(BACKTRACKED)、不可穿越的(WALL)。属于当前路径的格点,,需记录其前驱和后继格点的方向。
既然只有上、下、左、右四个连通方向,故以EAST、SOUTH、WEST和NORTH区分。
特别地,因尚未搜索到而仍处于初始 AVAILABLE状态的格点,邻格的方向都是未知的(UNKNOWN);经过回溯后处于BACKTRACKED状态的格点,与邻格之间的连通关系均已关闭,故标记为NO_WAY。

在路径试探过程中需反复确定当前位置的相邻格点
在这里插入图片描述
在这里插入图片描述
在确认某一相邻格点可用之后,算法将朝对应的方向向前试探一步,同时路径延长一个单元。
在这里插入图片描述
基于试探回溯策略实现寻径算法:
在这里插入图片描述
从算法的中间过程及最终结果都可清晰地看出,这里用以记录通路的栈结构的确相当于忒修斯手中的线绳,它确保了算法可沿着正确地方向回溯。另外,这里给所有回溯格点所做的状态标记则等效于用粉笔做的记号,正是这些标记确保了格点不致被重复搜索,从而有效地避免了沿环路的死循环现象。

队列

与栈一样,队列(queue)也是存放数据对象的一种容器,其中的数据对象也按线性的逻辑次序排列。队列结构同样支持对象的插入和删除,但两种操作的范围分别被限制于队列的两端——若约定新对象只能从某一端插入其中,则只能从另一端删除已有的元素。允许取出元素的一端称作队头(front),而允许插入元素的另一端称作队尾(rear)。

由以上的约定和限制不难看出,与栈结构恰好相反,队列中各对象的操作次序遵循所谓先进先出(first-in-first-out, FIFO)的规律:
更早(晚)出队的元素应为更早(晚)入队者,反之,更早(晚)入队者应更早(晚)出队。

队列应用——循环分配器

为在客户(client)群体中共享的某一资源(比如多个应用程序共享同一CPU),一套公平且高效的分配规则必不可少,而队列结构则非常适于定义和实现这样的一套分配规则。

队列应用——银行服务模拟

结语

如果您有修改意见或问题,欢迎留言或者通过邮箱和我联系。
手打很辛苦,如果我的文章对您有帮助,转载请注明出处。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章