算法——比较高级的数据结构“树”

相应的练习代码:https://github.com/liuxuan320/Algorithm_Exercises


0. 写在前面

这一章本来应该叫做基本检索与周游的。但是考虑到其实讲的更多的是树,所以也就直接写树了。

1. 二叉树的三种常用检索方式

我们常见的树,80%的例子都是二叉树,因为二叉树结构简单,易于处理,如果是多叉树,可能牵扯到的就是图论里的相关知识,这个在最后讲。现在我们讲解二叉树的三种常见的检索方式:先序遍历、中序遍历、后序遍历。

所谓的先序遍历,其中的先表示的是父节点是先访问,也就是说其顺序应该是:访问父节点→访问左叶子节点→访问右叶子节点。这么说完,可能你中序遍历和后序遍历应该都会了,中序遍历:访问左叶子节点→访问父节点→访问右叶子节点,后序遍历:访问左叶子节点→访问右叶子节点→访问父节点。下面给出他们的算法:

//先序遍历
procedure PREORDER(T)
    //T是一棵二元树,T中每个节点有3个信息段,LCHILD,DATA,RCHILD
    if(T≠0) then call VISIT(T)
                call PREORDER(LCHILD(T))
                call PREORDER(RCHILD(T))
    endif
end PREORDER
//中序遍历
procedure INORDER(T)
    if(T≠0) then call INORDER(LCHILD(T))
                call VISIT(T)
                call INORDER(RCHILD(T))
    endif
end INORDER
//后序遍历
procedure POSTORDER(T)
    //T是一棵二元树,T中每个节点有3个信息段,LCHILD,DATA,RCHILD
    if(T≠0) then call POSTORDER(LCHILD(T))
                call POSTORDER(RCHILD(T))
                call VISIT(T)
    endif
end POSTORDER

这里给出的是三种遍历的递归算法,非递归的大家可以自己重写。

2. 树的常见几种应用

二叉树常考的不适这三种遍历,而是基于这三种遍历的应用算法。我们这里讲3种,分别是镜像树、D-search检索、还有红黑树。

1. 镜像树

镜像树,顾名思义,就是和原来的树是镜像,原来的左孩子是现在的右孩子,原来的右孩子是现在的左孩子。我一同学面试时,也遇到过这题,因此我在这提到它。

它的简单原理就是利用递归在每个结点先进行左右互换,然后再不断递归,直到叶子节点。具体代码如下:

procedure swapTree(T)
    if(T!=NULL) then
        Node rootcreateNode()
        root.dataT.data
        root.rightswapTree(T.left)
        root.leftswapTree(T.right)
        return root
    else
        return NULL
    endif
end swapTree
2.深度优先与广度优先

深度优先和广度优先可以用在树中,也可以用在图中。我们这里讲解一下这两种优先遍历算法。对于广度优先来讲,主要是去维护一个未检测结点表的队列。

//深度优先检索
procedure DFS(V)
    VISITED(V)←1
    for 邻接于V的每个结点w do
        if VISITED(w)=0 then 
            call DFS(w) 
        endif
    repeat
end DFS
//宽度优先
procedure BFS(V)
    VISITED(V)←1;u←v
    将Q初始化为空
    loop
        for 邻接于u的所有结点w do
            if VISITED(w)=0 then
                call ADDQ(w,Q)
                VISITED(w)←1
            endif
        repeat
    if Q为空 then 
        return
    endif
    call DELETEQ(u,Q)
    repeat
end BFS 
3. D-search检索

D-search检索是一种不太常见但非常有用的检索,它与BFS(广度优先检索)不同之处在于,下一个要检测的结点时最新加到未检测结点表的那个算法。因此这个表应做成一个栈而不是一个队列。具体算法如下:

procedure D-search(V)
    VISITED(V)←1;u←V
    将STACK初始化为空
    loop
        for 邻接于u的所有结点 w do
            if VISIT(w)=0 then
                pull(w,STACK)
                VISITED(W)←1
            endif
        repeat
        if STACK=null then
            return
        endif
        push(u,STACK)
    repeat
end D-search
3. 红黑树

我们在这里不深入的讲解红黑树,对于红黑树,一定是有必要单开一篇来讲解的。这个作为是否真正算得上熟悉数据结构的必要条件之一,是十分重要的。但是我们这里只是简要介绍一下,具体的红黑树,会在以后的专题中介绍。
红黑树,是一种自平衡二叉查找树。它把结点分为红黑两种结点,才被称为红黑树。它具有5种性质:

性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3 每个叶节点(NIL节点,空节点)是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

但是其实这么说,我们可能没有直观的认识,在yang_yulei的博客里写了一个非常简单的红黑树的说法,那就是使用红链接的2-3树。而红黑树的具体实现,大家可以参考July的博客。

由于我们的重点不在于红黑树,所以这一章中我们只能简要介绍并给大家指明方向。希望能够共同学习。

3. 树的其他高级应用

1. 森林的三中检索方式

