堆栈的应用

Posted duan2

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了堆栈的应用相关的知识,希望对你有一定的参考价值。

<!doctype html>1.堆栈的应用

1 堆栈的应用

堆栈所具有的后进先出特性,使得堆栈成为程序设计中非常有用的工具。本节将讨论堆 栈应用的典型例子。

1.1进制转换

进制转换是一种常见的数值计算问题,例如将十进制数转换成八进制数。实现进制转换 的一种简单方法是重复以下两步,直到 N 等于 0。

技术图片

可以看到上述过程是从低位到高位产生 8 进制数的各个数位,而在输出时,一般来说都 是从高位到低位进行输出,这正好产生数位的顺序相反。换一个说法就是,越晚生成的数位 越早需要输出,结果数位的使用具有后出现先使用的特点,因此生成的结果数位可以使用一 个堆栈来存储,然后从栈顶开始依次输出即可得到相应的转换结果。

进制转换算法

 
 
 
xxxxxxxxxx
 
 
 
 
public void baseConversion(int i){
Stack s = new StackSLinked();
while (i>0){
s.push(i%8+"");
i = i/8;
}
while (!s.isEmpty()) System.out.print((String)s.pop());
}
 

1.2 括号匹配检测

假设表达式中包含三种括号:圆括号、方括号和花括号,并且它们可以任意相互嵌套。例如{{}}或[{()[]}]等为正确格式,而{[( ])}或({[()})等均为不正确的格式。该问题可按“期待匹配消解”的思想来设计算法,对表达式中的每一个左括号都期待一个相应的右括号与之匹配,表达式中越迟出现并且没有得到匹配的左括号期待匹配的程度越高。不是期待出现的右括号则是非法的。它具有天然的后进先出的特点。于是可以如下设计算法:算法需要一个堆栈,在读入字符的过程中,如果是左括号,则直接入栈,等待相匹配的同类右括号;若读入的是右括号,且与当前栈顶左括号匹配,则将栈顶左括号出栈,如果不匹配则属于不合法的情况。另外如果碰到一个右括号,而堆栈为空,说明没有左括号与之匹配,属于非法情况;或者字符读完,而堆栈不为空,说明有左括号没有得到匹配,也属于非法情况。当字符读完同时堆栈为空,并且在匹配过程中没有发现不匹配的情况,说明所有的括号是匹配的。

括号匹配算法

 
 
 
xxxxxxxxxx
 
 
 
 
public boolean bracketMatch(String str) {
?
Stack s = new StackSLinked();
for (int i=0;i<str.length();i++)
{
char c = str.charAt(i);
switch (c)
{
case ‘{‘:
case ‘[‘:
case ‘(‘: s.push(Integer.valueOf(c)); break;
case ‘}‘:
if (!s.isEmpty()&& ((Integer)s.pop()).intValue()==‘{‘)
break;
else return false;
case ‘]‘:
if (!s.isEmpty()&& ((Integer)s.pop()).intValue()==‘[‘)
break;
else return false;
case ‘)‘:
if (!s.isEmpty()&& ((Integer)s.pop()).intValue()==‘(‘)
break;
else return false;
}
}
if (s.isEmpty()) return true;
else return false;
}
 

1.3 迷宫求解

求解从迷宫中的起点到某个终点的路径是一个有趣的问题,如图 4-6 所示。使用计算机求解迷宫问题时,通常采用的方法是系统的尝试所有可能的路径:即从起点出发,顺着某个方向向前探索,例如向当前位置的左边探索,若当前位置除向左之外还有其他方向的没有被访问过的邻接点,则在向左探索之前,按固定的次序记录下当前位置其他可能的探索方向;若当前位置向左不能再走下去,则换到当前位置的其他方向进行探索;如果当前位置所有方向的探索均结束,却没有到达终点,则沿路返回当前位置的前一个位置,并在此位置还没有探索过的方向继续进行探索;直到所有可能的路径都被探索到为止。

技术图片

为了保证在任何位置上都能原路返回,因此需要使用一个后进先出的存储结构来保存从起点到当前位置的路径以及在路径上各位置还可能进行探索的方向。因此在迷宫问题中使用堆栈是自然的。首先在计算机中可以使用一个二维字符数组来表示图 4-6 所示的迷宫。我们使用字符‘1‘来表示迷宫中的墙体,即灰色的方块;用字符‘0‘来表示迷宫中可以通过的道路,即白色的方块。按上述方法,图 4-6 所示的迷宫可以用图 4-7(a)的二维字符数组表示。

技术图片

其次,求解迷宫的算法思想可以描述为: 初始化,将起点加入堆栈; while(堆栈不空){ 取出栈顶位置作为当前位置; 如果 当前位置是终点, 则 使用堆栈记录的路径标记从起点至终点的路径; 否则{ 按照向下、右、上、左的顺序将当前位置下一个可以探索的位置入栈; //从堆栈取出的探索方向顺序则是左、上、右、下 如果 当前位置没四周均不可通 则 当前位置出栈; } }

迷宫中当前位置的下一个可以探索的点是 未曾走到过的位置及其有待探索的下一个位置,即要求该位置不但是通道块,而且不在当前路径上,也不是曾经纳入到路径中或有待探索的通道块。这可以通过对每一个位置设置一个标志,来表明该位置是否可以作为下一个可探索的位置。为了在算法中可以对每一个位置进行操作,下面先定义迷宫中的每一个位置。

迷宫单元的定义

 
 
 
xxxxxxxxxx
 
 
 
 
private class Cell{
int x = 0; //单元所在行
int y = 0; //单元所在列
boolean visited = false; //是否访问过
char c = ‘ ‘; //是墙(‘1‘)、可通路(‘0‘)或起点到终点的路径(‘*‘)
public Cell(int x, int y, char c, boolean visited){
this.x = x; this.y = y;
this.c = c; this.visited = visited;
 }
}
 

算法 实现了迷宫中从起点到终点路径的求解

 
 
 
xxxxxxxxxx
 
 
 
 
public void mazeExit(char[][] maze,int sx,int sy, int ex,int ey){
Cell[][] cells = createMaze(maze); //创建化迷宫
printMaze(cells); //打印迷宫
Stack s = new StackSLinked(); //构造堆栈
Cell startCell = cells[sx][sy]; //起点
Cell endCell = cells[ex][ey]; //终点
s.push(startCell); //起点入栈
startCell.visited = true; //标记起点已被访问
while (!s.isEmpty()){
Cell current = (Cell)s.peek();
if (current==endCell){ //路径找到
while(!s.isEmpty()){
Cell cell = (Cell)s.pop();//沿路返回将路径上的单元设为*
cell.c = ‘*‘;
//堆栈中与 cell 相邻的单元才是路径的组成部分,除此之外,
//堆栈中还有记录下来但是未继续向下探索的单元,
//这些单元直接出栈
while(!s.isEmpty()&&!isAdjoinCell((Cell)s.peek(),cell)) s.pop();
}
System.out.println("找到从起点到终点的路径。");
printMaze(cells);
return;
} else { //如果当前位置不是终点
int x = current.x;
int y = current.y;
int count = 0;
if(isValidWayCell(cells[x+1][y])){ //向下
s.push(cells[x+1][y]); cells[x+1][y].visited = true; count++;}
if(isValidWayCell(cells[x][y+1])){ //向右
s.push(cells[x][y+1]); cells[x][y+1].visited = true; count++;}
if(isValidWayCell(cells[x-1][y])){ //向上
s.push(cells[x-1][y]); cells[x-1][y].visited = true; count++;}
if(isValidWayCell(cells[x][y-1])){ //向左
s.push(cells[x][y-1]); cells[x][y-1].visited = true; count++;}
if (count==0) s.pop();//如果是死点,出栈
}//end of if
}//end of while
76
 System.out.println("没有从起点到终点的路径。");
}
private void printMaze(Cell[][] cells){
for (int x=0;x<cells.length;x++){
for (int y=0;y<cells[x].length;y++)
System.out.print(cells[x][y].c);
System.out.println();
}
}
private boolean isAdjoinCell(Cell cell1, Cell cell2){
if (cell1.x==cell2.x&&Math.abs(cell1.y-cell2.y)<2) return true;
if (cell1.y==cell2.y&&Math.abs(cell1.x-cell2.x)<2) return true;
return false;
}
private boolean isValidWayCell(Cell cell){
return cell.c==‘0‘&&!cell.visited;
}
private Cell[][] createMaze(char[][] maze){
Cell[][] cells = new Cell[maze.length][];
for (int x=0;x<maze.length;x++){
char[] row = maze[x];
cells[x] = new Cell[row.length];
for (int y=0; y<row.length;y++)
cells[x][y] = new Cell(x,y,maze[x][y],false);
}
return cells;
}
 

还需要注意的一点是:因为迷宫四周有墙,因此从当前位置向四周探索时,数组下标不会越界;如果迷宫四周没有墙,则在向四周探索时,要验证探索位置的下标是否越界。如果以图 4-7(a)中的二维字符数组以及(8, 8)为起点坐标、以(1, 7)为终点坐标作为输入,算法 4-3 的输出如图 4-7(b)所示

以上是关于堆栈的应用的主要内容,如果未能解决你的问题,请参考以下文章

代码适用于与单个 html 文件不同的堆栈片段

Android TalkBack 和片段堆栈

后堆栈中的配置更改片段现在正在共享 FrameLayout?

在后台堆栈中保持片段视图运行

在后台堆栈中多次防止相同的片段

MvvmCross Android 片段添加到后台堆栈