数据结构 5.5章 :二叉树的建立遍历及各种操作(c语言版)

本代码用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()

下面是二叉搜索树的各种操作的时间复杂度分析:

  1. 搜索节点:O(log n)
  • 在一棵高度平衡的二叉树中,每个节点的深度最多为log n,因此搜索节点的时间复杂度为O(log n)。
  1. 插入节点:O(log n)
  • 同样地,在一棵高度平衡的二叉树中,每次插入新节点的时候,最多只需要遍历树的一条从根节点到叶子节点的路径,其长度最多为log n,因此插入节点的时间复杂度也为O(log n)。
  1. 删除节点:O(log n)
  • 删除节点的时间复杂度与搜索操作类似,需要先找到待删除的节点,然后进行删除操作。由于一棵高度平衡的二叉树中每个节点的深度最多为log n,因此删除节点的时间复杂度也为O(log n)。
  1. 遍历节点: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 的左子树中。

综上所述,使用双重指针可以更方便地在递归过程中修改节点的指针信息,进而完成二叉搜索树的节点插入操作。

运行结果:
在这里插入图片描述
有些代码因某些原因没写注释,过后会继续提升代码质量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Everrrrr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值