二叉树链式结构代码讲解与实现

本片将续之前的文章二叉树的概念进行二叉树基本操作的实现,二叉树oj题将在下篇文章讲解。

目录

a. 创建二叉树

代码:

一、二叉树的遍历

1.1 前序、中序以及后序遍历

代码:

如图:(前序遍历递归图解)

测试代码:

二、节点个数以及高度

2.1 二叉树节点个数

思想:要求二叉树的总节点个数,左子树节点个数加右子树节点个数再加上自己。再利用递归。

图解:

代码:

2.2  二叉树叶子节点个数

思想:左子树叶子节点个数+右子树叶子结点个数

代码:

2.3 二叉树的高度

思想:

图解:

代码:

2.4 二叉树第k层节点个数

思想:

图解:

代码:

2.5 二叉树查找值为x的节点(一棵树只有一个这样的节点有这个值)

思想:

代码:

图解:所示是一个递归展开图

三、本篇文章完整代码:


a. 创建二叉树

在学习其他二叉树的基本操作前,我们先简单的创建一颗二叉树:

我们需要创建一个如图的二叉树:

代码:
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}TreeNode;

TreeNode* BuyTreeNode(int x)
{
	TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
	assert(node);

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

TreeNode* CreateTree()
{
	TreeNode* node1 = BuyTreeNode(1);
	TreeNode* node2 = BuyTreeNode(2);
	TreeNode* node3 = BuyTreeNode(3);
	TreeNode* node4 = BuyTreeNode(4);
	TreeNode* node5 = BuyTreeNode(5);
	TreeNode* node6 = BuyTreeNode(6);
	TreeNode* node7 = BuyTreeNode(7);


	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	node5->right = node7;

	return node1;
}

(你也可以根据你自己的情况与想法改变代码,创建一颗合适的二叉树)

注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后续重点讲解

再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:
        1. 空树
        2. 非空:根节点,根节点的左子树、根节点的右子树组成的

从概念中可以看出,二叉树定义是递归式的,因此后续的基本操作中基本都是按照该概念实现的。

一、二叉树的遍历

1.1 前序、中序以及后序遍历

二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。
1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

// 二叉树前序遍历
void PreOrder(BTNode* root);
// 二叉树中序遍历
void InOrder(BTNode* root);
// 二叉树后序遍历
void PostOrder(BTNode* root);

二叉树的前中后序遍历都比较简单,这不做详细讲解:

代码:
void PrevOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}
void InOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}
void PostOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

注意:我们利用递归的方式对根节点的每个子树进行递归,每个子树又有新的“根”节点,每个节点都会被访问到,每个节点的左右子树都会被访问到。

代码中我们用"N"来表示空。

大家可以自己写一下前中后序,将会打印的顺序:

根据我创建的子树,我可以写出:

前:1 2 3 N N N 4 5 N 7 N N 6 N N
中:N 3 N 2 N 1 N 5 N 7 N 4 N 6 N
后:N N 3 N 2 N N N 7 5 N N 6 4 1

如图:(前序遍历递归图解)

测试代码:
int main()
{
	TreeNode* root = CreateTree();
	PrevOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	PostOrder(root);
	printf("\n");

    return 0;
}

二、节点个数以及高度

// 二叉树节点个数
int TreeSize(TreeNode* root);
// 二叉树叶子节点个数
int TreeLeafSize(TreeNode* root);

// 二叉树的高度

int TreeHeight(TreeNode* root)

// 二叉树第k层节点个数
int TreeLevelK(TreeNode* root, int k);
// 二叉树查找值为x的节点
TreeNode* TreeFind(TreeNode* root, BTDataType x)

2.1 二叉树节点个数

要求二叉树的节点个数,我们可以利用“分治”的思想,

每一棵树,都有许多节点,每一个节点都有自己的左右孩子节点,而第一层的节点就是一个最终统计节点。

我们来举一个例子:假如一个年级组长现在需要知道全校人数,那么第二层就是各班班主任,第三层可以是每班级的班长,第四层是一个班级中每个小组的组长。年级组长找班主任,班主任找班长,班长找组长。组长统计一个小组的人数再加上自己 告诉班长,班长统计每个组长所报的人数 加上自己,将一个班级的人数告诉班主任,每个班主任将每班的人数,加上自己 告诉年级组长,年级组长统计每个班主任所报的人数进行综合 再加上自己 。这就是我们的分治思想。

思想:要求二叉树的总节点个数,左子树节点个数加右子树节点个数再加上自己。再利用递归。
图解:

代码:
int TreeSize(TreeNode* root)
{
	return root == NULL ? 0 :
		TreeSize(root->left) +
		TreeSize(root->right) + 1;//左子树加右子树再加自己
 }

2.2  二叉树叶子节点个数

思想:左子树叶子节点个数+右子树叶子结点个数

返回条件:

1. 空 返回0

2. 叶子 返回1

