数据结构与算法(五)

文章目录

    • 数据结构与算法(五)
      • 33 与哈希函数有关的结构
        • 33.1 哈希函数
        • 33.2 布隆过滤器
        • 33.3 一致性哈希
      • 34 资源限制类题目的解题套路
        • 34.1 1G内存40亿个无符号整数的文件中找到出现次数最多的数
        • 34.2 内存限制为3KB,但是只用找到一个没出现过的数
        • 34.3 100亿个URL的大文件中找出其中所有重复的URL
        • 34.4 40亿个无符号整数找出所有出现了两次的数
        • 34.5 40亿个无符号整数的中位数
        • 34.6 对一个10G大小的文件中的数字排序
      • 35 有序表(上)
        • 35.1 平衡二叉树的插入与删除
        • 35.2 AVL树的左旋与右旋
        • 35.3 AVL树的实现
      • 36 有序表(中)
        • 36.1 size-balanced-tree实现
        • 36.3 skiplist实现
      • 37 有序表(下)
        • 37.1 区间和达标的子数组数量
        • 37.2 滑动窗口中位数
        • 37.3 设计一种复杂度O(logN)高效数据结构
        • 37.4 根据身高重建队列
      • 38 根据对数器找规律、根据数据量猜解法
        • 38.1 小虎买苹果
        • 38.2 牛羊吃N份青草谁会赢
        • 38.3 给定一个参数N,返回是不是可以表示成若干连续正数和的数
        • 38.4 对数器找规律小结
        • 38.5 打怪兽需要花的最小钱数
      • 39 根据数据量猜解法(续)、分治技巧、卡特兰数
        • 39.1 非负数组子序列中累加和%m的最大值
        • 39.2 背包中有多少种零食放法
        • 39.3 卡特兰数
        • 39.4 左右括号各N个组合出来的括号配对是否合法
        • 39.5 二叉树N个节点无差别能形成多少种不同的结构
      • 40 子数组达到规定累加和的最大长度系列问题、矩阵处理技巧题
        • 40.1 正整数组子数组累加和等于K的最大长度
        • 40.2 整数数组子数组累加和等于K的最大长度
        • 40.3 整数组成的无序数组中子数组的累加和小于等于K的最大长度
        • 40.4 子数组平均值小于等于v的最长子数组长度
        • 40.5 技巧小结
        • 40.6 给定一个正方形或者长方形矩阵matrix,实现转圈打印
        • 40.7 给定一个正方形矩阵matrix,原地调整成顺时针90度转动的样子
        • 40.8 给定一个正方形或者长方形矩阵matrix,实现zigzag打印
        • 40.9 转圈打印N边星号正方形

数据结构与算法(五)

33 与哈希函数有关的结构

  • 内容:
    • 哈希函数
    • 哈希函数的应用
    • 布隆过滤器
    • 一致性哈希
    • 原理讲述为主,面试只会聊设计,所以本节无题目
33.1 哈希函数
  • 哈希函数作用:可以把数据根据不同值,几乎均匀的分开
public class Hash {
   

	private MessageDigest hash;

	public Hash(String algorithm) {
   
		try {
   
			hash = MessageDigest.getInstance(algorithm);
		} catch (NoSuchAlgorithmException e) {
   
			e.printStackTrace();
		}
	}

	public String hashCode(String input) {
   
		return DatatypeConverter.printHexBinary(hash.digest(input.getBytes())).toUpperCase();
	}

	public static void main(String[] args) {
   
		System.out.println("支持的算法 : ");
		for (String str : Security.getAlgorithms("MessageDigest")) {
   
			System.out.println(str);
		}
		System.out.println("=======");

		String algorithm = "MD5";
		Hash hash = new Hash(algorithm);

		String input1 = "zuochengyunzuochengyun1";
		String input2 = "zuochengyunzuochengyun2";
		String input3 = "zuochengyunzuochengyun3";
		String input4 = "zuochengyunzuochengyun4";
		String input5 = "zuochengyunzuochengyun5";
		System.out.println(hash.hashCode(input1));
		System.out.println(hash.hashCode(input2));
		System.out.println(hash.hashCode(input3));
		System.out.println(hash.hashCode(input4));
		System.out.println(hash.hashCode(input5));
	}
}
支持的算法 : 
SHA-384
SHA-224
SHA-256
MD2
SHA
SHA-512
MD5
=======
B73390C90A49FEF569367FDF894AC89F
451AF105D1062B501663A17F19BA9196
C5A9C27C2BE4EE613311137528AF2D93
E001FC84040DA08AE3B74EB98FD4B468
D0424A6201FD9CB675FC2D40F20DD5D5
33.2 布隆过滤器
  • 利用哈希函数的性质,每一条数据提取特征,加入描黑库。

