数据结构 树与二叉树

树和二叉树

    • 树的概念
    • 树的结构
      • 树结构的基本术语
    • 树的存储结构
  • 二叉树
    • 二叉树的结构
      • 二叉树的结构分类
    • 二叉树的性质
    • 二叉树的遍历
    • 二叉树的操作
      • 1、创建二叉树
      • 2、二叉树的遍历
      • 3、获取二叉树的结点个数
      • 4、求二叉树的高度
      • 5、叶子结点的个数
      • 6、求第k层的结点个数
      • 7、查找二叉树中值为val的结点
      • 8、销毁二叉树
      • 9、层序遍历二叉树
      • 10、判断树是否为完全二叉树
      • 二叉树操作接口与实现
  • 二叉树的拓展及面试编程题.

树形结构是一类重要的非线性数据结构。其中以树和二叉树最为常用,直观来看,树是以分支关系定义的层次结构。树结构在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构都可以用树来形象表示。树在计算机领域也得到广泛应用,如在编译程序中,可用树来表示源程序的语法结构。又如在数据库系统中,树形结构也是信息的重要组织形式之一。

树的概念

树(Tree)是n(n>=0)个结点的有限集合。在任意一棵非空树中:(1)有且仅有一个特定的称为根(Root)的结点;(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,…Tm,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)

树的结构

1、一棵普通的树
数据结构 树与二叉树_第1张图片
2、单个结点的树

单
3、空树
在这里插入图片描述

树的结构必须包含以下 三点性质:
1、除了根节点外,每个结点有且仅有一个父节点
2、子树是不相交的
3、一棵有N个结点的树有N-1条边

1、子树相交,不是树
数据结构 树与二叉树_第2张图片
2、结点有两个父节点,不是树
数据结构 树与二叉树_第3张图片
3、结点和边的个数相同,不是树
数据结构 树与二叉树_第4张图片

树结构的基本术语

数据结构 树与二叉树_第5张图片

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

树的存储结构

树结构不是线性结构,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法,孩子表示法、孩子兄弟表示法等等。

1、孩子兄弟表示法

typedef int DataType; 
typedef struct CBTNode 
{
	struct CBTNode * _Child;  //表示孩子
	struct CBTNode * _Brother;  //表示兄弟
	DataType _data;	   //表示存的数据
}CBTNode;

数据结构 树与二叉树_第6张图片
2、双亲表示法

typedef int DataType; 
#define MAX_TREE_SIZE 100

typedef struct PTNode
{
	DataType _data; //结点的数据
	int _parent; //该结点父节点的位置
}PTNode;

typedef struct Tree
{
	PTNode nodes[MAX_TREE_SIZE]; 
	int _r; //根的位置
	int _n; //有效结点数
}

数据结构 树与二叉树_第7张图片
3、孩子表示法

typedef int DataType; 
#define MAX_TREE_SIZE 100

typedef struct CTNode
{
	int _child;
	struct CTNode *next;
}CTNode;

typedef struct CTBox
{
	DataType  _data;
	struct CTNode *child;
}

typedef struct CTree
{
	CTBox nodes[MAX_TREE_SIZE];
	int _n;
	int _r;
}

数据结构 树与二叉树_第8张图片

二叉树

二叉树(Binary Tree)是一种树形结构,它的特点是每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分,其次序不能任意颠倒。

二叉树的结构

1、空二叉树
在这里插入图片描述
2、仅有根节点的二叉树
在这里插入图片描述
3、右子树为空的二叉树
在这里插入图片描述
4、左子树为空的二叉树
在这里插入图片描述
5、左、右子树均为非空的二叉树
数据结构 树与二叉树_第9张图片

二叉树的结构分类

1、一棵普通的二叉树
数据结构 树与二叉树_第10张图片
2、满二叉树

一棵深度为k且有2^k -1个结点的二叉树称为满二叉树

数据结构 树与二叉树_第11张图片

3、完全二叉树

完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对 应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

数据结构 树与二叉树_第12张图片

二叉树的性质

性质1:在二叉树的第i层至多有2^(i-1)个结点(i>=1)
性质2:深度为k的二叉树的最大结点数为2^k-1个(满二叉树)
性质3:对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则有n0 = n2 + 1;
证明:设n1为二叉树中度为1的结点的个数,则有
n = n0 + n1 + n2 (1)
设B为边数,利用树的性质可知
n = B + 1 (2)
又因为分支是由度为1和度为2的结点射出的,所以有
B = n0 + 2n2 (3)
由(2)(3)得 n = n1 + 2n2 + 1 (4)
再由(1)(4)得 n0 = n2 + 1
性质4:具有n个结点的完全二叉树的深度为└ log2n┘+1
性质5:对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对 于序号为i的结点有:

  1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
  2. 若2i+1=n否则无左孩子
  3. 若2i+2=n否则无右孩子

