How to return the k-th element in TreeSet in Java?

后端 未结 8 2015
-上瘾入骨i
-上瘾入骨i 2020-12-30 01:07

Maybe I am not using the right data structure. I need to use a set, but also want to efficiently return the k-th smallest element. Can TreeSet in Java do this?

8条回答
  •  我在风中等你
    2020-12-30 01:24

    I had the same problem. So I took the source code of java.util.TreeMap and wrote IndexedTreeMap. It implements my own IndexedNavigableMap:

    public interface IndexedNavigableMap extends NavigableMap {
       K exactKey(int index);
       Entry exactEntry(int index);
       int keyIndex(K k);
    }
    

    The implementation is based on updating node weights in the red-black tree when it is changed. Weight is the number of child nodes beneath a given node, plus one - self. For example when a tree is rotated to the left:

        private void rotateLeft(Entry p) {
        if (p != null) {
            Entry r = p.right;
    
            int delta = getWeight(r.left) - getWeight(p.right);
            p.right = r.left;
            p.updateWeight(delta);
    
            if (r.left != null) {
                r.left.parent = p;
            }
    
            r.parent = p.parent;
    
    
            if (p.parent == null) {
                root = r;
            } else if (p.parent.left == p) {
                delta = getWeight(r) - getWeight(p.parent.left);
                p.parent.left = r;
                p.parent.updateWeight(delta);
            } else {
                delta = getWeight(r) - getWeight(p.parent.right);
                p.parent.right = r;
                p.parent.updateWeight(delta);
            }
    
            delta = getWeight(p) - getWeight(r.left);
            r.left = p;
            r.updateWeight(delta);
    
            p.parent = r;
        }
      }
    

    updateWeight simply updates weights up to the root:

       void updateWeight(int delta) {
            weight += delta;
            Entry p = parent;
            while (p != null) {
                p.weight += delta;
                p = p.parent;
            }
        }
    

    And when we need to find the element by index here is the implementation that uses weights:

    public K exactKey(int index) {
        if (index < 0 || index > size() - 1) {
            throw new ArrayIndexOutOfBoundsException();
        }
        return getExactKey(root, index);
    }
    
    private K getExactKey(Entry e, int index) {
        if (e.left == null && index == 0) {
            return e.key;
        }
        if (e.left == null && e.right == null) {
            return e.key;
        }
        if (e.left != null && e.left.weight > index) {
            return getExactKey(e.left, index);
        }
        if (e.left != null && e.left.weight == index) {
            return e.key;
        }
        return getExactKey(e.right, index - (e.left == null ? 0 : e.left.weight) - 1);
    }
    

    Also comes in very handy finding the index of a key:

        public int keyIndex(K key) {
        if (key == null) {
            throw new NullPointerException();
        }
        Entry e = getEntry(key);
        if (e == null) {
            throw new NullPointerException();
        }
        if (e == root) {
            return getWeight(e) - getWeight(e.right) - 1;//index to return
        }
        int index = 0;
        int cmp;
        if (e.left != null) {
            index += getWeight(e.left);
        }
        Entry p = e.parent;
        // split comparator and comparable paths
        Comparator cpr = comparator;
        if (cpr != null) {
            while (p != null) {
                cmp = cpr.compare(key, p.key);
                if (cmp > 0) {
                    index += getWeight(p.left) + 1;
                }
                p = p.parent;
            }
        } else {
            Comparable k = (Comparable) key;
            while (p != null) {
                if (k.compareTo(p.key) > 0) {
                    index += getWeight(p.left) + 1;
                }
                p = p.parent;
            }
        }
        return index;
    }
    

    You can find the result of this work at http://code.google.com/p/indexed-tree-map/. Moved to https://github.com/geniot/indexed-tree-map

提交回复
热议问题