c語言實現二叉樹的插入、查詢、刪除、列印樹
阿新 • • 發佈:2018-12-30
目錄:
二叉樹的關鍵概念:
- 每個節點是一個自引用結構體,形式如下:
struct TreeNode {
struct TreeNode *leftPtr; /* pointer to left subtree */
int data; /* node data */
struct TreeNode *rightPtr; /* pointer to right subtree */
};
- 從根部節點開始,每個節點擁有兩個子節點(NULL或者一個節點),稱為左節點與右節點,每個節點的左部分與右部分又分別稱為該節點的左子樹與右子樹。
- 每個節點的鍵值大於左節點,小於右節點;每個節點的鍵值大於左子樹所有節點的鍵值,小於右子樹所有節點的鍵值。所以二叉樹是按節點鍵值排序的資料結構。
- 二叉樹的某個節點,如果不是葉節點,則有左子樹或右子樹,是一個更小的樹,因此可以遞迴地處理關於樹的一些問題。
二叉樹的插入
- 思路:將要插入節點的鍵值與根節點鍵值比較,如果小於根節點鍵值,則插入根節點的左子樹,如果大於根節點的鍵值,則插入根節點的右子樹,插入子樹相當於插入一個更小的樹,因此可以用遞迴方法實現,直到找到沒有子樹的節點,將新節點插到其下面。注意,新節點插入後,最終只會成為葉節點。
函式程式碼如下(測試插入、刪除、列印功能的原始碼在最後面,此處只給出插入函式程式碼):
void insertNode(TreeNodePtr *treePtr, int value)
{
/* if treePtr is NULL */
if (*treePtr == NULL) {
*treePtr = malloc(sizeof(TreeNode));
if (*treePtr != NULL) {
(*treePtr)->data = value;
(*treePtr)->leftPtr = NULL;
(*treePtr)->rightPtr = NULL;
}
else {
printf("%d not inserted. No memory available.\n", value);
}
}
else {
/* insert node in left subtree */
if (value < (*treePtr)->data) {
insertNode(&((*treePtr)->leftPtr), value);
}
else {
/* insert node in right subtree */
if (value >(*treePtr)->data) {
insertNode(&((*treePtr)->rightPtr), value);
}
else {
printf("dup");
}
}
}
}
二叉樹的查詢
- 思路:與插入類似,從根節點開始,將查詢的鍵值與根節點鍵值比較,若相等,則返回指向該節點的指標,若查詢的鍵值比它大,則從根節點的右子樹開始查詢,若查詢的鍵值比它小,則從根節點的左子樹開始查詢。可以用遞迴方法實現,類似於插入。這裡我用迭代實現,能用迭代還是用迭代,因為遞迴開銷比較大。
函式程式碼如下:
TreeNode *binaryTreeSereach(TreeNode * const treePtr, int value)
{
TreeNode *tempPtr = treePtr;
while (tempPtr != NULL && tempPtr->data != value)
{
if (value > tempPtr->data)
tempPtr = tempPtr->rightPtr;
else
tempPtr = tempPtr->leftPtr;
}
return tempPtr;
}
二叉樹的刪除
相比於二叉樹的插入和查詢,刪除一個節點要複雜一些,原因是要保證二叉樹的排序性質。二叉樹刪除有如下三種情況:
1. 刪除節點是葉節點,即沒有子節點,或者說左右子節點都是NULL。這種情況下,只需要把刪除節點的父節點中對應的指標指向NULL即可。然後釋放掉刪除節點的空間。
2. 刪除節點有一個子節點(左子節點或右子節點),這種情況下,把刪除節點的父節點中對應的指標指向刪除節點的子節點即可。然後釋放掉刪除節點的空間
3. 刪除節點有兩個子節點,這種情況下,必須要找到一個替代刪除節點的替代節點,並且保證二叉樹的排序性。根據二叉樹的排序性,可知替代節點的鍵值必須最接近刪除節點鍵值。比刪除節點鍵值小的所有鍵值中最大那個,或者是比刪除節點鍵值大的所有鍵值中最小的那個,是符合要求的。這兩個鍵值所在的節點分別在刪除節點的左子樹中最右邊的節點,刪除節點右子樹中最左邊的節點。以從左子樹中找最大鍵值節點為例,演算法如下:
- 找到刪除節點以及它的父節點
- 在刪除節點的左子樹中,向下向右遍歷,找到替代節點以及它的父節點
- 刪除節點的父節點中對應的指標指向替代節點
- 替代節點中的右子節點指標指向刪除節點的右子樹
- 如果替代節點的父節點不是刪除節點,則將替代節點的左子節點指標指向刪除節點的左子樹,並且替代節點的父節點中對應的指標指向替代節點的左子節點
- 釋放刪除節點的空間
注意:第二步中找到的替代節點,可能會有左子樹,但一定沒有右子樹。第五步要判斷替代節點的父節點不是刪除節點後,才將替代節點的左子節點指標指向刪除節點的左子樹,否則會出現替代節點左子節點指標指向自己的情況,從而丟失替代節點的左子樹。
另外,還有一種實現相同效果的的方法,即將替代節點中的資料賦給刪除節點,然後釋放替代節點的空間。這種方法其實是刪除了替代節點,並沒有真正刪除想要刪除的節點。而且如果節點包括一個鍵值和很多其他的資料,則賦值語句會很多。在最後面的測試過程中,我也給出了這個函式的實現,void deleteNode2(TreeNode **treePtrP, int value)
程式碼如下:
void deleteNode(TreeNode **treePtrP, int value)
{
TreeNode *deleteNodePtr = *treePtrP;
TreeNode *parentNodeOfDeletePtr = NULL;
TreeNode *substituteNodePtr;
TreeNode *parentNodeOfSubstitutePtr;
//find deleNode and its parentNode
while (deleteNodePtr != NULL && value != deleteNodePtr->data)
{
parentNodeOfDeletePtr = deleteNodePtr;
if (deleteNodePtr->data > value)
{
deleteNodePtr = deleteNodePtr->leftPtr;
}
else
{
deleteNodePtr = deleteNodePtr->rightPtr;
}
}
//case that can't find such Node
if (deleteNodePtr == NULL)
{
printf("no such Node, delete fail\n\n");
return;
}
//delete a leafNode
if (deleteNodePtr->leftPtr == NULL && deleteNodePtr->rightPtr == NULL)
{
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = NULL;
}
else if (parentNodeOfDeletePtr->leftPtr == deleteNodePtr)
{
parentNodeOfDeletePtr->leftPtr = NULL;
}
else
{
parentNodeOfDeletePtr->rightPtr = NULL;
}
}
//delete a Node which has a left child Node
else if (deleteNodePtr->leftPtr != NULL && deleteNodePtr->rightPtr == NULL)
{
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = deleteNodePtr->leftPtr;
}
else if (parentNodeOfDeletePtr->rightPtr == deleteNodePtr)
parentNodeOfDeletePtr->rightPtr = deleteNodePtr->leftPtr;
else
parentNodeOfDeletePtr->leftPtr = deleteNodePtr->leftPtr;
}
//delete a Node which has a right child Node
else if (deleteNodePtr->leftPtr == NULL && deleteNodePtr->rightPtr != NULL)
{
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = deleteNodePtr->rightPtr;
}
else if (parentNodeOfDeletePtr->rightPtr == deleteNodePtr)
parentNodeOfDeletePtr->rightPtr = deleteNodePtr->rightPtr;
else
parentNodeOfDeletePtr->leftPtr = deleteNodePtr->rightPtr;
}
//delete a Node which has a left and a right child Node
else
{
parentNodeOfSubstitutePtr = deleteNodePtr;
substituteNodePtr = deleteNodePtr->leftPtr;
//search down and right to find substituteNode and its parentNode
while (substituteNodePtr->rightPtr != NULL)
{
parentNodeOfSubstitutePtr = substituteNodePtr;
substituteNodePtr = substituteNodePtr->rightPtr;
}
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = substituteNodePtr;
}
else if (parentNodeOfDeletePtr->leftPtr == deleteNodePtr)
{
parentNodeOfDeletePtr->leftPtr = substituteNodePtr;
}
else
{
parentNodeOfDeletePtr->rightPtr = substituteNodePtr;
}
substituteNodePtr->rightPtr = deleteNodePtr->rightPtr;
if (parentNodeOfSubstitutePtr != deleteNodePtr)
{
substituteNodePtr->leftPtr = deleteNodePtr->leftPtr;
if (parentNodeOfSubstitutePtr->leftPtr == substituteNodePtr)
{
parentNodeOfSubstitutePtr->leftPtr = substituteNodePtr->leftPtr;
}
else
{
parentNodeOfSubstitutePtr->rightPtr = substituteNodePtr->leftPtr;
}
}
}
free(deleteNodePtr);
}
二叉樹的列印
- 從根節點開始,先輸出右子樹,再輸出節點鍵值,再輸出左子樹。採用遞迴法
程式碼如下:
void outputTree(TreeNodePtr treePtr, int spaces)
{
int loop;
while (treePtr != NULL) {
outputTree(treePtr->rightPtr, spaces + 4);
for (loop = 1; loop <= spaces; loop++) {
printf(" ");
}
printf("%d\n", treePtr->data);
outputTree(treePtr->leftPtr, spaces + 4);
treePtr = NULL;
}
}
測試結果截圖
測試插入、刪除、列印樹原始碼
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct TreeNode {
struct TreeNode *leftPtr; /* pointer to left subtree */
int data; /* node data */
struct TreeNode *rightPtr; /* pointer to right subtree */
};
typedef struct TreeNode TreeNode;
void insertNode(TreeNode **treePtr, int value);
TreeNode * binaryTreeSereach(TreeNode * const treePtr, int value);
void deleteNode(TreeNode **treePtrP, int value);
void outputTree(TreeNode *treePtr, int spaces);
void deleteNode2(TreeNode **treePtrP, int value);
int main(void)
{
int arr[] = { 45, 83, 28, 97, 71, 40, 18, 77, 99, 92, 72, 69, 44, 32, 19, 11 };
int i; /* loop counter */
int item; /* value to deal with */
int totalSpaces = 0; /* spaces preceding output */
TreeNode *rootPtr = NULL; /* points to the tree root */
srand(time(NULL)); /* randomize */
printf("The numbers being placed in the tree are:\n\n");
for (i = 0; i < sizeof(arr) / sizeof(int); i++) {
item = arr[i];
printf("%3d", item);
insertNode(&rootPtr, item);
}
printf("\n\n\nnow the tree is:\n\n");
if (rootPtr == NULL)
printf("empty tree\n");
else
outputTree(rootPtr, totalSpaces);
//random delete Nodes, then output the tree
while (rootPtr != NULL)
{
item = rand() % 16;
printf("\n\nafter delete %d:\n\n", arr[item]);
deleteNode2(&rootPtr, arr[item]);
if (rootPtr == NULL)
printf("empty tree\n");
else
outputTree(rootPtr, totalSpaces);
}
return 0;
}
void insertNode(TreeNode **treePtr, int value)
{
/* if treePtr is NULL */
if (*treePtr == NULL) {
*treePtr = malloc(sizeof(TreeNode));
if (*treePtr != NULL) {
(*treePtr)->data = value;
(*treePtr)->leftPtr = NULL;
(*treePtr)->rightPtr = NULL;
}
else {
printf("%d not inserted. No memory available.\n", value);
}
}
else {
/* insert node in left subtree */
if (value < (*treePtr)->data) {
insertNode(&((*treePtr)->leftPtr), value);
}
else {
/* insert node in right subtree */
if (value >(*treePtr)->data) {
insertNode(&((*treePtr)->rightPtr), value);
}
else {
printf("dup");
}
}
}
}
TreeNode *binaryTreeSereach(TreeNode * const treePtr, int value)
{
TreeNode *tempPtr = treePtr;
while (tempPtr != NULL && tempPtr->data != value)
{
if (value > tempPtr->data)
tempPtr = tempPtr->rightPtr;
else
tempPtr = tempPtr->leftPtr;
}
return tempPtr;
}
void deleteNode(TreeNode **treePtrP, int value)
{
TreeNode *deleteNodePtr = *treePtrP;
TreeNode *parentNodeOfDeletePtr = NULL;
TreeNode *substituteNodePtr;
TreeNode *parentNodeOfSubstitutePtr;
//find deleNode and its parentNode
while (deleteNodePtr != NULL && value != deleteNodePtr->data)
{
parentNodeOfDeletePtr = deleteNodePtr;
if (deleteNodePtr->data > value)
{
deleteNodePtr = deleteNodePtr->leftPtr;
}
else
{
deleteNodePtr = deleteNodePtr->rightPtr;
}
}
//case that can't find such Node
if (deleteNodePtr == NULL)
{
printf("no such Node, delete fail\n\n");
return;
}
//delete a leafNode
if (deleteNodePtr->leftPtr == NULL && deleteNodePtr->rightPtr == NULL)
{
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = NULL;
}
else if (parentNodeOfDeletePtr->leftPtr == deleteNodePtr)
{
parentNodeOfDeletePtr->leftPtr = NULL;
}
else
{
parentNodeOfDeletePtr->rightPtr = NULL;
}
}
//delete a Node which has a left child Node
else if (deleteNodePtr->leftPtr != NULL && deleteNodePtr->rightPtr == NULL)
{
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = deleteNodePtr->leftPtr;
}
else if (parentNodeOfDeletePtr->rightPtr == deleteNodePtr)
parentNodeOfDeletePtr->rightPtr = deleteNodePtr->leftPtr;
else
parentNodeOfDeletePtr->leftPtr = deleteNodePtr->leftPtr;
}
//delete a Node which has a right child Node
else if (deleteNodePtr->leftPtr == NULL && deleteNodePtr->rightPtr != NULL)
{
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = deleteNodePtr->rightPtr;
}
else if (parentNodeOfDeletePtr->rightPtr == deleteNodePtr)
parentNodeOfDeletePtr->rightPtr = deleteNodePtr->rightPtr;
else
parentNodeOfDeletePtr->leftPtr = deleteNodePtr->rightPtr;
}
//delete a Node which has a left and a right child Node
else
{
parentNodeOfSubstitutePtr = deleteNodePtr;
substituteNodePtr = deleteNodePtr->leftPtr;
//search down and right to find substituteNode and its parentNode
while (substituteNodePtr->rightPtr != NULL)
{
parentNodeOfSubstitutePtr = substituteNodePtr;
substituteNodePtr = substituteNodePtr->rightPtr;
}
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = substituteNodePtr;
}
else if (parentNodeOfDeletePtr->leftPtr == deleteNodePtr)
{
parentNodeOfDeletePtr->leftPtr = substituteNodePtr;
}
else
{
parentNodeOfDeletePtr->rightPtr = substituteNodePtr;
}
substituteNodePtr->rightPtr = deleteNodePtr->rightPtr;
if (parentNodeOfSubstitutePtr != deleteNodePtr)
{
substituteNodePtr->leftPtr = deleteNodePtr->leftPtr;
if (parentNodeOfSubstitutePtr->leftPtr == substituteNodePtr)
{
parentNodeOfSubstitutePtr->leftPtr = substituteNodePtr->leftPtr;
}
else
{
parentNodeOfSubstitutePtr->rightPtr = substituteNodePtr->leftPtr;
}
}
}
free(deleteNodePtr);
}
void outputTree(TreeNode *treePtr, int spaces)
{
int loop;
while (treePtr != NULL) {
outputTree(treePtr->rightPtr, spaces + 4);
for (loop = 1; loop <= spaces; loop++) {
printf(" ");
}
printf("%d\n", treePtr->data);
outputTree(treePtr->leftPtr, spaces + 4);
treePtr = NULL;
}
}
void deleteNode2(TreeNode **treePtrP, int value)
{
TreeNode *deleteNodePtr = *treePtrP;
TreeNode *parentNodeOfDeletePtr = NULL;
TreeNode *substituteNodePtr;
TreeNode *parentNodeOfSubstitutePtr;
//find deleNode and its parentNode
while (deleteNodePtr != NULL && value != deleteNodePtr->data)
{
parentNodeOfDeletePtr = deleteNodePtr;
if (deleteNodePtr->data > value)
{
deleteNodePtr = deleteNodePtr->leftPtr;
}
else
{
deleteNodePtr = deleteNodePtr->rightPtr;
}
}
//case that can't find such Node
if (deleteNodePtr == NULL)
{
printf("no such Node, delete fail\n\n");
return;
}
// delete a leafNode
if (deleteNodePtr->leftPtr == NULL && deleteNodePtr->rightPtr == NULL)
{
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = NULL;
}
else if (parentNodeOfDeletePtr->leftPtr == deleteNodePtr)
{
parentNodeOfDeletePtr->leftPtr = NULL;
}
else
{
parentNodeOfDeletePtr->rightPtr = NULL;
}
}
//delete a Node which has a left child Node
else if (deleteNodePtr->leftPtr != NULL && deleteNodePtr->rightPtr == NULL)
{
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = deleteNodePtr->leftPtr;
}
else if (parentNodeOfDeletePtr->rightPtr == deleteNodePtr)
parentNodeOfDeletePtr->rightPtr = deleteNodePtr->leftPtr;
else
parentNodeOfDeletePtr->leftPtr = deleteNodePtr->leftPtr;
}
//delete a Node which has a right child Node
else if (deleteNodePtr->leftPtr == NULL && deleteNodePtr->rightPtr != NULL)
{
//delete Node is root
if (parentNodeOfDeletePtr == NULL)
{
*treePtrP = deleteNodePtr->rightPtr;
}
else if (parentNodeOfDeletePtr->rightPtr == deleteNodePtr)
parentNodeOfDeletePtr->rightPtr = deleteNodePtr->rightPtr;
else
parentNodeOfDeletePtr->leftPtr = deleteNodePtr->rightPtr;
}
//delete a Node which has a left and a right child Node
else
{
//find substituteNode and its parentNode
parentNodeOfSubstitutePtr = deleteNodePtr;
substituteNodePtr = deleteNodePtr->leftPtr;
//search down and right
while (substituteNodePtr->rightPtr != NULL)
{
parentNodeOfSubstitutePtr = substituteNodePtr;
substituteNodePtr = substituteNodePtr->rightPtr;
}
if (parentNodeOfSubstitutePtr->leftPtr == substituteNodePtr)
{
parentNodeOfSubstitutePtr->leftPtr = substituteNodePtr->leftPtr;
}
else
{
parentNodeOfSubstitutePtr->rightPtr = substituteNodePtr->leftPtr;
}
deleteNodePtr->data = substituteNodePtr->data;
deleteNodePtr = substituteNodePtr;
}
free(deleteNodePtr);
}