数据结构 前缀和 对数器

数据结构

任何数据结构都是由两个基本数据原件组成 :

00.前缀和 对数器 - 图1

数组(连续结构):擅长寻址 不擅长添加、删除数据

链表(跳转结构):不擅长寻址 擅长添加删除数据

前缀和

预处理结构: 单次查询 : 快速求出下标 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 的规模)

  1. 如果用一维数组表示:

    1. arr [ 3 , 2 , -1 , 6 , 7 , 2 , -2 ]
  1. 0 1 2 3 4 5 6
  1. H [ 3 , 5 , 4 , 10 , 17 , 19 , 17 ]
    1. 0 1 2 3 4 5 6
  1. 由此 H[ i ] = arr[ 0 ~ i ]累加和
  1. 3 ~ 7 = H[7] - H[2]
  1. 结论: m~n的累加和 :

    1. 如果m==0 sum=H[n];
    2. 如果m !=0 sum= H[n] - H[ m - 1 ]

测试:

  1. public static void main(String[] args) {
  2. int[] arr = {3, 2, -1, 6, 7, 2, -2};
  3. System.out.println(preSum01(arr,2,4));
  4. System.out.println(preSum02(arr,2,4));
  5. }

传统累加代码:

  1. public static int preSum02(int[] arr,int L,int R){
  2. int sum = 0;
  3. for (int i = L; i <= R; i++) {
  4. sum += arr[i];
  5. }
  6. return sum;
  7. }

前缀和代码:

  1. public static int preSum(int[] arr,int m,int n) {
  2. int[] newarr = new int[arr.length];
  3. newarr[0] = arr[0];
  4. for (int i = 1; i < arr.length; i++) {
  5. newarr[i] = newarr[i - 1] + arr[i];
  6. }
  7. return m == 0 ? newarr[n] : newarr[n] - newarr[m - 1];
  8. }

随机数

Math.random()

