二叉排序樹
二叉排序樹(Binary Sort Tree)又稱二叉查找樹(Binary Search Tree),亦稱二叉搜索樹。
性質
二叉排序樹或者是一棵空樹,是具有下列性質的二叉樹:
(1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
(2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
(3)左、右子樹也分別為二叉排序樹;
(4)沒有鍵值相等的節點。
可以看出,二叉查找樹是一個遞歸的數據結構,且對二叉查找樹進行中序遍歷,可以得到一個遞增的有序序列。
首先,我們來定義一下 BST 的結點結構體:
//樹的定義
typedef struct TreeNode
{
int val;
struct TreeNode *left;
struct TreeNode *right;
};
插入
//二叉排序樹的插入【遞歸】
int BST_insert(struct TreeNode *p,int k)
{
//二叉樹中插入一個關鍵字為k的結點
if(p == NULL)
{
p = (struct TreeNode*)malloc(sizeof(struct TreeNode));
p ->val = k;
p ->left = p ->right = NULL;
return 1; //返回1表示成功
}
//樹中存在相同的結點
else if(k == p ->val)
return 0;
//插入到左子樹中
else if(k < p ->val)
return BST_insert(p ->left,k);
//插入到右子樹中
else
return BST_insert(p ->right ,k);
}
注意,插入的新結點一定是某個葉結點。另外,插入操作既可以遞歸實現,也可以使用非遞歸(迭代)實現。通常來說非遞歸的效率會更高。
/**
* 非遞歸插入:將關鍵字k插入到二叉查找樹
*/
int BST_Insert_NonRecur(BSTree &T, int k)
{
Node* pre = NULL; // 記錄上一個結點
Node* t = T;
while(t != NULL)
{
pre = t;
if(k < t->key)
t = t->left;
else if(k > t->key)
t = t->right;
else
return 0;
}
Node* node = (Node*)malloc(sizeof(Node));
node->key = k;
node->left = NULL;
node->right = NULL;
node->parent = pre;
if(pre == NULL)
T = node;
else
{
if(k < pre->key)
pre->left = node;
else
pre->right = node;
}
return 1;
}
創建
//二叉樹的構建
void BST_create(struct TreeNode *T,int *str,int n)
{
//用關鍵字數組建立一個二叉排序樹
T = NULL; //初始時為空樹
int i = 0;
//依次將每個元素插入
while(i < n)
{
BST_insert(T,str[i]);
i++;
}
}
遍歷
//【前序遍歷】
void preorder(struct TreeNode *T)
{
if(T != NULL)
{
printf("%d\t",T ->val); //打印根結點
inorder(T ->left); //遞歸遍歷左子樹
inorder(T ->right); //遞歸遍歷右子樹
}
}
//【后序遍歷】
void inorder(struct TreeNode *T)
{
if(T != NULL)
{
inorder(T ->left); //遞歸遍歷左子樹
inorder(T ->right); //遞歸遍歷右子樹
printf("%d\t",T ->val); //打印根結點
}
}
//【中序遍歷】
void postorder(struct TreeNode *T)
{
if(T != NULL)
{
inorder(T ->left); //遞歸遍歷左子樹
printf("%d\t",T ->val); //打印根結點
inorder(T ->right); //遞歸遍歷右子樹
}
}
完整代碼
#include <stdio.h>
#include <stdlib.h>
//樹的定義
typedef struct TreeNode
{
int val;
struct TreeNode *left;
struct TreeNode *right;
};
struct TreeNode *T;
//二叉排序樹的插入
int BST_insert(struct TreeNode *p,int k)
{
//二叉樹中插入一個關鍵字為k的結點
if(p == NULL)
{
p = (struct TreeNode*)malloc(sizeof(struct TreeNode));
p ->val = k;
p ->left = p ->right = NULL;
return 1; //返回1表示成功
}
//樹中存在相同的結點
else if(k == p->val)
return 0;
//插入到左子樹中
else if(k < p ->val)
return BST_insert(p ->left,k);
//插入到右子樹中
else
return BST_insert(p ->right ,k);
}
//二叉樹的構建
void BST_create(struct TreeNode *T,int *str,int n)
{
//用關鍵字數組建立一個二叉排序樹
T = NULL;//初始時為空樹
int i;
//依次將每個元素插入
for(i = 0;i < n;i++)
{
BST_insert(T,str[i]);
}
}
//【前序遍歷】
void preorder(struct TreeNode *T)
{
if(T != NULL)
{
printf("%d\t",T ->val); //打印根結點
inorder(T ->left); //遞歸遍歷左子樹
inorder(T ->right); //遞歸遍歷右子樹
}
}
//【中序遍歷】
void inorder(struct TreeNode *T)
{
if(T != NULL)
{
inorder(T ->left); //遞歸遍歷左子樹
inorder(T ->right); //遞歸遍歷右子樹
printf("%d\t",T ->val); //打印根結點
}
}
//【后序遍歷】
void postorder(struct TreeNode *T)
{
if(T != NULL)
{
inorder(T ->left); //遞歸遍歷左子樹
printf("%d\t",T ->val); //打印根結點
inorder(T ->right); //遞歸遍歷右子樹
}
}
int main()
{
int length,str[] = {3,1,4,NULL,2};
struct TreeNode *root;
length = sizeof(str) / sizeof(str[0]);
BST_create(root,str,length);
printf("前序遍歷:");
preorder(root);
printf("\n中序遍歷:");
inorder(root);
printf("\n后序遍歷:");
postorder(root);
return 0;
}
問題
BST_insert(T,str[i]);每次調用時,傳進去的 T 為什么都是 NULL ?
該問題是:傳值出現問題,待有緣人解決!
下面給出新的思路:
以下程序均在VS下調試!
這位大哥在創建二叉樹時居然是一個節點一個節點寫入的,真的強![2]
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct node
{
int nValue;
struct node* pLeft;
struct node* pRight;
}BiTree;
BiTree* CreateBiTree(void)
{
BiTree* pRoot = NULL;
//根
pRoot = (BiTree*)malloc(sizeof(BiTree));
if (NULL == pRoot)
{
printf("pRoot空間分配失敗!\n");
exit(-1);
}
pRoot->nValue = 1;
pRoot->pLeft = NULL;
pRoot->pRight = NULL;
//根的左
pRoot->pLeft = (BiTree*)malloc(sizeof(BiTree));
if (NULL == pRoot->pLeft)
{
printf("pRoot->pLeft空間分配失敗!\n");
exit(-1);
}
pRoot->pLeft->nValue = 2;
pRoot->pLeft->pLeft = NULL;
pRoot->pLeft->pRight = NULL;
//根的右
pRoot->pRight = (BiTree*)malloc(sizeof(BiTree));
if (NULL == pRoot->pRight)
{
printf("pRoot->pRight空間分配失敗!\n");
exit(-1);
}
pRoot->pRight->nValue = 3;
pRoot->pRight->pLeft = NULL;
pRoot->pRight->pRight = NULL;
//左的左
pRoot->pLeft->pLeft = (BiTree*)malloc(sizeof(BiTree));
if (NULL == pRoot->pLeft->pLeft)
{
printf("pRoot->pLeft->pLeft空間分配失敗!\n");
exit(-1);
}
pRoot->pLeft->pLeft->nValue = 4;
pRoot->pLeft->pLeft->pLeft = NULL;
pRoot->pLeft->pLeft->pRight = NULL;
//左的右
pRoot->pLeft->pRight = (BiTree*)malloc(sizeof(BiTree));
if (NULL == pRoot->pLeft->pRight)
{
printf("pRoot->pLeft->pRight空間分配失敗!\n");
exit(-1);
}
pRoot->pLeft->pRight->nValue = 5;
pRoot->pLeft->pRight->pLeft = NULL;
pRoot->pLeft->pRight->pRight = NULL;
//右的左
pRoot->pRight->pLeft = (BiTree*)malloc(sizeof(BiTree));
if (NULL == pRoot->pRight->pLeft)
{
printf("pRoot->pRight->pLeft空間分配失敗!\n");
exit(-1);
}
pRoot->pRight->pLeft->nValue = 6;
pRoot->pRight->pLeft->pLeft = NULL;
pRoot->pRight->pLeft->pRight = NULL;
return pRoot;
}
//遞歸創建二叉樹
void RecCreateBiTree(BiTree** ppRoot)
{
int nNum;
assert(ppRoot != NULL);
//輸入節點的值
scanf("%d", &nNum);
//檢測是否是結束標志
if (0 == nNum)
{
return;
}
*ppRoot = (BiTree*)malloc(sizeof(BiTree));
if (NULL == *ppRoot)
{
printf("*ppRoot空間分配失敗!");
exit(-1);
}
(*ppRoot)->nValue = nNum;
(*ppRoot)->pLeft = NULL;
(*ppRoot)->pRight = NULL;
//處理當前節點的左和右
RecCreateBiTree(&(*ppRoot)->pLeft);
RecCreateBiTree(&(*ppRoot)->pRight);
}
//前序遍歷
void PreOrderTraversal(BiTree* pRoot)
{
if (NULL == pRoot)
{
return;
}
printf("%d ", pRoot->nValue);
PreOrderTraversal(pRoot->pLeft);
PreOrderTraversal(pRoot->pRight);
}
//中序遍歷
void MidOrderTraversal(BiTree* pRoot)
{
if (NULL == pRoot)
{
return;
}
MidOrderTraversal(pRoot->pLeft);
printf("%d ", pRoot->nValue);
MidOrderTraversal(pRoot->pRight);
}
//后序遍歷
void LastOrderTraversal(BiTree* pRoot)
{
if (NULL == pRoot)
{
return;
}
LastOrderTraversal(pRoot->pLeft);
LastOrderTraversal(pRoot->pRight);
printf("%d ", pRoot->nValue);
}
int main(void)
{
printf("新建二叉樹:");
BiTree* pRoot = CreateBiTree();
printf("前序遍歷:");
PreOrderTraversal(pRoot);
printf("\n");
printf("中序遍歷:");
MidOrderTraversal(pRoot);
printf("\n");
printf("后序遍歷:");
LastOrderTraversal(pRoot);
system("pause");
return 0;
}
下面的這個參考知乎一位老哥[1],稍微修改一下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
struct Node
{
int data;
struct Node* pleft;
struct Node* pright;
}Node;
//參數聲明
struct Node* createnode(int value);
struct Node* addnode(int value, struct Node* pnode);
void ppreorder(struct TreeNode* T);
void pinorder(struct TreeNode* T);
void ppostorder(struct TreeNode* T);
void listnodes(struct Node* pnode);
int Treeheight(struct Node* pnode);
struct Node* createnode(int value)
{
struct Node* pnode = (struct Node*)malloc(sizeof(struct Node));
pnode->data = value;
pnode->pleft = pnode->pright = NULL;
return pnode;
}
struct Node* addnode(int value, struct Node* pnode)
{
if (pnode == NULL)
return createnode(value);
if (value == pnode->data)
{
return pnode;
}
if (value < pnode->data)
{
if (pnode->pleft == NULL)
{
pnode->pleft = createnode(value);
return pnode->pleft;
}
else
{
return addnode(value, pnode->pleft);
}
}
else
{
if (pnode->pright == NULL)
{
pnode->pright = createnode(value);
return pnode->pright;
}
else
{
return addnode(value, pnode->pright);
}
}
}
//【前序遍歷】
void ppreorder(struct Node* pnode)
{
if (pnode != NULL)
{
printf("%d\t", pnode->data); //打印根結點
pinorder(pnode->pleft); //遞歸遍歷左子樹
pinorder(pnode->pright); //遞歸遍歷右子樹
}
}
//【中序遍歷】
void pinorder(struct Node* pnode)
{
if (pnode != NULL)
{
pinorder(pnode->pleft); //遞歸遍歷左子樹
pinorder(pnode->pright); //遞歸遍歷右子樹
printf("%d\t", pnode->data); //打印根結點
}
}
//【后序遍歷】
void ppostorder(struct Node* pnode)
{
if (pnode != NULL)
{
pinorder(pnode->pleft); //遞歸遍歷左子樹
printf("%d\t", pnode->data); //打印根結點
pinorder(pnode->pright); //遞歸遍歷右子樹
}
}
void listnodes(struct Node* pnode)
{
if (pnode != NULL)
{
listnodes(pnode->pleft);
printf("%d\n", pnode->data);
listnodes(pnode->pright);
}
}
int Treeheight(struct Node* pnode)
{
int LD, RD;
if (pnode == NULL)
{
return 0;
}
else
{
LD = Treeheight(pnode->pleft);
RD = Treeheight(pnode->pright);
return (LD >= RD ? LD : RD) + 1;
}
}
int main(void)
{
int i;
struct Node* proot = NULL;
int length, str[] = { 3,1,4,NULL,2 };
length = sizeof(str) / sizeof(str[0]);
for (i = 0; i < length; i++)
{
if (proot == NULL)
{
proot = createnode(str[i]);
}
else
{
addnode(str[i], proot);
}
}
printf("新建二叉樹:");
listnodes(proot);
printf("\nThe height of tree is %d!", Treeheight(proot));
printf("\n前序遍歷:");
ppreorder(proot);
printf("\n中序遍歷:");
pinorder(proot);
printf("\n后序遍歷:");
ppostorder(proot);
return 0;
}
參考
2、創建二叉樹
