树、森林、二叉树与并查集

在n个结点的树中有n-1条边。
树中一个结点的子结点个数称为该结点的度,树中结点的最大度数称为树的度。
有序树和无序树(左右子树是否有顺序)
路径只能从上到下,同一双亲结点的两个孩子结点之间不存在路径。
树的性质
1.结点数等于所有结点的度数加1
2.度为m的树中第i层上至多有m^(i-1)个结点
3.高度h的m叉树至多有(m^h-1)/(m-1)个结点
4.具有n个结点的m叉树的最小高度为:向上取整(log(m)(n(m-1)+1))


二叉树

满二叉树
高度为h的,含有2^h-1个结点
若从1开始,自上而下,自左向右对结点编号,i结点的父节点为i/2向下取整,左孩子2i,右孩子2i+1
完全二叉树
每个结点的编号都与与其同高度的满二叉树一致
1.若i<=n/2向下取整,则i为分支结点,否则为叶子结点
2.叶子结点只可能在层次最大的两层上出现。对于最大层次中的叶子结点,都依次排列在该层的最左边
3.若有度为1的结点,则只可能有一个,且该结点只有左孩子
4.按层序编号后,一旦出现某编号为i的结点为叶子结点或只有左孩子,则编号大于i的结点均为叶子结点
5.若n为奇数则每个分支结点都既有左节点又有右节点
6.i所在的层次为:向下取整(log(2)i)+1
7.具有n个结点的完全二叉树的高度为:向上取整(log(2)(n+1))或向下取整(log(2)n)+1

满二叉树与完全二叉树.PNG

二叉排序树
左子树上的所有结点的关键字均小于根结点的关键字,右子树上所有结点的关键字均大于根结点的关键字。左子树和右子树又各是一颗二叉排序树。
平衡二叉树
任一结点的左子树和右子树的深度之差不超过1

二叉树的性质
1.度为0,1,2的结点个数分别为n0,n1,n2
结点数(n0+n1+n2)-1=分支数=n1+2*n2
-》n0=n2+1(叶节点数)
2.非空二叉树第k层上至多有2^(k-1)个结点


二叉树的存储结构

完全二叉树和满二叉树比较适合顺序存储,通过序号来反映父子关系
二叉树一般采用链式存储结构

