平衡二叉树

平衡二叉树(\(Balanced \ Binary \ Tree\))又被称为\(AVL\)树,且具有以下性质:它是一 棵空树或是具有以下性质的二叉排序树:其左右两棵子树的高度差的绝对值不超过 \(1\),并且左右两个子树都是一棵平衡二叉树。二叉树中各节点的平衡因子定义为左子树的高度减去它的右子树的高度。

根据定义平衡二叉树的平衡因子只可能是\(-1,0,1\)。只要二叉排序树有一个节点的平衡因子的绝对值大于\(1\),则二叉排序树就是不平衡的。
屏幕快照 2016-08-03 下午8.09.32.png

假设向平衡二叉树中插入一个新节点后破坏了平衡二叉树的平衡性,则首先应该找出插入新节点之后失去平衡的最小子树根节点的指针,然后再调整这个子树中有关节点之间的链接关系,使之成为新的平衡二叉树。

失去平衡的最小子树是指离插入节点最近,且平衡因子的绝对值大于\(1\)的节点作为根的子树。即在新插入的结点向上查找,以第一个平衡因子的绝对值超过 \(1\) 的结点为根的子树称为最小不平衡子树。也就是说,一棵失衡的树,是有可能有多棵子树同时失衡的,而这个时候,我们只要调整最小的不平衡子树,就能够将不平衡的树调整为平衡的树.

屏幕快照 2016-08-03 下午8.20.10.jpg

平衡二叉树失衡的分类:
SumatraPDF_2016-08-03_20-50-07.png

  • \(LL型:\)左子树的左节点插入 屏幕快照 2016-08-03 下午8.54.53.jpg \(A\)的左孩子\(B\)向右上旋转代替\(A\)成为根节点,将 \(A\)节点向右下旋转成为\(B\)的右子树的根节点,而\(B\)的原右子树则作为\(A\)节点的左子树。 屏幕快照 2016-08-03 下午8.28.33.png 在向树中追加“节点\(1\)”的时候,根据定义我们知道这样会导致了“节点\(3\)"失衡,满足“左左情况“,可以这样想,把这棵树比作齿轮,我们在“节点\(5\)”处把齿轮往下拉一个位置,也就变成了后面这样“平衡”的形式
  • \(RR型:\)右子树的右节点插入 屏幕快照 2016-08-03 下午9.01.30.jpg \(A\)的右孩子\(B\)向左上旋转代替\(A\)成为根节点,将 \(A\)节点向左下旋转成为\(B\)的左子树的根节点,而\(B\)的原左子树则作为\(A\)节点的右子树。 屏幕快照 2016-08-03 下午8.58.01.png
  • \(LR型:\)左子树的右节点插入 屏幕快照 2016-08-03 下午9.28.50.jpg屏幕快照 2016-08-04 上午9.02.27.png屏幕快照 2016-08-03 下午9.10.43.png
  • \(RL型:\)右子树的左节点插入 屏幕快照 2016-08-03 下午9.38.11.jpg屏幕快照 2016-08-04 上午9.06.04.png屏幕快照 2016-08-03 下午9.34.27.png

平衡二叉树的删除一个节点的方法:

  • 查找:按关键字找到要删除的节点\(*p\)
  • 删除:删除\(*p\)节点分为以下几种情况
    • 叶子节点:由于没有左右子树,因此直接删除不会破坏原有二叉树的结构
    • 左单分支节点:只须将\(*p\)节点的左子树直接作为双亲节点\(*f\)的左孩子即可
    • 右单分支节点:只须将\(*p\)节点的右子树直接作为双亲节点\(*f\)的右孩子即可
    • 双分支节点: 若\(*p\)节点的左、右子树不空。分为两种方法:
    • 用\(*p\)节点的左子树的最右下节点(对应左子树的最右节点)\(*r\)的值代替\(*p\)节点的值,然后用第\(2\)种和第\(3\)种情况删除\(*r\)节点
    • 用\(*p\)节点的右子树的最左下节点(对应右子树的最左节点)\(*r\)的值代替\(*p\)节点的值,然后用第\(2\)种和第\(3\)种情况删除\(*r\)节点
  • 调整:按照上述方式调整

