精华内容
下载资源
问答
  • TreeNode

    千次阅读 2006-04-22 14:25:00
     TreeNode te=new TreeNode();  te.Text="中国";  tv.SelectExpands=true;  tv.Nodes.Add(te);  //添加下一节点类型  Hashtable ha=db.getHashatble();  IDictionaryEnumerator id=ha.GetEnumerator();  ...
    OledbClass db;
      private void Page_Load(object sender, System.EventArgs e)
      {
       // 在此处放置用户代码以初始化页面
       TreeInfos();
       if(Request.QueryString["id"]!=null)
       {
        TextBox2.Text="";
        TextBox2.Text="邮政编码是:";
        TextBox2.Text+=db.getYou(Request.QueryString["id"]);
        TreeNode bb=null;
        string id=(string)Request.QueryString["id"];
        foreach(TreeNode tn in tv.Nodes)
        {
         foreach(TreeNode tn1 in tn.Nodes)
         {
          foreach(TreeNode tn2 in tn1.Nodes)
          {
           if(tn2.NodeData.ToString().CompareTo(Request.QueryString["id"].ToString())==0)
           {
            bb=tn2;
           }
          }
         }
        }
        while(bb!=null)
        {
         bb.Expanded=true;
         try
         {
          bb=(TreeNode)bb.Parent;
         }
         catch
         {
          
         }
        }
       }
      }
      
      private void TreeInfos()
      {
       db=new OledbClass(Server.MapPath("db.mdb"));
     
       TreeNode te=new TreeNode();
       te.Text="中国";
       tv.SelectExpands=true;
       tv.Nodes.Add(te);
       //添加下一节点类型
       Hashtable ha=db.getHashatble();
       IDictionaryEnumerator id=ha.GetEnumerator();
       while(id.MoveNext())
       {
        TreeNode Newde=new TreeNode();
        Newde.NodeData=id.Key.ToString();
        Newde.Text=id.Value.ToString();
        tv.Nodes[0].Nodes.Add(Newde);
       }
       //添加下一节点省
       foreach(TreeNode tn in tv.Nodes)
       {
        foreach(TreeNode tn1 in tn.Nodes)
        {
         ha=db.getDiqu(tn1.NodeData.ToString());
         id=ha.GetEnumerator();
         while(id.MoveNext())
         {
          TreeNode NewTn=new TreeNode();
          NewTn.NodeData=id.Key.ToString();
          NewTn.Text=id.Value.ToString();
          tn1.Nodes.Add(NewTn);
         }
        }
       }
       //添加下一节点市
       foreach(TreeNode tn in tv.Nodes)
       {
        foreach(TreeNode tn1 in tn.Nodes)
        {
         foreach(TreeNode tn2 in tn1.Nodes)
         {
          ha=db.getHashatble(tn2.NodeData.ToString());
          id=ha.GetEnumerator();
          while(id.MoveNext())
          {
           TreeNode NewNo=new TreeNode();
           NewNo.NodeData=id.Key.ToString();
           NewNo.Text=id.Value.ToString();
           NewNo.ID=id.Key.ToString()+"/" οnclick=/"Text("+ id.Key.ToString() +")";
           NewNo.NavigateUrl="WebForm4.aspx?id="+id.Key.ToString();
           tn2.Nodes.Add(NewNo);
           tv.GetNodeFromIndex(tv.SelectedNodeIndex).Expanded=true;
          }
         }
        }
       }
      }
    展开全文
  • 思路:跟235思路差不多,都是判断左右子树是否存在p,q,如果左右子树分别存在pq,...public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if(root == null) return null; if(p == root...

    思路:跟235思路差不多,都是判断左右子树是否存在p,q,如果左右子树分别存在pq,则直接返回当前root,如果不存在,则继续递归左右子树

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null;
        if(p == root || q == root) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if(left != null && right != null) return root; //如果左右都有,就返回当前root
        return left != null ? left : right; //谁不为空,就返回谁 
    }

     

     

    展开全文
  • HashMap之TreeNode

    千次阅读 2018-10-17 11:06:13
    HashMap之TreeNode ##简述 在分析HashMap之前先说一下内部类TreeNodeTreeNode类是一颗红黑树的各种操作。当然TreeNode不只是简单的红黑树操作,还有与HashMap业务相关的代码 先看一下类的继承关系 Entry是一...

    HashMap之TreeNode

    ##简述
    在分析HashMap之前先说一下内部类TreeNode。TreeNode类是一颗红黑树的各种操作。当然TreeNode不只是简单的红黑树操作,还有与HashMap业务相关的代码
    先看一下类的继承关系
    这里写图片描述

    Entry是一个接口,主要有一些让子类去实现的get、set方法
    Node是一个单向链表
    最后就是TreeNode红黑树了

    先看一下简单的Node单向链表,然后再看复杂一点的TreeNode

    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
    
        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
    
        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }
    
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
    
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
    
        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }
    

    很简单,一个Node相当于链表的一个节点,next属性对应着下一个节点

    TreeNode
    TreeNode也是,一个TreeNode代表红黑树的一个节点
    红黑树是在二叉查找树基础上,对每一个节点添加一个颜色属性形成的,同时整个红黑二叉树需要同时满足一下五条性质

    1. 每个节点或是红色,或是黑色的
    2. 跟节点是黑色的
    3. 每个叶节点(NIL)是黑色的
    4. 如果一个节点是红色的,则它的两个子节点都是黑色的
    5. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数码的黑节点
      如图
      这里写图片描述
      叶节点(NIL)不包含任何数据,只是一个标记,没有实际意义

    下面通过红黑树的插入、删除、查找、左旋、右旋进一步了解TreeNode

    先看一下构造

    TreeNode(int hash, K key, V val, Node<K,V> next) {
        super(hash, key, val, next);
    }
    

    可以看到调用了父类的构造方法,也就是说,TreeNode是红黑树的同时也是一个单项链表

    ##旋转
    先说一下左旋和右旋操作
    添加和删除操作肯能会违反红黑树的的性质,而保持红黑树的性质是通过旋转来操作的
    这里写图片描述
    如添加了一个红节点N就违反了性质4,在通过2个动图看一下是怎么旋转的

    左旋
    这里写图片描述
    右旋
    这里写图片描述
    以上面的静态图来说明右旋操作,左旋一样,只是操作是相反的
    上图添加了节点N违反性质4,右旋G节点:

    • 使P成为根节点并变成黑色满足性质2
    • G成为P的右节点
    • P的右节点成为G的左节点
    static <K, V> TreeNode<K, V> rotateRight(TreeNode<K, V> root,
                                             TreeNode<K, V> p) {
        TreeNode<K, V> l, pp, lr;
        if (p != null && (l = p.left) != null) {
            if ((lr = p.left = l.right) != null)
                lr.parent = p;
            if ((pp = l.parent = p.parent) == null)
                (root = l).red = false;
            else if (pp.right == p)
                pp.right = l;
            else
                pp.left = l;
            l.right = p;
            p.parent = l;
        }
        return root;
    } 
    

    代码虽然不多,但是有点乱,我们一点一点的分析。我们假设传入的参数roo是G节点,p也是G节点
    参数root是根节点,p是被旋转的节点
    变量l是p的左节点、pp是p的父节点、lr是l右节点

    第4行判断p不等于null并把左节点赋值给l,如果l为null的话就不能右旋了
    第5行是完成上面性质c,并把值赋值给lr
    第7行把p的父节点赋值给l的父节点,并把值赋值给pp,因为我们传入的参数G没有父节点,所以pp为null,为了满足红黑树的性质2,把颜色设置成黑色
    第9行和第11行,是判断p是左节点还是右节点,如果是右节点则吧父节点的右节点设置成l,反之亦然
    第13行设置l的右节点为p,也就是P的右节点设置成G
    第14行设置p的父节点为l,也就是设置G的父节点为P

    ##查找
    从当前节点查找指定节点,根据参数h判断是从左边还是右边查找

       final TreeNode<K, V> find(int h, Object k, Class<?> kc) {
        TreeNode<K, V> p = this;
        do {
            int ph, dir;K pk;
            TreeNode<K, V> pl = p.left, pr = p.right, q;
            if ((ph = p.hash) > h)
                p = pl;
            else if (ph < h)
                p = pr;
            else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                return p;
            else if (pl == null)
                p = pr;
            else if (pr == null)
                p = pl;
            else if ((kc != null ||
                    (kc = comparableClassFor(k)) != null) &&
                    (dir = compareComparables(kc, k, pk)) != 0)
                p = (dir < 0) ? pl : pr;
            else if ((q = pr.find(h, k, kc)) != null)
                return q;
            else
                p = pl;
        } while (p != null);
        return null;
    }
    

    参数h是TreeNode的hash,而hash就是保持平衡二叉树的字段,通过它进行比较,小的放左边,大的放右边
    参数k是要查找的节点的key
    参数kc是要查找节点实现的key的运行时类型

    第2行p赋值当前节点,从当前节点开始查找
    变量ph是当前节点的hash
    变量dir是Comparable接口的比较结果
    变量pl当前节点的左节点
    变量pr当前节点的右节点
    变量q是右节点查找时候的返回值,如果找到了返回找到的节点

    分3点来说find方法

    1.find方法根据当前节点的hash与参数的h比较,如果大于pl赋值给p,如果小于pr赋值给p,相等的情况通过当前节点的key与参数的key比较,不相等在通过equals方法比较。

    2.接下来在判断如果pl等于null把br赋值给p,pr等于null的话吧pl赋值给p,都不等于空的话在根据key实现的Comparable比较。comparableClassFor方法是获取k的运行时类型,compareComparables方法先判断,key与运行时kc是同类型,在通过调用k和kc实现的Comparable接口的compareTo进行比较

    static int compareComparables(Class<?> kc, Object k, Object x) {
        return (x == null || x.getClass() != kc ? 0 :
                ((Comparable)k).compareTo(x));
    }
    

    3.以上这些条件如果都不满足,那右节点通过递归比较找到则返回,找不到,把左节点赋值给p,进入下次循环在比较

    ##插入
    从根节点遍历,然后通过hash比较,找到合适的位置插入
    插入的代码不多,但描述起来有点费劲,所以我分成3段来说明
    1.

    final TreeNode<K, V> putTreeVal(HashMap<K, V> map, Node<K, V>[] tab,
                                        int h, K k, V v) {
            Class<?> kc = null;
            boolean searched = false;
            TreeNode<K, V> root = (parent != null) ? root() : this;                                          
    

    参数map是用来创建新的节点,通过条用map的newTreeNode
    参数tab
    参数h是节点的hash,find方法说过hash的作用,不再敖述
    参数k是插入节点的key
    参数v是插入节点的value
    变量kc与find的kc作用一样
    变量 searched用来判断只搜索一次的标记
    变量root是根节点,如果parent不为空会通过root方法来获取

    final TreeNode<K, V> root() {
        for (TreeNode<K, V> r = this, p; ; ) {
            if ((p = r.parent) == null)
                return r;
            r = p;
        }
    }
    

    就剩下一个for循环了

    for (TreeNode<K,V> p = root;;) {
        int dir, ph; K pk;
        if ((ph = p.hash) > h)
            dir = -1;
        else if (ph < h)
            dir = 1;
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            return p;
        else if ((kc == null &&
                  (kc = comparableClassFor(k)) == null) ||
                 (dir = compareComparables(kc, k, pk)) == 0) {
            if (!searched) {
                TreeNode<K,V> q, ch;
                searched = true;
                if (((ch = p.left) != null &&
                     (q = ch.find(h, k, kc)) != null) ||
                    ((ch = p.right) != null &&
                     (q = ch.find(h, k, kc)) != null))
                    return q;
            }
            dir = tieBreakOrder(k, pk);
        }
    

    变量dir,如果小于等于0,节点插入到左边,否则插入到右边
    变量ph,当前遍历节点的hash
    变量pk,当前节点的key

    第1-8行,根据hash判断节点放在一边,如果hash值相等,并且key相等,不插入,直接返回节点
    9-21行,在用key实现的接口去比较如果还是相等,如果是第一次搜索,那么分别通过左节点和右节点搜索,如果搜索到了也不插入,直接返回。搜索不到会调用tieBreakOrder方法通过key的hasCode比较

    static int tieBreakOrder(Object a, Object b) {
        int d;
        if (a == null || b == null ||
            (d = a.getClass().getName().
             compareTo(b.getClass().getName())) == 0)
            d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
                 -1 : 1);
        return d;
    }
    

    通过条用System.identityHashCode进行比较
    3.
    插入的最后一段代码,如果上面找到了合适的位置那么这里就插入

    TreeNode<K,V> xp = p;
    if ((p = (dir <= 0) ? p.left : p.right) == null) {
        Node<K,V> xpn = xp.next;
        TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
        if (dir <= 0)
            xp.left = x;
        else
            xp.right = x;
        xp.next = x;
        x.parent = x.prev = xp;
        if (xpn != null)
            ((TreeNode<K,V>)xpn).prev = x;
        moveRootToFront(tab, balanceInsertion(root, x));
        return null;
    }
    

    变量xp是当前遍历的节点
    2行根据dir判断是左节点还是右节点,然后为null就插入
    3行,上面说TreeNode是红黑树,但它的父类是个链表,所以可以通过xp.next获得下一个节点
    4行通过map.newTreeNode创建一个新节点,稍后查看是怎么创建一个新节点的
    5-8行,判断节点x放在左还是右
    9-12行,设置链表的相关属性

    TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
        return new TreeNode<>(hash, key, value, next);
    }
    

    没什么特别的就是创建一个TreeNode

    13行,插入一个节点肯能会破坏红黑树的性质,所以调用balanceInsertion方法保持红黑树的性质。在说方法moveRootToFront之前得说一下参数tab,tab是HashMap存节点的数组,每一个节点都是一颗红黑树,下一章在细说这个tab,这里只知道它是一个红黑树的数组。
    balanceInsertion方法保持红黑树性质,根节点可能会被换掉,上面说过红黑树同时还是一个链表,根节点换了,那么链表头也就换了,所以moveRootToFront所做的是更正链表头。先看balanceInsertion方法,之后再看moveRootToFront方法

    有3种情况会打乱红黑树的性质
    1.插入节点的父节点是红色,父节点的邻居称叔节点,同时叔节点也是红色的情况。通过把父节点和爷爷节点变色来满足红黑树的性质,如图
    这里写图片描述
    2.叔节点也是黑色,父节点是红色,并且节点是一个右子节点。通过左旋父节点变成情况3,如图
    这里写图片描述
    3.叔节点也是黑色,父节点是红色,并且节点是一个左子节点。通过右旋父节点使红黑树满足条件
    这里写图片描述
    代码也是针对上面的3中情况来处理的

     static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                TreeNode<K,V> x) {
        x.red = true;
        for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
            if ((xp = x.parent) == null) {
                x.red = false;
                return x;
            }
            else if (!xp.red || (xpp = xp.parent) == null)
                return root;
            if (xp == (xppl = xpp.left)) {
                if ((xppr = xpp.right) != null && xppr.red) {
                    xppr.red = false;
                    xp.red = false;
                    xpp.red = true;
                    x = xpp;
                }
                else {
                    if (x == xp.right) {
                        root = rotateLeft(root, x = xp);
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }
                    if (xp != null) {
                        xp.red = false;
                        if (xpp != null) {
                            xpp.red = true;
                            root = rotateRight(root, xpp);
                        }
                    }
                }
            }
            else {
                if (xppl != null && xppl.red) {
                    xppl.red = false;
                    xp.red = false;
                    xpp.red = true;
                    x = xpp;
                }
                else {
                    if (x == xp.left) {
                        root = rotateRight(root, x = xp);
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }
                    if (xp != null) {
                        xp.red = false;
                        if (xpp != null) {
                            xpp.red = true;
                            root = rotateLeft(root, xpp);
                        }
                    }
                }
            }
        }
    }
    

    第3行,让插入的节点默认为红色。然后就是一个for循环了,直到满足红黑树的性质后返回
    第4行,变量xp是x的父节点,xpp是x的爷爷节点,xppl是爷爷节点的左节点,xppr是爷爷节点的右节点。
    第5-8行判断如果没有父节点,那么当前节点就是跟节点,然后返回
    第9-10行判断如果父节点是黑色,或者爷爷节点为空返回
    第11-31与32-52行是相反的操作,上面说的打乱红黑树的3种情况,第2、3种在32-52行也会相应的反过来,所以这里只说明11-31行,余下代码就明白了
    第11行判断,如果父节点是爷爷节点的左节点
    第12-17行判断就是上面说的第一种情况,父节点和叔节点都是红色,然后把爷爷的右节点变为黑色,父节点变为黑色,爷爷节点变为红色,把爷爷节点赋值给x,继续循环,直到满足条件
    18-32行的else包含了上述的2、3两种情况,这个就不做过多的解释了,上述描述已经解释过这两种情况

    好了,现在在来看moveRootToFront方法

    static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
        int n;
        if (root != null && tab != null && (n = tab.length) > 0) {
            int index = (n - 1) & root.hash;
            TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
            if (root != first) {
                Node<K,V> rn;
                tab[index] = root;
                TreeNode<K,V> rp = root.prev;
                if ((rn = root.next) != null)
                    ((TreeNode<K,V>)rn).prev = rp;
                if (rp != null)
                    rp.next = rn;
                if (first != null)
                    first.prev = root;
                root.next = first;
                root.prev = null;
            }
            assert checkInvariants(root);
        }
    }
    

    上面解释了tab参数,第5行取出经过balanceInsertion摧残之后的根节点,第6行判断如果与摧残之前的节点不同,通过7-17的代码调整链表顺序

    ##删除

    删除比较复杂一些,大流程和插入一样,先删除节点,然后可能会不满足红黑树的性质,在进行调整
    先说删除节点方法removeTreeNode,之后再说调整节点方法balanceDeletion,在删除完就会被调用

    removeTreeNode方法是删除调用当前方法的节点,删除节点的时候分为3种情况
    1.如果没有子节点,那么直接删除,并修改一下父节点
    2.如果只有一个孩子节点,将孩子节点提上来,并修改父节点,如图(a)和(b)
    3.如果有两个孩子节点,从右节点找到没有左节点的位置,并与要删除的节点互换。分两种情况,一种是图(c),z是被删除的节点,y没有左孩子与z互换,y是z的右孩子这种情况比较简单。另一种情况图(d)当z与y互换时,涉及到l、r、x节点,y移到z的位置,x成为r的左节点
    这里写图片描述
    图片截自《算法导论》

    代码处理这三种情况时,是先交换z与y的位置和颜色,然后在删除
    代码过程,我们来分段阅读

     final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                              boolean movable) {
        int n;
        if (tab == null || (n = tab.length) == 0)
            return;
        int index = (n - 1) & hash;
        TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
        TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
        if (pred == null)
            tab[index] = first = succ;
        else
            pred.next = succ;
        if (succ != null)
            succ.prev = pred;
        if (first == null)
            return;
        if (root.parent != null)
            root = root.root();
        if (root == null || root.right == null ||
            (rl = root.left) == null || rl.left == null) {
            tab[index] = first.untreeify(map);  // too small
            return;
        }
    

    3-5行,非空判断,并把tab的长度赋值给n,tab与插入方法的参数tab相同
    6行找到当前节点在tab的下标
    7-8行,变量first为当前节点,变量root为跟节点默认值为first,变量rl根节点的左节点,变量succ当前链表的下一个节点,变量prev当前链表的上一个节点
    9-14行,从链表中删除当前节点
    17-18行,把根节点赋值到root变量
    19-23行,判断如果当前没有很多节点,就不用红黑树,转换成链表返回

    TreeNode<K,V> p = this, pl = left, pr = right, replacement;
    if (pl != null && pr != null) {
        TreeNode<K,V> s = pr, sl;
        while ((sl = s.left) != null) // find successor
            s = sl;
        boolean c = s.red; s.red = p.red; p.red = c; // swap colors
        TreeNode<K,V> sr = s.right;
        TreeNode<K,V> pp = p.parent;
        if (s == pr) { // p was s's direct parent
            p.parent = s;
            s.right = p;
        }
        else {
            TreeNode<K,V> sp = s.parent;
            if ((p.parent = sp) != null) {
                if (s == sp.left)
                    sp.left = p;
                else
                    sp.right = p;
            }
            if ((s.right = pr) != null)
                pr.parent = s;
        }
        p.left = null;
        if ((p.right = sr) != null)
            sr.parent = p;
        if ((s.left = pl) != null)
            pl.parent = s;
        if ((s.parent = pp) == null)
            root = s;
        else if (p == pp.left)
            pp.left = s;
        else
            pp.right = s;
        if (sr != null)
            replacement = sr;
        else
            replacement = p;
    }
    

    变量p当前节点,变量pl当前节点的左节点,pr当前节点的右节点,变量replacement用来替换被删除节点的位置
    2行,是情况1,左右节点都不为null的情况
    3行,变量s是当前节点的右节点,然后通过查找没有左节点的节点,变量sl,s的左节点
    4-5行,找到s节点
    6行,交换s和p也就是被删除节点的颜色
    7-8行,变量sr是s的右节点,变量pp是当前节点的父节点
    9-12行,是判断当前节点的右节点没有左节点,s节点和当前节点互换位置
    13-23行,否则,还是s节点和当前节点互换位置,这段代码和9-12要做的事情是一样的,只是涉及到了s的父节点的属性要赋值
    24-26行,如果s有右节点,把p设置成sr的父节点
    27-28行,把p的左节点交接给s
    29-34行,把p的父节点交接给s
    35-39行,如果sr不为null替换p的节点就是sr,否则为p

    处理完左右节点都不为空的情况,接下来该处理,左节点不为null右节点为null、右节点不为null左节点为null、左右都为null

    else if (pl != null)
        replacement = pl;
    else if (pr != null)
        replacement = pr;
    else
        replacement = p;
    
      if (replacement != p) {
        TreeNode<K,V> pp = replacement.parent = p.parent;
        if (pp == null)
            root = replacement;
        else if (p == pp.left)
            pp.left = replacement;
        else
            pp.right = replacement;
        p.left = p.right = p.parent = null;
    }
    
    TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
    

    1-10行,上面把要删除的节点设置成了replacement的parent,这里把要删除的移除掉,并把移除掉节点的相关属性设置为null
    12行,就是调用矫正红黑树性质的方法balanceDeletion的地方,我们先把删除方法看完,然后再说这个方法

    这里删除节点通过判断replacement != p,那么如果replacement == p

    if (replacement == p) {  // detach
            TreeNode<K,V> pp = p.parent;
            p.parent = null;
            if (pp != null) {
                if (p == pp.left)
                    pp.left = null;
                else if (p == pp.right)
                    pp.right = null;
            }
        }
        if (movable)
            moveRootToFront(tab, r);
    }              
    

    根据上面有种情况会相等,一种是sr为null的情况,另一种是被删除的节点没有左右节点的情况,这两种情况其实都是被删除的节点没有子节点。2-9行移除当前节点
    11-12行,movable大部分情况都为true,moveRootToFront方法如上面所述

    好了现在说一下balanceDeletion方法,如果被删除的是黑色,为了便于理解balanceDeletion方法,我们假设替换被删除的节点,会继承被删除节点的颜色,使被替换节点拥有2种颜色,如果被替换节点是红色现在就是红加黑,如果是黑色现在就是黑加黑,这里所说的继承只是一种假设,只是为了便于理解下面的代码。如果我们不做这个假设,而被删除的是黑节点,那么性质5就会被破坏。
    分简单和复杂两种情况

    简单:如果replacement现在是跟节点黑加黑,什么也不做。或者当前是红黑色,只需要把当前节点染成黑色,就恢复了红黑树的性质

    复杂分四种情况:
    1.replacement的兄弟节点是红色(注意,现在replacement以及替换到了被删除节点的位置,而且颜色肯定是黑加黑)
    2.replacement的兄弟节点是黑色,而且两个孩子都是黑色节点
    3.replacement的兄弟节点是黑色,但左节点为红色,右节点为黑色
    4.replacement的兄弟节点是黑色,但右节点为红色,左节点为黑色
    接下来根据代码来解决这些问题

     static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
                                               TreeNode<K,V> x) {
        for (TreeNode<K,V> xp, xpl, xpr;;)  {
            if (x == null || x == root)
                return root;
            else if ((xp = x.parent) == null) {
                x.red = false;
                return x;
            }
            else if (x.red) {
                x.red = false;
                return root;
            }
            else if ((xpl = xp.left) == x) {
                if ((xpr = xp.right) != null && xpr.red) {
                    xpr.red = false;
                    xp.red = true;
                    root = rotateLeft(root, xp);
                    xpr = (xp = x.parent) == null ? null : xp.right;
                }
                if (xpr == null)
                    x = xp;
                else {
                    TreeNode<K,V> sl = xpr.left, sr = xpr.right;
                    if ((sr == null || !sr.red) &&
                        (sl == null || !sl.red)) {
                        xpr.red = true;
                        x = xp;
                    }
                    else {
                        if (sr == null || !sr.red) {
                            if (sl != null)
                                sl.red = false;
                            xpr.red = true;
                            root = rotateRight(root, xpr);
                            xpr = (xp = x.parent) == null ?
                                null : xp.right;
                        }
                        if (xpr != null) {
                            xpr.red = (xp == null) ? false : xp.red;
                            if ((sr = xpr.right) != null)
                                sr.red = false;
                        }
                        if (xp != null) {
                            xp.red = false;
                            root = rotateLeft(root, xp);
                        }
                        x = root;
                    }
                }
            }
            else { // symmetric
    

    参数root是上面传过来的根节点,参数x是上面传过来的替换被删除的节点
    第3行,变量xp是x的父节点,xpl是xp的左节点,xpr是xp的右节点
    4-13行,就是简单的情况
    15-51行,就是复杂的4种情况。52行是对称的情况
    15-20行,是上面所说的第一种情况,兄弟节点为红色,所以子节点必定是黑色,通过改变兄弟节点和x节点的颜色,然后通过左旋,就把情况转成了2、3或4了,因为x的新兄弟节点为黑色。19行对兄弟节点重新赋值
    21-22行,如果兄弟节点为null的话,把父节点付给x,继续从下向上遍历,直到满足条件
    24行,变量sl是兄弟节点的左节点,sr是兄弟节点的右节点
    这里写图片描述

    25-29行,是上面所说的第二种情况,通过把兄弟节点设置为红色,并把x的黑色在遗传给父节点,通过把父节点赋值给x,继续向上遍历,直至满足上面所说简单的情况(也就是把上面所假设的双颜色变成了单颜色)
    这里写图片描述
    蓝色只是我随便画的一个颜色,意义是这个颜色是什么颜色都行

    31-38行,是上面所说的第三种情况,兄弟节点与兄弟节点的左节点颜色交换,然后右旋,现在就成了情况4
    这里写图片描述
    39-48行,是上面所说的第四种情况,正常情况下这里xpr和xp都不会为null,改变兄弟节点和兄弟节点的又节点颜色,然后改变父节点颜色,左旋,去掉了x的双重黑色并满足红黑树的性质,48行,把root赋值给x,之后到简单情况的代码就退出循环了
    这里写图片描述

    TreeNode与红黑树相关的操作就这些了,旋转、查找、出入、删除。为了满足HashMap重置内部数组大小,还有一个拆分方法。当HashMap需要扩容时候,就会调用拆分方法,把红黑树拆分成两个链表,如果两个链表大于一定的值,那么就还转成红黑树,否则就转换成链表

    拆分

      final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
        TreeNode<K,V> b = this;
        // Relink into lo and hi lists, preserving order
        TreeNode<K,V> loHead = null, loTail = null;
        TreeNode<K,V> hiHead = null, hiTail = null;
        int lc = 0, hc = 0;
        for (TreeNode<K,V> e = b, next; e != null; e = next) {
            next = (TreeNode<K,V>)e.next;
            e.next = null;
            if ((e.hash & bit) == 0) {
                if ((e.prev = loTail) == null)
                    loHead = e;
                else
                    loTail.next = e;
                loTail = e;
                ++lc;
            }
            else {
                if ((e.prev = hiTail) == null)
                    hiHead = e;
                else
                    hiTail.next = e;
                hiTail = e;
                ++hc;
            }
        }
    
        if (loHead != null) {
            if (lc <= UNTREEIFY_THRESHOLD)
                tab[index] = loHead.untreeify(map);
            else {
                tab[index] = loHead;
                if (hiHead != null) // (else is already treeified)
                    loHead.treeify(tab);
            }
        }
        if (hiHead != null) {
            if (hc <= UNTREEIFY_THRESHOLD)
                tab[index + bit] = hiHead.untreeify(map);
            else {
                tab[index + bit] = hiHead;
                if (loHead != null)
                    hiHead.treeify(tab);
            }
        }
    }
    

    如上所说这个方法在重置HashMap大小时候回被调用
    参数map和tab没什么可说的和上面一样,index是拆分的树在tab数组中的位置,调用这个方法方法之前数组已经扩容了,参数bit是扩容之前的大小

    10-25行,拆分一颗红黑树是通过用节点的hash值与bit进行并操作,然后把节点赋值给不同的链表,并通过lc和hc进行计数
    28-45行,就是如果链表小于等于UNTREEIFY_THRESHOLD就把红黑树转换成Node链表,否则就转换成红黑树。转换成红黑树是通过treeify方法,转换成链表是通过untreeify这两个方法比较简单,条件判断什么的跟上面讲的都差不多

    static final int UNTREEIFY_THRESHOLD = 6;
    
      final void treeify(Node<K,V>[] tab) {
        TreeNode<K,V> root = null;
        for (TreeNode<K,V> x = this, next; x != null; x = next) {
            next = (TreeNode<K,V>)x.next;
            x.left = x.right = null;
            if (root == null) {
                x.parent = null;
                x.red = false;
                root = x;
            }
            else {
                K k = x.key;
                int h = x.hash;
                Class<?> kc = null;
                for (TreeNode<K,V> p = root;;) {
                    int dir, ph;
                    K pk = p.key;
                    if ((ph = p.hash) > h)
                        dir = -1;
                    else if (ph < h)
                        dir = 1;
                    else if ((kc == null &&
                              (kc = comparableClassFor(k)) == null) ||
                             (dir = compareComparables(kc, k, pk)) == 0)
                        dir = tieBreakOrder(k, pk);
    
                    TreeNode<K,V> xp = p;
                    if ((p = (dir <= 0) ? p.left : p.right) == null) {
                        x.parent = xp;
                        if (dir <= 0)
                            xp.left = x;
                        else
                            xp.right = x;
                        root = balanceInsertion(root, x);
                        break;
                    }
                }
            }
        }
        moveRootToFront(tab, root);
    }    
    
    inal Node<K,V> untreeify(HashMap<K,V> map) {
        Node<K,V> hd = null, tl = null;
        for (Node<K,V> q = this; q != null; q = q.next) {
            Node<K,V> p = map.replacementNode(q, null);
            if (tl == null)
                hd = p;
            else
                tl.next = p;
            tl = p;
        }
        return hd;
    }
    

    到这里TreeNode就说完了,这样下一篇看HashMap分析的时候就会轻松很多了

    展开全文
  • TreeNode检测用程序

    2021-02-03 22:03:38
    力扣 二叉树“TreeNode” 检测用程序 C++代码如下 struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode() :val(0), left(nullptr), right(nullptr) {} TreeNode(int _X) :val(_X), left...

    力扣 二叉树“TreeNode” 检测用程序
    C++代码如下

    struct TreeNode
    {
    	int val;
    	TreeNode* left;
    	TreeNode* right;
    
    	TreeNode() :val(0), left(nullptr), right(nullptr) {}
    	TreeNode(int _X) :val(_X), left(nullptr), right(nullptr) {}
    	TreeNode(int _X, TreeNode* R) :val(_X), left(nullptr), right(R) {}
    	TreeNode(int _X, TreeNode* L, TreeNode* R) :val(_X), left(L), right(R) {}
    };
    
    inline int getInt(string _X) {
    	stringstream SStmp(_X);
    	int tmp;
    	SStmp >> tmp;
    	return tmp;
    }
    
    TreeNode* goIntoTree(TreeNode* root, vector<string> Vtmp, int site) {
    	if (Vtmp[site - 1] == "null") return nullptr;
    	root->val = getInt(Vtmp[site - 1]);
    	site = (site << 1) - 1;
    	if (site < Vtmp.size() && Vtmp[site] != "null") {
    		root->left = new TreeNode();
    		root->left = goIntoTree(root->left, Vtmp, site + 1);
    	}
    	++site;
    	if (site < Vtmp.size() && Vtmp[site] != "null") {
    		root->right = new TreeNode();
    		root->right = goIntoTree(root->right, Vtmp, site + 1);
    	}
    	return root;
    }
    
    //待测试的代码写入区域
    
    int main() {
    	TreeNode valTree, * TreePtr = &valTree;
    	string Stmp;
    	vector<string> Vtmp;
    	//输入待测试的树序列,需要带']'
    	cin >> Stmp;
    	for (int r = Stmp.find(','); r != -1;) {
    		Stmp[r] = ' '; 
    		++r;
    		 r = Stmp.find(',', r);
    	}
    	stringstream SStmp(Stmp);
    	while (SStmp >> Stmp) {
    		if (Stmp.front() == '[') Stmp = Stmp.substr(1);
    		if (Stmp.back() == ']') Stmp.pop_back();
    		Vtmp.push_back(Stmp);
    	}
    	goIntoTree(TreePtr, Vtmp, 1);
    	//这个地方已经完成TreeNode的初始化
    	//输出语句区。
    	return 0;
    }
    
    展开全文
  • TreeNode 树形结构

    千次阅读 2019-12-06 14:39:31
    TreeNode 树形结构 package java2019; public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int val){ this.val=val; } }
  • HashMap中TreeNode解读

    2021-03-09 14:46:05
    发现一篇优秀的讲解TreeNode的文章,就不重复造车了,在这记录一下: https://blog.csdn.net/qq_40267688/article/details/83106637 TreeNode的类关系如下: TreeNode继承Node,具有Node的单向链表的特性,...
  • 在LeetCode中,TreeNode是经常用到的一个结构体,表示数据结构树(Tree)中的一个节点。其官方定义如下:public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }}123456在Tree的...
  • 指定TreeNode排序

    2017-08-10 17:11:17
    button1.Click调用: My_SortTreeNode(treeView1.SelectedNode); //选定的节点,右键可以添加排序弹出菜单 ... public int TreeNodeCompare(TreeNode x, TreeNode y)  {  TreeNode tx = x as TreeNode;
  • 【LeetCode】TreeNode

    千次阅读 2019-07-10 11:29:19
    public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } @Override public String toString() { return "" + val...
  • 1 packagedata.structure;23 importjava.util.ArrayList;45 public classTreeNode {6 public intval;7 publicTreeNode left;8 publicTreeNode right;9 public TreeNode(int x) { val =x; }1011 ...
  • import javax.swing.tree.TreeNode; //导入方法依赖的package包/类public static boolean findString(ProfilerTable table, String text, boolean matchCase, boolean next) {int rowCount = table.getRowCount();...
  • #include #include using namespace std; vector<int> c, b; struct TreeNode ... TreeNode *left;... TreeNode *right;...19 [错误]没有匹配的函数调用'TreeNode :: TreeNode(int&)' 怎么改呀?? 求大佬帮助
  • ’ not supported between instances of ‘treeNode’ and ‘treeNode’ 原因: python3 和python2 版本的问题。 解决方法: 将bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1])] 改为:加了...
  • java TreeNode接口

    千次阅读 2017-08-19 09:01:00
    今天写安卓程序见到一个方法getChildAt();不懂其用边去百度搜索了一下,看到了它的api,细致看看原来是在...接口 TreeNode 全部已知子接口:MutableTreeNode 全部已知实现类:AbstractDocument.AbstractElement,...
  • 工具类:TreeNode

    千次阅读 2019-06-13 23:37:22
    package com.yltd.cnyun.common.utils; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * ...public class TreeNode&lt;T&gt; imp...
  • java treeNode 树转list

    2019-08-16 11:03:44
    树转list一般转换方式递归;写起来复制;容易出错。这样就可以应用栈的进出规则进行转化(栈先进后出...public List<TreeNode> node2List(TreeNode node){ //返回结果集 List<TreeNode> result ...
  • 做力扣上的题的时候经常遇到二叉树相关的...public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } /** * 调用以构造二叉树,BFS * @param vals 数组 * @return
  • treeview的treenode显示tip

    2018-04-11 09:14:46
    可以在动态添加treenode或者设计器添加treenode 的时候,设置tooltipText,然后在treeview属性设置中把showNodeTops设置为true;
  • onNodeCreated 回调,捕获DOM创建完毕的回调,然后利用 zTree 的规则找到 treeNode.tId + "_a" 这样的 标签,自行添加 class 就是了 function zTreeOnNodeCreated(event, treeId, treeNode) { console.log($('#...
  • C# TreeView和TreeNode的问题

    千次阅读 2018-02-05 14:26:19
    不知道朋友们使用TreeView和TreeNode遇到类似我一样的问题没有的,反正我百度上面却发现没怎么记录的。 先,说一下我遇到的问题: 1. TreeView添加上万个节点TreeNode,速度奇慢。  网上有一些介绍的方法,但不...
  • 花落--HashMap系列之1.8TreeNode删除源码篇(四)

    千次阅读 多人点赞 2020-11-25 15:09:18
    嗨,同学们又见面了,前面hashmap的系列文章中我们依次从1.7讲到了1.8的树化,下面我会接着上篇文章的树化源码TreeNode接着往下讲,今天讲的是TreeNode的删除源码分析。 文章目录TreeNode删除removeTreeNode函数...
  • C# TreeNode的使用

    千次阅读 2014-12-10 16:54:13
    添加节点 private void Form1_Load(object sender, EventArgs e) { treeView1.LabelEdit = true;//可编辑状态。 //添加一个节点,这个结点是根节点。 TreeNode node = new TreeNode();
  • public TreeNode (string text,TreeNode[] children) 参数: text : 表示构造的节点的标签文本,是一个字符串类型; children: 表示构造的节点的子节点,是节点型的数组; 如图,要做到下面的效果: 新建一个C#的...
  • 递归 package com.company; class TreeNode{ int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } public class hasPathSum { /* 1 / \ 2 3 ...
  • /** * Definition for binary tree ... * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ c...
  • 标题:leetcode中出现Error - Found cycle in the TreeNode leetcode中出现这种错误,说明你有些地方写错了,写不不够周全 eg:我的方法二和方法三之前就出现了这样的错误,是因为我的left赋为null的位置写的错误...
  • public class Solution { public boolean isSameTree(TreeNode p, TreeNode q) { if(p== null && q== null) { return true; }else if(p != null && q==null || p==null && q!= null)
  • HashMap-TreeNode源码分析(jdk1.8HashMap-TreeNode源码分析属性及构造方法find()putTreeVal()removeTreeNode()treeify()方法untreeify()balanceInsertion()balanceDeletion()rotateLeft() 左旋rotateRight() 右旋...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 42,791
精华内容 17,116
关键字:

treenode