typedef struct BiTNode
{
    ElemType data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

在含有n个结点的二叉链表中,含有n+1个空链域。


二叉树的遍历

按照根结点被访问的时机,分为先序NLR,中序LNR,后序LRN
递归

void PreOrder(BiTree T)
{
    if(T!=NULL){
        visit(T);
        PreOrder(T->lchild);
        PreOrder(T->rchild);
    }
}

每个结点都访问一次仅访问一次所以时间复杂度为O(n)
递归栈的深度恰好为树的深度
非递归

void InOrder2(BiTree T)
{
    InitStack(S); BiTree p=T;
    while(p||!IsEmpty(S))
    {
        if(p){
            Push(S,p);
            p=p->lchild;
        }
        else{
            Pop(S,p); visit(p);
            p=p->rchild;
        }
    }
}

p首先指向根节点,将p入栈,并使p指向其左节点,若p为null(没有左节点),则出栈并访问,再将p指向其右节点。

后序的非递归
因为在左,右子树都处理完毕之前不可将根从栈中移除,则需要在入栈时添加额外信息rvisited来标识,是从左子树回来的还是从右树回来的。

typedef struct{
    BTNode *p;
    int rvisited;   //为1的时候代表p所指向的结点的右节点已被访问过
}SNode;     //栈中的结点定义
typedef struct{
    SNode Elem[maxsize]
    int top;
}SqStack;    //栈结构体
void PostOrder2(BiTree T)
{
    SNode sn;
    BTNode *pt=T
    InitStack(S);
    while(T){     //向左走到尽头,并把路径上的元素全部入栈
        Push(pt,0);
        pt=pt->lchild;
    }
    while(!S.IsEmpty()){
        sn=S.getTop();
        //栈顶结点的左子树一定是已经遍历完了
        //因为左子树上的结点一定在它的上面
        if(sn.p->rchild==NULL||sn.rvisited){     
            //若栈顶元素无rchild或rchild已被访问过,则出栈并访问它
            Pop(S, pt);
            visit(pt);
        }
        else{
            //如果有rchild且未被访问,则去访问它的rchild
            sn.rvisited=1;
            pt=sn.p->rchild;
            while(pt!=NULL){
                Push(pt, 0);
                pt=pt->lchild;
           }
        }
    }
}

层次遍历

void LevelOrder(BiTree T)
{
    InitQueue(Q);
    BiTree p;
    EnQueue(Q,T);
    while(!IsEmpty(Q)){
        DeQueue(Q,p);
        visit(p);
        if(p->lchild!=NULL)
            EnQueue(Q,p->lchild);
        if(p->rchild!=NULL)
            EnQueue(Q,p->rchild);
    }
}

由遍历序列构造二叉树
由二叉树的先序序列和中序序列可以唯一确定一棵二叉树
由二叉树的后序序列和中序序列可以唯一确定一棵二叉树
由二叉树的层序序列和中序序列可以唯一确定一棵二叉树
由二叉树的先序序列和后序序列不可以唯一确定一棵二叉树

二叉树遍历的应用
求二叉树的高度
递归 H=max(Hl+Hr)+1

int PostOrderGetHeight(BinTree BT)
{
    int Hl,Hr,maxH;
    if(BT){
        Hl=PostOrderGetHeight(BT->left);
        Hr=PostOrderGetHeight(BT->right);
        maxH=(Hl>Hr)?Hl:Hr;
        return (maxH+1);
    }
    else    return 0;
}

二元运算表达式及其遍历

二元运算表达式树.PNG

用后缀表达式构造一棵树
思路类似用后缀表达式求值
依次读取,若遇到操作数,则建立一个单节点树并入栈,若遇到操作符,建立一个单节点,则从栈中弹出栈顶的两棵树T1,T2,将该节点作为根,T2,T1作为左右子树,再将这个树入栈


树和森林

**树的存储结构
1.双亲表示法
采用一组连续空间来存储每个结点,在每个结点中增设一个伪指针,指示双亲结点在数组中的位置


树的双亲表示法.jpg
#define MAX_TREE_SIZE 100
typedef struct{
    ElemType data;
    int parent;
}PTNode;
typedef struct{
    PTNode nodes[MAX_TREE_SIZE];
    int n;
}PTree;

可快速找到父节点但求子节点时需遍历整个结构

2.孩子表示法
将每个结点的孩子结点都用单链表链接起来形成一个线性结构

3.孩子兄弟表示法
又称二叉树表示法
每个结点包括三部分:结点值,指向结点第一个孩子结点的指针,指向结点下一个兄弟结点的指针

typedef struct CSNode{
    ElemType data;
    struct CSNode *firstchild, *nextsibling;
}CSNode, *CSTree;
孩子表示法与孩子兄弟表示法.jpg

树,森林与二叉树的转换

树转二叉树.jpg

森林转二叉树.jpg

树和森林的遍历
树的遍历
先根遍历:若树非空,则先访问根结点,再按从左到右的顺序遍历根结点的每棵子树,其访问顺序与这棵树相应二叉树的先序遍历顺序相同。
后根遍历:若树非空,则按从左到右的顺序遍历根结点的每棵子树,之后再访问根结点。其访问顺序与这棵树对应的二叉树的中序遍历相同。
森林的遍历

树和森林的遍历.jpg


树的应用——并查集

有一些元素,各自为一个集合,若元素A与元素B有关系,则把它们并到同一集合,若A和C也有关系,就把C集合与之前AB合并后的集合合并。最后同一集合中的任意两个元素都是有关系可连通的。
并查集是一种集合表示,支持以下三种操作
1.Union(S, Root1, Root2)
把集合S中的子集合Root2并入Root1中,Root1与Root2互不相交
2.Find(S, x)
查找集合S中单元素x所在的子集合
3.Initial(S)
将集合S中的每个元素初始化为只有一个单元素的子集合
通常用双亲表示法作为并查集额存储结构,每个子集以一棵树表示。
初始化时每个元素自成一个单元素子集合
用数组存储

typedef struct{
    ElementType Data;
    int Parent;
}SetType;

查找某元素所在的集合

int Find(SetType S[], ElementType X)
{
    int i;
    for(i=0;i<MaxSize&&S[i].Data!=X;++i);
    if(i>=MaxSize)    return -1;  //cannot find
    for(;S[i].parent>=0;i=S[i].parent);
    return i;
}

集合的并运算

void Union(SetType S[], ElementType X1, ElementType X2)
{
    int Root1,Root2;
    Root1=Find(S, X1);    Root2=Find(S, X2);
    if(Root1!=Root2)
        S[Root2].parent=Root1;
}

数越深Find就越耗时,所以合并时尽量让小的树的parent指向大的树的根,改进方法是根节点的parent不再为-1而是数中元素个数的负值

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容