举例:假设有一个服务,存储了100亿个黑名单的 url。如果用 HashSet 去存,就得需要大概 640G 内存(一个url就是 64 byte,100亿个url就是 6400亿 byte ≈ 640G),及其浪费空间。而使用布隆过滤器可以极大的节省空间,差不多 20G 就可以实现了。但是,布隆过滤器有一定的失误率,这个失误率是针对非黑名单而言的(即:宁可错杀一千,也不可放过一个),并且这个失误率是可控的。

  • 位图:比如 int[10] 可以表示 40 Byte 信息,再比如 bit[n] 可以表示 n/8 Byte 信息。int[10] 就相等于 bit[320]。
  • 假设申请长度为m的bit数组,针对每一个 url,依次经过哈希函数(Hash1、Hash2、…、Hashk)处理后,再模m计算出一个位置并置为1。查询时,通过相同的方式 hashi(url)%m ,得到的位置结果如果全部命中,那么说明该url可能在黑名单中,只要有一个没命中就一定不在黑名单中。
  • 由此可见,问题的关键在于 m 的大小和 hash 函数的个数。

  • m越大,失误率越小

m 和什么有关?

  • 样本量:100亿
  • 失误率:0.01%
  • 单样本大小:64 byte(和这个无关)

image.png

  • 布隆过滤器重要的三个公式:

image.png

  • 以上三个公式需要背一下。

    • ① 根据确定的样本量 n 和预期的失误率 p,由第一个公式计算出一个m,对m向上取整得到一个实际的 m’;
    • ② 将 m’ 带入到第二个公式计算出一个k,对k向上取整得到一个实际的 k’;
    • ③ 将 m’ 和 k’ 带入到第三个公式计算出一个真实的失误率 p’,这个值一定小于 p。

接下来的问题是,如果需要 k 多个哈希函数,怎么来?

  • 只需要有两个哈希函数(f1、f2)就可以造出 k 个哈希函数,而且几乎都是独立的。

    • ① 1*f1() + f2
    • ② 2*f1() + f2
    • ③ 3*f1() + f2
  • 布隆过滤器的应用-HDFS

  • http://www.bubuko.com/infodetail-3803533.html

33.3 一致性哈希
  • 分布式存储结构最常见的结构
    • 哈希域成环的设计
    • 虚拟节点技术:既可以解决负载均衡,也可以实现负载管理(调整虚拟节点的个数)

image.png

34 资源限制类题目的解题套路

  • 内容:
    • 布隆过滤器用于集合的建立与查询,并可以节省大量空间(33讲)
    • 一致性哈希解决数据服务器的负载管理问题(33讲)
    • 利用并查集结构做岛问题的并行计算(14讲)
    • 哈希函数可以把数据按照种类均匀分流
    • 位图解决某一范围上数字的出现情况,并可以节省大量空间
    • 利用分段统计思想、并进一步节省大量空间
    • 利用堆、外排序来做多个处理单元的结果合并
34.1 1G内存40亿个无符号整数的文件中找到出现次数最多的数

32位无符号整数的范围是0~4,294,967,295,现在有一个正好包含40亿个无符号整数的文件,可以使用最多1GB的内存,怎么找到出现次数最多的数?

  • 如果将这 40 亿个数进行排序,内存空间至少需要 40亿 * 4Byte = 16 GB > 1GB,不可行;
  • 如果直接用 HashMap 进行词频统计,最坏情况下(所有数字都不同)内存空间至少需要 40亿 * 4Byte * 4Byte = 32GB > 1GB,也不可行;
  • 所有,我们反过来想,1GB 内存如果用 HashMap 最多可以存多少种不同的数字?答案是 1GB / 8 = 1.25 亿,然后我们保守一点(考虑一些其他的内存开销),比如只存1千万种数。那么我们可以将这个大文件分成 40亿 / 1千万 = 400 个小文件,然后循环利用这 1GB 内存分别得到这 400 个小文件中出现次数最多的数,并从这400个数中求一个最大值。
    • 怎么将这个 40 亿个数均匀分到这 400 个小文件?这里就用到了哈希函数,即对每个数利用哈希函数计算后,模 400 后写入到对应的文件中。
