数据结构 前缀和 对数器
数据结构
任何数据结构都是由两个基本数据原件组成 :
数组(连续结构):擅长寻址 不擅长添加、删除数据
链表(跳转结构):不擅长寻址 擅长添加删除数据
前缀和
预处理结构: 单次查询 : 快速求出下标 m 到 n 的和
[ 1 , 2 , 3 , 5 , 6 , 7 , 8 , 9 ]
0 1 2 3 4 5 6 7
二维数组 :
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 3 | 6 | 11 | 15 | 22 | 30 | 39 |
| 1 | × | 2 | 5 | 10 | 14 | 21 | 29 | 38 |
| 2 | × | × | 3 | 8 | 12 | 19 | 27 | 36 |
| 3 | × | × | × | 5 | 9 | 16 | 24 | 33 |
| 4 | × | × | × | × | 6 | 13 | 21 | 30 |
| 5 | × | × | × | × | × | 7 | 16 | 25 |
| 6 | × | × | × | × | × | × | 8 | 17 |
| 7 | × | × | × | × | × | × | × | 9 |
每一位都是前几位的和,这样可以快速求出 第m位到第n位的和 (生成表的代价为 n/2 的规模)
如果用一维数组表示:
arr [ 3 , 2 , -1 , 6 , 7 , 2 , -2 ]
0 1 2 3 4 5 6
- H [ 3 , 5 , 4 , 10 , 17 , 19 , 17 ]
0 1 2 3 4 5 6
- 由此 H[ i ] = arr[ 0 ~ i ]累加和
3 ~ 7 = H[7] - H[2]
结论: m~n的累加和 :
- 如果m==0 sum=H[n];
- 如果m !=0 sum= H[n] - H[ m - 1 ]
测试:
public static void main(String[] args) {int[] arr = {3, 2, -1, 6, 7, 2, -2};System.out.println(preSum01(arr,2,4));System.out.println(preSum02(arr,2,4));}
传统累加代码:
public static int preSum02(int[] arr,int L,int R){int sum = 0;for (int i = L; i <= R; i++) {sum += arr[i];}return sum;}
前缀和代码:
public static int preSum(int[] arr,int m,int n) {int[] newarr = new int[arr.length];newarr[0] = arr[0];for (int i = 1; i < arr.length; i++) {newarr[i] = newarr[i - 1] + arr[i];}return m == 0 ? newarr[n] : newarr[n] - newarr[m - 1];}
随机数
Math.random()
[ 0 , 1 ) 的数字等概率返回
如何返回等概率返回 1~n
`( int ) ( Math.random() * n ) + 1`
设定一个f()
f() = (int) (Math.random() * 5) + 1;// 此函数只能用,不能修改// 等概率返回1~5public static int f() {return (int) (Math.random() * 5) + 1;}
- 如何用 f() 等概率返回 0 和 1
// 等概率得到0和1public static int a() {int ans = 0;do {ans = f();} while (ans == 3);return ans < 3 ? 0 : 1;}
- 如何用 f() 等概率返回 0~6
// 等概率返回0~6public static int b() {int ans = 0;do {ans = (a() << 2) + (a() << 1) + a();} while (ans == 7);return ans;}
- 如何用 f() 等概率返回 1~7
// 等概率返回1~7public static int c() {return b() + 1;}
唯一的随机机制 只能初始化 不可修改
// 这个结构是唯一的随机机制// 你只能初始化并使用,不可修改public static class RandomBox {private final int min;private final int max;// 初始化时请一定不要让mi==mapublic RandomBox(int mi, int ma) {min = mi;max = ma;}// 13 ~ 17// 13 + [0,4]public int random() {return min + (int) (Math.random() * (max - min + 1));}public int min() {return min;}public int max() {return max;}}
- 利用条件RandomBox,如何等概率返回0和1
// 利用条件RandomBox,如何等概率返回0和1public static int rand01(RandomBox randomBox) {int min = randomBox.min();int max = randomBox.max();// min ~ maxint size = max - min + 1;// size是不是奇数,odd 奇数boolean odd = (size & 1) != 0;int mid = size / 2;int ans = 0;do {ans = randomBox.random() - min;} while (odd && ans == mid);return ans < mid ? 0 : 1;}
给你一个RandomBox,这是唯一能借助的随机机制
等概率返回from~to范围上任何一个数 要求from<=topublic static int random(RandomBox randomBox, int from, int to) {if (from == to) {return from;}// 3 ~ 9// 0 ~ 6// 0 ~ rangeint range = to - from;int num = 1;// 求0~range需要几个2进制位while ((1 << num) - 1 < range) {num++;}// 我们一共需要num位// 最终的累加和,首先+0位上是1还是0,1位上是1还是0,2位上是1还是0...int ans = 0;do {ans = 0;for (int i = 0; i < num; i++) {ans |= (rand01(randomBox) << i);}} while (ans > range);return ans + from;}
测试类
public static void main(String[] args) {System.out.println("测试开始");// Math.random() -> double -> [0,1)//int testTimes = 10000000;int count = 0;for (int i = 0; i < testTimes; i++) {if (Math.random() < 0.75) {count++;}}System.out.println((double) count / (double) testTimes);System.out.println("=========");// [0,1) -> [0,8)count = 0;for (int i = 0; i < testTimes; i++) {if (Math.random() * 8 < 5) {count++;}}System.out.println((double) count / (double) testTimes);System.out.println((double) 5 / (double) 8);int K = 9;// [0,K) -> [0,8]int[] counts = new int[9];for (int i = 0; i < testTimes; i++) {int ans = (int) (Math.random() * K); // [0,K-1]counts[ans]++;}for (int i = 0; i < K; i++) {System.out.println(i + "这个数,出现了 " + counts[i] + " 次");}System.out.println("=========");count = 0;double x = 0.17;for (int i = 0; i < testTimes; i++) {if (xToXPower2() < x) {count++;}}System.out.println((double) count / (double) testTimes);System.out.println((double) 1 - Math.pow((double) 1 - x, 2));System.out.println("==========");count = 0;for (int i = 0; i < testTimes; i++) {if (f2() == 0) {count++;}}System.out.println((double) count / (double) testTimes);System.out.println("==========");counts = new int[8];for (int i = 0; i < testTimes; i++) {int num = g();counts[num]++;}for (int i = 0; i < 8; i++) {System.out.println(i + "这个数,出现了 " + counts[i] + " 次");}}
- 返回[0,1)的一个小数
任意的x,x属于[0,1),[0,x)范围上的数出现概率由原来的x调整成x平方public static double xToXPower2() {return Math.max(Math.random(), Math.random());}
- 使用 f1() 等概率返回 0 和 1
public static int f2() {int ans = 0;do {ans = f1();} while (ans == 3);return ans < 3 ? 0 : 1;}
- 得到000 ~ 111 做到等概率 0 ~ 7等概率返回一个
public static int f3() {return (f2() << 2) + (f2() << 1) + f2();}
0 ~ 6等概率返回一个
public static int f4() {int ans = 0;do {ans = f3();} while (ans == 7);return ans;}public static int g() {return f4() + 1;}
- 你只能知道,x会以固定概率返回0和1,但是x的内容,你看不到!
public static int x() {return Math.random() < 0.84 ? 0 : 1;}
y()函数 自己写 : 等概率返回0和1
public static int y() {int ans = 0;do {ans = x();} while (ans == x());return ans;// ans = 0 1// ans = 1 0// 0 和 1 必然等概率}
- 出现 1 的概率为 p , 出现 0 的概率为1 - p
求出现 1 和 0 概率相同 ```java 1 1 -> p p 不要
1 0 -> p 1-p 要
0 1 -> 1-p p 要
0 0 -> 1-p 1-p 不要
则 1 0 和 0 1 的概率 一样
<a name="c35a5d61"></a>## 对数器```javapublic class Code03_Comp {public static void selectionSort(int[] arr) {if (arr == null || arr.length < 2) {return;}for (int i = 0; i < arr.length - 1; i++) {int minIndex = i;for (int j = i + 1; j < arr.length; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}swap(arr, i, minIndex);}}public static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}public static void insertionSort(int[] arr) {if (arr == null || arr.length < 2) {return;}for (int i = 1; i < arr.length; i++) { // 0 ~ i 做到有序for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {swap(arr, j, j + 1);}}}// 返回一个数组arr,arr长度[0,maxLen-1],arr中的每个值[0,maxValue-1]public static int[] lenRandomValueRandom(int maxLen, int maxValue) {int len = (int) (Math.random() * maxLen);int[] ans = new int[len];for (int i = 0; i < len; i++) {ans[i] = (int) (Math.random() * maxValue);}return ans;}public static int[] copyArray(int[] arr) {int[] ans = new int[arr.length];for (int i = 0; i < arr.length; i++) {ans[i] = arr[i];}return ans;}// arr1和arr2一定等长public static boolean isSorted(int[] arr) {if (arr.length < 2) {return true;}int max = arr[0];for (int i = 1; i < arr.length; i++) {if (max > arr[i]) {return false;}max = Math.max(max, arr[i]);}return true;}public static void main(String[] args) {int maxLen = 5;int maxValue = 1000;int testTime = 10000;for (int i = 0; i < testTime; i++) {int[] arr1 = lenRandomValueRandom(maxLen, maxValue);int[] tmp = copyArray(arr1);selectionSort(arr1);if (!isSorted(arr1)) {for (int j = 0; j < tmp.length; j++) {System.out.print(tmp[j] + " ");}System.out.println();System.out.println("选择排序错了!");break;}}}}
