diff --git a/src/main/java/snippet/Code_0058_ReservoirSampling.java b/src/main/java/snippet/Code_0058_ReservoirSampling.java deleted file mode 100644 index 76ad5fbe2a1c1377eb5972c9b65831cce64604ae..0000000000000000000000000000000000000000 --- a/src/main/java/snippet/Code_0058_ReservoirSampling.java +++ /dev/null @@ -1,97 +0,0 @@ -package snippet; - -//蓄水池算法 -//解决的问题: -//假设有一个源源吐出不同球的机器, -//只有装下10个球的袋子,每一个吐出的球,要么放入袋子,要么永远扔掉 -//如何做到机器吐出每一个球之后,所有吐出的球都等概率被放进袋子里 - -// 解法: -// 吐出1到10号球,完全入袋 -// 引入随机函数f(i),提供一个值i,等概率返回1-i的一个数字 -// 当K号球吐出的时候(K>10) ,我们通过以下决策决定是否要入袋 -// 1) 10/K的概率决定球是否入袋 f(K) -> 如果返回10以内的数,则入袋,如果返回10以外的数,则扔掉 -// 2) 第一步中如果决定入袋,那么袋子中已经存在的球以等概率丢弃一个。 -// TODO 证明 -//假设吐出了17号球,3号球在袋中的概率 -// -// ``` -// 1 * (1- 10/11 * 1/10) * (1 - 10/12 * 1/10) *....(1 - 10/17 * 1/10) = 10/17 -// ``` -// 用处 -// (10亿个抽100) -// 一台服务器 放100个用户 -// 用户检查是否是首次登录 -// 用户是第几个登录的用户 -// 如果非首次登录,直接丢弃,如果首次登录,那么以第100/N概率选中 -// 分布式蓄水池算法使用:https://www.jianshu.com/p/7a9ea6ece2af -// ## 相关习题 -// -//LeetCode_0398_RandomPickIndex.java -// -//LeetCode_0382_LinkedListRandomNode.java -// -//LeetCode_0692_TopKFrequentWords.java -// -//NowCoder_Bitorrent.java -// -//## UUID全局唯一生成器 -// -//base + 各区域的服务器 range + base 最小值+range 压力被range分散掉 -public class Code_0058_ReservoirSampling { - public static class RandomBox { - private int[] bag; - // 袋子容量 - private int capacity; - // 第几号球 - private int count; - - public RandomBox(int capacity) { - bag = new int[capacity]; - this.capacity = capacity; - count = 0; - } - - // 随机函数,等概率生成1-max之间随机的一个数字 - // Math.random() -> 生成[0,1)范围内的数 - // (int)i 是对i进行向下取整 - private int rand(int max) { - return (int) (Math.random() * max) + 1; - } - - public void add(int num) { - // 球个数增加 - count++; - // 如果球的个数没有超过容量 - if (count <= capacity) { - // 则入袋 - bag[count - 1] = num; - } else if (rand(count) <= capacity) { - // 否则以N/count的概率入袋 - bag[rand(capacity) - 1] = num; - } - } - - // 返回袋子中最终选中的球 - public int[] choices() { - int[] res = new int[capacity]; - System.arraycopy(bag, 0, res, 0, capacity); - return res; - } - - } - - public static void main(String[] args) { - System.out.println("hello"); - int all = 100; - int choose = 10; - RandomBox box = new RandomBox(choose); - for (int num = 1; num <= all; num++) { - box.add(num); - } - int[] ans = box.choices(); - for (int i = 0; i < ans.length; i++) { - System.out.println(i + " times : " + ans[i]); - } - } -} diff --git a/src/main/java/snippet/Code_0121_DynamicSegmentTree.java b/src/main/java/snippet/Code_0121_DynamicSegmentTree.java deleted file mode 100644 index 5887604bcba27d5d570aa847bbe90f0312ae84ac..0000000000000000000000000000000000000000 --- a/src/main/java/snippet/Code_0121_DynamicSegmentTree.java +++ /dev/null @@ -1,199 +0,0 @@ -package snippet; - -// TODO -// 同时支持范围增加 + 范围修改 + 范围查询的动态开点线段树(累加和) -// 真的用到!才去建立 -// 懒更新,及其所有的东西,和普通线段树,没有任何区别! -public class Code_0121_DynamicSegmentTree { - - public static class Node { - public int sum; - public int lazy; - public int change; - public boolean update; - public Node left; - public Node right; - } - - public static class DynamicSegmentTree { - public Node root; - public int size; - - public DynamicSegmentTree(int max) { - root = new Node(); - size = max; - } - - private void pushUp(Node c) { - c.sum = c.left.sum + c.right.sum; - } - - private void pushDown(Node p, int ln, int rn) { - if (p.left == null) { - p.left = new Node(); - } - if (p.right == null) { - p.right = new Node(); - } - if (p.update) { - p.left.update = true; - p.right.update = true; - p.left.change = p.change; - p.right.change = p.change; - p.left.lazy = 0; - p.right.lazy = 0; - p.left.sum = p.change * ln; - p.right.sum = p.change * rn; - p.update = false; - } - if (p.lazy != 0) { - p.left.lazy += p.lazy; - p.right.lazy += p.lazy; - p.left.sum += p.lazy * ln; - p.right.sum += p.lazy * rn; - p.lazy = 0; - } - } - - public void update(int s, int e, int v) { - update(root, 1, size, s, e, v); - } - - private void update(Node c, int l, int r, int s, int e, int v) { - if (s <= l && r <= e) { - c.update = true; - c.change = v; - c.sum = v * (r - l + 1); - c.lazy = 0; - } else { - int mid = (l + r) >> 1; - pushDown(c, mid - l + 1, r - mid); - if (s <= mid) { - update(c.left, l, mid, s, e, v); - } - if (e > mid) { - update(c.right, mid + 1, r, s, e, v); - } - pushUp(c); - } - } - - public void add(int s, int e, int v) { - add(root, 1, size, s, e, v); - } - - private void add(Node c, int l, int r, int s, int e, int v) { - if (s <= l && r <= e) { - c.sum += v * (r - l + 1); - c.lazy += v; - } else { - int mid = (l + r) >> 1; - pushDown(c, mid - l + 1, r - mid); - if (s <= mid) { - add(c.left, l, mid, s, e, v); - } - if (e > mid) { - add(c.right, mid + 1, r, s, e, v); - } - pushUp(c); - } - } - - public int query(int s, int e) { - return query(root, 1, size, s, e); - } - - private int query(Node c, int l, int r, int s, int e) { - if (s <= l && r <= e) { - return c.sum; - } - int mid = (l + r) >> 1; - pushDown(c, mid - l + 1, r - mid); - int ans = 0; - if (s <= mid) { - ans += query(c.left, l, mid, s, e); - } - if (e > mid) { - ans += query(c.right, mid + 1, r, s, e); - } - return ans; - } - - } - - public static class Right { - public int[] arr; - - public Right(int size) { - arr = new int[size + 1]; - } - - public void add(int s, int e, int v) { - for (int i = s; i <= e; i++) { - arr[i] += v; - } - } - - public void update(int s, int e, int v) { - for (int i = s; i <= e; i++) { - arr[i] = v; - } - } - - public int query(int s, int e) { - int sum = 0; - for (int i = s; i <= e; i++) { - sum += arr[i]; - } - return sum; - } - - } - - public static void main(String[] args) { - int n = 1000; - int value = 50; - int createTimes = 5000; - int operateTimes = 5000; - System.out.println("测试开始"); - for (int i = 0; i < createTimes; i++) { - int size = (int) (Math.random() * n) + 1; - DynamicSegmentTree dst = new DynamicSegmentTree(size); - Right right = new Right(size); - for (int k = 0; k < operateTimes; k++) { - double choose = Math.random(); - if (choose < 0.333) { - int a = (int) (Math.random() * size) + 1; - int b = (int) (Math.random() * size) + 1; - int s = Math.min(a, b); - int e = Math.max(a, b); - int v = (int) (Math.random() * value); - dst.update(s, e, v); - right.update(s, e, v); - } else if (choose < 0.666) { - int a = (int) (Math.random() * size) + 1; - int b = (int) (Math.random() * size) + 1; - int s = Math.min(a, b); - int e = Math.max(a, b); - int v = (int) (Math.random() * value); - dst.add(s, e, v); - right.add(s, e, v); - } else { - int a = (int) (Math.random() * size) + 1; - int b = (int) (Math.random() * size) + 1; - int s = Math.min(a, b); - int e = Math.max(a, b); - int ans1 = dst.query(s, e); - int ans2 = right.query(s, e); - if (ans1 != ans2) { - System.out.println("出错了!"); - System.out.println(ans1); - System.out.println(ans2); - } - } - } - } - System.out.println("测试结束"); - } - -} diff --git a/src/main/java/snippet/Code_0120_DynamicSegmentTree.java "b/src/main/java/\346\225\260\346\215\256\347\273\223\346\236\204/\345\212\250\346\200\201\345\274\200\347\202\271\347\272\277\346\256\265\346\240\221/Code_DynamicSegmentTree1.java" similarity index 97% rename from src/main/java/snippet/Code_0120_DynamicSegmentTree.java rename to "src/main/java/\346\225\260\346\215\256\347\273\223\346\236\204/\345\212\250\346\200\201\345\274\200\347\202\271\347\272\277\346\256\265\346\240\221/Code_DynamicSegmentTree1.java" index f5c2c288e0940511b6fc6864ba6acedf5113113c..5f34bd2be35af162aee5fed1023cd3fbdab6bbce 100644 --- a/src/main/java/snippet/Code_0120_DynamicSegmentTree.java +++ "b/src/main/java/\346\225\260\346\215\256\347\273\223\346\236\204/\345\212\250\346\200\201\345\274\200\347\202\271\347\272\277\346\256\265\346\240\221/Code_DynamicSegmentTree1.java" @@ -1,9 +1,9 @@ -package snippet; +package 数据结构.动态开点线段树; // TODO // 只支持单点增加 + 范围查询的动态开点线段树(累加和) // tips: -public class Code_0120_DynamicSegmentTree { +public class Code_DynamicSegmentTree1 { public static class Node { public int sum; diff --git "a/src/main/java/\346\225\260\346\215\256\347\273\223\346\236\204/\345\212\250\346\200\201\345\274\200\347\202\271\347\272\277\346\256\265\346\240\221/Code_DynamicSegmentTree2.java" "b/src/main/java/\346\225\260\346\215\256\347\273\223\346\236\204/\345\212\250\346\200\201\345\274\200\347\202\271\347\272\277\346\256\265\346\240\221/Code_DynamicSegmentTree2.java" new file mode 100644 index 0000000000000000000000000000000000000000..9b123b62e7b523c654ca4040814cda25c20c8b24 --- /dev/null +++ "b/src/main/java/\346\225\260\346\215\256\347\273\223\346\236\204/\345\212\250\346\200\201\345\274\200\347\202\271\347\272\277\346\256\265\346\240\221/Code_DynamicSegmentTree2.java" @@ -0,0 +1,199 @@ +package 数据结构.动态开点线段树; + +// TODO +// 同时支持范围增加 + 范围修改 + 范围查询的动态开点线段树(累加和) +// 真的用到!才去建立 +// 懒更新,及其所有的东西,和普通线段树,没有任何区别! +public class Code_DynamicSegmentTree2 { + + public static class Node { + public int sum; + public int lazy; + public int change; + public boolean update; + public Node left; + public Node right; + } + + public static class DynamicSegmentTree { + public Node root; + public int size; + + public DynamicSegmentTree(int max) { + root = new Node(); + size = max; + } + + private void pushUp(Node c) { + c.sum = c.left.sum + c.right.sum; + } + + private void pushDown(Node p, int ln, int rn) { + if (p.left == null) { + p.left = new Node(); + } + if (p.right == null) { + p.right = new Node(); + } + if (p.update) { + p.left.update = true; + p.right.update = true; + p.left.change = p.change; + p.right.change = p.change; + p.left.lazy = 0; + p.right.lazy = 0; + p.left.sum = p.change * ln; + p.right.sum = p.change * rn; + p.update = false; + } + if (p.lazy != 0) { + p.left.lazy += p.lazy; + p.right.lazy += p.lazy; + p.left.sum += p.lazy * ln; + p.right.sum += p.lazy * rn; + p.lazy = 0; + } + } + + public void update(int s, int e, int v) { + update(root, 1, size, s, e, v); + } + + private void update(Node c, int l, int r, int s, int e, int v) { + if (s <= l && r <= e) { + c.update = true; + c.change = v; + c.sum = v * (r - l + 1); + c.lazy = 0; + } else { + int mid = (l + r) >> 1; + pushDown(c, mid - l + 1, r - mid); + if (s <= mid) { + update(c.left, l, mid, s, e, v); + } + if (e > mid) { + update(c.right, mid + 1, r, s, e, v); + } + pushUp(c); + } + } + + public void add(int s, int e, int v) { + add(root, 1, size, s, e, v); + } + + private void add(Node c, int l, int r, int s, int e, int v) { + if (s <= l && r <= e) { + c.sum += v * (r - l + 1); + c.lazy += v; + } else { + int mid = (l + r) >> 1; + pushDown(c, mid - l + 1, r - mid); + if (s <= mid) { + add(c.left, l, mid, s, e, v); + } + if (e > mid) { + add(c.right, mid + 1, r, s, e, v); + } + pushUp(c); + } + } + + public int query(int s, int e) { + return query(root, 1, size, s, e); + } + + private int query(Node c, int l, int r, int s, int e) { + if (s <= l && r <= e) { + return c.sum; + } + int mid = (l + r) >> 1; + pushDown(c, mid - l + 1, r - mid); + int ans = 0; + if (s <= mid) { + ans += query(c.left, l, mid, s, e); + } + if (e > mid) { + ans += query(c.right, mid + 1, r, s, e); + } + return ans; + } + + } + + public static class Right { + public int[] arr; + + public Right(int size) { + arr = new int[size + 1]; + } + + public void add(int s, int e, int v) { + for (int i = s; i <= e; i++) { + arr[i] += v; + } + } + + public void update(int s, int e, int v) { + for (int i = s; i <= e; i++) { + arr[i] = v; + } + } + + public int query(int s, int e) { + int sum = 0; + for (int i = s; i <= e; i++) { + sum += arr[i]; + } + return sum; + } + + } + + public static void main(String[] args) { + int n = 1000; + int value = 50; + int createTimes = 5000; + int operateTimes = 5000; + System.out.println("测试开始"); + for (int i = 0; i < createTimes; i++) { + int size = (int) (Math.random() * n) + 1; + DynamicSegmentTree dst = new DynamicSegmentTree(size); + Right right = new Right(size); + for (int k = 0; k < operateTimes; k++) { + double choose = Math.random(); + if (choose < 0.333) { + int a = (int) (Math.random() * size) + 1; + int b = (int) (Math.random() * size) + 1; + int s = Math.min(a, b); + int e = Math.max(a, b); + int v = (int) (Math.random() * value); + dst.update(s, e, v); + right.update(s, e, v); + } else if (choose < 0.666) { + int a = (int) (Math.random() * size) + 1; + int b = (int) (Math.random() * size) + 1; + int s = Math.min(a, b); + int e = Math.max(a, b); + int v = (int) (Math.random() * value); + dst.add(s, e, v); + right.add(s, e, v); + } else { + int a = (int) (Math.random() * size) + 1; + int b = (int) (Math.random() * size) + 1; + int s = Math.min(a, b); + int e = Math.max(a, b); + int ans1 = dst.query(s, e); + int ans2 = right.query(s, e); + if (ans1 != ans2) { + System.out.println("出错了!"); + System.out.println(ans1); + System.out.println(ans2); + } + } + } + } + System.out.println("测试结束"); + } + +} diff --git "a/src/main/java/\347\256\227\346\263\225/\350\223\204\346\260\264\346\261\240\347\256\227\346\263\225/Code_ReservoirSampling.java" "b/src/main/java/\347\256\227\346\263\225/\350\223\204\346\260\264\346\261\240\347\256\227\346\263\225/Code_ReservoirSampling.java" new file mode 100644 index 0000000000000000000000000000000000000000..3e99a2e8f124d97bee3c5d31a701bbca464c85a5 --- /dev/null +++ "b/src/main/java/\347\256\227\346\263\225/\350\223\204\346\260\264\346\261\240\347\256\227\346\263\225/Code_ReservoirSampling.java" @@ -0,0 +1,97 @@ +package 算法.蓄水池算法; + +// 蓄水池算法 +// 解决的问题: +// 假设有一个源源吐出不同球的机器, +// 只有装下10个球的袋子,每一个吐出的球,要么放入袋子,要么永远扔掉 +// 如何做到机器吐出每一个球之后,所有吐出的球都等概率被放进袋子里 + +// 解法: +// 吐出1到10号球,完全入袋 +// 引入随机函数f(i),提供一个值i,等概率返回1-i的一个数字 +// 当K号球吐出的时候(K>10) ,我们通过以下决策决定是否要入袋 +// 1) 10/K的概率决定球是否入袋 f(K) -> 如果返回10以内的数,则入袋,如果返回10以外的数,则扔掉 +// 2) 第一步中如果决定入袋,那么袋子中已经存在的球以等概率丢弃一个。 +// TODO 证明 +// 假设吐出了17号球,3号球在袋中的概率 +// +// ``` +// 1 * (1- 10/11 * 1/10) * (1 - 10/12 * 1/10) *....(1 - 10/17 * 1/10) = 10/17 +// ``` +// 用处 +// (10亿个抽100) +// 一台服务器 放100个用户 +// 用户检查是否是首次登录 +// 用户是第几个登录的用户 +// 如果非首次登录,直接丢弃,如果首次登录,那么以第100/N概率选中 +// 分布式蓄水池算法使用:https://www.jianshu.com/p/7a9ea6ece2af +// ## 相关习题 +// +// LeetCode_0398_RandomPickIndex.java +// +// LeetCode_0382_LinkedListRandomNode.java +// +// LeetCode_0692_TopKFrequentWords.java +// +// NowCoder_Bitorrent.java +// +// ## UUID全局唯一生成器 +// +// base + 各区域的服务器 range + base 最小值+range 压力被range分散掉 +public class Code_ReservoirSampling { + public static class RandomBox { + private int[] bag; + // 袋子容量 + private int capacity; + // 第几号球 + private int count; + + public RandomBox(int capacity) { + bag = new int[capacity]; + this.capacity = capacity; + count = 0; + } + + // 随机函数,等概率生成1-max之间随机的一个数字 + // Math.random() -> 生成[0,1)范围内的数 + // (int)i 是对i进行向下取整 + private int rand(int max) { + return (int) (Math.random() * max) + 1; + } + + public void add(int num) { + // 球个数增加 + count++; + // 如果球的个数没有超过容量 + if (count <= capacity) { + // 则入袋 + bag[count - 1] = num; + } else if (rand(count) <= capacity) { + // 否则以N/count的概率入袋 + bag[rand(capacity) - 1] = num; + } + } + + // 返回袋子中最终选中的球 + public int[] choices() { + int[] res = new int[capacity]; + System.arraycopy(bag, 0, res, 0, capacity); + return res; + } + + } + + public static void main(String[] args) { + System.out.println("hello"); + int all = 100; + int choose = 10; + RandomBox box = new RandomBox(choose); + for (int num = 1; num <= all; num++) { + box.add(num); + } + int[] ans = box.choices(); + for (int i = 0; i < ans.length; i++) { + System.out.println(i + " times : " + ans[i]); + } + } +}