二叉樹的子樹
在二叉樹中以任何一個節點爲頭部的整棵樹稱作二叉樹的子樹。
平衡二叉樹(AVL樹)
1.空樹是平衡二叉樹
2.如果一棵樹不爲空,並且其中所有的子樹都滿足各自的左子樹與右子樹的高度都不超過1。
給定一顆二叉樹的頭節點head,判斷一棵樹是否是平衡二叉樹。
#include<iostream>
#include<string>
#include<stack>
#include<queue>
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);
}
}
int treeDepth(BinaryTreeNode* root){
if(root==NULL){
return 0;
}
if(root->Left==NULL){
return treeDepth(root->Right)+1;
}
if(root->Right==NULL){
return treeDepth(root->Left)+1;
}
return treeDepth(root->Right)>treeDepth(root->Left)?treeDepth(root->Right)+1:treeDepth(root->Left)+1;
}
bool isAVLtree(BinaryTreeNode* root){
if(root==NULL)
return true;
if(abs(treeDepth(root->Right)-treeDepth(root->Left))>1)
return false;
return isAVLtree(root->Left)&&isAVLtree(root->Right);
}
int main(){
BinaryTreeNode* T; //聲明一個指向二叉樹根節點的指針
createBiTree(T); //abd###ce##fg###
cout<<"二叉樹創建完成!"<<endl;
cout<<"二叉樹深度:"<<endl;
cout<<treeDepth(T)<<endl;
cout<<endl;
if(isAVLtree(T)==true){
cout<<"true"<<endl;
}
else
cout<<"false"<<endl;
return 0;
}
思路爲二叉樹的後序遍歷。(?)
搜索二叉樹
搜索二叉樹的特徵:
每顆子樹的頭節點的值都比各自左子樹上的所有節點值要大,也都比各自右子樹上的所有節點值要小。
搜索二叉樹按照中序遍歷得到的序列,一定是從小到大排列的。
紅黑樹、平衡搜索二叉樹(AVL樹)等,其實都是搜索二叉樹的不同實現。
給定一顆二叉樹的頭節點head,判斷一棵樹是否是搜索二叉樹。
1.改寫二叉樹的中序遍歷
2.遍歷到每個節點的值時,如果一直比上一個遍歷的節點值要大,則是搜索二叉樹,否則,不是搜索二叉樹。
3.爲了方便同時得到當前節點,和上一個遍歷的節點,二叉樹中序遍歷非遞歸的實現比較合適。
#include<iostream>
#include<string>
#include<stack>
#include<queue>
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);
}
}
bool sousuomidorder(BinaryTreeNode* &T){
stack<BinaryTreeNode*> s;
char last=0;
BinaryTreeNode* cur=T;
while(cur!=NULL||!s.empty()){
while(cur!=NULL){
s.push(cur);
cur=cur->Left;
}
BinaryTreeNode* node=s.top();
s.pop();
char now=node->data;
if(now<last){
return false;
}
last=node->data;
cout<<node->data;
cur=node->Right;
}
return true;
}
int main(){
BinaryTreeNode* T; //聲明一個指向二叉樹根節點的指針
createBiTree(T); //abd###ce##fg###
cout<<"二叉樹創建完成!"<<endl;
cout<<"判斷是否是搜索二叉樹:"<<endl;
if(sousuomidorder(T)==true){
cout<<"true"<<endl;
}
else
cout<<"false"<<endl;
return 0;
}
滿二叉樹
滿二叉樹是除了最後一層的節點無任何子節點外,剩下每一層上的節點都有兩個子節點。
層數爲L,節點數爲N,N=2^L-1
完全二叉樹
是指,除了最後一層之外,其他每一層的節點數都是滿的。最後一層如果也滿了,是一顆滿二叉樹,也是完全二叉樹。最後一層如果不滿,缺少的節點也全部的集中在右邊,那也是一顆完全二叉樹。
給定一顆二叉樹的頭節點head,判斷一棵樹是否是完全二叉樹。
1、採用按層遍歷二叉樹的方式,從每層的左邊向右邊依次遍歷所有的節點。
2、如果當前的節點有右孩子,但沒有左孩子,直接返回false。
3、如果當前節點並不是左右孩子全有,那之後的節點必須都爲葉節點,否則返回false。
4、遍歷過程中如果不返回false,遍歷結束後返回true即可。
#include<iostream>
#include<string>
#include<stack>
#include<queue>
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);
}
}
bool wanquanerchashu(BinaryTreeNode* &T){
queue<BinaryTreeNode*> q1;
q1.push(T);
char yeflag=false;
while(!q1.empty()){
BinaryTreeNode* cur=q1.front();
q1.pop();
cout<<cur->data;
if(yeflag==true){//判斷後面的各節點是不是都是葉節點
if(cur->Left!=NULL||cur->Right!=NULL){
return false;
}
}
if(cur->Left==NULL&&cur->Right!=NULL){
return false;
}
if(cur->Left==NULL||cur->Right==NULL){
yeflag=true;//啓動判斷後面的各節點是不是都是葉節點標識
}
if(cur->Left!=NULL){
q1.push(cur->Left);
}
if(cur->Right!=NULL){
q1.push(cur->Right);
}
}
return true;
}
int main(){
BinaryTreeNode* T; //聲明一個指向二叉樹根節點的指針
createBiTree(T); //abd###ce##fg###
cout<<"二叉樹創建完成!"<<endl;
cout<<"判斷完全二叉樹:"<<endl;
if(wanquanerchashu(T)==true){
cout<<"true"<<endl;
}
else
cout<<"false"<<endl;
return 0;
}
注意
面試中,二叉樹節點類型僅包括:數據項、左孩子、右孩子。
工程上的二叉樹節點類型,往往多一條指向父節點的指針。
一般默認面試中的二叉樹節點結構不包括指向父節點的指針,除非特別說明。
後繼節點
一個節點的後繼節點是指,這個節點在中序遍歷序列中的下一個節點。
前驅節點
一個節點的前驅節點是指,這個節點在中序遍歷序列中的上一個節點。
案例
現在有一種新的二叉樹節點類型,定義如下。
public class Node{
public int value;
public Node left;
public Node right;
public Node parent;
public Node(int data){
this.value=data;
}
}
該結構比普通二叉樹節點結構多了一個指向父節點的parent指針。假設有一顆這種類型的節點組成的二叉樹,樹中每個節點的parent指針都正確指向自己的父節點,頭節點的parent指向空。只給定在二叉樹中的某個節點node。該節點不一定是頭節點,可能是樹中任何一個節點,請實現返回後繼節點的函數。
普通方法:
1.不斷向上找到頭節點
2.利用頭結點進行中序遍歷,進而獲得後續節點。
普通方法要遍歷所有節點,如果二叉樹節點爲N,時間複雜度爲O(N),額外空間複雜度爲O(N)。
最優解法:
情況一:如果node有右子樹,那麼後繼節點就是右子樹上最左邊的節點。
情況二:如果node沒有右子樹,那麼先看node是不是node父節點的左孩子,如果是左孩子,那麼此時node的父節點就是node的後繼節點,如果是右孩子,就向上尋找node的後繼節點,假設向上移動的節點記爲s,s的父節點記爲p,如果發現s是p的左孩子,那麼節點p就是node節點的後繼節點,否則就一直向上移動。
情況三:如果一直向上,都移動到空節點了,還是沒有發現node的後繼節點,說明node根本不存在後繼節點,返回空即可。
如果node節點的node後繼節點之間的實際距離爲L,最優解法只用走過L個節點,時間複雜度爲O(L),額外空間複雜度爲O(1),也就是常數級別。