二叉树
无论是哪种顺序的遍历,都属于深度优先的遍历,因此都需要用到stack,同时还需要一个游标,类似for循环的游标,只是for循环是从前往后的顺序,这里的游标是深度优先的顺序,stack则控制push和pop的顺序,以达到前中后序遍历的效果
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
//root为根节点,将遍历的结果按顺序放入List中并返回
class Solution {
//前序1
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> s = new Stack();
TreeNode p = root;
while(!s.empty()||p!=null){
//先访问父节点,同时将右子树入栈,一直往左下深入
while(p!=null){
result.add(p.val);
s.push(p.right);
p=p.left;
}
//父节点和左子树都访问过了,这时候就轮到栈顶的右子树了,也就是父->左->右的访问顺序
//对于右子树依然是从子树的根节点开始,一直往左下深入
//这里栈必定不为空,结合上面两个while条件可以很容易推断出来
p=s.pop();
}
return result;
}
//前序2
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> s = new Stack();
TreeNode p = null;
s.push(root);
while(!s.empty()){
p=s.pop();
if(p==null)
continue;
result.add(p.val);
s.push(p.right);
s.push(p.left);
}
return result;
}
//中序
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> s = new Stack();
TreeNode p = root;
while(!s.empty()||p!=null){
//仍然是一直向坐下深入,但这次push的不是p.right,而是p本身
//因为中序的顺序为左->父->右,所以需要先将左子树按序入栈,最终栈顶节点即树最左下的节点,也就是中序遍历的第一个节点
while(p!=null){
s.push(p);
p=p.left;
}
p=s.pop();
//到这里p没有左子树,但可能有右子树,按照左->父->右的顺序,左子树为空,因此访问父节点
//然后对右子树进行同样的迭代
result.add(p.val);
p=p.right;
}
return result;
}
//后序
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> s = new Stack();
TreeNode p = root,lastVisited=null;
while(!s.empty()||p!=null){
//和中序一样
while(p!=null){
s.push(p);
p=p.left;
}
//后序的顺序是先右节点再父节点,所以这里的关键就是:
//如果栈顶节点没有右子树,那么直接访问栈顶节点,也就是父节点
//或者栈顶节点有右子树,但这个右子树已经被访问过了,也是直接访问父节点
//那么如何判断右子树有没有被访问过呢?
//由于当前栈顶节点必定是上次访问节点(lastVisited中保存)的父节点(这点手动模拟push和pop的过程就知道了)
//因此只需要判断上次访问的节点是不是栈顶节点的右孩子就可以了
TreeNode cur =s.peek();
if(cur.right==null || cur.right==lastVisited){
lastVisited=s.pop();
result.add(lastVisited.val);
}else{
//走到这里说明右子树还没有访问过,则对右子树进行同样的迭代
p=cur.right;
}
}
return result;
}
}
快排
和二叉树的前序非递归遍历基本是一模一样,只需要理解栈的执行过程就好了,这个执行过程画出来就是一颗二叉树的前序遍历
public static void qSort(int[] a,int st,int ed){
//每次划分的区间,对应二叉树的节点
class Node{
int s;
int e;
public Node(int s, int e) {
this.s = s;
this.e = e;
}
}
Stack<Node> stack = new Stack<>();
int p;
//st < ed 对应前面的父节点判空
while (st < ed || !stack.empty()) {
while (st < ed) {
//对应访问父节点
p = partion(a,st, ed);
//一样的将右节点压栈
stack.push(new Node(p + 1, ed));
//对应将迭代对象换为左节点
ed = p - 1;
}
//一样的出栈
Node node = stack.pop();
st = node.s;
ed = node.e;
}
}
public int partion(int[] a, int st, int ed){
int s=st,e=ed;
int temp;
while(s<e){
while(s<e&&a[s]<=a[e])
e--;
if(s<e){
temp=a[s];
a[s]=a[e];
a[e]=temp;
s++;
}
while(s<e&&a[s]<=a[e])
s++;
if(s<e){
temp=a[s];
a[s]=a[e];
a[e]=temp;
e--;
}
}
return s;
}