二叉树的遍历

在二叉树的一些应用中,常常需求在树中查找具有某种特征的结点,或者对树中全部结点逐一进行某种处理,这就提出了一个遍历二叉树的问题,即如何按某条搜索路径巡防树中的每一个结点,使得每个结点均被访问一次,而且仅被访问一次。“访问”的含义很广,可以是对结点作各种处理,如输出结点的信息等。

回顾二叉树的递归定义,二叉树是由3种基本单元组成:根节点,左子树,右子树。因此,若能依次遍历这三部分,便是遍历了整个二叉树。

遍历算法:

先序遍历二叉树
若二叉树为空,则空操作;否则
1、访问根节点
2、先遍历左子树
3、先序遍历右子树
中序遍历二叉树
若二叉树为空,则空操作;否则
1、中序遍历左子树
2、访问根节点
3、中序遍历右子树
后序遍历二叉树
若二叉树为空,则空操作;否则
1、后序遍历左子树
2、后序遍历右子树
3、访问根节点
层次遍历
依次从跟结点从左往右访问结点

总结
1、该先序中序后序都是以根节点的访问次序为标准
2、遍历二叉树的过程也是递归的一个过程

应用实例
数据结构 树与二叉树_第13张图片
分别用三种遍历二叉树的算法写出该树的遍历顺序
1、先序遍历:ABDEGHCF
2、中序遍历:DBGEHAFC
3、后序遍历:DGHEBFCA
4、层次遍历:ABCDEFGH
遍历算法代码比较简单,在下文我们会给出

二叉树的操作

1、创建二叉树

数据结构 树与二叉树_第14张图片
该树的存储在顺序表中的结构是
char str[] = “ABD##EG##H##CF###”; (#表示空)

typedef char DataType;

typedef struct BNode
{
	DataType _data;
	struct BNode* _left;
	struct BNode* _right;
}Node;

typedef struct BTree
{
	//二叉树的根节点
	Node* _root;
}BTree;

//创建二叉树(以先序遍历的数组为例)
//ABD##EG##H##CF###
//数组名,数组索引(为指针的目的是让函数在调用递归时索引的改变起全局作用)
Node* CreatBinaryTree(DataType arr[], int* idx)
{
	if (arr[*idx] == '#')
	{
		(*idx)++;
		return NULL;	//空结点
	}
	//当前树的根节点
	Node* root = (Node*)malloc(sizeof(Node));
	if (root != NULL)
	{
		root->_data = arr[*idx];
	}
	else
		return NULL;
	(*idx)++;

	root->_left = CreatBinaryTree(arr, idx);
	root->_right = CreatBinaryTree(arr, idx);

	return root;
}

数据结构 树与二叉树_第15张图片
数据结构 树与二叉树_第16张图片
数据结构 树与二叉树_第17张图片
数据结构 树与二叉树_第18张图片

2、二叉树的遍历

先序遍历

void PreOrder(Node* root)
{
	if (root != NULL)
	{
		//根
		printf("%c ", root->_data);
		//左子树
		PreOrder(root->_left);
		//右子树
		PreOrder(root->_right);
	}
}

数据结构 树与二叉树_第19张图片
数据结构 树与二叉树_第20张图片
数据结构 树与二叉树_第21张图片
数据结构 树与二叉树_第22张图片
2、中序遍历

void InOrder(Node* root)
{
	if (root != NULL)
	{
		//左子树
		InOrder(root->_left);
		//根
		printf("%c ", root->_data);
		//右子树
		InOrder(root->_right);
	}
}

3、后序遍历

void PostOrder(Node* root)
{
	if (root != NULL)
	{
		//左子树
		PostOrder(root->_left);
		//右子树
		PostOrder(root->_right);
		//根
		printf("%c ", root->_data);
	}
}

运行截图
数据结构 树与二叉树_第23张图片

3、获取二叉树的结点个数

int BTreeSize(Node* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return BTreeSize(root->_left)  //左子树结点数 + 右子树结点数+ 当前节点
			+ BTreeSize(root->_right) + 1;
}

数据结构 树与二叉树_第24张图片
数据结构 树与二叉树_第25张图片
数据结构 树与二叉树_第26张图片

4、求二叉树的高度

求二叉树的高度,我们只要求左右子树的最大高度在加上根节点1就可以了得出树的高度