代码:
int TreeLeafSize(TreeNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

2.3 二叉树的高度

思想:

返回条件:

1. 空 返回0

2. 不是空,左子树高度和右子树高度比较,更大的+1(加1,是加的自己)

图解:

代码:

注意:比较几种代码

int TreeHeight(TreeNode* root)
{
	//错误写法:形式参数无法记忆值
	//if (root == NULL)
	//{
	//	return 0;
	//}
	//int leftheight = TreeHeight(root->left);
	//int rightheight = TreeHeight(root->right);
	//return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
	//第一种
	/*if (root == NULL)
	{
		return 0;
	}
	return TreeHeight(root->left) > TreeHeight(root->right) ?
		TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;*/

	//第二种
	/*return root == NULL ? 0 :
		TreeHeight(root->left) > TreeHeight(root->right) ?
		TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;*/

	//第三种:利用内置函数防止值丢失
	if (root == NULL)
	{
		return 0;
	}
	return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;
}

2.4 二叉树第k层节点个数

思想:

返回条件:

1. 空 返回0

2. 不为空 ,k = 1,返回1

3. 不为空 ,k > 1,返回左子树的k+1层 + 右子树的k+1层。

图解:

代码:
int TreeLevelK(TreeNode* root, int k)
{
	assert(k>0);
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return TreeLevelK(root->left,k - 1)+
		TreeLevelK(root->right, k - 1);
}

2.5 二叉树查找值为x的节点(一棵树只有一个这样的节点有这个值)

TreeNode* TreeFind(TreeNode* root, BTDataType x)

这里,需要返回的值是一个指针

思想:

1. 当树为空:返回NULL

2. 当节点值为要找的值,返回该节点

3. 在左右子树遍历查找,找到后直接返回

4. 当节点左右不为空,返回该节点,用于记忆。

代码:
TreeNode* TreeFind(TreeNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;

	if (root->data == x)
		return root;

	TreeNode* ret1 = TreeFind(root->left, x);
	if (ret1)
		return ret1;

	TreeNode* ret2 = TreeFind(root->right, x);
	if (ret2)
		return ret2;

	return NULL;
}
图解:所示是一个递归展开图

可以自己画一画再理解。

在这里,我们需要搜索的 x = 5

三、本篇文章完整代码:

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}TreeNode;

TreeNode* BuyTreeNode(int x)
{
	TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
	assert(node);

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

TreeNode* CreateTree()
{
	TreeNode* node1 = BuyTreeNode(1);
	TreeNode* node2 = BuyTreeNode(2);
	TreeNode* node3 = BuyTreeNode(3);
	TreeNode* node4 = BuyTreeNode(4);
	TreeNode* node5 = BuyTreeNode(5);
	TreeNode* node6 = BuyTreeNode(6);
	TreeNode* node7 = BuyTreeNode(7);


	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	node5->right = node7;

	return node1;
}
void PrevOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}
void InOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}
void PostOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}
int TreeSize(TreeNode* root)
{
	return root == NULL ? 0 :
		TreeSize(root->left) +
		TreeSize(root->right) + 1;//左子树加右子树再加自己
 }
int TreeLeafSize(TreeNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

int TreeHeight(TreeNode* root)
{
	//错误写法:形式参数无法记忆值
	//if (root == NULL)
	//{
	//	return 0;
	//}
	//int leftheight = TreeHeight(root->left);
	//int rightheight = TreeHeight(root->right);
	//return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
	//第一种
	/*if (root == NULL)
	{
		return 0;
	}
	return TreeHeight(root->left) > TreeHeight(root->right) ?
		TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;*/

	//第二种
	/*return root == NULL ? 0 :
		TreeHeight(root->left) > TreeHeight(root->right) ?
		TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;*/

	//第三种:利用内置函数防止值得丢失
	if (root == NULL)
	{
		return 0;
	}
	return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;
}

//第k层的节点个数
int TreeLevelK(TreeNode* root, int k)
{
	assert(k>0);
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return TreeLevelK(root->left,k-1)+
		TreeLevelK(root->right, k - 1);
}

// 二叉树查找值为x的结点
TreeNode* TreeFind(TreeNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;

	if (root->data == x)
		return root;

	TreeNode* ret1 = TreeFind(root->left, x);
	if (ret1)
		return ret1;

	TreeNode* ret2 = TreeFind(root->right, x);
	if (ret2)
		return ret2;

	return NULL;
}
//int main()
//{
//	//CreateNDate();
//	PrintTopK("data.txt", 5);
//	return 0;
//}

int main()
{
	TreeNode* root = CreateTree();
	PrevOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	PostOrder(root);
	printf("\n");
	


	printf("treesize:%d\n", TreeSize(root));


	printf("TreeLeafSize:%d\n", TreeLeafSize(root));
	printf("TreeHeight:%d\n", TreeHeight(root));
	printf("TreeLevelK:%d\n", TreeLevelK(root, 4));
	TreeNode* ret = TreeFind(root, 5);
	printf("TreeFind:%p\n", ret);
	ret->data++;//这里用来测试

	PrevOrder(root);
	printf("\n");

	return 0;
}

  • 19
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值