34.2 内存限制为3KB,但是只用找到一个没出现过的数

32位无符号整数的范围是0~4,294,967,295,现在有一个正好包含40亿个无符号整数的文件,所以在整个范围中必然存在没出现过的数,可以使用最多1GB的内存,怎么找到所有未出现过的数?

  • 如果直接用 HashSet 存这 40 亿个数,内存空间最少需要 40亿 * 4Byte = 16GB > 1GB,不可行;
  • 可以利用位图 bit[] 数组,某一bit位为1表示出现过,0表示没出现过,那么最坏情况 40 亿个数都不同,需要的内存空间是 40亿/8 Byte = 500 MB < 1GB,这种方式可行。
    • 也可以利用整型数组 int[]来存,int[0] 表示 0~31位、int[1] 表示 32~63位、int[2] 表示 64~95位,针对每个数 x 先计算 x/32 定位到整型数组的位置 i,然后 x%32 定位到 int[i] 中具体第几位,置为1;
    • 那么所有为 0 的位置就都是没有出现过的数。

进阶:内存限制为3KB,但是只用找到一个没出现过的数即可

  • 如果 3KB 都用来存 int[] arr数组,得有多长?3000 / 4 = 750,然后长度取 512 < 750最近的 2 的某次方。
  • 接下来,将32位无符号整数(2^32)均分成 512 份,那么每一份负责的数有 2^32 / 512 = 2^23 = 8388608 个,即 arr[0] 统计 0~8388607、arr[1] 统计 8388608~16777215、arr[2] 统计 16777216~25165823、…
    • 从中找到统计不满的(不全是1的),利用同样的方式除以 512,均分成 512 份,即 即 arr[0] 统计 0~16383、arr[1] 统计 16384~32767、arr[2] 统计 32768~49152、…
    • 这样,经过有限的几轮就能定位到一个没出现过的数。
  • 这就是 512 分法

再次升级:用有限几个变量,找到一个没出现过的数

  • 二分法:首先 L = 0、R = (2^32)-1、mid = (L+R)/2,分别统计左右两侧出现过的数有几个,如果那侧不够 2^31 个数,就二分递归下去,最后总能定位到这个没出现过的数。
34.3 100亿个URL的大文件中找出其中所有重复的URL

有一个包含100亿个URL的大文件,假设每个URL占用64B,请找出其中所有重复的URL。
补充:某搜索公司一天的用户搜索词汇是海量的(百亿数据量),请设计一种求出每天热门Top100词汇的可行办法

  • 如果允许有失误率,可以使用布隆过滤器
  • 如果不允许有失误率,可以使用哈希分流的思想
34.4 40亿个无符号整数找出所有出现了两次的数

32位无符号整数的范围是0~4294967295,现在有40亿个无符号整数,可以使用最多1GB的内存,找出所有出现了两次的数

  • 用两个 bit 位表示一个数字出现了几次:00 出现0次、01 出现1次、10 出现2次、11 出现3次及以上
  • 40 亿个数需要内存空间 = 40亿 * 2bit = 80亿bit = 10亿Byte = 1GB 刚好够
  • 如果担心内存不够,就用分段统计
34.5 40亿个无符号整数的中位数

32位无符号整数的范围是0~4294967295,现在有40亿个无符号整数,可以使用最多3K的内存,怎么找到这40亿个整数的中位数?

  • 申请一个长度512的整型数组 int[] arr,将32位无符号整数均分成 512 份,arr[0] 统计 0~8388607、arr[1] 统计 8388608~16777215、arr[2] 统计 16777216~25165823、…。然后依次累加每一份出现的数个数,假设 到 arr[170] 时已经总共有 19 亿个数了,arr[171] 有 2 亿数,接下来找到 arr[171] 中第 1 亿小的数(刚好第20亿个数)就是所求中位数。
34.6 对一个10G大小的文件中的数字排序

32位无符号整数的范围是0~4294967295,有一个10G大小的文件,每一行都装着这种类型的数字,整个文件是无序的,给你5G的内存空间,请你输出一个10G大小的文件,就是原文件所有数字排序的结果。

  • 利用堆结构,堆中元素是每个数出现的词频<k,v>,5G内存空间最多可以存 5G/(4+8)Byte ≈ 4.1亿个数,担心内存不够,就保守一点按 4000万个数来算。每次读入10G文件,利用堆得到前 4000万 的数,然后从小到大依次写入文件,并记下第 4000万大的数;下一次读入10G文件时,遇到小于第 4000万大的数直接跳过,收集第 4000万大数之后的 4000万个数,再从小到大依次写入文件,再记下这一次第 4000万大的数;依此循环往复,直到凑不齐 4000万个数,结束循环,将剩下的数从小到大依次写入文件。

