-
数据结构分为物理结构与逻辑结构:
逻辑结构: 有序表,链表,二叉树等
物理结构: 紧密结构(连续结构),跳转结构
ps: 所有的逻辑结构都是基于物理结构所形成的(单一或组合。) -
基本数据结构:
数组:便于寻址,不便于增加和删除
链表:便于增加和删除,不便于寻址
稍微特殊一点的是,当数组很大时,可能会出现少量的跳转结构。 比如一亿长度的数组,1000万紧密存放在一起,然后尾指针指向下一个块的地址。
- 前缀和解法 --> 针对范围和问题
给定一个数组,任意给出左右边界的值,,求以O(1)的时间复杂度返回累加和。
(1)二维数组
(2)前缀和 --> 一维数组
public class Code01_PreSum {
public static class RangeSum1 {
private int[] arr;
public RangeSum1(int[] array) {
arr = array;
}
public int rangeSum(int L, int R) {
int sum = 0;
for (int i = L; i <= R; i++) {
sum += arr[i];
}
return sum;
}
}
public static class RangeSum2 {
private int[] preSum;
public RangeSum2(int[] array) {
int N = array.length;
preSum = new int[N];
preSum[0] = array[0];
for (int i = 1; i < N; i++) {
preSum[i] = preSum[i - 1] + array[i];
}
}
public int rangeSum(int L, int R) {
return L == 0 ? preSum[R] : preSum[R] - preSum[L - 1];
}
}
}
随机函数
-
JAVA中的随机函数 -> Math.random() --> 返回double类型 [0, 1)区间 等概率返回 (因为小数有精度,所以小数不是无穷的)
–> [0, 1) --> [0, 8) 注意是double类型
Math.random() * 8–>double [0, 1) --> (int)(Math.random() * K) --> int [0, K) --> int [0, K-1]
-
通过随机函数,可以得到任意范围的值 e.g. [16, 54] ([0, 1) * 39 + 16 )。
Math.random * K + t -
通过随机函数,实现不等概率返回,对于生成的任意数值,使得出现在[0 ,x]出现的概率的
3.1 使得 [0, x) 的出现的概率为 x^2
3.2 使得[0, x)出现的概率为 1-(1 - x)^2
答: Math.min(Math.random(), Math.random()); -
随机函数面试题
4.1 从 1 ~ 5 随机 f(x) 到 1 ~ 7 g(x) 随机 等概率
(1)1 2 --> 0; 3 重做; 4 5 --> 1 >>>>>>>>> 实现等概率01生成器
(2)1 ~ 7共7个数字, 三位二进制可以满足(8种组合),对二进制进行移位 (x<<3 + x<<2 + x<<1) 属于范围 [0, 7]
(3)封装函数, do { } while (x == 0) ----> 0重做,其余输出,从而完成 1 - 7等概率随机// 1~5随机 [1, 5] --> int [1, 6) --> int [0, 5) + 1 public static int f() { return (int) (Math.random() * 5 + 1); } // 等概率01生成器 public static int _01Generator() { int ans; do { ans = f(); } while (ans == 3); return ans < 3 ? 0 : 1; } public static int g() { int ans; do { // 小心这里,记不清顺序 就乖乖把 括号加上,保证运算顺序 ans = (_01Generator() << 2) + (_01Generator() << 1) + (_01Generator() << 0); } while (ans == 0); return ans; }
OR
(3)在第二步之后,将其变成 [0, 6]的等概率生成器(7重做), 然后 封装函数,使其返回值 +1 ,就变成了 [1, 7]。// return [0, 6] public static int g1() { int ans = 0; do { ans = (_01Generator() << 2) + (_01Generator() << 1) + (_01Generator() << 0); } while (ans == 7); // return [0,7] ----> [0, 6] return ans; } public static int g2(){ return g1() + 1; }
4.2 从 a ~ b 随机到 c ~ d 随机 - 等概率
推荐使用后一种(3),基本方法相同。但latter更有普适性。(比如 3-19生成器 变为 128-150生成器,只需将[128,150] —> [0, 22] + 128,从而不需要生成过多二进制位)
构造01等概率发生器(奇数重新生成,偶数直接对半分),寻找包含范围的二进制位数,调整对应的生成随机数范围
4.3 从01不等概率到01等概率随机
0 0 --> 重做
1 1 --> 重做
1 0 --> 50% --> 返回 1
0 1 --> 50% --> 返回 0// 01不等概率生成器 0的概率为 P = 0.8, 1的概率为 0.2 public static int x(){ return Math.random() > 0.8 ? 0: 1; } // 转换为01等概率 public static int y(){ int ans; do{ ans = x(); }while( ans == x()); return ans; }
根本思路: 重点:** 01等概率发生器 ** + do-while循环 使得部分重做(重新生成), 针对01进行二进制移位 (看看需要几个二进制位)+ do-while, over
对数器 - 生成随机样本自己做比对的机器
对数器是干什么用的?
对数器是用来调bug的,对数器让你不需要依赖线上的测试,自己就可以调试代码。 可以随意控制样本大小。 大样本随机。
(factor 1)比对器 (两个方向 : 一个是生成正确结果,然后与测试函数进行比较; 另一个是将测试函数的结果作为参数,进行正误校验)
(factor 2) 生成随机数组生成器,创建数组,其中random length & random value
(factor 3 optional)数组拷贝,从而将实例备份,当出现错误实例时,输出错误实例
当出现错误实例后,将错误实例代入代码中,就可以调BUG了。
举例:
package class02;
import class01.BasicSort;
public class Comp {
// 返回int[] arr, arr长度在[0, maxLen - 1], arr中的值在[0, maxValue - 1]
public static int[] lenRandomValueRandom(int maxLen, int maxValue) {
//长度随机
int len = (int) (Math.random() * maxLen);
int[] arr = new int[len];
// 给数组中每一个元素赋随机值
for (int i = 0; i < len; i++) {
arr[i] = (int) (Math.random() * maxValue);
}
// 返回数组,这个数组的长度和值都是随机的
return arr;
}
// 传入一个数组, 返回一个一模一样的新数组
public static int[] copyArray(int[] oldArr) {
int[] newArr = new int[oldArr.length];
for (int i = 0; i < oldArr.length; i++) {
newArr[i] = oldArr[i];
}
return newArr;
}
// 暴力方法,验证一个数组是有序的, 这里验证升序
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 = 50;
int maxValue = 1000;
int testTimes = 10000;
// 开始 **实测**
for (int i = 0; i < testTimes; i++) {
// 首先创建一个 随机数组 ----》 随即长度 + 随机值
int[] arr1 = lenRandomValueRandom(maxLen, maxValue);
// 克隆出一个tmp备份, 与arr1相同,但是两个内存区域。 arr1出错时, 打印备份
int[] tmp = copyArray(arr1);
BasicSort.selectSort(arr1);
if(!isSorted(arr1)) {
// 如果没有生成 下列语句 那么说明 结果一定正确 ----》 REASON: 大样本随机都对
System.out.println("排序出错了");
// 如果出错了,输出 错误的 实例 -----》 使用错误实例 来 调bug
// 不要使用 这个 System.out.println(tmp);, 这个打印的是toString方法,[I@77a2aa4a
for (int j = 0; j < tmp.length; j++) {
System.out.print(tmp[j] + " ");
}
System.out.println();
// 加上这句话,出错一个之后就跳出,从而对单个错误实例进行检验
break;
}
}
}
}