二叉樹的非遞迴先序,中序,後序遍歷
前幾天面試美團的java後臺崗位,第一題就是手寫二叉樹非遞迴先序遍歷,當時我就不樂意了。然後其實能想出來的,但是沒私底下實現過,還真沒把握給面試官,最後掛了。所以痛定思痛,我們來手寫一下二叉樹的非遞迴先序,中序和後序遍歷,並對其中的邏輯部分進行步驟的講解,我覺得一切的東西你只要給他賦予一定的規則,那麼它就沒那麼難了(後面的部分不適合對資料結構不熟悉的同學)
/** * 非遞迴先序遍歷 * @param root */ public void depthOrderTraversal(BinTree root){ if(root==null){ System.out.println("null"); return; } ArrayDeque<BinTree> stack=new ArrayDeque<BinTree>(); stack.push(root); while(stack.isEmpty()==false){ BinTree node = stack.pop(); System.out.print(node.getData()+" "); if(node.rChild!=null) stack.push(node.rChild); if(node.lChild!=null) stack.push(node.lChild); } }
非遞迴先序遍歷需要借用棧(後進先出)這種資料結構,下面我梳理一下這個過程,分步驟ABC
A:將根節點傳入進方法中,也就是當作引數傳遞過來,你可以在節點類中寫一個getRoot()方法,返回他的節點物件
B:判斷節點是否為空,為空就返回return;(什麼都不用返回)。因為return;這樣這個方法就結束了;((●'◡'●))
接下來的C-D我們講解一下從棧到實現先序遍歷的整個過程的邏輯梳理
C:建立一個棧,並將根節點率先加入到棧中(這裡為什麼用ArrayDeque來建立棧呢?因為網上說用做棧的時候效能比Stack好)
D:這裡我們思考一個問題,到底用什麼來做迴圈的條件,首先我們現在有兩個東西在手裡,一個棧,一個根節點資料,能改變的貌似也只有棧的size()了,所以我們通過判斷棧是否為空來迴圈我們的先序遍歷的邏輯部分
E-G我們就用三句話來簡短的敘述一下過程
E:棧頂出棧並輸出
F:“出棧”右孩子入棧(E中出棧的那個元素稱為出棧)
G:”出棧“左孩子入棧
然後E-F整個過程包括在while迴圈當中,當棧中元素全部出棧的時候迴圈結束。
接下來我們講一下二叉樹非遞迴中序遍歷
/** * 非遞迴中序遍歷 * @param root */ public void depthMidTraversal(BinTree root) { Stack<BinTree> s = new Stack<BinTree>(); BinTree p = root; while(p != null || !s.empty()) { while (p != null) { s.push(p); p = p.lChild; } p = s.pop(); System.out.print(p.getData()+" "); if (p.rChild != null) { p = p.rChild; } else p = null; } }
下面我還是通過A-F的步驟講解一下非遞迴中序遍歷的過程
A:首先建立一個棧s (BinTree p = root 方便後序編寫,我沒試過直接用root來寫行不行,我感覺應該按道理,邏輯來說應該不行)
B:確定迴圈條件,p節點不為空或者棧不為空
C-F:中間這個步驟我統一說一下,其實我支援大家通過Dubug來看一下這個過程,我想了想要用語言闡述太為難我這個理科生了,中間邏輯大家看程式碼吧。
非遞迴後序遍歷
/**
* 非遞迴後序遍歷
* @param root
*/
public void depthAfterTraversal(BinTree root){
Stack<BinTree> s = new Stack<BinTree>();
BinTree p = root;
while (p != null || !s.empty()) {
while(p != null) {
s.push(p);
p = p.lChild;
}
p = s.pop();
System.out.print(p.getData()+" ");
//這裡需要判斷一下,當前p是否為棧頂的左子樹,如果是的話那麼還需要先訪問右子樹才能訪問根節點
//如果已經是不是左子樹的話,那麼說明左右子書都已經訪問完畢,可以訪問根節點了,所以講p複製為NULL
//取根節點
if (!s.empty() && p == s.peek().lChild) {
p = s.peek().rChild;
}
else p = null;
}
}
我把全部程式碼貼上供大家參考,其中也有遞迴實現先序,中序,後序的方法,大家加油
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.Vector;
public class BinTree {
private BinTree lChild;//左孩子
private BinTree rChild;//右孩子
private BinTree root;//根節點
private Object data; //資料域
private List<BinTree> datas;//儲存所有的節點
private Vector<Vector<Integer>> vec;
private Vector<Integer> son;
public BinTree(BinTree lChild, BinTree rChild, Object data) {
super();
this.lChild = lChild;
this.rChild = rChild;
this.data = data;
}
public BinTree(Object data) {
this(null, null, data);
}
public BinTree() {
super();
}
public void createTree(Object[] objs){
datas=new ArrayList<BinTree>();
for (Object object : objs) {
datas.add(new BinTree(object));
}
root=datas.get(0);//將第一個作為根節點
for (int i = 0; i < objs.length/2; i++) {
datas.get(i).lChild=datas.get(i*2+1);
if(i*2+2<datas.size()){//避免偶數的時候 下標越界
datas.get(i).rChild=datas.get(i*2+2);
}
}
}
//先序遍歷
public void preorder(BinTree root){
if(root!=null){
visit(root.getData());
preorder(root.lChild);
preorder(root.rChild);
}
}
//中序遍歷
public void inorder(BinTree root){
if(root!=null){
inorder(root.lChild);
visit(root.getData());
inorder(root.rChild);
}
}
//後序遍歷
public void afterorder(BinTree root){
if(root!=null){
afterorder(root.lChild);
afterorder(root.rChild);
visit(root.getData());
}
}
/**
* 非遞迴先序遍歷
* @param root
*/
public void depthOrderTraversal(BinTree root){
if(root==null){
System.out.println("null");
return;
}
ArrayDeque<BinTree> stack=new ArrayDeque<BinTree>();
stack.push(root);
while(stack.isEmpty()==false){
BinTree node = stack.pop();
System.out.print(node.getData()+" ");
if(node.rChild!=null)
stack.push(node.rChild);
if(node.lChild!=null)
stack.push(node.lChild);
}
}
/**
* 非遞迴中序遍歷
* @param root
*/
public void depthMidTraversal(BinTree root) {
Stack<BinTree> s = new Stack<BinTree>();
BinTree p = root;
while(p != null || !s.empty()) {
while (p != null) {
s.push(p);
p = p.lChild;
}
p = s.pop();
System.out.print(p.getData()+" ");
if (p.rChild != null) {
p = p.rChild;
}
else p = null;
}
}
/**
* 非遞迴後序遍歷
* @param root
*/
public void depthAfterTraversal(BinTree root){
Stack<BinTree> s = new Stack<BinTree>();
BinTree p = root;
while (p != null || !s.empty()) {
while(p != null) {
s.push(p);
p = p.lChild;
}
p = s.pop();
System.out.print(p.getData()+" ");
//這裡需要判斷一下,當前p是否為棧頂的左子樹,如果是的話那麼還需要先訪問右子樹才能訪問根節點
//如果已經是不是左子樹的話,那麼說明左右子書都已經訪問完畢,可以訪問根節點了,所以講p複製為NULL
//取根節點
if (!s.empty() && p == s.peek().lChild) {
p = s.peek().rChild;
}
else p = null;
}
}
//層序遍歷
// public void levelOrder(BinTree root) {
// if(root!=null){
//
// Queue<Pair<BinTree,Integer>> q = new LinkedList<>();
// Map<BinTree,Integer> record = new HashMap<>();
// record.put(root, 0);
// q.offer(record);
// while(!q.isEmpty()){
// int level = q.peek().values();
//
// }
// }
//
//
// }
private void visit(Object obj) {
System.out.print(obj+" ");
}
public Object getData() {
return data;
}
public BinTree getRoot() {
return root;
}
}
下面是呼叫這個類的main方法,大家加油,用Dubug看一下這個執行過程,比我用語言闡述要強的多
public class TestTree {
public static void main(String[] args) {
BinTree binTree=new BinTree();
Object[] objs={0,1,2,3,4,5,6,7};
binTree.createTree(objs);
// binTree.preorder(binTree.getRoot()); 先序遍歷
// binTree.inorder(binTree.getRoot()); 中序遍歷
// binTree.afterorder(binTree.getRoot()); //後序遍歷
// binTree.depthOrderTraversal(binTree.getRoot()); //非遞迴先序遍歷
// binTree.depthMidTraversal(binTree.getRoot());//非遞迴中序遍歷
binTree.depthAfterTraversal(binTree.getRoot());
}
}
覺得比較實在的點個關注,一起加油努力學習