一、用栈实现队列
用两个栈实现队列(先进先出),队列应当支持(push、pop、peek、empty)
思路:双栈一个负责输入一个负责输出(压入操作正常,弹出时将输入栈的元素依次弹出然后压入输出栈)
private static Stack<Integer> inStack; //输入栈
private static Stack<Integer> outStack; //输出栈
public void push(int x);
{
inStack.push(x);
}
public int pop()
{
if(outStack.isEmpty())
in2Out();
return outStack.pop();
}
public int peek()
{
if(outStack.isEmpty())
in2Out();
return outStack.peek();
}
public bool empty()
{
return inStack.isEmpty() && outStack.IsEmpty();
}
public void in2Out() //循环弹栈后压栈
{
while(!inStack.isEmpty())
outStack.push(inStack.pop());
}
public int peek()
{
if(outStack.isEmpty())
in2Out();
return ouStack.peek();
}
二、字符串解码
思路:遇到 ] 时,表示其前方的字符串需要重复(截止到 [ ),重复次数为 [ 前的(最近的)数字。所以我们需要按顺序回溯之前的字符串-->使用栈进行处理
①将数据压栈直到遇到 ] 为止
②开始出栈,直到遇到第一个 [ 前一个位置
③将已出栈的元素刨去 [ ] 组成一个临时字符串(可能是多位数字的时候需要单独处理),重复n次
④将重复后的临时字符串再次压栈,然后继续步骤①
int ptr;
public String decodeString(String s)
{
LinkedList<String> stk = new LinkedList<string>();
ptr = 0;
while(ptr < s.length())
{
char cur = s.charAt(ptr);
if(Character.isDigit(cur))
{
String digits = getDigits(s); //单独处理数字
stk.addLast(digits);
}
else if(Caracter.isLetter(cur) || cur == '[') //处理普通字符/[
stk.addLast(String.valueOf(s.charAt(ptr++)));
else //处理]
{
++ptr;
LinkedList<String> sub = new LinkedList<String>(); //使用另一个list处理字符串
while(!'['.equals(stk.peekLast()))
sub.addLast(stk.removeLast());
Collections.reverse(sub);//倒置一次后出栈
stk.removeLast();
int repTime = Integer.parseInt(stk.removeLast());
StringBuffer t = new StringBuffer();
String o = getString(sub);
while(repTime-- > 0)
t.append(o); //将临时字符串重复repTime遍
stk.addLast(t.toString()); //将构造好的字符串入栈
}
}
return geString(stk);
}
三、二叉树遍历
1、中序遍历
思路:从根结点开始,先遍历左子树,后遍历右子树(表现顺序为:左->根->右)
遍历顺序G D H B A E I C F
public void accessTree(TreeNode root, List<Integer> res)
{
if(root == null)
return;
//遍历顺序:左->根->右
accessTree(root.left,res);
res.add(root.val);
accessTree(root.right,res);
}
思路2:利用栈实现循环访问
①从根结点开始依次访问左子树,若此节点的左子树不为空则将此节点压入栈中
②依次弹栈,若该节点无右子树则直接加入队列(访问结果);若其有右子树则先将该节点入队,再将其右子树压栈
③重复步骤②
public List<int> inorderTraversalWithLoop(TreeNode root)
{
List<int> res = new ArrayList<int>();
Deque <TreeNode> stack = new LinkList<TreeNode>();
while(root != null || stack.isEmpty())
{
while(root != null)
{
stack.push(root);
root = root.left;
}
root = stack.pop();
res.add(root.val);
root = root.right;
}
return res;
}
2、前序遍历
思路:输出的时候先输出根结点,再输出左子树,随后输出右子树
public void accessTree(TreeNode root, List<Integer> res)
{
if(root == null)
return;
//遍历顺序:根->左->右
res.add(root.val); //输出语句前移
accessTree(root.left,res);
accessTree(root.right,res);
}
思路2:同中序遍历,仅需将输出语句嵌入while循环内部即可
public List<int> inorderTraversalWithLoop(TreeNode root)
{
List<int> res = new ArrayList<int>();
Deque <TreeNode> stack = new LinkList<TreeNode>();
while(root != null || stack.isEmpty())
{
while(root != null)
{
res.add(root.val);
stack.push(root);
root = root.left;
}
root = stack.pop();
root = root.right;
}
return res;
}
3、后序遍历
思路:输出的时候先输出左子树,再输出右子树,随后输出根结点
public void accessTree(TreeNode root, List<Integer> res)
{
if(root == null)
return;
//遍历顺序:根->左->右
accessTree(root.left,res);
accessTree(root.right,res);
res.add(root.val); //输出语句后移
}
思路2:在中序遍历的基础上,输出左子树后需要输出右子树再输出根结点。代码需要进行一定的调整(经过根结点到达右子树)
public List<int> inorderTraversalWithLoop(TreeNode root)
{
List<int> res = new ArrayList<int>();
Deque <TreeNode> stack = new LinkList<TreeNode>();
TreeNode prevAccess = null;
while(root != null || stack.isEmpty())
{
while(root != null)
{
res.add(root.val);
stack.push(root);
root = root.left;
}
if(root.right == null || root.rigth == prevAccess) //右子树被访问过
{
root = stack.pop();
prevAccess = root;
root = null;
}
else
{
stack.push(root);
root = root.right;
}
}
return res;
}