二叉樹的應用
阿新 • • 發佈:2018-11-13
一、層序遍歷的應用:
映象:還需要引入佇列,同上一篇部落格。
.h
# pragma once # include<assert.h> # include<malloc.h> # include<stdio.h> # include<stdlib.h> # include<string.h> # include"Queue.h" typedef char BTDataType; typedef struct BinTreeBTNode { struct BinTreeBTNode* _pLeft; struct BinTreeBTNode* _pRight; BTDataType _data; }BTBTNode,*PBTNode; PBTNode BuyBinTreeBTNode(BTDataType data); void _CreateBinTree(PBTNode* pRoot, BTDataType array[], int size, int* index, BTDataType invalid); void CreateBinTree(PBTNode* pRoot, BTDataType array[], int size, BTDataType invalid); //拷貝一棵樹,根加左子樹加右子樹 PBTNode CopyBinTree(PBTNode pRoot); //二叉樹的前需遍歷:根+左子樹+右子樹 void PreOrder(PBTNode pRoot); //中序遍歷:左子樹+根節點+右子樹 void InOrder(PBTNode pRoot); //後序遍歷:左子樹+右子樹+根節點 void PostOrder(PBTNode pRoot); void DestroyBinTree(PBTNode* pRoot); void MirrorBinTree(PBTNode pRoot); void MirrorBinTreeNor(PBTNode pRoot);
.c
# include"BinaryTree.h" //#define NULL 0 PBTNode BuyBinTreeNode(DataType data) { PBTNode pNewNode = (PBTNode)malloc(sizeof(Node)); if (NULL == pNewNode) { assert(0); return NULL; } pNewNode->_data = data; pNewNode->_pLeft = NULL; pNewNode->_pRight = NULL; return pNewNode; } void _CreateBinTree(PBTNode* pRoot, DataType array[], int size, int* index, DataType invalid) { assert(pRoot);//此時pRoot代表外部實參的地址,可以改變指向 assert(index); if (*index < size&&invalid != array[*index]){ *pRoot = BuyBinTreeNode(array[*index]); //建立根節點的左子樹 ++(*index); _CreateBinTree(&(*pRoot)->_pLeft, array, size, index, invalid); //建立根節點的右子樹 ++(*index); _CreateBinTree(&(*pRoot)->_pRight, array, size, index, invalid); } } void CreateBinTree(PBTNode* pRoot, DataType array[], int size, DataType invalid) { int index = 0; _CreateBinTree(pRoot, array, size, &index, invalid); } //拷貝一棵樹,根加左子樹加右子樹 PBTNode CopyBinTree(PBTNode pRoot){ PBTNode pNewRoot = NULL; if (pRoot){ //拷貝根節點 pNewRoot = BuyBinTreeNode(pRoot->_data); //拷貝根節點的左子樹 if (pRoot->_pLeft) pNewRoot->_pLeft = CopyBinTree(pRoot->_pLeft); //拷貝根節點的右子樹 if (pRoot->_pRight) pNewRoot->_pRight = CopyBinTree(pRoot->_pRight); } return pNewRoot; } //二叉樹的前需遍歷:根+左子樹+右子樹 void PreOrder(PBTNode pRoot) { if (pRoot) { printf("%c ", pRoot->_data); PreOrder(pRoot->_pLeft); PreOrder(pRoot->_pRight); } } //中序遍歷:左子樹+根節點+右子樹 void InOrder(PBTNode pRoot) { if (pRoot) { InOrder(pRoot->_pLeft); printf("%c ", pRoot->_data); InOrder(pRoot->_pRight); } } //後序遍歷:左子樹+右子樹+根節點 void PostOrder(PBTNode pRoot) { if (pRoot) { PostOrder(pRoot->_pLeft); PostOrder(pRoot->_pRight); printf("%c ", pRoot->_data); } } void LevelOrder(PBTNode pRoot) { Queue q; if (NULL == pRoot) return; QueueInit(&q);//初始化根節點 //把根節點的地址加到樹裡面 QueuePush(&q, pRoot); while (!QueueEmpty(&q)){ //遍歷 PBTNode pCur = QueueFront(&q); printf("%c ", pCur->_data); //QueuePop(&q);出佇列的操作也可以放在這個位置上 //把元素放到佇列裡 if (pCur->_pLeft) QueuePush(&q, pCur->_pLeft); if (pCur->_pRight) QueuePush(&q, pCur->_pRight); //從佇列裡面拿出去 QueuePop(&q); } } void DestroyBinTree(PBTNode* pRoot) { assert(pRoot); if (*pRoot){ //銷燬左子樹 DestroyBinTree(&(*pRoot)->_pLeft); //銷燬右子樹 DestroyBinTree(&(*pRoot)->_pRight); //銷燬根節點 free(*pRoot); *pRoot = NULL; } } void Swap(PBTNode* pLeft, PBTNode* pRight) { PBTNode tmp = *pLeft; *pLeft = *pRight; *pRight = tmp; } //非遞迴實現 void MirrorBinTreeNor(PBTNode pRoot){ Queue q; if (NULL == pRoot) return; QueueInit(&q); QueuePush(&q, pRoot); while (!QueueEmpty(&q)) { //如果當前佇列不為空,取對頭元素 PBTNode pCur = QueueFront(&q); //交換左右孩子 Swap(&(pCur->_pLeft),&(pCur->_pRight)); if (pCur->_pLeft) QueuePush(&q, pCur->_pLeft);//將左孩子寫入 if (pCur->_pRight) QueuePush(&q, pCur->_pRight); } } //遞迴實現映象 void MirrorBinTree(PBTNode pRoot) { if (pRoot) { Swap(&(pRoot->_pLeft), &(pRoot->_pRight)); MirrorBinTree(pRoot->_pLeft); MirrorBinTree(pRoot->_pRight); } } void TestBinTree() { char* str = "ABD###CE##F"; PBTNode pRoot = NULL, pNewRoot; CreateBinTree(&pRoot, str, strlen(str), '#'); pNewRoot = CopyBinTree(pRoot); printf("前序遍歷:"); PreOrder(pRoot); printf("\n"); printf("中序遍歷:"); InOrder(pRoot); printf("\n"); printf("後序遍歷:"); PostOrder(pRoot); printf("\n"); printf("層序遍歷:"); LevelOrder(pRoot); MirrorBinTree(pRoot); MirrorBinTreeNor(pRoot); printf("層序遍歷:"); LevelOrder(pRoot); } //A B D # # # C E #
二、
1、求二叉樹中結點總的個數;
遞迴呼叫的時間複雜度:遞迴呼叫總的次數*每次遞迴呼叫的次數,一個結點兩個指標域,n個結點則有2*n個指標域;每個指標域都是一次函式呼叫,遞迴程式總的呼叫次數為2n,每一次呼叫了兩次,則為4n。所以節點總的個數為O(n)。
int BinTreeSize(PBTNode pRoot) { if (NULL == pRoot) return 0; int left = BinTreeSize(pRoot->_pLeft); int right = BinTreeSize(pRoot->_pRight); return left + right + 1; }
測試的程式碼:
void TestBinTree()
{
char* str = "ABD###CE##F";
PBTNode pRoot = NULL, pNewRoot;
CreateBinTree(&pRoot, str, strlen(str), '#');
pNewRoot = CopyBinTree(pRoot);
printf("前序遍歷:");
PreOrder(pRoot);
printf("\n");
printf("中序遍歷:");
InOrder(pRoot);
printf("\n");
printf("後序遍歷:");
PostOrder(pRoot);
printf("\n");
printf("層序遍歷:");
LevelOrder(pRoot);
MirrorBinTree(pRoot);
MirrorBinTreeNor(pRoot);
printf("層序遍歷:");
LevelOrder(pRoot);
printf("\n");
printf("二叉樹中結點的個數為:%d\n", BinTreeSize(pRoot));
}
2、二叉樹中葉子節點的總的個數
葉子節點:沒有孩子的節點,度為0的節點
int BinTreeLeaf(PBTNode pRoot)
{
if (pRoot == NULL)
return 0;
if (NULL == pRoot->_pLeft&&NULL == pRoot->_pRight)
//左孩子和右孩子都為空,則只有一個葉子節點
return 1;
return BinTreeLeaf(pRoot->_pLeft) + BinTreeLeaf(pRoot->_pRight);
}
測試程式碼:
printf("二叉樹中葉子結點的個數為:%d\n", BinTreeLeaf(pRoot));
3、判斷一個節點是否在一棵二叉樹中
struct BinaryTree
{
BinaryTree(char data)
:_pLeft(NULL)
, _pRight(NULL)
, _data(data)
{}
BinaryTree *_pLeft;
BinaryTree *_pRight;
char _data;
};
//建立二叉樹
void CreateBinaryTree(BinaryTree *&pRoot, char *str,size_t size, size_t &index)
{
if (index < size && str[index] != '#')
{
pRoot = new BinaryTree(str[index]);
CreateBinaryTree(pRoot->_pLeft, str, size, ++index);
CreateBinaryTree(pRoot->_pRight, str, size, ++index);
}
}
//遞迴實現
bool IsNodeInTree(BinaryTree *pRoot,BinaryTree *pNode)
{
if (NULL == pRoot || NULL == pNode)
return false;
if (pRoot->_data == pNode->_data)
return true;
if (IsNodeInTree(pRoot->_pLeft, pNode) || IsNodeInTree(pRoot->_pRight, pNode))
return true;
return false;
}
4、獲取一個節點的雙親節點
5、獲取一個節點的左孩子節點
6、獲取一個節點的右孩子節點
7、判斷一棵二叉樹是否為完全二叉樹(層序遍歷變形)
1>如果樹為空,則直接返回錯
2>如果樹不為空:層序遍歷二叉樹
2.1>如果一個結點左右孩子都不為空,則pop該節點,將其左右孩子入佇列;
2.1>如果遇到一個結點,左孩子為空,右孩子不為空,則該樹一定不是完全二叉樹;
2.2>如果遇到一個結點,左孩子不為空,右孩子為空;或者左右孩子都為空;則該節點之後的佇列中的結點都為葉子節點;該樹才是完全二叉樹,否則就不是完全二叉樹;
int IsCompleteBinTree(pNode pRoot)
{
Queue q;
int flag=0;
if (NULL==pRoot)
return 1;
QueueInit(&q);
QueuePush(&q,pRoot);
while (!QueueEmpty(&q))
{
pNode pCur=QueueFront(&q);
if (flag)
{
if (pCur->_pLeft||pCur->_pRight)
return 0;
}
else
{
if (pCur ->_pLeft&&pCur->_pRight)
{
QueuePush(&q,pRoot->_pLeft);
QueuePush(&q,pRoot->_pRight);
}
else if (pCur->_pRight)
return 0;
else if(pCur->_pLeft) {
QueuePush(&q,pRoot->_pLeft);
flag=1;
}
else
flag=1;
QueuePop(&q);
}
}
return 1;
}
bool IsCompleteTree(BinaryTreeNode *pRoot)
{
if(pRoot == NULL)
return false;
queue<BinaryTreeNode*> q;
q.push(pRoot);
BinaryTreeNode* pCur = q.front();
while(pCur != NULL)
{
q.pop();
q.push(pCur -> left);
q.push(pCur -> right);
pCur = q.front();
}
q.pop();//把空pop出來
//因為以經有一個空了,所以只要頭不為空就不是完全二叉樹
while(! q.empty())
{
if(q.front() != NULL)
return false;
q.pop();
}
return true;
}