二叉樹

二叉樹的描述

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);

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