二叉樹的描述
struct TreeNode {
int val; //根節點存儲的數據
struct TreeNode *left; //根節點的左孩子
struct TreeNode *right; //根節點的右孩子
};
二叉樹是一種特殊的樹,特殊之處就在於,它好像每一個根最多只能有兩個分叉,並且它在邏輯和物理上是一個倒着的樹,它的根節點在上面,自上而下進行分叉。
二叉樹的組織
既然二叉樹是一種倒着存放的結構,那麼它的定義就可以使用遞歸來實現
//初始化
void BinaryTreeInit(BTNode* pt);
//二叉樹的深度
int BinaryTreeDepth(BTNode* root);
// 通過前序遍歷的數組"ABD##E#H##CF##G##"構建二叉樹
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
// 二叉樹銷燬
void BinaryTreeDestory(BTNode** root);
// 二叉樹節點個數
int BinaryTreeSize(BTNode* root);
// 二叉樹葉子節點個數
int BinaryTreeLeafSize(BTNode* root);
// 二叉樹第k層節點個數
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉樹查找值爲x的節點
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉樹前序遍歷
void BinaryTreePrevOrder(BTNode* root);
// 二叉樹中序遍歷
void BinaryTreeInOrder(BTNode* root);
// 二叉樹後序遍歷
void BinaryTreePostOrder(BTNode* root);
// 層序遍歷
void BinaryTreeLevelOrder(BTNode* root);
// 判斷二叉樹是否是完全二叉樹
int BinaryTreeComplete(BTNode* root);
二叉樹的初始化呢,就是讓根節點的左右子樹都等於NULL,畢竟初始化只是一個根嘛
//初始化
void BinaryTreeInit(BTNode* pt)
{
pt->_left = pt->_right = NULL;
}
求二叉樹的深度,可以採用遞歸來實現,先找到葉子節點,然後逐層向上,直到根節點位置
//二叉樹的深度
int BinaryTreeDepth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
//求左子樹的深度
int lDepth = BinaryTreeDepth(root->_left) + 1;
//求右子樹的深度
int rDepth = BinaryTreeDepth(root->_right) + 1;
//返回左右子樹最深的一個深度給上一層的根
return lDepth > rDepth ? lDepth : rDepth;
}
通過前序遍歷來構建一顆二叉樹,構建二叉樹要注意的是我們直接輸入的時候,無法分辨出哪一個是葉子節點,不能找到左子樹停止的條件,這時就需要一個符號來表示這棵樹的葉子節點的下一層,以此來區分葉子節點和其他節點。
// 通過前序遍歷的數組"ABD##E#H##CF##G##"構建二叉樹
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
if (n <= *pi || a[*pi] == '#')
{
return NULL;
}
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
BinaryTreeInit(root);
root->_data = a[*pi];
(*pi)++;
root->_left = BinaryTreeCreate(a, n,pi);
(*pi)++;
root->_right = BinaryTreeCreate(a, n,pi);
return root;
}
二叉樹的銷燬,這裏要採取後序遍歷的方式來進行銷燬,所傳的參數得是這個樹每一個根節點的地址
// 二叉樹銷燬
void BinaryTreeDestory(BTNode** root)
{
if (*root == NULL)
{
return;
}
BinaryTreeDestory(&(*root)->_left);
BinaryTreeDestory(&(*root)->_right);
free(*root);
}
二叉樹所有結點個數,葉子結點的個數,第K層的節點個數
// 二叉樹節點個數
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
return BinaryTreeSize(root->_left) \
+ BinaryTreeSize(root->_right) + 1;
}
// 二叉樹葉子節點個數
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (!root->_left && !root->_right)
{
return 1;
}
return BinaryTreeLeafSize(root->_left) \
+ BinaryTreeLeafSize(root->_right);
}
// 二叉樹第k層節點個數
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if (root == NULL )
{
return 0;
}
if (k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->_left, k - 1) \
+ BinaryTreeLevelKSize(root->_right, k - 1);
}
二叉樹查找值爲K的結點(採取的是先序遍歷)
// 二叉樹查找值爲x的節點
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->_data == x)
{
return root;
}
BTNode* node;
node = BinaryTreeFind(root->_left, x);
//如果左子樹返回後的結果不是NULL
//就說明找到了該結點,直接返回
if (node != NULL)
{
return node;
}
//左子樹返回結果是空
//說明結點可能在右子樹
node = BinaryTreeFind(root->_right, x);
return node;
}
二叉樹的遞歸遍歷,二叉樹在遍歷時,不論它是先序,中序還是後序,都是隻能打印一個根結點,講所有的節點都當成不同情況的一個根結點來進行打印。
// 二叉樹前序遍歷
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
// 二叉樹中序遍歷
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
// 二叉樹後序遍歷
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ", root->_data);
}
非遞歸遍歷,這裏需要利用棧的後進先出的特性,來找到當前子樹根結點的上一層根結點,然後再進行判斷
//非遞歸中序遍歷
int* inorderTraversal(struct TreeNode* root, int* returnSize){
int* a=(int*)malloc(sizeof(int)*1000);
*returnSize=0;
if(root==NULL)
{
return a;
}
Stack* st=(Stack*)malloc(sizeof(Stack));
StackInit(st);
struct TreeNode* p=root;
//如果棧爲空 並且根節點爲空,則跳出循環
while(p!=NULL || StackEmpty(st)==0)
{
//如果P不爲空,說明左子樹還沒有遍歷完畢
//繼續遍歷左子樹,並保存當前根節點
if(p!=NULL)
{
StackPush(st,p);
p=p->left;
}
else
{
//此時 P爲空,通過彈棧來拿到上一層的跟節點
//打印跟節點,再判斷跟節點右節點的情況
p=StackTop(st);
StackPop(st);
a[(*returnSize)++]=p->val;
p=p->right;
}
}
return a;
}
//非遞歸後序遍歷
int* postorderTraversal(struct TreeNode* root, int* returnSize){
int* a=(int*)malloc(sizeof(int)*1000);
*returnSize=0;
if(root==NULL)
{
return a;
}
Stack* st=(Stack*)malloc(sizeof(Stack));
StackInit(st);
struct TreeNode* p=root;
struct TreeNode* q=NULL;
//如果棧爲空 並且根節點爲空,則跳出循環
while(p!=NULL || StackSize(st)!=0)
{
//如果P不爲空,說明左子樹還沒有遍歷完畢
//繼續遍歷左子樹,並保存當前根節點
if(p!=NULL)
{
StackPush(st,p);
p=p->left;
}
else
{
//此時左子樹已經遍歷完畢
p=StackTop(st);
//再通過彈棧拿到上一層跟節點
//如果根的右節點是空,或者它的右節點已經遍歷過了
//就打印當前根節點
if((p->right==NULL) || (p->right==q))
{
a[(*returnSize)++]=p->val;
StackPop(st);
q=p;//保存上一個打印的節點
p=NULL;
}
else
{
p=p->right;
}
}
}
return a;
}
二叉樹的層序遍歷(層序需要利用隊列先進先出的特性,保證每一層都是按照從左到右的順序來打印的)
// 層序遍歷
void BinaryTreeLevelOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
//建立一個隊列
Queue* qu = (Queue*)malloc(sizeof(Queue));
QueueInit(qu);
QueuePush(qu, root);
while (!QueueEmpty(qu))
{
int i = 0;
//獲取當前層節點數
int num = QueueSize(qu);
for (i = 0; i < num; i++)
{
BTNode* node = QueueFront(qu);
printf("%c ", node->_data);
//左右子樹不爲空 入隊列
if (node->_left)
{
QueuePush(qu, node->_left);
}
if (node->_right)
{
QueuePush(qu, node->_right);
}
//隊頭元素出隊列
QueuePop(qu);
}
printf("\n");
}
}
判斷是否是完全二叉樹
還是利用隊列先進先出的特點,一層一層來判斷,如果遇到根節點爲NULL,就判斷隊列中其他數據有沒有非空的,如果有,就不是一個完全二叉樹。
// 判斷二叉樹是否是完全二叉樹
//返回值爲 0 --> 不是完全二叉樹
//返回值爲 1 --> 是完全二叉樹
int BinaryTreeComplete(BTNode* root)
{
if (root == NULL)
{
return 1;
}
//建立一個隊列
Queue* qu = (Queue*)malloc(sizeof(Queue));
QueueInit(qu);
QueuePush(qu, root);
while (!QueueEmpty(qu))
{
int i = 0;
//獲取當前層節點數
int num = QueueSize(qu);
for (i = 0; i < num; i++)
{
BTNode* node = QueueFront(qu);
//如果遇到空節點,就開始判斷是否爲完全二叉樹
if (node == NULL)
{
int j = 0;
int count = QueueSize(qu);
for (j = 0; j < count; j++)
{
BTNode* cur = QueueFront(qu);
if (cur)
{
return 0;
}
QueuePop(qu);//隊頭元素出隊列
}
return 1;
}
//左右子樹入隊列
QueuePush(qu, node->_left);
QueuePush(qu, node->_right);
//隊頭元素出隊列
QueuePop(qu);
}
}
return 1;
}
LeetCode一些二叉樹的簡單面試題
1.單值二叉樹
如果二叉樹每個節點都具有相同的值,那麼該二叉樹就是單值二叉樹。
只有給定的樹是單值二叉樹時,才返回 true;否則返回 false。
bool isUnivalTree(struct TreeNode* root){
if(root==NULL)
{
return true;
}
//如果左子樹不是空 根節點和左子樹的值不相等,返回false
if(root->left!=NULL && root->val!=root->left->val)
{
return false;
}
//如果右子樹不是空 根節點和右子樹的值不相等,返回false
if(root->right!=NULL && root->val!=root->right->val)
{
return false;
}
//遞歸判斷根節點的左右子樹的情況
return isUnivalTree(root->left)&&isUnivalTree(root->right);
}
2.翻轉一棵二叉樹
這裏也是用先序遍歷二叉樹的思想,進行左右子樹的交換
struct TreeNode* invertTree(struct TreeNode* root){
if(root==NULL)
{
return NULL;
}
//交換根節點的左右子樹
struct TreeNode* tmp=root->left;
root->left=root->right;
root->right=tmp;
//先序遞歸交換根節點的左右子樹
invertTree(root->left);
invertTree(root->right);
return root;
}
3.相同的樹
給定兩個二叉樹,編寫一個函數來檢驗它們是否相同。
如果兩個樹在結構上相同,並且節點具有相同的值,則認爲它們是相同的。
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
//如果根節點同時爲空,可以是相同的
if(p==NULL && q==NULL)
{
return true;
}
//根節點 只有一個爲空 則爲不同的樹
if(p==NULL || q==NULL)
{
return false;
}
//根節點的值不同也不是相同的樹
if(q->val!=p->val)
{
return false;
}
//遞歸遍歷每個樹的左右子樹,然後結果與一下
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
4.對稱二叉樹
給定一個二叉樹,檢查它是否是鏡像對稱的。
樹都可以分成根節點,左子樹和右子樹三部分,對稱二叉樹則是判斷左子樹的右子樹 和 右子樹的左子樹是否相同
bool isJudge(struct TreeNode* left,struct TreeNode* right)
{
if(left==NULL && right==NULL)
{
return true;
}
if(left==NULL || right==NULL)
{
return false;
}
if(left->val!=right->val)
{
return false;
}
return isJudge(left->left,right->right) && isJudge(right->left,left->right);
}
//判斷是否對稱
bool isSymmetric(struct TreeNode* root){
if(root==NULL)
{
return true;
}
//遞歸判斷第一個根節點的左右子樹是否是相同的
return isJudge(root->left,root->right);
}
5.另一個樹的子樹
給定兩個非空二叉樹 s 和 t,檢驗 s 中是否包含和 t 具有相同結構和節點值的子樹。s 的一個子樹包括 s 的一個節點和這個節點的所有子孫。s 也可以看做它自身的一棵子樹。
是一個判斷兩顆樹是否相同的思想,樹都可以分成根節點,左子樹和右子樹三部分,再利用拆分的這一點,判斷兩棵樹是否相等
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
if(p==NULL && q==NULL)
{
return true;
}
if(p==NULL || q==NULL)
{
return false;
}
if(q->val!=p->val)
{
return false;
}
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
bool isSubtree(struct TreeNode* s, struct TreeNode* t){
if(s == NULL)
{
return false;
}
//二叉樹的拆分,判斷兩個樹是否相同 中間爲或的關係
return isSameTree(s,t) || \
isSubtree(s->left,t) || \
isSubtree(s->right,t);
}
6.平衡二叉樹判斷
給定一個二叉樹,判斷它是否是高度平衡的二叉樹。
本題中,一棵高度平衡二叉樹定義爲:
一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過1。
//求樹的深度
int len(struct TreeNode* root)
{
if(root==NULL)
{
return 0;
}
int a=len(root->left)+1;
int b=len(root->right)+1;
return a>b?a:b;
}
bool isBalanced(struct TreeNode* root){
if(root==NULL)
{
return true;
}
//遞歸找到左右子樹的深度
int a=len(root->left);
int b=len(root->right);
//返回左右子樹相減後的真值
return abs(a-b)<2 \
&& isBalanced(root->left)\
&& isBalanced(root->right);
}