35 有序表(上)

  • 内容:
    • 平衡搜索二叉树:对于任意节点,左右子树的高度差不大于1
    • 左旋
    • 右旋
    • AVL树的节点违规4种类型(LL,LR,RL,RR)
35.1 平衡二叉树的插入与删除
  • 插入:10 和根节点 20 比较 ==>> 10 和 17 比较 ==>> 10 和 3 比较 ==>> 插入到 3 节点的右子树

image.png

  • 删除:
    • 叶子节点:10、28、30 直接删除
    • 删除出度为1的节点:3、17、32 提升 3 的唯一子树
    • 删除出度为2的节点:20、29 找到前驱或者后继节点进行替换,替换后转换为删除度为1或度为0的问题

image.png

35.2 AVL树的左旋与右旋
  • 左旋

image.png

  • 右旋

image.png

  • 失衡类型:

image.png

  • LL型的失衡调整:对根节点进行一次大右旋即可;
  • RR型的失衡调整:对根节点进行一次大左旋即可;
  • LR型的失衡调整:先左子树进行一次小左旋,再根节点进行一次大右旋即可;
  • RL型的失衡调整:先右子树进行一次小右旋,再根节点进行一次大左旋即可;
  • LL+LR失衡:按LL失衡调整;
  • RR+RL失衡:按RR失衡调整;
  • 不可能出现 LL+RR失衡。
35.3 AVL树的实现
public static class AVLNode<K extends Comparable<K>, V> {
   
	public K k;
	public V v;
	public AVLNode<K, V> l;
	public AVLNode<K, V> r;
	public int h;

	public AVLNode(K key, V value) {
   
		k = key;
		v = value;
		h = 1;
	}
}

public static class AVLTreeMap<K extends Comparable<K>, V> {
   
	private AVLNode<K, V> root;
	private int size;

	public AVLTreeMap() {
   
		root = null;
		size = 0;
	}

	private AVLNode<K, V> rightRotate(AVLNode<K, V> cur) {
   
		AVLNode<K, V> left = cur.l;
		cur.l = left.r;
		left.r = cur;
		cur.h = Math.max((cur.l != null ? cur.l.h : 0), (cur.r != null ? cur.r.h : 0)) + 1;
		left.h = Math.max((left.l != null ? left.l.h : 0), left.r.h) + 1;
		return left;
	}

	private AVLNode<K, V> leftRotate(AVLNode<K, V> cur) {
   
		AVLNode<K, V> right = cur.r;
		cur.r = right.l;
		right.l = cur;
		cur.h = Math.max((cur.l != null ? cur.l.h : 0), (cur.r != null ? cur.r.h : 0)) + 1;
		right.h = Math.max(right.l.h, (right.r != null ? right.r.h : 0)) + 1;
		return right;
	}

	private AVLNode<K, V> rebalance(AVLNode<K, V> cur) {
   
		if (cur == null) return null;
		int leftHeight = cur.l != null ? cur.l.h : 0;
		int rightHeight = cur.r != null ? cur.r.h : 0;
		if (Math.abs(leftHeight - rightHeight) <= 1) return cur;
		if (leftHeight > rightHeight) {
   
			int leftLeftHeight = cur.l != null && cur.l.l != null ? cur.l.l.h : 0;
			int leftRightHeight = cur.l != null && cur.l.r != null ? cur.l.r.h : 0;
			if (leftLeftHeight >= leftRightHeight) {
   
				return rightRotate(cur);
			} else {
   
				cur.l = leftRotate(cur.l);
				return rightRotate(cur);
			}
		} else {
   
			int rightLeftHeight = cur.r != null && cur.r.l != null ? cur.r.l.h : 0;
			int rightRightHeight = cur.r != null && cur.r.r != null ? cur.r.r.h : 0;
			if (rightRightHeight >= rightLeftHeight) {
   
				return leftRotate(cur);
			} else {
   
				cur.r = rightRotate(cur.r);
				return leftRotate(cur);
			}
		}
	}

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

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

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

