二叉樹節點的結構
class Node{
int value;
Node left;
Node right;
Node(int data){
this.valve=data;
}
}
要求掌握內容
1.能結合隊列、棧、鏈表、字符串等很多數據結構。
2.需掌握圖的遍歷方式,比如BFS和DFS。
3.需掌握遞歸函數的使用,並自己設計出遞歸過程。
先序、中序和後序遍歷
先序遍歷:中、左、右
中序遍歷:左、中、右
後序遍歷:左、右、中
1
23
4567
先序:1 2 4 5 3 6 7
中序:4 2 5 1 6 3 7
後序:4 5 2 6 7 3 1
遞歸方式實現先序遍歷
public void preOrderRecur(Node head){
if(head==null){
return;
}
System.out.print(head.valve + " ");
preOrdeRecur(head.left);
preOrdeRecur(head.right);
}
思想:若樹爲空,返回。若不爲空,先打印當前頭結點的值,然後遍歷左子樹,最後遍歷右子樹。
非遞歸方式實現先序遍歷
具體過程:
1.先申請一個新的棧,記爲stack。
2.然後將頭結點head壓入stack中。
3.每次從stack中彈出棧頂節點,記爲cur,然後打印cur節點的值。若cur右孩子不爲空的話,將cur的右孩子先壓入stack中。最後如果cur的左孩子不爲空的話,將cur的左孩子壓入stack中。
4.不斷重複步驟3,直到stack爲空,全部過程結束。
遞歸方式實現中序遍歷
public void preOrderRecur(Node head){
if(head==null){
return;
}
preOrdeRecur(head.left);
System.out.print(head.valve + " ");
preOrdeRecur(head.right);
}
非遞歸方式實現中序遍歷
具體過程:
1.先申請一個新的棧,記爲stack,申請一個變量cur,初始時令cur等於頭節點。
2.先把cur節點壓入棧中,對以cur節點爲頭的整棵子樹來說,依次把整棵樹的左邊界壓入棧中,即不斷令cur=cur.left,然後重複步驟2。
3.不斷重複步驟2,直到發現cur爲空,此時從stack中彈出一個節點,記爲node。打印node的值,並讓cur=node.right,然後重複步驟2。
4.當stack爲空並且cur爲空時,整個過程結束。
遞歸方式實現後序遍歷
public void preOrderRecur(Node head){
if(head==null){
return;
}
preOrdeRecur(head.left);
preOrdeRecur(head.right);
System.out.print(head.valve + " ");
}
非遞歸方式實現後序遍歷
方法一:使用兩個棧實現
具體過程:
1.先申請一個新的棧,記爲s1,然後將頭結點壓入s1中。
2.從s1中彈出的節點記爲cur,然後先把cur的左孩子壓入s1中,然後把cur1的右孩子壓入s1中。
3.在整個過程中,每一個從s1中彈出的節點都放進第二個棧s2中。
4.不斷重複步驟2和步驟3,直到s1爲空,過程停止。
5.從s2中以此彈出節點並打印,打印的順序就是後序遍歷的順序了。
方法二:使用一個棧實現
具體過程:
1.先申請一個新的棧,記爲stack。將頭節點壓入stack,同時設置兩個變量h和c。在整個流程中,h代表最近一次彈出並打印的節點,c代表當前stack的棧頂節點,初始時令h爲頭節點,c爲null。
2.每次令c等於當前stack的棧頂節點,但是不從stack中彈出節點,此時分以下三種情況。
(1)如果c的左孩子不爲空,並且h不等於c的左孩子,也不等於c的右孩子,則把c的左孩子壓入stack中。
(2)如果情況1不成立,並且c的右孩子不爲空,並且h不等於c的右孩子,則把c的右孩子壓入stack中。
(3)如果情況1和情況2都不成立,那麼從stack中彈出c並打印,然後令h等於c。
3.不斷重複步驟2,直到stack爲空,全部過程結束。
遞歸方式實現深度遍歷
#include<iostream>
#include<string>
using namespace std;
class BinaryTreeNode
{
public:
char data;
BinaryTreeNode *Left;
BinaryTreeNode *Right;
};
//創建二叉樹,順序依次爲中間節點->左子樹->右子樹
void createBiTree(BinaryTreeNode* &T) //這裏加上&意思是傳遞的參數爲指針的引用,括號裏面等價於 BiTreeNode* &T
{ //這樣的意義在於在函數執行過後,傳遞進來的指針會發生改變(引用的作用),不可以去掉&
char c;
cin >> c;
if('#' == c) //當遇到#時,令樹的根節點爲NULL,從而結束該分支的遞歸
T = NULL;
else
{
T = new BinaryTreeNode;
T->data=c;
createBiTree(T->Left);
createBiTree(T->Right);
}
}
void preorder(BinaryTreeNode* &T){
if(T){
cout<<T->data<<" ";
preorder(T->Left);
preorder(T->Right);
}
}
void midorder(BinaryTreeNode* &T){
if(T){
midorder(T->Left);
cout<<T->data<<" ";
midorder(T->Right);
}
}
void postorder(BinaryTreeNode* &T){
if(T){
postorder(T->Left);
postorder(T->Right);
cout<<T->data<<" ";
}
}
int main(){
BinaryTreeNode* T; //聲明一個指向二叉樹根節點的指針
createBiTree(T);
cout<<"二叉樹創建完成!"<<endl;
cout<<"前序遍歷二叉樹:"<<endl;
preorder(T);
cout<<endl;
cout<<"中序遍歷二叉樹:"<<endl;
midorder(T);
cout<<endl;
cout<<"後序遍歷二叉樹:"<<endl;
postorder(T);
return 0;
}
非遞歸方式實現深度遍歷
#include<iostream>
#include<string>
#include<stack>
using namespace std;
class BinaryTreeNode
{
public:
char data;
BinaryTreeNode *Left;
BinaryTreeNode *Right;
};
//創建二叉樹,順序依次爲中間節點->左子樹->右子樹
void createBiTree(BinaryTreeNode* &T) //這裏加上&意思是傳遞的參數爲指針的引用,括號裏面等價於 BiTreeNode* &T
{ //這樣的意義在於在函數執行過後,傳遞進來的指針會發生改變(引用的作用),不可以去掉&
char c;
cin >> c;
if('#' == c) //當遇到#時,令樹的根節點爲NULL,從而結束該分支的遞歸
T = NULL;
else
{
T = new BinaryTreeNode;
T->data=c;
createBiTree(T->Left);
createBiTree(T->Right);
}
}
void preorder(BinaryTreeNode* &T){
stack<BinaryTreeNode*> s;
s.push(T);
while(!s.empty()){
BinaryTreeNode* cur=s.top();
s.pop();
cout<<cur->data;
if(cur->Right!=NULL){
s.push(cur->Right);
}
if(cur->Left!=NULL){
s.push(cur->Left);
}
}
}
void midorder(BinaryTreeNode* &T){
stack<BinaryTreeNode*> s;
BinaryTreeNode* cur=T;
while(cur!=NULL||!s.empty()){
while(cur!=NULL){
s.push(cur);
cur=cur->Left;
}
BinaryTreeNode* node=s.top();
s.pop();
cout<<node->data;
cur=node->Right;
}
}
void postorder(BinaryTreeNode* &T){
stack<BinaryTreeNode*> s1;
stack<BinaryTreeNode*> s2;
s1.push(T);
while(!s1.empty()){
BinaryTreeNode* cur=s1.top();
s1.pop();
s2.push(cur);
if(cur->Left!=NULL){
s1.push(cur->Left);
}
if(cur->Right!=NULL){
s1.push(cur->Right);
}
}
while(!s2.empty()){
BinaryTreeNode* node=s2.top();
s2.pop();
cout<<node->data;
}
}
int main(){
BinaryTreeNode* T; //聲明一個指向二叉樹根節點的指針
createBiTree(T); //abd###ce##fg###
cout<<"二叉樹創建完成!"<<endl;
cout<<"前序遍歷二叉樹:"<<endl;
preorder(T);
cout<<endl;
cout<<"中序遍歷二叉樹:"<<endl;
midorder(T);
cout<<endl;
cout<<"後序遍歷二叉樹:"<<endl;
postorder(T);
cout<<endl;
return 0;
}
總結
不管是遞歸方法還是非遞歸方法,遍歷整棵樹的時間複雜度都是O(N),N爲二叉樹節點數,額外空間複雜度爲O(L),L爲二叉樹的層數。