//求二叉树的高度
int BTreeHigh(Node* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int left = BTreeHigh(root->_left);
	int right = BTreeHigh(root->_right);

	//返回该结点的左右孩子的度的最大值
	return left > right ? left + 1 : right + 1;
}

数据结构 树与二叉树_第27张图片
数据结构 树与二叉树_第28张图片
数据结构 树与二叉树_第29张图片
数据结构 树与二叉树_第30张图片

5、叶子结点的个数

左右子树叶子结点和
1、空结点 0个叶子结点
2、没有左右孩子的结点 1个叶子结点
3、非叶子结点->递归(左右子树叶子的和)

int BTreeLeafSize(Node* root)
{
	//空树
	if (root == NULL)
	{
		return 0;
	}
	//叶子结点
	if (root->_left == NULL 
			&& root->_right == NULL)
	{
		return 1;
	}
	//非叶子结点
	return BTreeLeafSize(root->_left) 
			+ BTreeLeafSize(root->_right);
}

6、求第k层的结点个数

第k层的结点个数 = 左右子树第k-1层的和

//第k层的结点
int BTreeKSize(Node* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	
	return BTreeKSize(root->_left, k - 1)
		+ BTreeKSize(root->_right, k - 1);
}

7、查找二叉树中值为val的结点

//查找二叉树树中的结点
Node* BTreeFind(Node* root, DataType val)
{
	if (root != NULL)
	{
		if (root->_data == val)
		{
			return root;
		}
		Node* node = BTreeFind(root->_left, val);
		if (node)
		{
			return node;
		}
		else
			return BTreeFind(root->_right, val);
	}
	else
	{
		return NULL;
	}
}

8、销毁二叉树

//销毁二叉树
void BTreeDestory(Node** root)
{
	if (*root == NULL)
	{
		return;
	}
	else
	{
		BTreeDestory(& ( (*root)->_left) );
		BTreeDestory(& ( (*root)->_right)) ;
		free(*root);
		*root = NULL;
	}
}

9、层序遍历二叉树

这里要实现层序遍历,我们不能再用递归的方式去实现。我们可以借助一个队列,先将根节点入队。然后再将根节点出队的同时,将根节点的左孩子和右孩子依次入队,注意,这里只能左孩子先入队,右孩子才能入队。当然,这里是在孩子存在的情况下,如果孩子为空,就不入队。然后出队头元素,在出的同时将队头元素的左右孩子依次入队,依次循环,直到队列为空为止
数据结构 树与二叉树_第31张图片

void BinaryTreeLevelOrder(Node* root)
{
	//队列
	Queue q;
	QueueInit(&q);

	//将根节点先入队
	if (root)
		QueuePush(&q, root);
	//只要队列不为空就继续循环
	while (!QueueEmpty(&q))
	{
		//获取队头元素
		Node* front = QueueFront(&q);
		//出队
		QueuePop(&q);
		printf("%c ", front->_data);
		//左孩子非空,入队
		if (front->_left)
			QueuePush(&q, front->_left);
		//右孩子非空,入队
		if (front->_right)
			QueuePush(&q, front->_right);
	}
}

运行截图
在这里插入图片描述

10、判断树是否为完全二叉树

要判断树是否为二叉树,其实跟层序遍历是相似的,我们知道,完全二叉树是连续的结构,层序遍历时,如果遇到空结点,那么它之后的结点就必须为空,否则不是完全二叉树,读者可自行画图。这里直接上代码

int BinaryTreeComplete(Node* root)
{
	Queue q;
	QueueInit(&q);

	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		Node* front = QueueFront(&q);
		QueuePop(&q);

		if (front)
		{
			QueuePush(&q, front->_left);
			QueuePush(&q, front->_right);
		}
		else
		{ //遇到空结点,结束第一个循环,进入第二个循环去判断后面是否全为空结点
			break;
		}
	}

	while (!QueueEmpty(&q))
	{
		Node* front = QueueFront(&q);
		QueuePop(&q);
		//有结点不为空,不是完全二叉树
		if (front)
			return 0;
	}

	return 1;
}

二叉树操作接口与实现

操作接口

#pragma once
#include
#include

typedef char DataType;

typedef struct BNode
{
	DataType _data;
	struct BNode* _left;
	struct BNode* _right;
}Node;

//创建二叉树
Node* CreatBinaryTree(DataType arr[], int* idx);

//先序遍历
void PreOrder(Node* root);

//中序遍历
void InOrder(Node* root);

//后序遍历
void PostOrder(Node* root);

//求二叉树结点的个数
int BTreeSize(Node* root);

//求二叉树的高度
int BTreeHigh(Node* root);