	private AVLNode<K, V> add(AVLNode<K, V> cur, K key, V value) {
   
		if (cur == null) return new AVLNode<>(key, value);

		if (key.compareTo(cur.k) < 0) {
   
			cur.l = add(cur.l, key, value);
		} else {
   
			cur.r = add(cur.r, key, value);
		}
		cur.h = Math.max(cur.l != null ? cur.l.h : 0, cur.r != null ? cur.r.h : 0) + 1;
		return rebalance(cur);
	}

	// 在cur这棵树上,删掉key所代表的节点
	// 返回cur这棵树的新头部
	private AVLNode<K, V> delete(AVLNode<K, V> cur, K key) {
   
		if (key.compareTo(cur.k) > 0) {
   
			cur.r = delete(cur.r, key);
		} else if (key.compareTo(cur.k) < 0) {
   
			cur.l = delete(cur.l, key);
		} else {
   
			if (cur.l == null && cur.r == null) {
   
				cur = null;
			} else if (cur.l == null) {
   
				cur = cur.r;
			} else if (cur.r == null) {
   
				cur = cur.l;
			} else {
   
				AVLNode<K, V> des = cur.r;
				while (des.l != null) {
   
					des = des.l;
				}
				cur.r = delete(cur.r, des.k);
				des.l = cur.l;
				des.r = cur.r;
				cur = des;
			}
		}
		if (cur != null) {
   
			cur.h = Math.max(cur.l != null ? cur.l.h : 0, cur.r != null ? cur.r.h : 0) + 1;
		}
		return rebalance(cur);
	}

	public int size() {
   
		return size;
	}

	public boolean containsKey(K key) {
   
		if (key == null) {
   
			return false;
		}
		AVLNode<K, V> lastNode = findLastIndex(key);
		return lastNode != null && key.compareTo(lastNode.k) == 0;
	}

	public void put(K key, V value) {
   
		if (key == null) {
   
			return;
		}
		AVLNode<K, V> lastNode = findLastIndex(key);
		if (lastNode != null && key.compareTo(lastNode.k) == 0) {
   
			lastNode.v = value;
		} else {
   
			size++;
			root = add(root, key, value);
		}
	}

	public void remove(K key) {
   
		if (key == null) {
   
			return;
		}
		if (containsKey(key)) {
   
			size--;
			root = delete(root, key);
		}
	}

	public V get(K key) {
   
		if (key == null) {
   
			return null;
		}
		AVLNode<K, V> lastNode = findLastIndex(key);
		if (lastNode != null && key.compareTo(lastNode.k) == 0) {
   
			return lastNode.v;
		}
		return null;
	}

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

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

	public K floorKey(K key) {
   
		if (key == null) {
   
			return null;
		}
		AVLNode<K, V> lastNoBigNode = findLastNoBigIndex(key);
		return lastNoBigNode == null ? null : lastNoBigNode.k;
	}

	public K ceilingKey(K key) {
   
		if (key == null) {
   
			return null;
		}
		AVLNode<K, V> lastNoSmallNode = findLastNoSmallIndex(key);
		return lastNoSmallNode == null ? null : lastNoSmallNode.k;
	}
}

36 有序表(中)

  • 内容:
    • size-balanced-tree:对于任意节点,其叔叔节点的子树节点个数 不能少于当前节点个数。也有4种失衡类型,也需要进行左旋和右旋调整,除此之外对于受影响的节点会依次递归调整
      • LL失衡
      • LR失衡
      • RL失衡
      • RR失衡
    • skiplist详解
    • 聊聊红黑树
36.1 size-balanced-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;
		// 区别于AVL树,这里是互换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;
		// 区别于AVL树,这里是互换size
		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> reBalance(SBTNode<K, V> cur) {
   
		if (cur == null) return null;
		int leftSize = cur.l != null ? cur.l.size : 0;
		int leftLeftSize = cur.l != null && cur.l.l != null ? cur.l.l.size : 0;
		int leftRightSize = cur.l != null && cur.l.r != null ? cur.l.r.size : 0;
		int rightSize = cur.r != null ? cur.r.size : 0;
		int rightLeftSize = cur.r != null && cur.r.l != null ? cur.r.l.size : 0;
		int rightRightSize = cur.r != null && cur.r.r != null ? cur.r.r.size : 0;
		if (leftLeftSize > rightSize) {
    // LL
			cur = rightRotate(cur);
			cur.r = reBalance(cur.r);
			cur = reBalance(cur);
		} else if (leftRightSize > rightSize) {
    // LR
			cur.l = leftRotate(cur.l);
			cur = rightRotate(cur);
			cur.l = reBalance(cur.l);
			cur.r = reBalance(cur.r);
			cur = reBalance(cur);
		} else if (rightRightSize > leftSize) {
    // RR
			cur = leftRotate(cur);
			cur.l = reBalance(cur.l);
			cur = reBalance(cur);
		} else if (rightLeftSize > leftSize) {
    // RR
			cur.r = rightRotate(cur.r);
			cur = leftRotate(cur);
			cur.l = reBalance(cur.l);
			cur.r = reBalance(cur.r);
			cur = reBalance(cur);
		}
		return cur;
	}

	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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/280895.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《深入理解JAVA虚拟机笔记》并发与线程安全原理

