树的介绍
一,树的定义
1.树(Tree)是n(n≥0)个结点的有限集T, 如果n = 0,称为空树;否则:
①有且仅有一个称为树的根(root)的结点。
②其余结点可分为m(m>0)个互不相交的有限集T1,T2,……Tm,其中每一个集合本身又是一棵树,称为根的子树(subtree)。
对于非空树,树的子树是不相交的。
二,树的术语
1.结点(node)—表示树中的元素,包括数据项及若干指向其子树的分支。
2.结点的度(degree of a node)—结点拥有的子树个数 。
3.树的度(degree of a tree)— 一棵树中最大的结点度数(子树数)。
4.叶子(leaf)—度为0的结点 。
5.孩子(Children)—结点的子树的根称为 该结点的孩子结点 。
6.双亲(parent)—孩子结点的上一层结点 叫该结点的父结点。
7.兄弟(siblings)——同一双亲的孩子互称兄弟结点。 堂兄弟–其双亲在同一层的结点互称为堂兄弟。
8.结点的祖先(ancestors of a node )—从根结点到该结点所经分支上的所有结点。
9.结点的子孙(descendants of a node)—以某结点为根的子树中的任一结点都称之为该结点的子孙。
10.结点的层次(level of a node)——从根结点算起,根为第一层,它的孩子为第二层……
11.树的深度(树高, height /depth of a tree )—树中结点的最大层次数 有序树和无序树:树中各结点的子树 从左到右有次序(不能互换), 称该树为有序树,否则为无序树。 (有序树:第一个孩子、第二个孩子…)
三,二叉树
二叉树:树中每个结点最多有两个孩子。
二叉树中,有两个孩子的结点和没有孩子的结点(叶子)的数量关系为:
n0 = n2 + 1
四,完全二叉树
完全二叉树:从左至右填满的二叉树。
完全二叉树的高度:
h = log2(n)+1(其中:n为完全二叉树的结点数)
如果按层序遍历遍历完全二叉树:
①从1开始为每个结点编号:
对于编号为i的结点,他的左儿子编号为2i,右儿子编号为2i+1。
②如果从0开始编号:
对于编号为i的结点,其左儿子编号为2i+1,右儿子编号为2i+2。
五,树的遍历
#include<cstdio>
#include<stack>
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode
{
int Data;
BinTree Left;
BinTree Right;
};
/*二叉树递归先序遍历*/
void PreorderTravseral(BinTree t)
{
if(t){
printf("%d ",t->Data);
PreorderTravseral(t->Left);
PreorderTravseral(t->Right);
}
}
/*二叉树递归中序遍历*/
void InorderTravseral(BinTree t)
{
if(t){
InorderTravseral(t->Left);
printf("%d ",t->Data);
InorderTravseral(t->Right);
}
}
/*二叉树递归后序遍历*/
void PostorderTravseral(BinTree t)
{
if(t){
InorderTravseral(t->Left);
InorderTravseral(t->Right);
printf("%d ",t->Data);
}
}
/*二叉树的非递归遍历都需要使用数据结构栈栈*/
/*二叉树非递归先序遍历*/
void PreorderTraversal_unrecursive(BinTree t)
{
using std::stack;
stack<BinTree>s;
while(t || !s.empty()){
while(t){
s.push(t);
printf("%d ",t->Left);
t = t->Left;
}
if(!s.empty()){
t = s.top();
s.pop();
t = t->Right;
}
}
}
/*二叉树非递归中序遍历*/
void InorderTraversal_unrecursive(BinTree t)
{
using std::stack;
stack<BinTree>s;
while(t || !s.empty()){
while(t){
s.push(t);
t = t->Left;
}
if(!s.empty()){
t = s.top();
s.pop();
printf("%d ",t->Data);
t = t->Right;
}
}
}
下面单独介绍树的后序遍历非递归算法。
在树的非递归后序遍历,对于每个根节点,要确保其左结点和右结点都被访问了以后才能访问根结点。没有孩子则可以直接访问。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了 每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
void PostorderTraversal_unrecusive(BinTree t)
{
if(!t)
return;
stack<BinTree>s;
s.push(t);
BinTree cur,prev = NULL;
//对于兄弟结点,栈一定先弹出左兄弟,然后再弹出右兄弟
//栈元素排列顺序一定是:根 右结点 左结点
while(!s.empty()){
cur = s.top();
if((cur->Left == NULL && cur->Data == NULL) ||
prev != NULL && (prev == cur->Left || prev == cur->Right)){
printf("%d ",cur->Data);
s.pop();
prev = cur;
}
else{
if(t->Right)
s.push(t->Right);
if(t->Left)
s.push(t->Left);
}
}
}
六,BST/AVL树基础
BST树只介绍删除,因为其它操作基本上会写AVL的就会BST的。
BinTree Delete( BinTree BST, ElementType X )
{
Position temp;
if(!BST){
printf("Not Found\n");
return BST;
}
else if(X > BST->Data)
BST->Right = Delete(BST->Right,X);
else if(X < BST->Data)
BST->Left = Delete(BST->Left,X);
else if(X == BST->Data){/*这里其实有两个办法删除,即找该结点右子树的最小值或找该结点左子树的最大值*/
if(BST->Right && BST->Left){
temp = FindMin(BST->Right);
BST->Data = temp->Data;
BST->Right = Delete(BST->Right, BST->Data);
}
else{
temp = BST;
if(!BST->Left)
BST = BST->Right;
else
BST = BST->Left;
free(temp);
}
}
return BST;
}
BST树不是严格logn的,说不定就最后搞得和链表一样,所以几乎不用它,一般我们用AVL树。AVL树中的重难点是旋转。
右单旋
其实右单旋,左单旋的名字其实是非常有误导性的,至少我第一次记错了。所谓左单旋,并不是树的一部分向左旋转,而是一开始结点插入在某结点(所谓“发现者”)的左子树的左子树上,树是需要向右旋转的。右单旋同理。
左右旋:对于根的右子树先进行左单旋,再返回根的右单旋。
右左旋:先对左子树行右单旋,再返回对根的左单旋。左单旋
左右旋
右左旋
下面的代码其实源自于一道题目:
The Root of AVL
#include <cstdio>
#include <cstdlib>
#include<algorithm>
using namespace std;
struct node{
int val;
node* left;
node* right;
};
typedef node* AVLTree;
node* NewNode(int val)
{
node* temp = (node*)malloc(sizeof(node));
temp->val = val;
temp->left = temp->right = NULL;
return temp;
}
node* SingleLeftRotation(node* tree)
{
node* temp = tree->left;
tree->left = temp->right;
temp->right = tree;
return temp;
}
node* SingleRightRotation(node* tree)
{
node* temp = tree->right;
tree->right = temp->left;
temp->left = tree;
return temp;
}
node* LeftRightRotation(node* tree)
{
tree->left = SingleRightRotation(tree->left);
return SingleLeftRotation(tree);
}
node* RightLeftRotation(node* tree)
{
tree->right = SingleLeftRotation(tree->right);
return SingleRightRotation(tree);
}
int GetHeight(node *tree)
{
if(!tree)
return 0;
else
return max(GetHeight(tree->left),GetHeight(tree->right))+1;
}
node* InsertNode(node* tree,int v)
{
if(!tree){
tree = NewNode(v);
}
else{
if(v > tree->val){
tree->right = InsertNode(tree->right,v);//插入的地方在右边
if(GetHeight(tree->right) - GetHeight(tree->left) == 2){//失衡了
if(v > tree->right->val)//插入到右子树的右边,右单旋
tree = SingleRightRotation(tree);
else if(v < tree->right->val)//插入到右子树的左边,右左旋
tree = RightLeftRotation(tree);
}
}
else if(v < tree->val){
tree->left = InsertNode(tree->left,v);//插入到左子树上
if(GetHeight(tree->left) - GetHeight(tree->right) == 2){//失衡了
//不建议对子树的高度进行储存,现用现算,否则不易更新
if(v > tree->left->val)//左子树的右子树上,左右旋
tree = LeftRightRotation(tree);
else if(v < tree->left->val)//左子树的左子树上,左单旋
tree = SingleLeftRotation(tree);
}
}
}
return tree;
}
int main(void)
{
int n,v;
node* tree = NULL;
scanf("%d", &n);
for(int i = 0;i < n; i++){
scanf("%d", &v);
tree = InsertNode(tree,v);
}
printf("%d",tree->val);
return 0;
}