森林和树的不同在于,森林里不只有一个根节点,也就是说,森林里不止一棵树。那么那三种周游先根、中根、后根周游都有相应的变化。

  1. 树先根次序周游(F)
    1)若F为空,则返回;
    2)访问F的第一棵树的根
    3)按树先根次序周游F的第一棵树的子树;
    4)按树先跟次序周游F其余的树。
  2. 树中根次序周游(F)
    1)若F为空,则返回;
    2)按树中根次序周游F的第一棵树的子树;
    3)访问F的第一棵树的根;
    4)按树中跟次序周游F其余的树。
  3. 树后根次序周游(F)
    1)若F为空,则返回;
    2)按树后根次序周游F的第一棵树的子树;
    3)按树后跟次序周游F其余的树;
    4)访问F的第一棵树的根;
    这样,三种遍历就已经改造完成了。
2. 双连通分图

双连通分图是很久之前说的了,现在只有双连通图与双连通分量。顾名思义,对于一个图来讲,如果一个图的两个部分,不存在唯一一个结点(学术上称为关节点)使得这两个分图相交,那么这个图就称为双连通分图。
对于双连通分图有这么几个问题,第一个问题就是生成双连通分图,第二个问题则是如何把一个不是双连通分图的图改成双连通分图。
首先,对于第一个问题来讲,如何生成双连通分图,在这之前,又需要解决两个部分,但是我们先不说,我们先看算法:

算法 生成双连通分图

procedure ART(u,v)
    global DFN(n),L(n),num,n,S
    置存放边的栈S为空
    DFN(u)num;L(u)←num;num←num+1
    for 每个邻接于u的结点 w do
        if v≠w and DFN(w)<DFN(u) then
            将(u,w)加到S的顶部
        endif
        if DFN(w)=0 then call ART(w,u)
            if L(w)≥DFN(u) then 
                print('new biconnected component')
                loop
                    从栈S的顶部删去一条边
                    设这条边是(x,y)
                    print('(',x,',',y,')')
                until((x,y)=(u,w) or (x,y)=(w,u)) repeat
            endif
            L(u)←min(L(u),L(w))
        else if w≠v then 
                L(u)←min(L(u),DFN(w))
            endif
        endif
    repeat
end ART

看到这,肯定第一个疑问就是DFN和L到底是什么东西。DFN是深度优先的次数,L是后跟遍历,这其实就是著名的Tarjan算法。具体求这两个数的思想如下:

1. 先深度优先遍历找出DFN(i2. 后根遍历找到L(i)
首先考虑结点i是不是叶子节点,
    如果是叶子节点,
        若有逆边就用逆边的DFN
        否则,用自己的结点的DFN
    如果不是叶子节点,
        就用孩子结点中最小的L(u)来作为自己的L。

所谓的逆边,则是指除了深度优先检索以外还剩下的边,称为逆边。那么,使用以上的算法就可以解决了第一个问题。

对于第二个问题,如何把非双连通分图改造成双连通分图。那么还需要额外一个双连通分图列表,另外还有一个双连通分图改造函数。具体算法如下,代码请见我的github地址:

procedure ART2(u,v)
    global DFN(n),L(n),num,n,S,LL
    置存放边的栈S为空
    DFN(u)←num;L(u)←num;numnum+1
    for 每个邻接于u的结点 w do
        if v≠w and DFN(w)<DFN(u) then
            将(u,w)加到S的顶部
        endif
        if DFN(w)=0 then call ART(w,u)
            if L(w)≥DFN(u) then 
                print('new biconnected component')
                创建一个双连通分图结点L//新添加的语句
                loop
                    从栈S的顶部删去一条边
                    设这条边是(x,y)
                    把(x,y)加入到L中//新添加的语句
                    print('(',x,',',y,')')
                until((x,y)=(u,w) or (x,y)=(w,u)) repeat
                把L加入到双连通分图结点列表LL中//新添加的语句
            endif
            L(u)←min(L(u),L(w))
        else if w≠v then 
                L(u)←min(L(u),DFN(w))
            endif
        endif
    repeat
end ART2
porcedure changeBG(LL)
    global c(N:2) //c为已标记两双连通分图连接列表
    遍历LL中两个双连通分图结点(u,w),u≠w
    遍历c
        if(u,w)还没有连接过 then
            遍历u中的结点i
            遍历w中的结点j
            if i=j then
                标记(u,w)为连接
                添加(u,w)到c中
                查找u中任意一结点m,m≠i
                查找w中任意一结点n,n≠j
                print(" 要连接(",m,",",n")")
            endif
        endif
endchangeBG

4. 小结

这一节中,我们主要讲了关于树的若干算法,主要是三种遍历方法,三种检索方法,以及其红黑树、双连通分图这两个应用。树论经常和图论一起讨论,我们这里只讨论一些常见的,基础的问题。在接下来的更高级的面试算法中,我们会详细介绍一些具体的算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI让世界更懂你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值