除了增加高速缓存之外&#xff0c;为了使处理器内部的运算单元能尽量被充分利用&#xff0c;处理器可能对输入代码进行乱序执行&#xff08;Out-Of-Order Execution&#xff09;优化。处理器会在计算之后将乱序执行的结果重组&#xff0c;保证该结果与顺序执行的结果一致&#…

分库分表之Mycat应用学习四

4 分片策略详解 分片的目标是将大量数据和访问请求均匀分布在多个节点上&#xff0c;通过这种方式提升数 据服务的存储和负载能力。 4.1 Mycat 分片策略详解 总体上分为连续分片和离散分片&#xff0c;还有一种是连续分片和离散分片的结合&#xff0c;例如先 范围后取模。 …

深度学习中的感知机

感知机是一种判别模型&#xff0c;其目标是求得一个能够将数据集中的正实例点和负实例点完全分开的分离超平面。 感知机在1957年由弗兰克罗森布拉特提出&#xff0c;是支持向量机和神经网络的基础。感知机是一种二类分类的线性分类模型&#xff0c;输入为实例的特征向量&#x…

TOGAF架构开发方法

TOGAF针对架构开发方法定义了一系列阶段和步骤&#xff0c;这些阶段和步骤对架构的迭代过程进行了详细、标准的描述。 企业架构的项目过程 一、预备阶段&#xff08;Preliminary&#xff09; 1、目标 预备阶段的目标是&#xff1a; 对组织的背景和环境进行审查&#xff08;调…

适应变化:动态预测在机器学习中的作用

一、介绍 机器学习 (ML) 中的动态预测是指随着新数据的出现而不断更新预测的方法。这种方法在从医疗保健到金融等各个领域越来越重要&#xff0c;其中实时数据分析和最新预测可以带来更好的决策和结果。在本文中&#xff0c;我将讨论机器学习中动态预测的概念、其优势、挑战以及…

Fiddler Classic 实现汉化

安装&#xff1a;https://www.telerik.com/fiddler/fiddler-classichttps://www.telerik.com/fiddler/fiddler-classic 汉化 链接&#xff1a;https://pan.baidu.com/s/1wWgVqrXlh0Gjpbwlg6pPNA 提取码&#xff1a;xq9t 下载到本地之后&#xff0c;得到了两个文件 FdToChinese…

【jdk与tomcat配置文件夹共享防火墙设置(入站出站规则)】

目录 一、jdk与tomcat配置 1.1 jdk配置 1.2 tomcat配置 二、文件夹共享 2.1 为什么需要配置文件夹共享功能 2.2 操作步骤 2.2.1 高级共享 2.2.2 普通共享 2.3 区别 三、防火墙设置&#xff08;入站规则&出站规则&#xff09; 3.1 入站规则跟出站规则 3.2 案例…

ssrf之gopher协议的使用和配置,以及需要注意的细节

gopher协议 目录 gopher协议 &#xff08;1&#xff09;安装一个cn &#xff08;2&#xff09;使用Gopher协议发送一个请求&#xff0c;环境为&#xff1a;nc起一个监听&#xff0c;curl发送gopher请求 &#xff08;3&#xff09;使用curl发送http请求&#xff0c;命令为 …

YOLOv8改进 | 2023注意力篇 | EMAttention注意力机制(附多个可添加位置)

一、本文介绍 本文给大家带来的改进机制是EMAttention注意力机制&#xff0c;它的核心思想是&#xff0c;重塑部分通道到批次维度&#xff0c;并将通道维度分组为多个子特征&#xff0c;以保留每个通道的信息并减少计算开销。EMA模块通过编码全局信息来重新校准每个并行分支中…

