C实现二叉查找树(binary search tree)

261 阅读2分钟

树的定义

BST的实现

通用头文件 fatal.h

#include <stdio.h>
#include <stdlib.h>

#define Error( Str )        FatalError( Str )
#define FatalError( Str )   fprintf( stderr, "%s\n", Str ), exit( 1 )

声明头文件 tree.h

typedef int ElementType;

#ifndef _Tree_H
#define _Tree_H

struct TreeNode;
typedef struct TreeNode *Position;
typedef struct TreeNode *SearchTree;

SearchTree MakeEmpty(SearchTree);
Position Find(ElementType X, SearchTree T);
Position FindMin(SearchTree T);
Position FindMax(SearchTree T);
SearchTree Insert(ElementType X, SearchTree T);
SearchTree Delete(ElementType X, SearchTree T);
ElementType Retrieve(Position P);

#endif

实现头文件 tree.c

#include "tree.h"
#include <stdlib.h>
#include "fatal.h"

struct TreeNode
{
	ElementType Element;
	SearchTree Left;
	SearchTree Right;
};

SearchTree
MakeEmpty(SearchTree T)
{
	if (T != NULL)
	{
		MakeEmpty(T->Left);
		MakeEmpty(T->Right);
		free(T);
	}
	return NULL;
}

Position
Find(ElementType X, SearchTree T)
{
	if (T == NULL)
		return NULL;
	if (X < T->Element)
		return Find(X, T->Left);
	else if (X > T->Element)
		return Find(X, T->Right);
	else
		return T;
}

Position
FindMin(SearchTree T)
{
	if (T == NULL)
		return NULL;
	else if (T->Left == NULL)
		return T;
	else
		return FindMin(T->Left);
}

Position
FindMax(SearchTree T)
{
	if (T != NULL)
		while (T->Right != NULL)
			T = T->Right;

	return T;
}

SearchTree
Insert(ElementType X, SearchTree T)
{
	// 插入新的节点
	if (T == NULL)
	{
		T = malloc(sizeof(struct TreeNode));
		if (T == NULL)
			FatalError("Out of space!!!");
		else
		{
			T->Element = X;
			T->Left = T->Right = NULL;
		}
	}
	else if (X < T->Element)
	{
		T->Left = Insert(X, T->Left);
	}
	else if (X > T->Element)
	{
		T->Right = Insert(X, T->Right);
	}

	// 需要返回当前节点 因为是递归调用的
	return T;
}

SearchTree
Delete(ElementType X, SearchTree T)
{
	Position TmpCell;

	if (T == NULL)
		Error("Element not found");
	else if (X < T->Element)
		T->Left = Delete(X, T->Left);
	else if (X > T->Element)
		T->Right = Delete(X, T->Right);
	else if (T->Left && T->Right)
	{
    	// 左右子节点都有的情况复杂一些 我们需要先找到原先准备删除的节点的右子节点的最小子节点,然后把它的元素值赋值给这个节点,然后去这个递归调用Delete去删除这个右节点的最小子节点,而这个子节点是不满足同时有左右子节点条件的,这样子就进入了下面的else情况,就通过递归调用解决了
		TmpCell = FindMin(T->Right);
		T->Element = TmpCell->Element;
		T->Right = Delete(T->Element, T->Right);
	}
	else
	{
    	// 删除一个只有左节点或者只有右子节点的情况 比较简单 用对应的右节点/左节点替代即可 保持全树的整体有序性
		TmpCell = T;
		if (T->Left == NULL)
			T = T->Right;
		else if (T->Right == NULL)
			T = T->Left;

		free(TmpCell);
	}

	return T;
}

ElementType
Retrieve(Position P)
{
	return P->Element;
}

测试代码 testtree.c

#include "tree.h"
#include <stdio.h>

main()
{
	SearchTree T;
	Position P;
	int i;
	int j = 0;

	T = MakeEmpty(NULL);
	for (i = 0; i < 50; i++, j = (j + 7) % 50)
	{
		T = Insert(j, T);
	}
		
	for (i = 0; i < 50; i++)
		if ((P = Find(i, T)) == NULL || Retrieve(P) != i)
			printf("Error at %d\n", i);

	for (i = 0; i < 50; i += 2)
		T = Delete(i, T);

	for (i = 1; i < 50; i += 2)
		if ((P = Find(i, T)) == NULL || Retrieve(P) != i)
			printf("Error at %d\n", i);
	for (i = 0; i < 50; i += 2)
		if ((P = Find(i, T)) != NULL)
			printf("Error at %d\n", i);

	printf("Min is %d, Max is %d\n", Retrieve(FindMin(T)),
		Retrieve(FindMax(T)));

	return 0;
}

测试输出

Min is 1, Max is 49

BST实战

leetcode 1373题 给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。

二叉搜索树的定义如下:

任意节点的左子树中的键值都 小于 此节点的键值。 任意节点的右子树中的键值都 大于 此节点的键值。 任意节点的左子树和右子树都是二叉搜索树。  

示例 1:

输入:root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6] 输出:20

解释:键值为 3 的子树是和最大的二叉搜索树。

示例 2:

输入:root = [4,3,null,1,2]

输出:2

解释:键值为 2 的单节点子树是和最大的二叉搜索树。

示例 3:

输入:root = [-4,-2,-5]

输出:0

解释:所有节点键值都为负数,和最大的二叉搜索树为空。

示例 4:

输入:root = [2,1,3]

输出:6

示例 5:

输入:root = [5,4,8,3,null,6,3]

输出:7  

提示:

每棵树最多有 40000 个节点。 每个节点的键值在 [-4 * 10^4 , 4 * 10^4] 之间。

代码:

int max_val = 0;
 
int is_valid_tree(struct TreeNode* root, int *val)
{
	/* 0:sum, 1:max_val, 2:min_val */
	int l_value[3] = {0, INT_MIN, INT_MAX};
	int r_value[3] = {0, INT_MIN, INT_MAX};
	int ret_l, ret_r;

	if (!root) {
		return 1;
	}
	
	ret_l = is_valid_tree(root->left, l_value);
	ret_r = is_valid_tree(root->right, r_value);
	
    // 左右子树都是二叉搜索树 且当前节点满足 大于左边最大节点 小于右边最小节点
	if (!ret_l || !ret_r || root->val <= l_value[1] || root->val >= r_value[2])
 		return 0;
	
    // 答案只出现在叶子节点 以及所有的子二叉搜索树中
	val[0] += root->val + l_value[0] + r_value[0];
	val[1] = root->right ? r_value[1] : root->val;
	val[2] = root->left ? l_value[2] : root->val;

    if (max_val < val[0])
        max_val = val[0];
	
	return 1;
}


int maxSumBST(struct TreeNode* root){
    int val[3] = {0, INT_MIN, INT_MAX};
    
    max_val = 0;
    is_valid_tree(root, val);
    return max_val;
}

参考资料 《数据结构与算法分析: C语言描述》