树的介绍

一,树的定义
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树中的重难点是旋转。

右单旋

npm 执行命令时使用递归向上寻找node_modules 编写递归函数遍历node_结点


其实右单旋,左单旋的名字其实是非常有误导性的,至少我第一次记错了。所谓左单旋,并不是树的一部分向左旋转,而是一开始结点插入在某结点(所谓“发现者”)的左子树的左子树上,树是需要向右旋转的。右单旋同理。

左右旋:对于根的右子树先进行左单旋,再返回根的右单旋。

右左旋:先对左子树行右单旋,再返回对根的左单旋。左单旋

npm 执行命令时使用递归向上寻找node_modules 编写递归函数遍历node_子树_02


左右旋

npm 执行命令时使用递归向上寻找node_modules 编写递归函数遍历node_Data_03


右左旋

npm 执行命令时使用递归向上寻找node_modules 编写递归函数遍历node_结点_04


下面的代码其实源自于一道题目:

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;
}