数据结构——树

二叉树

  • 前言
  • 一、树概念及结构
    • 树的概念
    • 树的特点
    • 树的相关概念
    • 树的表示
  • 二、二叉树概念及结构
    • 二叉树的概念
    • 现实中的二叉树
    • 特殊的二叉树
    • 二叉树的性质
      • 性质习题检验
    • 二叉树的顺序存储结构
    • 堆的概念及结构
    • 堆的实现
      • 性质习题检验
  • 总结


前言

本文我们来一起学习在数据结构中的又一个重要内容,二叉树!
二叉树无论是结构还是应用都属于比较复杂的结构!
但没关系,一起加油,这些都是小困难!


一、树概念及结构

树的概念

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

树的特点

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

数据结构——树_第2张图片

树的相关概念

数据结构——树_第3张图片

节点的度: 一个节点含有的子树的个数称为该节点的度; 如上图: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)棵互不相交的树的集合称为森林;

树的表示

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

typedef int DataType;
struct Node
{
     struct Node* _firstChild1; // 第一个孩子结点
     struct Node* _pNextBrother; // 指向其下一个兄弟结点
     DataType _data; // 结点中的数据域
};

数据结构——树_第4张图片

二、二叉树概念及结构

二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:

  1. 或者为空
  2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成

数据结构——树_第5张图片
从上图可以看出:

  1. 二叉树不存在度大于2的结点
  2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
    注意:对于任意的二叉树都是由以下几种情况复合而成的:
    数据结构——树_第6张图片

现实中的二叉树


(图片来源于网络)

特殊的二叉树

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

数据结构——树_第7张图片

二叉树的性质

数据结构——树_第8张图片
对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:

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

性质习题检验

  1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( B)
    A 不存在这样的二叉树
    B 200
    C 198
    D 199

解析:
在二叉树中,设叶子结点个数为n,度为2的结点个数为n2,叶子结点的个数计算方法n=n2+1=199+1=200,所以选项B正确。

2.下列数据结构中,不适合采用顺序存储结构的是( A)
A 非完全二叉树
B 堆
C 队列
D 栈

解析:
根据完全二叉树的性质,满二叉树和完全二叉树可以按层序进行顺序存储,但一般的二叉树不适用。堆可以用一维数组来存储也可以用完全二叉树来直观地表示堆的结构。队列、栈本身就是顺序存储的。故本题答案为A选项。

3.在具有 2n 个结点的完全二叉树中,叶子结点个数为( A)
A n
B n+1
C n-1
D n/2

解析:
由二叉树的定义可知,树中必定存在度为O的结点和度为2的结点,设度为0结点有a个,根据度为0的结点(即叶子结点)总比度为2的结点多一个,得度为2的结点有a一1个。再根据完全二叉树的定义,度为1的结点有0个或1个,假设度1结点为0个,a 0 a一1=2n,得2a=2n—1,由于结点个数必须为整数,假设不成立;当度为1的结点为1个时,a 1 a一1=2n,得a=n,即叶子结点个数为n。

4.一棵完全二叉树的节点数位为531个,那么这棵树的高度为( B)
A 11
B 10
C 8
D 12

解析:
一个n层的完全二叉树最多有2^n-1个结点;
2^ 9-1 <531 < 2^ 10-1

5.一个具有767个节点的完全二叉树,其叶子节点个数为(B)
A 383
B 384
C 385
D 386

解析:
根据性质“深度为h的二叉树至多有2h-1个结点(h>=1)”可知,具有结点767是深度为10 完全二叉树。前9层的结构有2^9-1=511个结点,在第10层的结点个数为767-511=256,那么在第9层中具有两个子节点的节点数为256/2=128,则整个二叉树具有两个子节点的节点数为2 ^8-1+128=383,又根据性质“对任何一颗二叉树,如果其叶子结点数为n,具有两个子节点的节点数为m,则n=m+1”,叶子结点的数为383+1=384

二叉树的顺序存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
今天我们先来学习简单的顺序存储!

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树

数据结构——树_第9张图片

堆的概念及结构

如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: <= 且 <=> ( >= 且 >= ) i = 0,1, 2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆根节点最小的堆叫做最小堆或小根堆

堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

数据结构——树_第10张图片

堆的实现

关于堆的建立这里只是提及一下调整方法,后面我会出完整的堆的建立的代码!

堆向下调整算法
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int array[] = {27,15,19,18,28,34,65,49,25,37};

数据结构——树_第11张图片
代码实现:

void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		// 找出比较之下符合条件的那个孩子
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			// 继续往下调整
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

堆向上调整算法

int array[] = {15,10,21,34,16,12};

数据结构——树_第12张图片
代码实现:

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

性质习题检验

1.下列关键字序列为堆的是:(A)
A 100,60,70,50,32,65
B 60,70,65,50,32,100
C 65,100,70,32,50,60
D 70,65,100,32,50,60
E 32,50,100,70,65,60
F 50,100,70,65,60,32

解析:
堆中某个节点的值总是不大于或不小于其父节点的值;
将选项中的数据按完全二叉树排列,只有A为大根堆;

2.已知小根堆为8,15,10,21,34,16,12,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次数是(C)。
A 1
B 2
C 3
D 4

解析:
删除8之后,堆为[15,10,21,34,16,12],按小堆排序,首先从最后一个结点开始向上调整,21和12比较,15和12比较,12和10比较,一共3次;

3.一组记录排序码为(5 11 7 2 3 17),则利用堆排序方法建立的初始堆为 (C)
A(11 5 7 2 3 17)
B(11 5 7 2 17 3)
C(17 11 7 2 3 5)
D(17 11 7 5 3 2)
E(17 7 11 3 5 2)
F(17 7 11 3 2 5)

解析:
数据结构——树_第13张图片

4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是(C)
A[3,2,5,7,4,6,8]
B[2,3,5,7,4,6,8]
C[2,3,4,5,7,8,6]
D[2,3,4,5,6,7,8]

解析:
将0变为最后的元素8,再建立长度-1的堆[8,3,2,5,7,4,6,8],再将8向下调整,
得到[2,3,4,5,7,8,6] ;


总结

本文我们来一起学习在数据结构中的又一个重要内容,二叉树!
二叉树无论是结构还是应用都属于比较复杂的结构!
但没关系,一起加油,这些都是小困难!

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