题目
将一个Stack里面的所有元素反转,也就是逆序排列,要求不能使用其它容器。
分析
(注:如果不想看分析过程,在最下面有完整的代码。)
如果能使用另外一个Stack,那就简单了,只需将第一个Stack中的元素依次pop出来,并push到另一个Stack里面即可。
while (!stack1.empty())
stack2.push(stack1.pop());
但是题目要求不能使用其它容器,这就相当有难度了。
假设Stack里面的元素是 [1, 2, 3, 4, 5]
,想要变为 [5, 4, 3, 2, 1]
,则必须在Stack为空的情况下,依次push 5、4、3、2、1才行。但显然只能先把5、4、3、2、1都依次pop出来,才能push 5
,因此,一定需要一些变量来记录之前pop出来的元素。
问题是,既不知道Stack的size,又不能用容器,那怎么知道该用几个变量呢?看来只能使用递归了,把变量定义放在递归函数里面,随着递归调用,就能产生N个变量了。
先写一个pop栈底元素的方法试试看:
public Integer popLast() {
Integer result = null;
if (stack.empty())
throw new RuntimeException("Stack is empty");
Integer e1 = stack.pop();
if (stack.empty())
result = e1;
else {
Integer e2 = popLast();
stack.push(e1);
result = e2;
}
return result;
}
为什么要把 e1
push回去呢?因为没地方放置 e1
呀!如果不push回去的话,那么调用该方法后,栈底元素(即 1
)是取到了,但是前面的元素都丢了。
先拿一个只包含2个元素的Stack试试看:
public static void main(String[] args) {
Test0417 myStack = new Test0417();
myStack.push(1);
myStack.push(2);
// myStack.push(3);
// myStack.push(4);
// myStack.push(5);
System.out.println(myStack);
Integer e = myStack.popLast();
myStack.push(e);
System.out.println(myStack);
}
运行结果如下:
stack=[1, 2]
stack=[2, 1]
OK,顺利的完成了逆序。
但是,对于超过2个元素的Stack,是有问题的,比如,把上面的Stack改为5个元素,运行结果如下:
stack=[1, 2, 3, 4, 5]
stack=[2, 3, 4, 5, 1]
可见,只是把 1
和“所有其它元素”做了交换,并没有实现把其它元素逆序排列。
再看一下 popLast()
方法的实现,其中的 stack.push(e1)
,刚才说了,不push不行,但是push了似乎也不正确,因为 3
比 2
先pop出来,所以 2
比 3
先push回去(注意:在递归方法里,对于递归调用之后的action,其运行顺序为:内嵌的action在先,外部的action在后),这就不对了,需要先push 3
才对。
换个思路,我们来看看递归结束时,我们得到了什么:
- 栈底元素
1
- Stack
[2, 3, 4, 5]
既然如此,那么在此基础上,再做一次pop last,就能取出栈底元素 2
和 Stack [3, 4, 5]
了。以此类推,就能依次得到1、2、3、4、5和空的Stack了。
System.out.println(myStack);
while (!myStack.empty()) {
Integer e = myStack.popLast();
System.out.println(e);
}
System.out.println(myStack);
运行结果如下:
stack=[1, 2, 3, 4, 5]
1
2
3
4
5
stack=[]
(题外话,我们现在用Stack实现了一个Queue!)
我们需要持续的pop last,但是pop last出来的元素要晚点再push回去。
回顾前面提到的原则:
在递归方法里,对于递归调用之后的action,其运行顺序为:内嵌的action在先,外部的action在后。
因此,此处需要再用一个递归方法:
public void reverse() {
if (!myStack.empty()) {
Integer e = popLast();
reverse();
myStack.push(e);
}
}
代码
完整的代码如下:
package package0417;
import java.util.Stack;
import java.util.stream.IntStream;
public class ReversedStack {
private Stack<Integer> stack = new Stack<Integer>();
public Integer popLast() {
Integer result = null;
if (stack.empty())
throw new RuntimeException("Stack is empty");
Integer e1 = stack.pop();
if (stack.empty())
result = e1;
else {
Integer e2 = popLast();
stack.push(e1);
result = e2;
}
return result;
}
public void reverse() {
if (!stack.empty()) {
Integer e = popLast();
reverse();
push(e);
}
}
public Integer push(Integer e) {
return stack.push(e);
}
public Integer pop() {
return stack.pop();
}
public Integer peek() {
return stack.peek();
}
@Override
public String toString() {
return "stack=" + stack;
}
public static void main(String[] args) {
ReversedStack rs = new ReversedStack();
rs.push(1);
rs.push(2);
rs.push(3);
rs.push(4);
rs.push(5);
System.out.println(rs);
rs.reverse();
System.out.println(rs);
}
}
运行结果如下:
stack=[1, 2, 3, 4, 5]
stack=[5, 4, 3, 2, 1]
顺利实现了Stack的逆序排列。
注:由于刚才提到了“意外的用Stack实现了Queue”,所以 popLast()
是public方法,如果不需要对外暴露此方法,也可以将其设置为private方法。
另外,可以在每次迭代时,打印出Stack,以帮助我们理解迭代的逻辑。完整的代码如下:
package package0417;
import java.util.Stack;
import java.util.stream.IntStream;
public class ReversedStack {
private Stack<Integer> stack = new Stack<Integer>();
private int i = 0;
private int j = 0;
public Integer popLast() {
j++;
StringBuffer space = new StringBuffer();
IntStream.range(0, j).forEach(e -> space.append("\t"));
System.out.println(space + "before popLast: " + this);
Integer result = null;
if (stack.empty())
throw new RuntimeException("Stack is empty");
Integer e1 = stack.pop();
if (stack.empty())
result = e1;
else {
Integer e2 = popLast();
stack.push(e1);
result = e2;
}
System.out.println(space + "after popLast: result = " + result + ", " + this);
return result;
}
public void reverse() {
i++;
StringBuffer space = new StringBuffer();
IntStream.range(0, i).forEach(e -> space.append("\t"));
System.out.println();
System.out.println(space + "before reverse: " + this);
if (!stack.empty()) {
j = i;
Integer e = popLast();
reverse();
push(e);
}
System.out.println(space + "after reverse: " + this);
}
public Integer push(Integer e) {
return stack.push(e);
}
public Integer pop() {
return stack.pop();
}
public Integer peek() {
return stack.peek();
}
@Override
public String toString() {
return "stack=" + stack;
}
public static void main(String[] args) {
ReversedStack rs = new ReversedStack();
rs.push(1);
rs.push(2);
rs.push(3);
rs.push(4);
rs.push(5);
System.out.println(rs);
rs.reverse();
System.out.println(rs);
}
}
运行结果如下:
stack=[1, 2, 3, 4, 5]
before reverse: stack=[1, 2, 3, 4, 5]
before popLast: stack=[1, 2, 3, 4, 5]
before popLast: stack=[1, 2, 3, 4]
before popLast: stack=[1, 2, 3]
before popLast: stack=[1, 2]
before popLast: stack=[1]
after popLast: result = 1, stack=[]
after popLast: result = 1, stack=[2]
after popLast: result = 1, stack=[2, 3]
after popLast: result = 1, stack=[2, 3, 4]
after popLast: result = 1, stack=[2, 3, 4, 5]
before reverse: stack=[2, 3, 4, 5]
before popLast: stack=[2, 3, 4, 5]
before popLast: stack=[2, 3, 4]
before popLast: stack=[2, 3]
before popLast: stack=[2]
after popLast: result = 2, stack=[]
after popLast: result = 2, stack=[3]
after popLast: result = 2, stack=[3, 4]
after popLast: result = 2, stack=[3, 4, 5]
before reverse: stack=[3, 4, 5]
before popLast: stack=[3, 4, 5]
before popLast: stack=[3, 4]
before popLast: stack=[3]
after popLast: result = 3, stack=[]
after popLast: result = 3, stack=[4]
after popLast: result = 3, stack=[4, 5]
before reverse: stack=[4, 5]
before popLast: stack=[4, 5]
before popLast: stack=[4]
after popLast: result = 4, stack=[]
after popLast: result = 4, stack=[5]
before reverse: stack=[5]
before popLast: stack=[5]
after popLast: result = 5, stack=[]
before reverse: stack=[]
after reverse: stack=[]
after reverse: stack=[5]
after reverse: stack=[5, 4]
after reverse: stack=[5, 4, 3]
after reverse: stack=[5, 4, 3, 2]
after reverse: stack=[5, 4, 3, 2, 1]
stack=[5, 4, 3, 2, 1]
其中的缩进代表了迭代的层次关系。通过查看每次迭代前后的Stack,可以帮助我们理解代码逻辑。
参考
http://www.broadview.com.cn/book/4889