[ 0 , 1 ) 的数字等概率返回

  1. 如何返回等概率返回 1~n

    1. `( int ) ( Math.random() * n ) + 1`
  2. 设定一个f() f() = (int) (Math.random() * 5) + 1;

    1. // 此函数只能用,不能修改
    2. // 等概率返回1~5
    3. public static int f() {
    4. return (int) (Math.random() * 5) + 1;
    5. }
  1. 如何用 f() 等概率返回 0 和 1
    1. // 等概率得到0和1
    2. public static int a() {
    3. int ans = 0;
    4. do {
    5. ans = f();
    6. } while (ans == 3);
    7. return ans < 3 ? 0 : 1;
    8. }
  1. 如何用 f() 等概率返回 0~6
    1. // 等概率返回0~6
    2. public static int b() {
    3. int ans = 0;
    4. do {
    5. ans = (a() << 2) + (a() << 1) + a();
    6. } while (ans == 7);
    7. return ans;
    8. }
  1. 如何用 f() 等概率返回 1~7
    1. // 等概率返回1~7
    2. public static int c() {
    3. return b() + 1;
    4. }
  1. 唯一的随机机制 只能初始化 不可修改

    1. // 这个结构是唯一的随机机制
    2. // 你只能初始化并使用,不可修改
    3. public static class RandomBox {
    4. private final int min;
    5. private final int max;
    6. // 初始化时请一定不要让mi==ma
    7. public RandomBox(int mi, int ma) {
    8. min = mi;
    9. max = ma;
    10. }
    11. // 13 ~ 17
    12. // 13 + [0,4]
    13. public int random() {
    14. return min + (int) (Math.random() * (max - min + 1));
    15. }
    16. public int min() {
    17. return min;
    18. }
    19. public int max() {
    20. return max;
    21. }
    22. }
  1. 利用条件RandomBox,如何等概率返回0和1
    1. // 利用条件RandomBox,如何等概率返回0和1
    2. public static int rand01(RandomBox randomBox) {
    3. int min = randomBox.min();
    4. int max = randomBox.max();
    5. // min ~ max
    6. int size = max - min + 1;
    7. // size是不是奇数,odd 奇数
    8. boolean odd = (size & 1) != 0;
    9. int mid = size / 2;
    10. int ans = 0;
    11. do {
    12. ans = randomBox.random() - min;
    13. } while (odd && ans == mid);
    14. return ans < mid ? 0 : 1;
    15. }
  1. 给你一个RandomBox,这是唯一能借助的随机机制
    等概率返回from~to范围上任何一个数 要求from<=to

    1. public static int random(RandomBox randomBox, int from, int to) {
    2. if (from == to) {
    3. return from;
    4. }
    5. // 3 ~ 9
    6. // 0 ~ 6
    7. // 0 ~ range
    8. int range = to - from;
    9. int num = 1;
    10. // 求0~range需要几个2进制位
    11. while ((1 << num) - 1 < range) {
    12. num++;
    13. }
    14. // 我们一共需要num位
    15. // 最终的累加和,首先+0位上是1还是0,1位上是1还是0,2位上是1还是0...
    16. int ans = 0;
    17. do {
    18. ans = 0;
    19. for (int i = 0; i < num; i++) {
    20. ans |= (rand01(randomBox) << i);
    21. }
    22. } while (ans > range);
    23. return ans + from;
    24. }
  1. 测试类

    1. public static void main(String[] args) {
    2. System.out.println("测试开始");
    3. // Math.random() -> double -> [0,1)
    4. //
    5. int testTimes = 10000000;
    6. int count = 0;
    7. for (int i = 0; i < testTimes; i++) {
    8. if (Math.random() < 0.75) {
    9. count++;
    10. }
    11. }
    12. System.out.println((double) count / (double) testTimes);
    13. System.out.println("=========");
    14. // [0,1) -> [0,8)
    15. count = 0;
    16. for (int i = 0; i < testTimes; i++) {
    17. if (Math.random() * 8 < 5) {
    18. count++;
    19. }
    20. }
    21. System.out.println((double) count / (double) testTimes);
    22. System.out.println((double) 5 / (double) 8);
    23. int K = 9;
    24. // [0,K) -> [0,8]
    25. int[] counts = new int[9];
    26. for (int i = 0; i < testTimes; i++) {
    27. int ans = (int) (Math.random() * K); // [0,K-1]
    28. counts[ans]++;
    29. }
    30. for (int i = 0; i < K; i++) {
    31. System.out.println(i + "这个数,出现了 " + counts[i] + " 次");
    32. }
    33. System.out.println("=========");
    34. count = 0;
    35. double x = 0.17;
    36. for (int i = 0; i < testTimes; i++) {
    37. if (xToXPower2() < x) {
    38. count++;
    39. }
    40. }
    41. System.out.println((double) count / (double) testTimes);
    42. System.out.println((double) 1 - Math.pow((double) 1 - x, 2));
    43. System.out.println("==========");
    44. count = 0;
    45. for (int i = 0; i < testTimes; i++) {
    46. if (f2() == 0) {
    47. count++;
    48. }
    49. }
    50. System.out.println((double) count / (double) testTimes);
    51. System.out.println("==========");
    52. counts = new int[8];
    53. for (int i = 0; i < testTimes; i++) {
    54. int num = g();
    55. counts[num]++;
    56. }
    57. for (int i = 0; i < 8; i++) {
    58. System.out.println(i + "这个数,出现了 " + counts[i] + " 次");
    59. }
    60. }
  1. 返回[0,1)的一个小数
    任意的x,x属于[0,1),[0,x)范围上的数出现概率由原来的x调整成x平方
    1. public static double xToXPower2() {
    2. return Math.max(Math.random(), Math.random());
    3. }
  1. 使用 f1() 等概率返回 0 和 1
    1. public static int f2() {
    2. int ans = 0;
    3. do {
    4. ans = f1();
    5. } while (ans == 3);
    6. return ans < 3 ? 0 : 1;
    7. }
  1. 得到000 ~ 111 做到等概率 0 ~ 7等概率返回一个
    1. public static int f3() {
    2. return (f2() << 2) + (f2() << 1) + f2();
    3. }
  1. 0 ~ 6等概率返回一个

    1. public static int f4() {
    2. int ans = 0;
    3. do {
    4. ans = f3();
    5. } while (ans == 7);
    6. return ans;
    7. }
    8. public static int g() {
    9. return f4() + 1;
    10. }
  1. 你只能知道,x会以固定概率返回0和1,但是x的内容,你看不到!
    1. public static int x() {
    2. return Math.random() < 0.84 ? 0 : 1;
    3. }


