树型结构和图型就是其中十分重要的非线性结构,可以用来描述客观
世界中广泛存在的层次结构和网状结构的关系,如家族谱、城市交通
等。
二叉树不是树的特例
二叉树与无序树不同:
二叉树中,每个结点最多只能有两棵子树,并且有左右之分。二叉
树并非是树的特殊情形,它们是两种不同的数据结构。
二叉树与度数为2的有序树不同:
在有序树中,虽然一个结点的孩子之间是有左右次序的,但是若该
结点只有一个孩子,就无须区分其左右次序。
而在二叉树中,即使是一个孩子也有左右之分。这是二叉树与树的最主要的差别。
满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶结点都在同一层上,这样的一棵二叉树称作满二叉树。
完全二叉树:一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
特点:叶结点只能出现在最下层和次下层,且最下层的叶结点集中在树的左部。
二叉树的性质:
性质1: 在二叉树的第i 层上至多有2^(i-1) 个结点 个结点(i≥1)。
性质2: 深度为k 的二叉树至多有2^(k -1) 个结点(k≥1)。
性质3: 对任何一棵二叉树,如果其叶结点数为n 0 ,度为2 的结点数为n 2 ,则n 0 =n 2 +1。
顺序存储结构:
用一维数组存储存放二叉树中的结点,一般是按照二叉树结点从上至下、从左到右的顺序存储 。
这样的结点在存储位置上的前驱后继关系并不一定就是它们在逻辑上的邻接关系,然而只有通过一些方法确定某结点在逻辑上的前驱结点和后继结点,这种存储才有意义 。
因此 ,依据二叉树的性质,完全二叉树和满二叉树采用顺序存储比较合适,树中结点的序号可以唯一地反映出结点之间的逻辑关系,这样既能够最大可能地节省存储空间,又可以利用数组元素的下标值确定结点在二叉树中的位置,以及结点之间的关系。
二叉树的顺序存储表示可描述为:
#define MaxNode 100 // 二叉树 二叉树 的最大结点数
typedef ElemType SqBiTree[MaxNode]
// 0号单元存放根结点 号单元存放根结点
SqBiTree bt;
即将bt 定义为含有MaxNode 个ElemType类型元素的一维数组类型元素的一维数组。
链式存储结构:
二叉树的链式存储结构是用链表来表示一棵二叉树,即用链来指示着元素的逻辑关系。通常有下面两种形式:
1.二叉链表存储
二叉链表的定义:
template <class Entry>
class Binary_tree
{
public:
// Add methods here.
protected:
// Add auxiliary function prototypes here.
Binary_node<Entry> *root;
};
template <class Entry>
struct Binary_node
{
// data members:
Entry data;
Binary_node<Entry> *left;
Binary_node<Entry> *right;
// constructors:
Binary_node( );
Binary_node(const Entry &x);
};
2.三叉链表存储:
遍历二叉树
1. 先序遍历(DLR)
递归过程为:若二叉树为空,遍历结束。否则,
⑴访问根结点;
⑵先序遍历根结点的左子树;
⑶先序遍历根结点的右子树。
void PreOrder(BiTree bt) // 先序遍历二叉树bt
{
if (bt==NULL) return;// 递归调用的结束条件
Visite(bt->data); // 访问结点的数据域
PreOrder(bt->lchild);// 先序递归遍历bt的左子树
PreOrder(bt->rchild); // 先序递归遍历bt的右子树
}
2. 中序遍历(LDR)
递归过程为:若二叉树为空,遍历结束。否则,
⑴中序遍历根结点的左子树;
⑵访问根结点;
⑶中序遍历根结点的右子树。
void InOrder(BiTree bt) // 中序遍历二叉树bt
{
if (bt==NULL) return; // 递归调用的结束条件
InOrder(bt->lchild); // 中序递归遍历bt的左子树
Visite(bt->data); // 访问结点的数据域
InOrder(bt->rchild); // 中序递归遍历bt的右子树
}
3. 后序遍历(LRD)
递归过程为:若二叉树为空,遍历结束。否则,
⑴后序遍历根结点的左子树;
⑵后序遍历根结点的右子树。
⑶访问根结点;
void PostOrder(BiTree bt) // 后序遍历二叉树bt
{
if (bt==NULL) return; // 递归调用的结束条件
PostOrder(bt->lchild); // 后序递归遍历bt的左子树
PostOrder(bt->rchild); // 后序递归遍历bt的右子树
Visite(bt->data); // 访问结点的数据域
}
4.层次遍历
从二叉树的第一层(根结点)开始,从上至下逐层遍历,在同一层中,则按从左到右的顺序对结点逐个访问。
在进行层次遍历时,可设置一个队列结构,遍历从二叉树的根结点开始,首先将根结点指针入队列,然后从队头取出一个元素,每取一个元素,执行下面两个操作:
⑴ 访问该元素所指结点;
⑵ 若该元素所指结点的左、右孩子结点非空,则将该元素所指结点的左孩子指针和右孩子指针顺序入队。
此过程不断进行,当队列为空时,二叉树的层次遍历结束。
void LevelOrder(BiTree bt) // 层次遍历二叉树bt
{
BiTree Queue[MaxNode];
int front,rear;
if (bt==NULL) return;
front=-1; rear=0; queue[rear]=bt;
while(front!=rear)
{
front++;
Visite(queue[front]->data); // 访问队首结点的数据域
if (queue[front]->lchild!=NULL) // 将队首结点的左孩子结点入队列
{
rear++;
queue[rear]=queue[front]->lchild;
}
if (queue[front]->rchild!=NULL) //将队首结点的右孩子结点入队列
{
rear++;
queue[rear]=queue[front]->rchild;
}
}
}