树,二叉树

文章目录

    • 树概念及结构
      • 树的概念
      • 树的相关概念
      • 树的表示
  • 二叉树概念及结构
    • 概念
    • 特殊的二叉树
    • 二叉树的性质
  • 二叉树链式结构的实现
    • 二叉树的创建
    • 二叉树的遍历
      • 前序遍历
      • 中序遍历
      • 后序遍历
      • 求二叉树的节点个数
      • 求二叉树叶子节点的个数
      • 求二叉树的高度
      • 求二叉树第k层节点个数
      • 整体代码

树概念及结构

树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

有一个特殊的结点,称为根结点,根节点没有前驱结点.
除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
因此,树是递归定义的。

注意:树形结构中,子树之间不能有交集,否则就不是树形结构

树的相关概念

树,二叉树_第1张图片

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6
叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点
非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G…等节点为分支节点
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m(m>0)棵互不相交的树称为森林

树的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既要保存值域,也要保存结点和结点之间
的关系
,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法
等。我们这里就简单的了解其中最常用的孩子兄弟表示法

树,二叉树_第2张图片
树,二叉树_第3张图片

二叉树概念及结构

概念

一棵二叉树是结点的一个有限集合,可以由根节点,左子树,右子树和空构成

树,二叉树_第4张图片

特点:
1.不存在度大于2的结点
2.子树有左右之分,次序不能颠倒,因此二叉树是有序树

特殊的二叉树

  1. 满二叉树:每一层的节点都满
    2.完全二叉树:最下层的节点必须按从左到右的顺序排,满二叉树是一种特殊的完全二叉树

树,二叉树_第5张图片

二叉树的性质

  1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 2^(i-1)个结点.
  2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h-1 .
  3. 对任何一棵二叉树, 如果度为0其叶结点个数为 n0个, 度为2的分支结点个数为 n2个,则有n0=n2+1。
  4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h是log以2
    为底,n+1为真数的对数
  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对
    于序号为i的结点有:
    1.若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
    2.若2i+1=n否则无左孩子
    3.若2i+2=n否则无右孩子

二叉树链式结构的实现

二叉树的创建

由于现在对二叉树结构掌握还不够深入,为了降低学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,再来研究二叉树真正的创建方式。

typedef int BTDataType;

typedef struct BinaryTreeNode
{
	struct BinTreeNode* left; // 指向当前节点左孩子
	struct BinTreeNode* right; // 指向当前节点右孩子
	BTDataType data; // 当前节点值域
}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);

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

	return node1;
}

这里创建的二叉树是通过一个个节点构成的,由节点间的相互指向关系形成逻辑,逻辑图:
树,二叉树_第6张图片

二叉树的遍历

二叉树的递归遍历结构可以分为前序遍历,中序遍历和后序遍历

前序遍历

访问节点的顺序:根、左子树、右子树

我们用递归去实现前序遍历时要注意返回条件和子问题分治

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

这里我们把空节点的值当作N,便于更加清楚前序遍历的逻辑,如果是空节点就打印N并结束当前函数,如果不是空节点就先打印节点的数据,再进入当前节点的左孩子节点,此时进入递归的第二层,如果该节点不为空,就先打印当前节点的值,然后当前节点的左孩子节点,也就是第三层递归。如果递归第二层的节点为空,就返回递归第一层,进入递归第一层节点的右孩子节点判断,一次类推,下面放一张前序遍历的递归图解:

树,二叉树_第7张图片
下面看看这棵树前序遍历的打印结果:
树,二叉树_第8张图片

中序遍历

访问节点的顺序:左子树、根、右子树

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

}

打印结果:

树,二叉树_第9张图片

后序遍历

访问节点的顺序:左子树、右子树、根

void OutOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	OutOrder(root->left);
	OutOrder(root->right);
	printf("%d ", (root->data));

}

打印结果:
树,二叉树_第10张图片

很容易看出,这三种递归遍历结构只是递归的先后顺序有变化,思路如出一辙。

求二叉树的节点个数

int TreeSize(TreeNode* root)
{
	
		return root==NULL?0:1 + TreeSize(root->left) + TreeSize(root->right);
}

同样利用递归分治,如果根节点是空直接返回0,否则返回左子树的节点数+右子树的节点数+1(根节点的个数)。这里的递归过程是如果有孩子节点就进入孩子节点,进入后就有+1(代表当前的节点),如果还有子节点就继续递归,没有就返回上一级递归,这样就把所有非空节点都过了一遍并完成计数。打印结果:

树,二叉树_第11张图片

求二叉树叶子节点的个数

定义:叶子节点:度为0的节点,就是没有子节点的节点

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);
}

根据递归分治法,根据叶子节点的特点,我们能知道当当前节点的左右孩子都为空时,当前节点就是叶子节点,于是返回1,如果还不是叶子节点就继续检验左右孩子节点。打印结果:

树,二叉树_第12张图片

求二叉树的高度

定义:树中结点的最大层次称为树的深度或高度。(层次:结点的层次从根开始定义起,根为第一层,根的孩子为第二层。树中任一结点的层次等于其双亲结点的层次加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 : 1 + rightheight;
}

同样先判断根节点为空时高度是否为0,之后比较左子树、右子树的高度,返回高的那个,再+1(根节点)
打印结果:

树,二叉树_第13张图片

求二叉树第k层节点个数

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

求第k层节点数量,等于求以根节点的左孩子为根的k-1层节点数,加上以根节点的右孩子为根的k-1层节点数。打印第2层节点数的结果结果:

树,二叉树_第14张图片

整体代码

typedef int BTDataType;

typedef struct BinaryTreeNode
{
	struct BinTreeNode* left; // 指向当前节点左孩子
	struct BinTreeNode* right; // 指向当前节点右孩子
	BTDataType data; // 当前节点值域
}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);


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


	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 OutOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	OutOrder(root->left);
	OutOrder(root->right);
	printf("%d ", (root->data));

}

int TreeSize(TreeNode* root)
{
	
		return root==NULL?0:1 + TreeSize(root->left) + TreeSize(root->right);
}

int TreeLeafSize(TreeNode* root)
{
	/*return (TreeSize(root) + 1) / 2;*/
	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 : 1 + rightheight;

	/*if (TreeHeight(root->left) > TreeHeight(root->right))
	{
		return 1 + TreeHeight(root->left);
	}
	else
		return 1 + TreeHeight(root->right);*/

	//return root == NULL ? 0 :
	//	TreeHeight(root->left) > TreeHeight(root->right) ? 
	//	1 + TreeHeight(root->left) : 1 + TreeHeight(root->right);
}

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

int main()
{
	TreeNode* root = CreateTree();
	PrevOrder(root);
	printf("\n");
	InOrder(root);
	printf("\n");
	OutOrder(root);
	printf("\n");
	printf("%d ",TreeSize(root));
	printf("\n");

	printf("%d ", TreeLeafSize(root));
	printf("\n%d \n", TreeHeight(root));
	printf("%d ", TreeKSize(root, 2));
	return 0;
}

你可能感兴趣的:(数据结构,数据结构,算法)