y()函数 自己写 : 等概率返回0和1

  1. public static int y() {
  2. int ans = 0;
  3. do {
  4. ans = x();
  5. } while (ans == x());
  6. return ans;
  7. // ans = 0 1
  8. // ans = 1 0
  9. // 0 和 1 必然等概率
  10. }
  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 的概率 一样

  1. <a name="c35a5d61"></a>
  2. ## 对数器
  3. ```java
  4. public class Code03_Comp {
  5. public static void selectionSort(int[] arr) {
  6. if (arr == null || arr.length < 2) {
  7. return;
  8. }
  9. for (int i = 0; i < arr.length - 1; i++) {
  10. int minIndex = i;
  11. for (int j = i + 1; j < arr.length; j++) {
  12. if (arr[j] < arr[minIndex]) {
  13. minIndex = j;
  14. }
  15. }
  16. swap(arr, i, minIndex);
  17. }
  18. }
  19. public static void swap(int[] arr, int i, int j) {
  20. int tmp = arr[i];
  21. arr[i] = arr[j];
  22. arr[j] = tmp;
  23. }
  24. public static void insertionSort(int[] arr) {
  25. if (arr == null || arr.length < 2) {
  26. return;
  27. }
  28. for (int i = 1; i < arr.length; i++) { // 0 ~ i 做到有序
  29. for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
  30. swap(arr, j, j + 1);
  31. }
  32. }
  33. }
  34. // 返回一个数组arr,arr长度[0,maxLen-1],arr中的每个值[0,maxValue-1]
  35. public static int[] lenRandomValueRandom(int maxLen, int maxValue) {
  36. int len = (int) (Math.random() * maxLen);
  37. int[] ans = new int[len];
  38. for (int i = 0; i < len; i++) {
  39. ans[i] = (int) (Math.random() * maxValue);
  40. }
  41. return ans;
  42. }
  43. public static int[] copyArray(int[] arr) {
  44. int[] ans = new int[arr.length];
  45. for (int i = 0; i < arr.length; i++) {
  46. ans[i] = arr[i];
  47. }
  48. return ans;
  49. }
  50. // arr1和arr2一定等长
  51. public static boolean isSorted(int[] arr) {
  52. if (arr.length < 2) {
  53. return true;
  54. }
  55. int max = arr[0];
  56. for (int i = 1; i < arr.length; i++) {
  57. if (max > arr[i]) {
  58. return false;
  59. }
  60. max = Math.max(max, arr[i]);
  61. }
  62. return true;
  63. }
  64. public static void main(String[] args) {
  65. int maxLen = 5;
  66. int maxValue = 1000;
  67. int testTime = 10000;
  68. for (int i = 0; i < testTime; i++) {
  69. int[] arr1 = lenRandomValueRandom(maxLen, maxValue);
  70. int[] tmp = copyArray(arr1);
  71. selectionSort(arr1);
  72. if (!isSorted(arr1)) {
  73. for (int j = 0; j < tmp.length; j++) {
  74. System.out.print(tmp[j] + " ");
  75. }
  76. System.out.println();
  77. System.out.println("选择排序错了!");
  78. break;
  79. }
  80. }
  81. }
  82. }