本代码用switch语句可供选择二叉搜索树的十一个基本操作,主要思想为递归思想
操作包括:
插入节点、查找、查找最大值、查找最小值、按值删除、求二叉树深度、层序遍历、先序遍历、中序遍历、后序遍历、二叉树的销毁等
具体情况请看代码:
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef struct BiTNode
{
int data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
BiTree CreateBiTNode(int data);
void InsertBiTNode(BiTree *root, int data);
BiTree FindMinNode(BiTree root);
BiTree FindMaxNode(BiTree root);
BiTree DeleteBiTNode(BiTree root, int data);
BiTree SearchBiTNode(BiTree root, int data);
int Depth(BiTree root);
bool isBiTreeEmpty(BiTree root);
void PreOrder(BiTree root);
void InOrder(BiTree root);
void PostOrder(BiTree root);
void LevelTraverse(BiTree root);
void DestroyBiTree(BiTree root);
int main(int argc, char *argv[])
{
BiTree Tree = NULL;
int value, choice, target;
//先插入一组数据建立一棵二叉树
InsertBiTNode(&Tree, 2);
InsertBiTNode(&Tree, 23);
InsertBiTNode(&Tree, 12);
InsertBiTNode(&Tree, 45);
InsertBiTNode(&Tree, 11);
InsertBiTNode(&Tree, 18);
InsertBiTNode(&Tree, 0);
InsertBiTNode(&Tree, 56);
while(1)
{
printf("\n请选择操作:\n");
printf("1. 插入\n");
printf("2. 查找\n");
printf("3. 查找最大值\n");
printf("4. 查找最小值\n");
printf("5. 按值删除\n");
printf("6. 求该二叉树深度\n");
printf("7. 层次遍历\n");
printf("8. 先序遍历\n");
printf("9. 中序遍历\n");
printf("10. 后序遍历\n");
printf("11. 退出程序及销毁二叉树\n");
scanf("%d", &choice);
switch (choice)
{
case 1:
{
printf("请输入要插入的数据:\n");
scanf("%d", &value);
InsertBiTNode(&Tree, value);
break;
}//of case1
case 2:
{
printf("请输入要查找的数据:\n");
scanf("%d", &target);
BiTree result = SearchBiTNode(Tree, target);
if (result == NULL)
{
printf("未找到数据 %d。\n", target);
} //of if
else
{
printf("已找到数据 %d。\n", target);
}//of else
break;
}//of case2
case 3:
{
BiTree result = FindMaxNode(Tree);
if(result == NULL)
{
printf("该二叉树为空!\n");
}//of if
else
{
printf("最大值为:%d\n", result->data);
} //of else
break;
}
case 4:
{
BiTree result = FindMinNode(Tree);
if(result == NULL)
{
printf("该二叉树为空!\n");
}//of if
else
{
printf("最小值为:%d\n", result->data);
} //of else
break;
}
case 5:
{
printf("请输入要删除的数据:\n");
scanf("%d", &target);
Tree = DeleteBiTNode(Tree, target);
break;
}//of case5
case 6:
{
int depth = Depth(Tree);
printf("%d", depth);
break;
}//of case6
case 7:
{
printf("层次遍历结果为:\n");
LevelTraverse(Tree);
printf("\n");
break;
}//of case7
case 8:
{
printf("先序遍历结果为:\n");
PreOrder(Tree);
printf("\n");
break;
}//of case8
case 9:
{
printf("中序遍历结果为:\n");
InOrder(Tree);
printf("\n");
break;
}//of case9
case 10:
{
printf("后序遍历结果为:\n");
PostOrder(Tree);
printf("\n");
break;
}//of case10
case 11:
{
printf("程序已退出。\n");
//DestroyBiTree(Tree);
return 0;
}//of case11
default:
{
printf("无效操作,请重新输入。\n");
break;
}//of default
}//of switch
}
return 0;
}//of main
//创建一个未链接节点
BiTree CreateBiTNode(int data)
{
BiTree tempNode = (BiTree)malloc(sizeof(BiTNode));
if(tempNode == NULL)
{
printf("内存不够,申请空间失败!\n");
exit(1);
}//of if
tempNode->data = data;
tempNode->lchild = NULL;
tempNode->rchild = NULL;
return tempNode;
}
//按一定顺序插入节点
void InsertBiTNode(BiTree *root, int data)
{
if(*root == NULL)
{
*root = CreateBiTNode(data);
return;
}//of if
else if(data < (*root)->data)
{
InsertBiTNode(&((*root)->lchild), data);
}//of else if
else if(data > (*root)->data)
{
InsertBiTNode(&((*root)->rchild), data);
}//of else if
else
{
printf("%d 已存在, 无需插入.\n", data);
return;
}//of else
}//of InsertBiTNode()
//找最小节点
BiTree FindMinNode(BiTree root)
{
if(isBiTreeEmpty(root))
{
return NULL;
}//of if
BiTree p = root;
while(!isBiTreeEmpty(p->lchild))
{
p = p->lchild;
}//of while
return p;
}//of FindMinNode()
//找最大节点
BiTree FindMaxNode(BiTree root)
{
BiTree p = root;
if(isBiTreeEmpty(p))
{
return NULL;
}//of if
while(!isBiTreeEmpty(p->rchild))
{
p = p->rchild;
}//of while
return p;
}//of FindMaxNode()
//按值删除节点
BiTree DeleteBiTNode(BiTree root, int data)
{
if(isBiTreeEmpty(root))
{
return NULL;
}//of if
if(data == root->data)
{
//case1:删除叶子节点
if(root->lchild == NULL && root->rchild == NULL)
{
free(root);
return NULL;
}//of if
//case2:删除节点有一个孩子节点
else if(root->lchild == NULL || root->rchild == NULL)
{
BiTree tempChild = (root->lchild != NULL) ? root->lchild : root->rchild;
free(root);
return tempChild;
}//of else if
//case3:删除节点有两个孩子节点
else
{
BiTree successor = FindMinNode(root->rchild);
root->data = successor->data;
root->rchild = DeleteBiTNode(root->rchild, data);
return root;
}//of else
}//of if
else if(data < root->data)
{
root->lchild = DeleteBiTNode(root->lchild, data);
}//of else if
else if(data > root->data)
{
root->rchild = DeleteBiTNode(root->rchild, data);
}//of else if
return root;
}//of DeleteBiTNode()
//按值查找节点
BiTree SearchBiTNode(BiTree root, int data)
{
if(isBiTreeEmpty(root))
{
return NULL;
}//of if
if(data == root->data)
{
return root;
}//of if
else if(data < root->data)
{
return SearchBiTNode(root->lchild, data);
}//of else if
else if(data > root->data)
{
return SearchBiTNode(root->rchild, data);
}//of else if
}//SearchBiNode()
//求二叉树的深度
int Depth(BiTree root)
{
if(root == NULL)
{
return 0;
}//of if
else
{
int m = Depth(root->lchild);
int n = Depth(root->rchild);
return (m > n) ? (m + 1) : (n + 1);
}//of else
}//Depth()
//二叉树判空
bool isBiTreeEmpty(BiTree root)
{
if(root == NULL)
{
return true;
}//of if
return false;
}//of isBiTreeEmpty()
//先序遍历二叉树
void PreOrder(BiTree root)
{
if(isBiTreeEmpty(root))
{
return;
}//of if
printf("%d ", root->data);
PreOrder(root->lchild);
PreOrder(root->rchild);
} //of PreOrder()
//中序遍历二叉树
void InOrder(BiTree root)
{
if(isBiTreeEmpty(root))
{
return;
}//of if
InOrder(root->lchild);
printf("%d ", root->data);
InOrder(root->rchild);
} //of InOrder()
//后序遍历二叉树
void PostOrder(BiTree root)
{
if(isBiTreeEmpty(root))
{
return;
}//of if
PostOrder(root->lchild);
PostOrder(root->rchild);
printf("%d ", root->data);
} //of PostOrder()
//层序遍历二叉树
void LevelTraverse(BiTree root)
{
if(isBiTreeEmpty(root)) //树为空结束递归
{
return;
}//of if
//创建一个简单顺序队列
BiTree queue[1000];
int rear = 0, front = 0;
//先将根节点入队
queue[rear++] = root;
while(rear > front) //队列不为空则继续循环
{
//出队并输出
BiTree tempNode = queue[front++];
printf("%d ", tempNode->data);
if(tempNode->lchild != NULL) //左孩子不为空则入队
{
queue[rear++] = tempNode->lchild;
}//of if
if(tempNode->rchild != NULL) //右孩子不为空则入队
{
queue[rear++] = tempNode->rchild;
}//of if
}//of while
}//of LevelTraverse
//递归销毁二叉树
void DestroyBiTree(BiTree root)
{
//若不为空,递归销毁左右子树
if(!isBiTreeEmpty(root))
{
DestroyBiTree(root->lchild);
DestroyBiTree(root->rchild);
free(root);
}//of if
} //of DestroyBiTree()
下面是二叉搜索树的各种操作的时间复杂度分析:
- 搜索节点:O(log n)
- 在一棵高度平衡的二叉树中,每个节点的深度最多为log n,因此搜索节点的时间复杂度为O(log n)。
- 插入节点:O(log n)
- 同样地,在一棵高度平衡的二叉树中,每次插入新节点的时候,最多只需要遍历树的一条从根节点到叶子节点的路径,其长度最多为log n,因此插入节点的时间复杂度也为O(log n)。
- 删除节点:O(log n)
- 删除节点的时间复杂度与搜索操作类似,需要先找到待删除的节点,然后进行删除操作。由于一棵高度平衡的二叉树中每个节点的深度最多为log n,因此删除节点的时间复杂度也为O(log n)。
- 遍历节点:O(n)
- 对于二叉搜索树的遍历操作,最坏情况下,需要遍历整个树的所有节点,因此时间复杂度为O(n)。其中,前序遍历、中序遍历和后序遍历的时间复杂度都为O(n),而层次遍历的时间复杂度为O(n),但是空间复杂度为O(n),需要使用队列维护。
总的来说,二叉搜索树是一种高效的数据结构,能够在O(log n)的时间内完成许多基本操作。然而,在实际运用中,如果二叉搜索树高度不平衡,有些操作的复杂度可能会退化为O(n),因此需要注意维护二叉搜索树的平衡性。
对插入操作双重指针的理解:
二叉搜索树插入节点函数要用双重指针,因为我们需要修改原来指针所指向的地址,也就是修改节点的指针信息。如果只使用一个指向根节点的指针,即 BiTree *root,那么在递归过程中,我们只能修改根节点的左右指针,而无法修改它的父指针。
举个例子,假设树中已存在值为 3 的节点,并要插入一个值为 2 的节点。如果我们只使用单指针,即 *root,那么在递归到节点值为 3 的位置时,无法修改它的父指针,也就无法将节点 2 插入到 3 的左子树。
因此,我们需要使用双重指针,即 BiTree **root,来保存父节点的指针地址。这样,当我们递归到节点值为 3 时,可以通过 **root 找到其父节点的指针,并修改其左右指针所对应的地址,将节点 2 插入到 3 的左子树中。
综上所述,使用双重指针可以更方便地在递归过程中修改节点的指针信息,进而完成二叉搜索树的节点插入操作。
运行结果:
有些代码因某些原因没写注释,过后会继续提升代码质量。