竞赛保研 基于机器学习与大数据的糖尿病预测

文章目录 1 前言1 课题背景2 数据导入处理3 数据可视化分析4 特征选择4.1 通过相关性进行筛选4.2 多重共线性4.3 RFE&#xff08;递归特征消除法&#xff09;4.4 正则化 5 机器学习模型建立与评价5.1 评价方式的选择5.2 模型的建立与评价5.3 模型参数调优5.4 将调参过后的模型重…

Peter算法小课堂—浮点数危机

大家先想想下面这个代码运行结果&#xff1a; #include <bits/stdc.h> using namespace std; int main(){double x5.2;double y4.11.1;cout<<(x<y)<<endl;cout<<x-y<<endl;return 0; } 最终发现&#xff0c; &#xff1f;&#xff1f;&…

【数据结构】八、查找

一、基本概念 静态查找&#xff1a;只查找&#xff0c;不改变集合内数据元素 动态查找&#xff1a;有则输出元素&#xff0c;无则添加元素 二、静态查找表 2.1顺序查找 在线性表、链表、树中依次查找 2.2折半查找&#xff08;二分查找&#xff09; 在有序的线性表中&…

条件编译处理多端差异

条件编译https://uniapp.dcloud.net.cn/tutorial/platform.html#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9%E6%9D%A1%E4%BB%B6%E7%BC%96%E8%AF%91%E5%A4%84%E7%90%86%E8%B7%A8%E7%AB%AF%E5%85%BC%E5%AE%B9 <template><view class"container"><…

分类模型评估方法

1.数据集划分 1.1 为什么要划分数据集? 思考&#xff1a;我们有以下场景&#xff1a; 将所有的数据都作为训练数据&#xff0c;训练出一个模型直接上线预测 每当得到一个新的数据&#xff0c;则计算新数据到训练数据的距离&#xff0c;预测得到新数据的类别 存在问题&…

【滑动窗口】C++算法:可见点的最大数目

作者推荐 动态规划 多源路径 字典树 LeetCode2977:转换字符串的最小成本 本题涉及知识点 滑动窗口 LeetCode 1610可见点的最大数目 给你一个点数组 points 和一个表示角度的整数 angle &#xff0c;你的位置是 location &#xff0c;其中 location [posx, posy] 且 point…

【MySQL】事务Transaction

1. 事务的概念 事务是什么 在业务逻辑中使用sql&#xff0c;面对一些较复杂的场景&#xff0c;是需要多个sql语句组合起来实现的。如&#xff1a;银行的转账业务&#xff0c;若客户A要转账100元给客户B&#xff0c;就要两条sql&#xff1a;A余额减100&#xff0c;B余额加100&a…

react-router-dom5升级到6

前言 升级前版本为5.1.2 下载与运行 下载 npm install react-router-dom6运行 运行发现报错: 将node_modules删除&#xff0c;重新执行npm i即可 运行发现如下报错 这是因为之前有引用react-router-dom.min&#xff0c;v6中取消了该文件&#xff0c;所以未找到文件导致报错。…

浅谈数字孪生的应用与发展

1、数字孪生概念 ”数字孪生是充分利用物理模型、传感器更新、运行历史等数据,集成多学科、多物理量、多尺度、多概率的仿真过程,在虚拟空间中完成映射,从而反映相对应的实体装备的全生命周期过程。数字孪生是一种超越现实的概念,可以被视为一个或多个重要的、彼此依赖的装…

Kubernetes集群部署Rook Ceph实现文件存储,对象存储,块存储

Kubernetes集群部署Rook Ceph部署Ceph集群 1. Rook Ceph介绍 Rook Ceph是Rook项目中的一个存储方案&#xff0c;专门针对Ceph存储系统进行了优化和封装。Ceph是一个高度可扩展的分布式存储系统&#xff0c;提供了对象存储、块存储和文件系统的功能&#xff0c;广泛应用于提供…

Spring Data Redis对象缓存序列化问题

相信在项目中&#xff0c;你一定是经常使用 Redis &#xff0c;那么&#xff0c;你是怎么使用的呢&#xff1f;在使用时&#xff0c;有没有遇到同我一样&#xff0c;对象缓存序列化问题的呢&#xff1f;那么&#xff0c;你又是如何解决的呢&#xff1f; Redis 使用示例 添加依…