算法与数据结构进阶课第十二节笔记

有序表的原理、应用、扩展

搜索二叉树

搜索二叉树一定要说明以什么标准来排序

经典的搜索二叉树,树上没有重复的用来排序的key值

如果有重复节点的需求,可以在一个节点内部增加数据项

 

搜索二叉树查询key(查询某个key存在还是不存在

1)如果当前节点的value==key,返回true
2)如果当前节点的value<key,当前节点向左移动
3)如果当前节点的value>key,当前节点向右移动
4)如果当前节点变成null,返回false

搜索二叉树插入新的key

和查询过程一样,但当前节点滑到空的时候,就插入在这里

搜索二叉树删除key

0)先找到key所在的节点
1)如果该节点没有左孩子、没有右孩子,直接删除即可
2)如果该节点有左孩子、没有右孩子,直接用左孩子顶替该节点
3)如果该节点没有左孩子、有右孩子,直接用右孩子顶替该节点
4)如果该节点有左孩子、有右孩子,用该节点后继节点顶替该节点

搜索二叉树特别不讲究

1)基础的搜索二叉树,添加、删除时候不照顾平衡性

2)数据状况很差时,性能就很差


给搜索二叉树引入两个动作:左旋、右旋

自平衡的机制——平衡搜索二叉树

AVL树、SB树、红黑树的共性

1)都是搜索二叉树

2)插入、删除、查询(一切查询)搜索二叉树怎么做,这些结构都这么做

3)使用调整的基本动作都只有左旋、右旋

4)插入、删除时,从最底层被影响到的节点开始,对往上路径的节点做平衡性检查

5)因为只对一条向上路径的每个节点做O(1)的检查和调整,所以可以做到O(logN)

AVL树、SB树、红黑树的不同

1)平衡性的约束不同

AVL树最严格、任何节点左树高度和右树高度差不超过1

SB树稍宽松、任何叔节点的节点数,不少于任何他的任何一个侄子节点

红黑树最宽松、长链的长度比短链的长度不超过两倍以上

2)插入、删除和搜索二叉树一样,但是额外,做各自的平衡性调整。各自的平衡性调整所使用的动作都是左旋或者右旋

AVL树

1)最严格的平衡性,任何节点左树高度和右树高度差不超过1

2)往上沿途检查每个节点时,都去检查四种违规情况:LL、RR、LR、RL

3)不同情况虽然看起来复杂,但是核心点是:
LL(做一次右旋)、RR(做一次左旋)
LR和RL(利用旋转让底层那个孙子上到顶部)

有序表的重要功能

//有序表

TreeMap<Integer,String> treeMap = new TreeMap<>();

// 在treeMap中,请你告诉我,离3最近,>=3的key是什么
treeMap.ceilingKey(3);
// 所有key中最小
treeMap.firstKey();
// 所有key中最大
treeMap.lastKey();

// 哈希表的增删改查 O(1)
// treeMap         O(N)

SB树(size-balance-tree)

1)让每一个叔叔节点为头的数,节点个数都不少于其任何一个侄子节点
2)也是从底层被影响节点开始向上做路径每个节点检查
3)与AVL树非常像,也是四种违规类型:LL、RR、LR、RL
4)与AVL树非常像,核心点是:
LL(做一次右旋)、RR(做一次左旋)
LR和RL(利用旋转让底层那个上到顶部)
5)与AVL树不同的是,每轮经过调整后,谁的孩子发生变化了,谁就再查

SB树在使用时候的改进

1)删除时候可以不用检查

2)就把平衡性的调整放在插入的时候

3)因为这种只要变就递归的特性,别的树没有,虽然有递归但是收敛于O(1)

4)可以在节点上封装别的数据项,来增加功能

5) 平衡性要求低,所以各个操作所带来的扰动小

size balance tree 实现

       public static class SBTNode<K extends Comparable<K>, V> {
		public K key;
		public V value;
		public SBTNode<K, V> l;
		public SBTNode<K, V> r;
		public int size; // 不同的key的数量

		public SBTNode(K key, V value) {
			this.key = key;
			this.value = value;
			size = 1;
		}
	}

	public static class SizeBalancedTreeMap<K extends Comparable<K>, V> {
		private SBTNode<K, V> root;

		private SBTNode<K, V> rightRotate(SBTNode<K, V> cur) {
			SBTNode<K, V> leftNode = cur.l;
			cur.l = leftNode.r;
			leftNode.r = cur;
			// 交换节点的size互换
			leftNode.size = cur.size;
			cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1;
			return leftNode;
		}

		private SBTNode<K, V> leftRotate(SBTNode<K, V> cur) {
			SBTNode<K, V> rightNode = cur.r;
			cur.r = rightNode.l;
			rightNode.l = cur;
			rightNode.size = cur.size;
			cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1;
			return rightNode;
		}

		private SBTNode<K, V>  maintain(SBTNode<K, V> cur) {
			if (cur == null) {
				return null;
			}
			if (cur.l != null && cur.l.l != null && cur.r != null && cur.l.l.size > cur.r.size) {
				cur = rightRotate(cur);
				cur.r = maintain(cur.r);
				cur = maintain(cur);
			} else if (cur.l != null && cur.l.r != null && cur.r != null && cur.l.r.size > cur.r.size) {
				cur.l = leftRotate(cur.l);
				cur = rightRotate(cur);
				cur.l = maintain(cur.l);
				cur.r = maintain(cur.r);
				cur = maintain(cur);
			} else if (cur.r != null && cur.r.r != null && cur.l != null && cur.r.r.size > cur.l.size) {
				cur = leftRotate(cur);
				cur.l = maintain(cur.l);
				cur = maintain(cur);
			} else if (cur.r != null && cur.r.l != null && cur.l != null && cur.r.l.size > cur.l.size) {
				cur.r = rightRotate(cur.r);
				cur = leftRotate(cur);
				cur.l = maintain(cur.l);
				cur.r = maintain(cur.r);
				cur = maintain(cur);
			}
			return cur;
		}

		/**
		 * 如果没找到,返回离这个key最近不空的结点
		 * @param key
		 * @return
		 */
		private SBTNode<K, V> findLastIndex(K key) {
			SBTNode<K, V> pre = root;
			SBTNode<K, V> cur = root;
			while (cur != null) {
				pre = cur;
				if (key.compareTo(cur.key) == 0) {
					break;
				} else if (key.compareTo(cur.key) < 0) {
					cur = cur.l;
				} else {
					cur = cur.r;
				}
			}
			return pre;
		}

		private SBTNode<K, V> findLastNoSmallIndex(K key) {
			SBTNode<K, V> ans = null;
			SBTNode<K, V> cur = root;
			while (cur != null) {
				if (key.compareTo(cur.key) == 0) {
					ans = cur;
					break;
				} else if (key.compareTo(cur.key) < 0) {
					ans = cur;
					cur = cur.l;
				} else {
					cur = cur.r;
				}
			}
			return ans;
		}

		private SBTNode<K, V> findLastNoBigIndex(K key) {
			SBTNode<K, V> ans = null;
			SBTNode<K, V> cur = root;
			while (cur != null) {
				if (key.compareTo(cur.key) == 0) {
					ans = cur;
					break;
				} else if (key.compareTo(cur.key) < 0) {
					cur = cur.l;
				} else {
					ans = cur;
					cur = cur.r;
				}
			}
			return ans;
		}

		// 现在,以cur为头的树上,加(key, value)这样的记录
		// 加完之后,会对cur做检查,该调整调整
		// 返回,调整完之后,整棵树的新头部
		private SBTNode<K, V> add(SBTNode<K, V> cur, K key, V value) {
			if (cur == null) {
				return new SBTNode<K, V>(key, value);
			} else {
				cur.size++;
				if (key.compareTo(cur.key) < 0) {
					cur.l = add(cur.l, key, value);
				} else {
					cur.r = add(cur.r, key, value);
				}
				return maintain(cur);
			}
		}

		// 在cur这棵树上,删掉key所代表的节点
		// 返回cur这棵树的新头部
		private SBTNode<K, V> delete(SBTNode<K, V> cur, K key) {
			cur.size--;
			if (key.compareTo(cur.key) > 0) {
				cur.r = delete(cur.r, key);
			} else if (key.compareTo(cur.key) < 0) {
				cur.l = delete(cur.l, key);
			} else { // 当前要删掉cur
				if (cur.l == null && cur.r == null) {
					// free cur memory -> C++
					cur = null;
				} else if (cur.l == null && cur.r != null) {
					// free cur memory -> C++
					cur = cur.r;
				} else if (cur.l != null && cur.r == null) {
					// free cur memory -> C++
					cur = cur.l;
				} else { // 有左有右
					SBTNode<K, V> pre = null;
					SBTNode<K, V> des = cur.r;
					des.size--;
					while (des.l != null) {
						pre = des;
						des = des.l;
						des.size--;
					}
					if (pre != null) {
						pre.l = des.r;
						des.r = cur.r;
					}
					des.l = cur.l;
					des.size = des.l.size + (des.r == null ? 0 : des.r.size) + 1;
					// free cur memory -> C++
					cur = des;
				}
			}
			// cur = maintain(cur); 可以不加调整
			return cur;
		}

		private SBTNode<K, V> getIndex(SBTNode<K, V> cur, int kth) {
			if (kth == (cur.l != null ? cur.l.size : 0) + 1) {
				return cur;
			} else if (kth <= (cur.l != null ? cur.l.size : 0)) {
				return getIndex(cur.l, kth);
			} else {
				return getIndex(cur.r, kth - (cur.l != null ? cur.l.size : 0) - 1);
			}
		}

		public int size() {
			return root == null ? 0 : root.size;
		}

		public boolean containsKey(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			SBTNode<K, V> lastNode = findLastIndex(key);
			return lastNode != null && key.compareTo(lastNode.key) == 0 ? true : false;
		}

		public void put(K key, V value) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			SBTNode<K, V> lastNode = findLastIndex(key);
			if (lastNode != null && key.compareTo(lastNode.key) == 0) {
				lastNode.value = value;
			} else {
				root = add(root, key, value);
			}
		}

		public void remove(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			if (containsKey(key)) {
				root = delete(root, key);
			}
		}

		public K getIndexKey(int index) {
			if (index < 0 || index >= this.size()) {
				throw new RuntimeException("invalid parameter.");
			}
			return getIndex(root, index + 1).key;
		}

		public V getIndexValue(int index) {
			if (index < 0 || index >= this.size()) {
				throw new RuntimeException("invalid parameter.");
			}
			return getIndex(root, index + 1).value;
		}

		public V get(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			SBTNode<K, V> lastNode = findLastIndex(key);
			if (lastNode != null && key.compareTo(lastNode.key) == 0) {
				return lastNode.value;
			} else {
				return null;
			}
		}

		public K firstKey() {
			if (root == null) {
				return null;
			}
			SBTNode<K, V> cur = root;
			while (cur.l != null) {
				cur = cur.l;
			}
			return cur.key;
		}

		public K lastKey() {
			if (root == null) {
				return null;
			}
			SBTNode<K, V> cur = root;
			while (cur.r != null) {
				cur = cur.r;
			}
			return cur.key;
		}

		public K floorKey(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			SBTNode<K, V> lastNoBigNode = findLastNoBigIndex(key);
			return lastNoBigNode == null ? null : lastNoBigNode.key;
		}

		public K ceilingKey(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			SBTNode<K, V> lastNoSmallNode = findLastNoSmallIndex(key);
			return lastNoSmallNode == null ? null : lastNoSmallNode.key;
		}

	}

数组保存的方式:

     public static class SizeBalancedTreeMap<K extends Comparable<K>, V> {
		private int root;
		private int len;
		private int[] left;
		private int[] right;
		private int[] size;
		private ArrayList<K> keys;
		private ArrayList<V> values;

		public SizeBalancedTreeMap(int init) {
			left = new int[init + 1];
			right = new int[init + 1];
			size = new int[init + 1];
			keys = new ArrayList<K>();
			values = new ArrayList<V>();
			keys.add(null);
			values.add(null);
			root = 0;
			len = 0;
		}

		private int rightRotate(int index) {
			int iLeft = left[index];
			left[index] = right[iLeft];
			right[iLeft] = index;
			size[iLeft] = size[index];
			size[index] = size[left[index]] + size[right[index]] + 1;
			return iLeft;
		}

		private int leftRotate(int index) {
			int iRight = right[index];
			right[index] = left[iRight];
			left[iRight] = index;
			size[iRight] = size[index];
			size[index] = size[left[index]] + size[right[index]] + 1;
			return iRight;
		}

		private int matain(int index) {
			if (size[left[left[index]]] > size[right[index]]) {
				index = rightRotate(index);
				right[index] = matain(right[index]);
				index = matain(index);
			} else if (size[right[left[index]]] > size[right[index]]) {
				left[index] = leftRotate(left[index]);
				index = rightRotate(index);
				left[index] = matain(left[index]);
				right[index] = matain(right[index]);
				index = matain(index);
			} else if (size[right[right[index]]] > size[left[index]]) {
				index = leftRotate(index);
				left[index] = matain(left[index]);
				index = matain(index);
			} else if (size[left[right[index]]] > size[left[index]]) {
				right[index] = rightRotate(right[index]);
				index = leftRotate(index);
				left[index] = matain(left[index]);
				right[index] = matain(right[index]);
				index = matain(index);
			}
			return index;
		}

		private int findLastIndex(K key) {
			int pre = root;
			int cur = root;
			while (cur != 0) {
				pre = cur;
				if (key.compareTo(keys.get(cur)) == 0) {
					break;
				} else if (key.compareTo(keys.get(cur)) < 0) {
					cur = left[cur];
				} else {
					cur = right[cur];
				}
			}
			return pre;
		}

		private int findLastNoSmallIndex(K key) {
			int ans = 0;
			int cur = root;
			while (cur != 0) {
				if (key.compareTo(keys.get(cur)) == 0) {
					ans = cur;
					break;
				} else if (key.compareTo(keys.get(cur)) < 0) {
					ans = cur;
					cur = left[cur];
				} else {
					cur = right[cur];
				}
			}
			return ans;
		}

		private int findLastNoBigIndex(K key) {
			int ans = 0;
			int cur = root;
			while (cur != 0) {
				if (key.compareTo(keys.get(cur)) == 0) {
					ans = cur;
					break;
				} else if (key.compareTo(keys.get(cur)) < 0) {
					cur = left[cur];
				} else {
					ans = cur;
					cur = right[cur];
				}
			}
			return ans;
		}

		private int add(int index, K key, V value) {
			if (index == 0) {
				index = ++len;
				keys.add(key);
				values.add(value);
				size[index] = 1;
				left[index] = 0;
				right[index] = 0;
				return index;
			} else {
				size[index]++;
				if (key.compareTo(keys.get(index)) < 0) {
					left[index] = add(left[index], key, value);
				} else {
					right[index] = add(right[index], key, value);
				}
				return matain(index);
			}
		}

		private int getIndex(int index, int kth) {
			if (kth == size[left[index]] + 1) {
				return index;
			} else if (kth <= size[left[index]]) {
				return getIndex(left[index], kth);
			} else {
				return getIndex(right[index], kth - size[left[index]] - 1);
			}
		}

		public int size() {
			return len;
		}

		public boolean containsKey(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			int lastIndex = findLastIndex(key);
			return lastIndex != 0 && key.compareTo(keys.get(lastIndex)) == 0 ? true : false;
		}

		public void put(K key, V value) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			if (len == size.length - 1) {
				throw new RuntimeException("size balanced tree is full.");
			}
			int lastIndex = findLastIndex(key);
			if (lastIndex != 0 && key.compareTo(keys.get(lastIndex)) == 0) {
				values.set(lastIndex, value);
			} else {
				root = add(root, key, value);
			}
		}

		public K getIndexKey(int index) {
			if (index < 0 || index >= len) {
				throw new RuntimeException("invalid parameter.");
			}
			return keys.get(getIndex(root, index + 1));
		}

		public V getIndexValue(int index) {
			if (index < 0 || index >= len) {
				throw new RuntimeException("invalid parameter.");
			}
			return values.get(getIndex(root, index + 1));
		}

		public V get(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			int lastIndex = findLastIndex(key);
			if (lastIndex != 0 && key.compareTo(keys.get(lastIndex)) == 0) {
				return values.get(lastIndex);
			} else {
				return null;
			}
		}

		public K firstKey() {
			int cur = root;
			while (left[cur] != 0) {
				cur = left[cur];
			}
			return cur == 0 ? null : keys.get(cur);
		}

		public K lastKey() {
			int cur = root;
			while (right[cur] != 0) {
				cur = right[cur];
			}
			return cur == 0 ? null : keys.get(cur);
		}

		public K floorKey(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			int lastNoBigIndex = findLastNoBigIndex(key);
			return lastNoBigIndex == 0 ? null : keys.get(lastNoBigIndex);
		}

		public K ceilingKey(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			int lastNoSmallIndex = findLastNoSmallIndex(key);
			return lastNoSmallIndex == 0 ? null : keys.get(lastNoSmallIndex);
		}
	}

跳表(skiplist)

1)结构上根本和搜索二叉树无关

2)利用随机概率分布来使得高层索引可以无视数据规律,做到整体性能优良

3)思想是所有有序表中最先进的

4)结构简单就是多级单链表

        // 跳表的节点定义
	public static class SkipListNode<K extends Comparable<K>, V> {
		public K key;
		public V val;
		// 0  nextNodes.get(0)
		// 1  nextNodes.get(1)
		// i  nextNodes.get(i)
		// nextNodes.size()
		public ArrayList<SkipListNode<K, V>> nextNodes;

		public SkipListNode(K k, V v) {
			key = k;
			val = v;
			// node  7层指针
			// nextNodes.add(null)
			// nextNodes.add(null)
			// nextNodes.add(null)
			// nextNodes.add(null)
			// nextNodes.add(null)
			// nextNodes.add(null)
			// nextNodes.add(null)
			nextNodes = new ArrayList<SkipListNode<K, V>>();
		}

		// 遍历的时候,如果是往右遍历到的null(next == null), 遍历结束
		// 头(null), 头节点的null,认为最小
		// node  -> 头,node(null, "")  node.isKeyLess(!null)  true
		// node里面的key是否比otherKey小,true,不是false
		public boolean isKeyLess(K otherKey) {
			//  otherKey == null -> false 
			return otherKey != null && (key == null || key.compareTo(otherKey) < 0);
		}

		public boolean isKeyEqual(K otherKey) {
			return (key == null && otherKey == null)
					|| (key != null && otherKey != null && key.compareTo(otherKey) == 0);
		}

	}

	public static class SkipListMap<K extends Comparable<K>, V> {
		private static final double PROBABILITY = 0.5; // < 0.5 继续做,>=0.5 停
		private SkipListNode<K, V> head;
		private int size;
		private int maxLevel;

		public SkipListMap() {
			head = new SkipListNode<K, V>(null, null);
			head.nextNodes.add(null); // 0 层
			size = 0;
			maxLevel = 0;
		}

		// 从最高层开始,一路找下去,
		// 最终,找到第0层的<key的最右的节点
		private SkipListNode<K, V> mostRightLessNodeInTree(K key) {
			if (key == null) {
				return null;
			}
			int level = maxLevel;
			SkipListNode<K, V> cur = head;
			while (level >= 0) { // 从上层跳下层
				//  cur  level  -> level-1
				cur = mostRightLessNodeInLevel(key, cur, level--);
			}
			return cur;
		}

		// 在level层里,如何往右移动
		// 现在来到的节点是cur,来到了cur的level层,在level层上,找到<key最后一个节点并返回
		private SkipListNode<K, V> mostRightLessNodeInLevel(K key, 
				SkipListNode<K, V> cur, 
				int level) {
			SkipListNode<K, V> next = cur.nextNodes.get(level);
			while (next != null && next.isKeyLess(key)) {
				cur = next;
				next = cur.nextNodes.get(level);
			}
			return cur;
		}

		public boolean containsKey(K key) {
			if (key == null) {
				return false;
			}
			SkipListNode<K, V> less = mostRightLessNodeInTree(key);
			SkipListNode<K, V> next = less.nextNodes.get(0);
			return next != null && next.isKeyEqual(key);
		}

		public void put(K key, V value) {
			if (key == null) {
				return;
			}
			// 0层上,最右一个,小于key的Node,再往下一个要么等于key,要么大于key
			SkipListNode<K, V> less = mostRightLessNodeInTree(key);
			SkipListNode<K, V> find = less.nextNodes.get(0);
			if (find != null && find.isKeyEqual(key)) {
				find.val = value;
			} else {
				size++;
				int newNodeLevel = 0;
				while (Math.random() < PROBABILITY) {
					newNodeLevel++;
				}
				// 如果层数比最左节点大,最左就要补成一样
				while (newNodeLevel > maxLevel) {
					head.nextNodes.add(null);
					maxLevel++;
				}
				SkipListNode<K, V> newNode = new SkipListNode<K, V>(key, value);
				for (int i = 0; i <= newNodeLevel; i++) {
					newNode.nextNodes.add(null);
				}
				int level = maxLevel;
				SkipListNode<K, V> pre = head;
				while (level >= 0) {
					// 在level层中,找到最右的小于key的节点
					pre = mostRightLessNodeInLevel(key, pre, level);
					if (level <= newNodeLevel) {
						newNode.nextNodes.set(level, pre.nextNodes.get(level));
						pre.nextNodes.set(level, newNode);
					}
					level--;
				}
			}
		}

		public V get(K key) {
			if (key == null) {
				return null;
			}
			SkipListNode<K, V> less = mostRightLessNodeInTree(key);
			SkipListNode<K, V> next = less.nextNodes.get(0);
			return next != null && next.isKeyEqual(key) ? next.val : null;
		}

		public void remove(K key) {
			if (containsKey(key)) {
				size--;
				int level = maxLevel;
				SkipListNode<K, V> pre = head;
				while (level >= 0) {
					pre = mostRightLessNodeInLevel(key, pre, level);
					SkipListNode<K, V> next = pre.nextNodes.get(level);
					// 1)在这一层中,pre下一个就是key
					// 2)在这一层中,pre的下一个key是>要删除key
					if (next != null && next.isKeyEqual(key)) {
						// free delete node memory -> C++
						// level : pre -> next(key) -> ...
						pre.nextNodes.set(level, next.nextNodes.get(level));
					}
					// 在level层只有一个节点了,就是默认节点head
					if (level != 0 && pre == head && pre.nextNodes.get(level) == null) {
						head.nextNodes.remove(level);
						maxLevel--;
					}
					level--;
				}
			}
		}

		public K firstKey() {
			return head.nextNodes.get(0) != null ? head.nextNodes.get(0).key : null;
		}

		public K lastKey() {
			int level = maxLevel;
			SkipListNode<K, V> cur = head;
			while (level >= 0) {
				SkipListNode<K, V> next = cur.nextNodes.get(level);
				while (next != null) {
					cur = next;
					next = cur.nextNodes.get(level);
				}
				level--;
			}
			return cur.key;
		}

		public K ceillingKey(K key) {
			if (key == null) {
				return null;
			}
			SkipListNode<K, V> less = mostRightLessNodeInTree(key);
			SkipListNode<K, V> next = less.nextNodes.get(0);
			return next != null ? next.key : null;
		}

		public K floorKey(K key) {
			if (key == null) {
				return null;
			}
			SkipListNode<K, V> less = mostRightLessNodeInTree(key);
			SkipListNode<K, V> next = less.nextNodes.get(0);
			return next != null && next.isKeyEqual(key) ? next.key : less.key;
		}

		public int size() {
			return size;
		}

	}

 

聊聊红黑树

1)平衡性规定非常诡异
2)平衡性调整最为复杂
3)优点在于每次插入删除扰动较好,但是在今天看来这个优势也极其微弱了
原因:贪图扰动小的话,B+树、2-3-4树可能更好(可以少读写硬盘),还是那句话,到底图什么
4)除此之外,红黑树并不比AVL树、SB树、跳表更加优秀,关键在于红黑树在扰动上达到了一种中间状态
5)面试上遇到,说清楚道理,不行就举报

 

红黑树的性质(本质上还是一种平衡二叉树)

1、节点非红即黑

2、整颗树的头和叶一定是黑

3、红的子不能是红

4、从叶节点出发到头,任何一条链,黑的一样多,这样最长的链不会超过最短的链的两倍

插入违规有5种情况,删除违规有8种情况

redis为什么用跳表不去使用AVL,SB,因为跳表序列化更方便

 

例1:给定一些长度不一的数组,每个数组都有序,整体是一个二维数组,想要找到一个最短区间[a,b],每个数组中至少有一个数字在这个区间里。

准备一个有序表,将每个数组中最小的数放进去,那么就能取到一个区间[x,y],弹出最小的,从包含最小数的数组中取下一个放入有序表,又得到[z,y],比较有没有比[x,y]好

每次都弹出最小,再加入最小所在数组中的下一个,放入,比较区间,最短的就是[a,b]

实际每次求的是以x开头,让每个数字在区间里的最短的区间是什么(做了一步贪心,只是列举在数组里的数字就行,不需要穷举每一个整数)

 

例2:给定一个数组arr,和两个整数a和b(a<=b),求arr中有多少个子数组,累加和在[a,b]这个范围上,返回达标的子数组数量。

 

     public static class SBTNode {
		public long key;
		public SBTNode l;
		public SBTNode r;
		public long size; // 不同key的size
		public long all; // 总的size

		public SBTNode(long k) {
			key = k;
			size = 1;
			all = 1;
		}
	}

	public static class SizeBalancedTreeSet {
		private SBTNode root;
		private HashSet<Long> set = new HashSet<>();

		private SBTNode rightRotate(SBTNode cur) {
			long same = cur.all - (cur.l != null ? cur.l.all : 0) - (cur.r != null ? cur.r.all : 0);
			SBTNode leftNode = cur.l;
			cur.l = leftNode.r;
			leftNode.r = cur;
			leftNode.size = cur.size;
			cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1;
			// all modify
			leftNode.all = cur.all;
			cur.all = (cur.l != null ? cur.l.all : 0) + (cur.r != null ? cur.r.all : 0) + same;
			return leftNode;
		}

		private SBTNode leftRotate(SBTNode cur) {
			long same = cur.all - (cur.l != null ? cur.l.all : 0) - (cur.r != null ? cur.r.all : 0);
			SBTNode rightNode = cur.r;
			cur.r = rightNode.l;
			rightNode.l = cur;
			rightNode.size = cur.size;
			cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1;
			// all modify
			rightNode.all = cur.all;
			cur.all = (cur.l != null ? cur.l.all : 0) + (cur.r != null ? cur.r.all : 0) + same;
			return rightNode;
		}

		private SBTNode matain(SBTNode cur) {
			if (cur == null) {
				return null;
			}
			if (cur.l != null && cur.l.l != null && cur.r != null && cur.l.l.size > cur.r.size) {
				cur = rightRotate(cur);
				cur.r = matain(cur.r);
				cur = matain(cur);
			} else if (cur.l != null && cur.l.r != null && cur.r != null && cur.l.r.size > cur.r.size) {
				cur.l = leftRotate(cur.l);
				cur = rightRotate(cur);
				cur.l = matain(cur.l);
				cur.r = matain(cur.r);
				cur = matain(cur);
			} else if (cur.r != null && cur.r.r != null && cur.l != null && cur.r.r.size > cur.l.size) {
				cur = leftRotate(cur);
				cur.l = matain(cur.l);
				cur = matain(cur);
			} else if (cur.r != null && cur.r.l != null && cur.l != null && cur.r.l.size > cur.l.size) {
				cur.r = rightRotate(cur.r);
				cur = leftRotate(cur);
				cur.l = matain(cur.l);
				cur.r = matain(cur.r);
				cur = matain(cur);
			}
			return cur;
		}

		private SBTNode add(SBTNode cur, long key, boolean contains) {
			if (cur == null) {
				return new SBTNode(key);
			} else {
				cur.all++;
				if (key == cur.key) {
					return cur;
				} else { // 还在左滑或者右滑
					if (!contains) {
						cur.size++;
					}
					if (key < cur.key) {
						cur.l = add(cur.l, key, contains);
					} else {
						cur.r = add(cur.r, key, contains);
					}
					return matain(cur);
				}
			}
		}

		public void add(long sum) {
			boolean contains = set.contains(sum);
			root = add(root, sum, contains);
			set.add(sum);
		}

		public long lessKeySize(long key) {
			SBTNode cur = root;
			long ans = 0;
			while (cur != null) {
				if (key == cur.key) {
					return ans + (cur.l != null ? cur.l.all : 0);
				} else if (key < cur.key) { // 左滑
					cur = cur.l;
				} else { // 右滑
					ans += cur.all - (cur.r != null ? cur.r.all : 0);
					cur = cur.r;
				}
			}
			return ans;
		}

		// > 7 8...
		// <8 ...<=7
		public long moreKeySize(long key) {
			return root != null ? (root.all - lessKeySize(key + 1)) : 0;
		}

	}

	public static int countRangeSum2(int[] nums, int lower, int upper) {
		SizeBalancedTreeSet treeSet = new SizeBalancedTreeSet();
		long sum = 0;
		int ans = 0;
		treeSet.add(0); // 一个数都没有的时候,就已经有一个前缀累加和为0
		for (int i = 0; i < nums.length; i++) {
			sum += nums[i];
			// sum  i 结尾的时候[lower,upper]
			// 之前所有前缀累加和中,有多少累加和落在[sum - upper, sum - lower]
			// 查 ? < sum - lower + 1 a
			// 查? < sum - upper b
			long a = treeSet.lessKeySize(sum - lower + 1);
			// sum = x [a,b] start < x-b -start > -x+b x-start > b
			long b = treeSet.lessKeySize(sum - upper);
			ans += a - b;
			treeSet.add(sum);
		}
		return ans;
	}

例3:

有一个滑动窗口(讲过的):

1)L是滑动窗口最左位置、R是滑动窗口最右位置,一开始LR都在数组左侧
2)任何一步都可能R往右动,表示某个数进了窗口
3)任何一步都可能L往右动,表示某个数出了窗口

想知道每一个窗口状态的中位数,偶数个的时候上中位与下中位除2,

       public static class SBTNode<K extends Comparable<K>> {
		public K key;
		public SBTNode<K> l;
		public SBTNode<K> r;
		public int size;

		public SBTNode(K k) {
			key = k;
			size = 1;
		}
	}

	public static class SizeBalancedTreeMap<K extends Comparable<K>> {
		private SBTNode<K> root;

		private SBTNode<K> rightRotate(SBTNode<K> cur) {
			SBTNode<K> leftNode = cur.l;
			cur.l = leftNode.r;
			leftNode.r = cur;
			leftNode.size = cur.size;
			cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1;
			return leftNode;
		}

		private SBTNode<K> leftRotate(SBTNode<K> cur) {
			SBTNode<K> rightNode = cur.r;
			cur.r = rightNode.l;
			rightNode.l = cur;
			rightNode.size = cur.size;
			cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1;
			return rightNode;
		}

		private SBTNode<K> matain(SBTNode<K> cur) {
			if (cur == null) {
				return null;
			}
			if (cur.l != null && cur.l.l != null && cur.r != null && cur.l.l.size > cur.r.size) {
				cur = rightRotate(cur);
				cur.r = matain(cur.r);
				cur = matain(cur);
			} else if (cur.l != null && cur.l.r != null && cur.r != null && cur.l.r.size > cur.r.size) {
				cur.l = leftRotate(cur.l);
				cur = rightRotate(cur);
				cur.l = matain(cur.l);
				cur.r = matain(cur.r);
				cur = matain(cur);
			} else if (cur.r != null && cur.r.r != null && cur.l != null && cur.r.r.size > cur.l.size) {
				cur = leftRotate(cur);
				cur.l = matain(cur.l);
				cur = matain(cur);
			} else if (cur.r != null && cur.r.l != null && cur.l != null && cur.r.l.size > cur.l.size) {
				cur.r = rightRotate(cur.r);
				cur = leftRotate(cur);
				cur.l = matain(cur.l);
				cur.r = matain(cur.r);
				cur = matain(cur);
			}
			return cur;
		}

		private SBTNode<K> findLastIndex(K key) {
			SBTNode<K> pre = root;
			SBTNode<K> cur = root;
			while (cur != null) {
				pre = cur;
				if (key.compareTo(cur.key) == 0) {
					break;
				} else if (key.compareTo(cur.key) < 0) {
					cur = cur.l;
				} else {
					cur = cur.r;
				}
			}
			return pre;
		}

		private SBTNode<K> add(SBTNode<K> cur, K key) {
			if (cur == null) {
				return new SBTNode<K>(key);
			} else {
				cur.size++;
				if (key.compareTo(cur.key) < 0) {
					cur.l = add(cur.l, key);
				} else {
					cur.r = add(cur.r, key);
				}
				return matain(cur);
			}
		}

		private SBTNode<K> delete(SBTNode<K> cur, K key) {
			cur.size--;
			if (key.compareTo(cur.key) > 0) {
				cur.r = delete(cur.r, key);
			} else if (key.compareTo(cur.key) < 0) {
				cur.l = delete(cur.l, key);
			} else {
				if (cur.l == null && cur.r == null) {
					// free cur memory -> C++
					cur = null;
				} else if (cur.l == null && cur.r != null) {
					// free cur memory -> C++
					cur = cur.r;
				} else if (cur.l != null && cur.r == null) {
					// free cur memory -> C++
					cur = cur.l;
				} else {
					SBTNode<K> pre = null;
					SBTNode<K> des = cur.r;
					des.size--;
					while (des.l != null) {
						pre = des;
						des = des.l;
						des.size--;
					}
					if (pre != null) {
						pre.l = des.r;
						des.r = cur.r;
					}
					des.l = cur.l;
					des.size = des.l.size + (des.r == null ? 0 : des.r.size) + 1;
					// free cur memory -> C++
					cur = des;
				}
			}
			return cur;
		}

		private SBTNode<K> getIndex(SBTNode<K> cur, int kth) {
			if (kth == (cur.l != null ? cur.l.size : 0) + 1) {
				return cur;
			} else if (kth <= (cur.l != null ? cur.l.size : 0)) {
				return getIndex(cur.l, kth);
			} else {
				return getIndex(cur.r, kth - (cur.l != null ? cur.l.size : 0) - 1);
			}
		}

		public int size() {
			return root == null ? 0 : root.size;
		}

		public boolean containsKey(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			SBTNode<K> lastNode = findLastIndex(key);
			return lastNode != null && key.compareTo(lastNode.key) == 0 ? true : false;
		}

		public void add(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			SBTNode<K> lastNode = findLastIndex(key);
			if (lastNode == null || key.compareTo(lastNode.key) != 0) {
				root = add(root, key);
			}
		}

		public void remove(K key) {
			if (key == null) {
				throw new RuntimeException("invalid parameter.");
			}
			if (containsKey(key)) {
				root = delete(root, key);
			}
		}

		public K getIndexKey(int index) {
			if (index < 0 || index >= this.size()) {
				throw new RuntimeException("invalid parameter.");
			}
			return getIndex(root, index + 1).key;
		}

	}

	public static class Node implements Comparable<Node> {
		public int index;
		public int value;

		public Node(int i, int v) {
			index = i;
			value = v;
		}

		@Override
		public int compareTo(Node o) {
			return value != o.value ? Integer.valueOf(value).compareTo(o.value)
					: Integer.valueOf(index).compareTo(o.index);
		}
	}

	public static double[] medianSlidingWindow(int[] nums, int k) {
		SizeBalancedTreeMap<Node> map = new SizeBalancedTreeMap<>();
		for (int i = 0; i < k - 1; i++) {
			map.add(new Node(i, nums[i]));
		}
		double[] ans = new double[nums.length - k + 1];
		int index = 0;
		for (int i = k - 1; i < nums.length; i++) {
			map.add(new Node(i, nums[i]));
			if (map.size() % 2 == 0) {
				Node upmid = map.getIndexKey(map.size() / 2 - 1);
				Node downmid = map.getIndexKey(map.size() / 2);
				ans[index++] = ((double) upmid.value + (double) downmid.value) / 2;
			} else {
				Node mid = map.getIndexKey(map.size() / 2);
				ans[index++] = (double) mid.value;
			}
			map.remove(new Node(i - k + 1, nums[i - k + 1]));
		}
		return ans;
	}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值