java算法之二叉樹排序

對於樹(ADT)形結構,元素的排序不是重點,如果需要排序,一般使用鏈表、棧、隊列等數據結構。

算法中常用二叉樹,對於二叉樹排序這裏先不介紹,給個傳送門https://zhuanlan.zhihu.com/p/25623301。

在這裏講一下二叉樹的遍歷。

二叉樹的遍歷按當前節點分爲三類:

1.前序遍歷DLR  D是當前節點 L在D左邊 R在D右邊 (這其中LR是可以交換的,因爲正常是六種遍歷)

2.中序遍歷LDR

3.後序遍歷LRD

還有一種遍歷方法不需要依賴上述順序,即層續遍歷,也就是按照層級一層一層的遍歷

1.前序遍歷DLR

步驟如下:

    1)訪問根節點

    2)按照遍歷方式遍歷左子樹

    3)遍歷右子樹

代碼如下: 爲了方便直接寫中文名的方法了哈~.~

private static void 前序遍歷(BinaryTreeNode a) {
// TODO Auto-generated method stub
if(a!=null){
System.out.println(a.getData());
前序遍歷(a.getLeft());
前序遍歷(a.getRight());
}

}

    從方法中可以看出,應用了遞歸方法不斷的尋找每個節點的左子樹和右子樹。

那如果正常考慮來遍歷呢?請看如下

private static void 非遞歸前序遍歷(BinaryTreeNode a) {
// TODO Auto-generated method stub
if(a == null);
Stack<BinaryTreeNode> S = new Stack<BinaryTreeNode>();
while(true){
while(a != null){
System.out.println(a.getData());
S.push(a);
a = a.getLeft();
}
if(S.isEmpty()){ //這裏要用到isEmpty函數而非S == NULL
break;
}
a = S.pop();
a = a.getRight();
}
}

以上代碼是非遞歸版的前序遍歷,應用了棧的思想,把每個具有左子樹的節點壓入到棧中, 遍歷之後取出節點 找到其右子樹。這樣有人會問,那麼如果右子樹節點包含了左子樹那麼遍歷時是否會忽略呢,答案是:不會 因爲在重新尋找右子樹節點的時候沒有重新定義變量,所以右子樹的做節點還是會接着進行遍歷的。

2.中序遍歷 LDR

顧名思義,還是先遍歷左子樹在遍歷當前節點,然後遍歷右子樹。代碼如下:

private static void 遞歸中序遍歷(BinaryTreeNode a) {
// TODO Auto-generated method stub
if(a==null);
else{
遞歸中序遍歷(a.getLeft());
System.out.println(a.getData());
遞歸中序遍歷(a.getRight());
}

}

依舊,遞歸也是最好理解且最簡單的 主函數如下:

              public static void main(String[] args) {

BinaryTreeNode A = new BinaryTreeNode();
BinaryTreeNode B = new BinaryTreeNode();
BinaryTreeNode C = new BinaryTreeNode();
BinaryTreeNode D = new BinaryTreeNode();
A.setData(5);
B.setData(6);
C.setData(7);
D.setData(8);
A.setLeft(B);
B.setRight(C);
C.setLeft(D);
/*前序遍歷(A);
非遞歸前序遍歷(A);*/
遞歸中序遍歷(A);

            }

運行結果是:6 8 7 5 大家可以仔細思考,程序在中序遞歸運行時的具體過程。

下面給出非遞歸中續遍歷的例程

private static void 非遞歸中序遍歷(BinaryTreeNode a) {
// TODO Auto-generated method stub
if(a == null);
else{
Stack<BinaryTreeNode> S = new Stack<BinaryTreeNode>();
while(true){
while(a!=null){
a = a.getLeft();
S.push(a);
}
if(S.isEmpty()){
break;
}
S.pop();
System.out.println(a.getData());
a = a.getRight();
}
}

}

可以看出,在非遞歸中,是否出棧和進棧時輸出數據可判斷是什麼遍歷。

3.後續遍歷

遞歸方案這裏就不贅述啦,相信小夥伴們也已經看懂了,這裏詳細說一下後續遍歷的非遞歸版本。

在非遞歸中前序和中序都很好做,但後續要考慮如何最後輸出當前節點,其具體代碼如下

private static void 非遞歸後序遍歷(BinaryTreeNode a) {
// TODO Auto-generated method stub
Stack<BinaryTreeNode> S = new Stack<BinaryTreeNode>();
while(true){
if(a!=null){
S.push(a);
a = a.getLeft();
}else{
if(S.isEmpty()){
break;
}else{
if(S.get(0).getRight() == null){
a = S.pop();
System.out.println(a.getData());
if(a == S.get(0).getRight()){
System.out.println(S.get(0).getData());
S.pop();
}
}
if(!S.isEmpty()){
a = S.get(0).getRight();
}else{
a = null;
}
}
}
}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章