筆者查閱了網上關於二叉樹尋找根結點的算法,衆說紛紜,有:在節點結構體中加一條指向父節點的指針;非遞歸遍歷樹的方式尋找父節點;直接利用樹的遞歸遍歷,打印輸出父節點……
筆者認爲添加父節點指針的方法,不能很好地體現樹的邏輯結構。樹的結構體(特指二叉樹的存儲結構)已經很成熟而且近乎約定俗成了,再重新添加一個指針,很多關於二叉樹的基本操作都要重新變化,實在繁瑣。所以本文直接利用樹的遞歸遍歷方式尋找樹的根結點,因爲樹的定義中本就體現了遞歸的特點,所以大多數關於樹的操作,遞歸的方法都可有效實現,而且代碼可讀性強。此外,對於非遞歸方式的解法,本文不再贅述,請讀者自便。
*另:本文只提供一種思路,並不是萬能模板,具體問題還需結合實際情況決定!
【問題描述】構造一棵二叉排序樹(也叫二叉查找樹),可以是一棵空樹。
二叉排序樹定義:
1.若左子樹非空,則左子樹上所有關鍵字值均不大於根結點的關鍵字值;
2.若右子樹非空,則右子樹上所有關鍵字值均不小於根結點的關鍵字值;
3.左、右子樹本身也是一棵二叉排序樹。
現在給你N個關鍵字值各不相同的結點,要求你按順序插入一個初始爲空樹的二叉排序樹中,每次插入成功後,求相應的父節點的關鍵字值。如果沒有父親節點,則輸出-1。
input:
第一行,一個數字N(N<=100),表示待插入的結點數。
第二行,N個互不相同的正整數,表示要順序插入結點的關鍵字值,這些值不超過10^8(即控制在int型數據類型所能表示的範圍內)。
output:
輸出共N行,每次插入節點後,該節點對應的父親節點的關鍵字值。(根結點的父節點默認爲-1)
代碼中採用數組二叉樹的方式存儲樹,有兩點好處:loc值可知結點總數,利於結點遍歷步數的控制;輸入值保存在對應數組,易操作。
指針二叉樹的存儲方法,還望讀者指教。
#include <stdio.h>
struct Node{
int data;
Node* lchild;
Node* rchild;
}Tree[101]; // 數組二叉樹
int loc; // loc表示當前節點的個數
Node* creat(){ // 申請一個樹結點
Tree[loc].lchild=Tree[loc].rchild=NULL;
return &Tree[loc++]; // 返回指向新結點的指針
}
/*在一棵二叉樹中,依次插入結點,構造二叉排序樹*/
Node* Insert(Node *T,int x){
if(T==NULL){
T=creat();
T->data=x;
return T;
}
else if(x < T->data){
T->lchild=Insert(T->lchild,x);
}
else if(x > T->data){
T->rchild=Insert(T->rchild,x);
}
return T;
}
/*遍歷一棵樹找到各結點的父節點--先序遍歷模型,注意遍歷孩子節點的條件:孩子非空*/
void findParent(Node *T,int x){
if(!T) return;
if(T->lchild!=NULL && T->lchild->data==x) // 左孩子非空纔可遍歷,否則遞歸邊界出錯
printf("%d的父節點是:%d\n",x,T->data);
if(T->rchild!=NULL && T->rchild->data==x)
printf("%d的父節點是:%d\n",x,T->data);
if(T->lchild)
findParent(T->lchild,x);
if(T->rchild)
findParent(T->rchild,x);
return;
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
loc=0;
Node *T=NULL; // 建立一個根結點
for(int i=0;i<n;i++){
int x;
scanf("%d",&x);
T=Insert(T,x); // 返回樹的根結點
}
/*對根結點的父節點的判斷有點投機取巧,應在函數中判斷所訪問結點是否爲樹的根結點,是則打印-1*/
printf("%d的父結點是:-1\n",Tree[0].data); // 寫者這裏直接跳過判斷,因爲二叉排序樹的首節點必爲根結點
for(int i=1;i<loc;i++){ // 依次遍歷樹,找到並輸出根結點
findParent(T,Tree[i].data);
}
}
return 0;
}
執行結果: