详细认识二叉树【图片+代码】

目录

一、树的概念及结构

1.1树的概念

1.2树的相关概念

1.3树的表示

1.4树在实际中的应用(目录树) 

二、二叉树概念及结构

2.1概念

2.2特殊的二叉树

2.3二叉树的性质 

2.4二叉树存储结构

三、二叉树的顺序结构及实现

3.1二叉树的顺序结构

3.2堆的结构及概念

四、二叉树的链式结构及实现

五、二叉树的遍历(前序、中序、后序、层序遍历)

5.1前序遍历

5.2中序遍历

5.3后序遍历

5.4层序遍历

六、求二叉树的节点个数、叶子节点个数、高度、查找节点等功能

6.1二叉树节点的个数

6.2二叉树叶子节点的个数

6.3二叉树的高度

6.4二叉树第K层节点的个数

6.5查找节点

6.6二叉树度为1的节点个数

七、创建二叉树、销毁二叉树

7.1创建二叉树

7.2销毁二叉树

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

九、OJ练习——二叉树部分

9.1检查两颗树是否相同

9.2单值二叉树

9.3翻转二叉树

9.4对称二叉树

9.5另一颗数的子树

9.6判断一颗二叉树是否是平衡二叉树


一、树的概念及结构

1.1树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的
  • 有一个特殊的结点,称为根结点,根节点没有前驱结点。
  • 除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。
  • 因此,树是递归定义的。

详细认识二叉树【图片+代码】_第1张图片详细认识二叉树【图片+代码】_第2张图片

1.2树的相关概念

详细认识二叉树【图片+代码】_第3张图片

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

1.3树的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既然保存值域,也要保存结点和结点之间 的关系,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。
typedef int DataType;
struct Node
{
struct Node* _firstChild1;                 // 第一个孩子结点
struct Node* _pNextBrother;            // 指向其下一个兄弟结点
DataType _data;                               // 结点中的数据域
};

详细认识二叉树【图片+代码】_第4张图片

1.4树在实际中的应用(目录树) 

详细认识二叉树【图片+代码】_第5张图片

二、二叉树概念及结构

2.1概念

一棵二叉树是结点的一个有限集合,该集合:
  1. 或者为空
  2. 由一个根节点加上两颗别称为左子树和右子树的二叉树组成

详细认识二叉树【图片+代码】_第6张图片

注意: 

  1. 二叉树不存在度大于2的节点
  2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
  3. 对于任意的二叉树都是由以下几种情况复合而成的:

详细认识二叉树【图片+代码】_第7张图片

2.2特殊的二叉树

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k - 1,则它就是满二叉树。
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

详细认识二叉树【图片+代码】_第8张图片

2.3二叉树的性质 

详细认识二叉树【图片+代码】_第9张图片

2.4二叉树存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
        1.顺序存储:顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为
                             不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储关
                             于堆我们后面的章节会专门讲解。二叉树顺序存储在物理上是一个数组,在逻辑
                              上是一颗二叉树。
详细认识二叉树【图片+代码】_第10张图片

         2.链式存储:二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的                               逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域                               左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。

                             链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链。

详细认识二叉树【图片+代码】_第11张图片

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
        struct BinTreeNode* _pLeft;         // 指向当前节点左孩子
        struct BinTreeNode* _pRight;       // 指向当前节点右孩子
        BTDataType _data;                       // 当前节点值域
}
// 三叉链
struct BinaryTreeNode
{
        struct BinTreeNode* _pParent;         // 指向当前节点的双亲
        struct BinTreeNode* _pLeft;              // 指向当前节点左孩子
        struct BinTreeNode* _pRight;            // 指向当前节点右孩子
        BTDataType _data;                            // 当前节点值域
};

三、二叉树的顺序结构及实现

3.1二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结 构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
详细认识二叉树【图片+代码】_第12张图片

3.2堆的结构及概念

有关堆的结构及概念,堆的实现和应用,请看上一篇:http://t.csdn.cn/Dbj44

四、二叉树的链式结构及实现

树的简单的创建:

typedef int BTDataType;
typedef struct BinaryTreeNode
{
        BTDataType _data;
        struct BinaryTreeNode* _left;
        struct BinaryTreeNode* _right;
}BTNode;
BTNode* CreatBinaryTree()
{
        BTNode* node1 = BuyNode(1);
        BTNode* node2 = BuyNode(2);
        BTNode* node3 = BuyNode(3);
        BTNode* node4 = BuyNode(4);
        BTNode* node5 = BuyNode(5);
        BTNode* node6 = BuyNode(6);
        node1->_left = node2;
        node1->_right = node4;
        node2->_left = node3;
        node4->_left = node5;
        node4->_right = node6;
        return node1;
}

五、二叉树的遍历(前序、中序、后序、层序遍历)

5.1前序遍历

前序遍历——访问根结点的操作发生在遍历其左右子树之前。

代码:

void PrevOrder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->lchild);
	PrevOrder(root->rchild);
}

递归展开图:

详细认识二叉树【图片+代码】_第13张图片

5.2中序遍历

中序遍历——访问根结点的操作发生在遍历其左右子树之中(间)。

代码:

void InOrder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	InOrder(root->lchild);
	printf("%d ", root->data);
	InOrder(root->rchild);
}

递归展开图:

详细认识二叉树【图片+代码】_第14张图片

5.3后序遍历

后序遍历——访问根结点的操作发生在遍历其左右子树之后。

代码:

void PostOrder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	PostOrder(root->lchild);
	PostOrder(root->rchild);
	printf("%d ", root->data);
}

递归展开图:

详细认识二叉树【图片+代码】_第15张图片

5.4层序遍历

层序遍历——设二叉树的根节点所在 层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层 上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

详细认识二叉树【图片+代码】_第16张图片

 层序遍历思想:要借助队列来完成,将根节点入队列,然后开始循环,若队列为空就循环结束,每次出来队头的数据,在队头的数据出去之后要立即将出去的那个节点的孩子入队列(孩子为空就不入队列)

代码:

void LevelOrder(TNode* root)
{
	if (root == NULL)
	{
		return;
	}
	Queue q;
	QueueInit(&q);
	//进队列
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		TNode* node = QueueFront(&q);
		QueuePop(&q);
		printf("%d ", node->data);
		if (node->lchild)
			QueuePush(&q, node->lchild);
		if (node->rchild)
			QueuePush(&q, node->rchild);

	}
	printf("\n");
}

六、求二叉树的节点个数、叶子节点个数、高度、查找节点等功能

6.1二叉树节点的个数

int BTreeSize(TNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return BTreeSize(root->lchild) + BTreeSize(root->rchild) + 1;
}

6.2二叉树叶子节点的个数

int BTreeLeafSize(TNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->lchild == NULL && root->rchild == NULL)
	{
		return 1;
	}
	return BTreeLeafSize(root->lchild) + BTreeLeafSize(root->rchild);
}

6.3二叉树的高度

int BTreeHeight(TNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	int leftHeight = BTreeHeight(root->lchild);
	int rightHeight = BTreeHeight(root->rchild);

	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

6.4二叉树第K层节点的个数

int BTreeLevelKSize(TNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BTreeLevelKSize(root->lchild, k - 1) + BTreeLevelKSize(root->rchild, k - 1);
	
}

6.5查找节点

TNode* BTreeFind(TNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	TNode* left = BTreeFind(root->lchild, x);
	if (left)
		return left;
	TNode* right = BTreeFind(root->rchild, x);
	if (right)
		return right;

	return NULL;
}

6.6二叉树度为1的节点个数

int BTreeDeg1Size(TNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int left = BTreeDeg1Size(root->lchild);
	int right = BTreeDeg1Size(root->rchild);
	if ((root->lchild == NULL && root->rchild != NULL)
		|| (root->lchild != NULL && root->rchild == NULL))
	{
		return left + right + 1;
	}
	else
	{
		return left + right;
	}
}

七、创建二叉树、销毁二叉树

7.1创建二叉树

 

TNode* TCreateTree(char* tree, int* pi)
{
	if (tree[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}

	TNode* root = (TNode*)malloc(sizeof(TNode));
	root->data = tree[*pi];
	(*pi)++;

	root->lchild = TCreateTree(tree, pi);
	root->rchild = TCreateTree(tree, pi);
	return root;
}

7.2销毁二叉树

void DestoryTree(TNode* root)
{
	if (root == NULL)
	{
		return;
	}

	DestoryTree(root->lchild);
	DestoryTree(root->rchild);
	free(root);
}

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

借助队列,将一个二叉树层序遍历进入队列,遇到NULL也进入队列,直到出队列时遇到NULL停止,判断此时队列中是否存在非NULL元素,如果存在,不是完全二叉树,如果全为NULL,是完全二叉树。

bool BTreeComplete(TNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		TNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueuePush(&q, front->lchild);
			QueuePush(&q, front->rchild);
		}

		else
		{
			break;
		}
	}
	while (!QueueEmpty(&q))
	{
		TNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestory(&q);
			return false;
		}
	}
	QueueDestory(&q);
	return true;
}

九、OJ练习——二叉树部分

9.1检查两颗树是否相同

详细认识二叉树【图片+代码】_第17张图片

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if(p == NULL && q == NULL)
    {
        return true;
    }
    if(p == NULL || q == NULL)
    {
        return false;
    }
    if(p->val != q->val)
    {
        return false;
    }
    return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}

 

9.2单值二叉树

详细认识二叉树【图片+代码】_第18张图片

bool isUnivalTree(struct TreeNode* root){
    if(root == NULL)
    {
        return true;
    }
    if(root->left!= NULL && root->val != root->left->val)
    {
        return false;
    }
    if(root->right!= NULL && root->val != root->right->val)
    {
        return false;
    }
    return isUnivalTree(root->left) && isUnivalTree(root->right);
}

9.3翻转二叉树

详细认识二叉树【图片+代码】_第19张图片

struct TreeNode* invertTree(struct TreeNode* root){
    if(root == NULL)
    {
        return NULL;
    }

    //保存一下原来的左孩子
    struct TreeNode* left = root->left;
    root->left = invertTree(root->right);
    root->right = invertTree(left);
    return root;
}

9.4对称二叉树

详细认识二叉树【图片+代码】_第20张图片

bool _isSymmetric(struct TreeNode* r1, struct TreeNode* r2)
{
    if(r1 == NULL && r2 == NULL)
    {
        return true;
    }
    if(r1 == NULL || r2 == NULL)
    {
        return false;
    }
    if(r1->val != r2->val)
    {
        return false;
    }
    return _isSymmetric(r1->left, r2->right) && _isSymmetric(r1->right, r2->left);
}

bool isSymmetric(struct TreeNode* root){

    return _isSymmetric(root->left, root->right);
}

9.5另一颗数的子树

详细认识二叉树【图片+代码】_第21张图片

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if(p == NULL && q == NULL)
    {
        return true;
    }
    if(p == NULL || q == NULL)
    {
        return false;
    }
    if(p->val != q->val)
    {
        return false;
    }
    return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
    if(root == NULL && subRoot == NULL)
    {
        return true;
    }
    if(root == NULL || subRoot == NULL)
    {
        return false;
    }
    if(isSameTree(root, subRoot))
    {
        return true;
    }
    return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}

9.6判断一颗二叉树是否是平衡二叉树

详细认识二叉树【图片+代码】_第22张图片

 

int BTreeHeight(struct TreeNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	int leftHeight = BTreeHeight(root->left);
	int rightHeight = BTreeHeight(root->right);

	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

bool isBalanced(struct TreeNode* root){
    if(root == NULL)
    {
        return true;
    }
    int left = BTreeHeight(root->left);
    int right = BTreeHeight(root->right);
    int sub = abs(left - right);
    if(sub > 1)
    {
        return false;
    }
    return isBalanced(root->left) && isBalanced(root->right);
}

你可能感兴趣的:(数据结构,c语言)