//叶子结点
int BTreeLeafSize(Node* root);

//第k层的结点
int BTreeKSize(Node* root, int k);

//查找二叉树中的结点
Node* BTreeFind(Node* root, DataType val);

//销毁二叉树
void BTreeDestory(Node** root);

//层序遍历二叉树
void BinaryTreeLevelOrder(Node* root);

//判断树是否为二叉树
int BinaryTreeComplete(Node* root);

操作接口的实现

#include"btree.h"

//数组名,数组索引(为指针的目的是让函数在调用递归时索引的改变起全局作用)
Node* CreatBinaryTree(DataType arr[], int* idx) 
{
	if (arr[*idx] == '#')
	{
		(*idx)++;
		return NULL;	//空结点
	}
	//当前树的根节点
	Node* root = (Node*)malloc(sizeof(Node));
	if (root != NULL)
	{
		root->_data = arr[*idx];
	}
	else
		return NULL;
	(*idx)++;

	root->_left = CreatBinaryTree(arr, idx);
	root->_right = CreatBinaryTree(arr, idx);

	return root;
}

void PreOrder(Node* root)
{
	if (root != NULL)
	{
		//根
		printf("%c ", root->_data);
		//左子树
		PreOrder(root->_left);
		//右子树
		PreOrder(root->_right);
	}
}

void InOrder(Node* root)
{
	if (root != NULL)
	{
		//左子树
		InOrder(root->_left);
		//根
		printf("%c ", root->_data);
		//右子树
		InOrder(root->_right);
	}
}

void PostOrder(Node* root)
{
	if (root != NULL)
	{
		//左子树
		PostOrder(root->_left);
		//右子树
		PostOrder(root->_right);
		//根
		printf("%c ", root->_data);
	}
}

int BTreeSize(Node* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return BTreeSize(root->_left)  //左子树结点数 + 右子树结点数+ 当前节点
			+ BTreeSize(root->_right) + 1;
}

int BTreeHigh(Node* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int left = BTreeHigh(root->_left);
	int right = BTreeHigh(root->_right);

	//返回该结点的左右孩子的度的最大值
	return left > right ? left + 1 : right + 1;
}

int BTreeLeafSize(Node* root)
{
	//空树
	if (root == NULL)
	{
		return 0;
	}
	//叶子结点
	if (root->_left == NULL 
			&& root->_right == NULL)
	{
		return 1;
	}
	//非叶子结点
	return BTreeLeafSize(root->_left) 
			+ BTreeLeafSize(root->_right);
}

int BTreeKSize(Node* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	
	return BTreeKSize(root->_left, k - 1)
		+ BTreeKSize(root->_right, k - 1);
}

Node* BTreeFind(Node* root, DataType val)
{
	if (root != NULL)
	{
		if (root->_data == val)
		{
			return root;
		}
		Node* node = BTreeFind(root->_left, val);
		if (node)
		{
			return node;
		}
		else
			return BTreeFind(root->_right, val);
	}
	else
	{
		return NULL;
	}
}

void BTreeDestory(Node** root)
{
	if (*root == NULL)
	{
		return;
	}
	else
	{
		BTreeDestory(& ( (*root)->_left) );
		BTreeDestory(& ( (*root)->_right)) ;
		free(*root);
		*root = NULL;
	}
}

void BinaryTreeLevelOrder(Node* root)
{
	//队列
	Queue q;
	QueueInit(&q);

	//将根节点先入队
	if (root)
		QueuePush(&q, root);
	//只要队列不为空就继续循环
	while (!QueueEmpty(&q))
	{
		//获取队头元素
		Node* front = QueueFront(&q);
		//出队
		QueuePop(&q);
		printf("%c ", front->_data);
		//左孩子非空,入队
		if (front->_left)
			QueuePush(&q, front->_left);
		//右孩子非空,入队
		if (front->_right)
			QueuePush(&q, front->_right);
	}
}

int BinaryTreeComplete(Node* root)
{
	Queue q;
	QueueInit(&q);

	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		Node* front = QueueFront(&q);
		QueuePop(&q);

		if (front)
		{
			QueuePush(&q, front->_left);
			QueuePush(&q, front->_right);
		}
		else
		{ //遇到空结点,结束第一个循环,进入第二个循环去判断后面是否全为空结点
			break;
		}
	}

	while (!QueueEmpty(&q))
	{
		Node* front = QueueFront(&q);
		QueuePop(&q);
		//有结点不为空,不是完全二叉树
		if (front)
			return 0;
	}

	return 1;
}

二叉树的拓展及面试编程题.

你可能感兴趣的:(数据结构,数据结构,二叉树)