判断一棵二叉树是否为平衡二叉树
屏幕快照 2016-08-05 上午8.22.02.png

将整数序列\({4,5,7,2,1,3,6}\)中的数依次插入到一棵空的二叉平衡树,试构造相应的平衡二叉树。
屏幕快照 2016-08-06 上午11.46.08.png

将整数序列\({16,3,7,11,9,26,18,14,15}\)中的数依次插入到一棵空的二叉平衡树,试构造相应的平衡二叉树。
屏幕快照 2016-08-06 上午11.46.36.png
删除一个双分支节点\(11\):
屏幕快照 2016-08-06 上午11.51.40.png
屏幕快照 2016-08-06 上午11.50.27.png

线索二叉树

对于 \(n\) 个节点的二叉树,在二叉链存储结构中有 \(n+1\) 个空链域,利用这些空链域存放在某种遍历次序下该节点的前驱节点和后继节点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树。

建立线索二叉树,实质上就是遍历一棵二叉树,在遍历过程中检查当前节点的左、右指针域是否为空;如果为空,将它们改为指向前驱节点或后继节点的线索

  • 先序线索二叉树中查找节点\(*p\)的后继节点的过程:在先序线索二叉树中,若\(*p\)节点的左孩子非空,则左孩子为后继节点;否则\(*p\)节点的后继为右孩子或者右线索。
  • 中序线索树\(t\)中查找\(*p\)的前驱节点:若\(p->ltag==1,则p->lchild\)指向前驱节点,否则查找\(*p\)节点的左孩子节点的最右下节点 屏幕快照 2016-08-05 上午9.10.27.png
  • 中序线索树\(t\)中查找\(*p\)的前驱节点:若\(p->rtag==1,则p->rchild\)指向前驱节点,否则查找\(*p\)节点的右孩子节点的最左下节点 屏幕快照 2016-08-05 上午9.13.18.png

最近公共祖先

最近公共祖先简称\(LCA(Lowest \ Common \ Ancestor)\),所谓\(LCA\),是当给定一个有树\(T\)时,对于任意两个结点\(u、v\),找到一个离根最远的结点\(x\),使得\(x\)同时是\(u\)和\(v\)的祖先,\(x\) 便是\(u、v\)的最近公共祖先。

屏幕快照 2016-08-05 上午9.37.18.png
结点\(3和结点4\)的最近公共祖先是结点2,即\(LCA(3,4)=2\) 。在此,需要注意到当两个结点在同一棵子树上的情况,如结点\(3\)和结点\(2\)的最近公共祖先为\(2\),即 \(LCA(3,2)=2\)。同理:\(LCA(5,6)=4,LCA(6,10)=1\)。

  • 暴力算法:一种简单的方法是\(DFS\)分别寻找到两个节点\(p和q\)的路径,然后对比路径,查看他们的第一个分岔口,则为\(LCA\)。很简单的思路就是看两个值在\(root\)的哪边:
    • 两个值都在左边,则\(LCA\)在左边
    • 两个值都在右边,则\(LCA\)在右边
    • 一个在左一个在右,则说明\(LCA\)就是当前的\(root\)节点。 屏幕快照 2016-08-05 上午9.42.16.png
  • 使用自定义带父节点信息的节点:根据层次遍历记录父节点信息,然后当两个节点的父信息都记录完之后,就向上查找父节点,直到两个节点的父节点相遇。 屏幕快照 2016-08-06 下午2.34.10.png屏幕快照 2016-08-06 下午2.34.41.png
posted @ 2017-04-17 22:16  I呆呆  阅读(1401)  评论(0编辑  收藏  举报