对数器二分法异或
对数器
- 1.你想要测的方法a
- 2.实现复杂度不好但是容易实现的方法b
- 3.实现一个随机样本产生器
- 4.把方法a和方法b跑相同的随机样本,看看得到的结果是否一样
- 5.如果有一个随机样本使得比对结果不一致,打印样本进行人工干预,改对方法a和方法b
- 6.当样本数量很多时比对测试依然正确,可以确定方法a已经正确。
三个可以参考的对数器:
package duishuqi;
import java.util.List;
import java.util.ArrayList;
import java.util.Random;
public class util2 {
public static void generateTree(int size, int min, int max){
//对于当前的树结构,有几个选项
Random random = new Random();
List<Object> list = new ArrayList<>();
int rest;
if(size == 0)
rest = 0;
else{
rest = 1;
size--;
int val = random.nextInt(max - min + 1) + min;
list.add(val);
}
while(rest > 0 && size > 0){
//对于当前的rest节点,我们有两种选择
//选择一,在rest > 1 时 可以选择放入两个null
//选择二,在rest == 1时 必须让节点后继有人
if(rest > 1){
rest--;
size--;
int rand;
if(size == 1)
rand = random.nextInt(2 + 1);
else
rand = random.nextInt(3 + 1);
if(rand == 0){
//没有后继节点
list.add("null");
list.add("null");
size++;
}
else if(rand == 1){
//只有左节点
int val = random.nextInt(max - min + 1) + min;
list.add(val);
list.add("null");
rest++;
}
else if(rand == 2){
//只有右节点
int val = random.nextInt(max - min + 1) + min;
list.add("null");
list.add(val);
rest++;
}
else {
//左右都有
int left = random.nextInt(max - min + 1) + min;
int right = random.nextInt(max - min + 1) + min;
list.add(left);
list.add(right);
rest += 2;
size--;
}
}
else {
rest--;
size--;
int rand;
if(size == 1)
rand = random.nextInt(2 - 1 + 1) + 1;
else
rand = random.nextInt(3 - 1 + 1) + 1;
if(rand == 1){
//只有左节点
int val = random.nextInt(max - min + 1) + min;
list.add(val);
list.add("null");
rest++;
}
else if(rand == 2){
//只有右节点
int val = random.nextInt(max - min + 1) + min;
list.add("null");
list.add(val);
rest++;
}
else {
//左右都有
int left = random.nextInt(max - min + 1) + min;
int right = random.nextInt(max - min + 1) + min;
list.add(left);
list.add(right);
rest += 2;
size--;
}
}
}
System.out.println(list);
}
}
package duishuqi;
import java.util.HashSet;
import java.util.Random;
public class Util1 {
public static void generateNoReuseSingleLevelArrSpecial(int arrLen, int minVal, int maxVal, int target){
//ArrayList<Integer> list = new ArrayList<>();
HashSet<Integer> set = new HashSet<>();
HashSet<Integer> filter = new HashSet<>();
Random random = new Random();
for(int i = 0; i < arrLen - 2;){
//list.add(random.nextInt(maxVal - minVal + 1) + minVal);
int val = random.nextInt(maxVal - minVal + 1) + minVal;
if(!filter.contains(val)){
if(set.contains(val))
continue;
set.add(val);
filter.add(val);
filter.add(target - val);
i++;
}
}
//尝试构建最后的匹配值
int onePiece = random.nextInt(100000000 - (-100000000) + 1) + (-10000000);
if(set.contains(onePiece)){
set.add(target - onePiece);
}
else {
set.add(onePiece);
set.add(target - onePiece);
}
System.out.println(set);
//System.out.println(list);
}
public static void generateString(int len, char startC, char endC){
StringBuilder sb = new StringBuilder();
Random random = new Random();
for(int i = 0; i < len; i++){
int val = random.nextInt(endC - startC + 1) + startC;
char c = (char)val;
sb.append(c);
}
System.out.println("\"" + sb.toString() + "\"");
}
public static String getString(int len, char startC, char endC){
StringBuilder sb = new StringBuilder();
Random random = new Random();
for(int i = 0; i < len; i++){
int val = random.nextInt(endC - startC + 1) + startC;
char c = (char)val;
sb.append(c);
}
return sb.toString();
}
}
package duishuqi;
import java.util.List;
import java.util.ArrayList;
import java.util.Random;
public class util2 {
public static void generateTree(int size, int min, int max){
//对于当前的树结构,有几个选项
Random random = new Random();
List<Object> list = new ArrayList<>();
int rest;
if(size == 0)
rest = 0;
else{
rest = 1;
size--;
int val = random.nextInt(max - min + 1) + min;
list.add(val);
}
while(rest > 0 && size > 0){
//对于当前的rest节点,我们有两种选择
//选择一,在rest > 1 时 可以选择放入两个null
//选择二,在rest == 1时 必须让节点后继有人
if(rest > 1){
rest--;
size--;
int rand;
if(size == 1)
rand = random.nextInt(2 + 1);
else
rand = random.nextInt(3 + 1);
if(rand == 0){
//没有后继节点
list.add("null");
list.add("null");
size++;
}
else if(rand == 1){
//只有左节点
int val = random.nextInt(max - min + 1) + min;
list.add(val);
list.add("null");
rest++;
}
else if(rand == 2){
//只有右节点
int val = random.nextInt(max - min + 1) + min;
list.add("null");
list.add(val);
rest++;
}
else {
//左右都有
int left = random.nextInt(max - min + 1) + min;
int right = random.nextInt(max - min + 1) + min;
list.add(left);
list.add(right);
rest += 2;
size--;
}
}
else {
rest--;
size--;
int rand;
if(size == 1)
rand = random.nextInt(2 - 1 + 1) + 1;
else
rand = random.nextInt(3 - 1 + 1) + 1;
if(rand == 1){
//只有左节点
int val = random.nextInt(max - min + 1) + min;
list.add(val);
list.add("null");
rest++;
}
else if(rand == 2){
//只有右节点
int val = random.nextInt(max - min + 1) + min;
list.add("null");
list.add(val);
rest++;
}
else {
//左右都有
int left = random.nextInt(max - min + 1) + min;
int right = random.nextInt(max - min + 1) + min;
list.add(left);
list.add(right);
rest += 2;
size--;
}
}
}
System.out.println(list);
}
}
二分法
经常见到的类型是在一个有序数组上,开展二分搜索但有序真的是所有问题求解时使用二分的必要条件吗?只要能正确构建左右两侧的淘汰逻辑,你就可以二分。
认识二分法
1)在一个有序数组中,找某个数是否存在
2)在一个有序数组中,找>=某个数最左侧的位置
3)在一个有序数组中,找<=某个数最右侧的位置
4)局部最小值问题
有序数组中,找某个数是否存在
package dichotomy;
import java.util.Arrays;
public class BSExist {
public static boolean Exist(int[] sortedArr,int num){
if (sortedArr == null || sortedArr.length == 0) {
return false;
}
int L = 0;
int R = sortedArr.length - 1;
int mid = 0;
while(L<R){
//mid=(L+R)/2
//mid=L+(R-L)/2
//N/2-> N>>1
mid=L+((R-L)>>1);
if(sortedArr[mid]==num){
return true;
}else if(sortedArr[mid]>num){
R=mid-1;
}else {
L=mid+1;
}
}
return sortedArr[L]==num;
}
// for test
public static boolean test(int[] sortedArr, int num) {
for(int cur : sortedArr) {
if(cur == num) {
return true;
}
}
return false;
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 10;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(maxSize, maxValue);
Arrays.sort(arr);
int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
if (test(arr, value) != Exist(arr, value)) {
succeed = false;
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
}
}
//Nice!
上述的位运算可以提高速度,如果遇到N*2+1,就可以写成((N<<1)|1) 往左移一位,最低位是0,或上1,就补成1
有序数组中,找>=某个数最左侧的位置
package dichotomy;
public class BSNearLeft {
public static int nearestIndex(int[] arr, int value) {
int L=0;
int R=arr.length;
int index=-1;
while(L<=R){
int mid=L+((R-L)>>1);
if (arr[mid]>value) {
R=mid-1;
index=mid;
}else {
L=mid+1;
}
}
return index;
}
public static void main(String[] args) {
int [] arr={1,2,3,4,5,5,6,7,8};
int value=2;
System.out.println(nearestIndex(arr, value));
}
}
//2
有序数组中,找<=某个数最右侧的位置
package dichotomy;
public class BSNearRight {
public static int nearestIndex(int[] arr, int value) {
int L=0;
int R=arr.length;
int index=-1;
while(L<=R){
int mid=L+((R-L)>>1);
if (arr[mid]<=value) {
L=mid+1;
index=mid;
}else {
R=mid-1;
}
}
return index;
}
public static void main(String[] args) {
int [] arr={1,2,3,4,5,5,6,7,8};
int value=2;
System.out.println(nearestIndex(arr, value));
}
}
//1
局部最小值问题
下列代码返回的是下标
package dichotomy;
public class BSAwesome {
public static int getLessIndex(int[] arr) {
if (arr==null||arr.length==0) {
return -1;
}
if (arr.length==1||arr[0]<arr[1]) {
return 0;
}
if (arr[arr.length-1]<arr[arr.length-2]) {
return arr.length-1;
}
int Left=1;
int Right=arr.length-2;
int mid=0;
while(Left<Right){
mid=(Left+Right)/2;
if(arr[mid]>arr[mid-1]){
Right=mid-1;
}
else if (arr[mid]>arr[mid+1]) {
Left=mid+1;
}
else {
return mid;
}
}
return Left;
}
public static void main(String[] args) {
int [] arr={3,2,5,4,3,5,5,6,7,8};
System.out.println(getLessIndex(arr));
}
}
//4
认识异或运算
异或运算:相同为0,不同为1
同或运算:相同以1,不同为0
能长时间记住的概率接近0%
所以,异或运算就记成无进位相加!
异或运算的性质
1)0异或NN , N异或N0,^
2)异或运算满足交换律和结合率
上面的两个性质用无进位相加来理解就非常的容易
如何不用额外变量交换两个数
package dichotomy;
public class swap {
public static void main(String[] args) {
//单个数字转换
int a=16;
int b=163;
a=a^b;
b=a^b;
a=a^b;
System.out.println(a);
System.out.println(b);
//数组转换
int[] arr = {3,1,100};
swap(arr, 0, 2);
System.out.println(arr[0]+" "+arr[1]+" "+arr[2]);
}
public static void swap(int[] arr,int i,int j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
}
上述面试建议不使用,炫技太多,巨无聊,容易给人装逼的感觉
数组中出现一种奇数次的数
一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
^n+1:取反加1,就是相当于把最后的一个1保留起来,其它都是不一样的
数组中出现两种奇数次的数
一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
package dichotomy;
public class EvenTimesOddTimes {
// arr中,只有一种数,出现奇数次
public static void printOddTimesNum1(int[] arr) {
int err=0;
for(int i=0;i<arr.length;i++){
err^=arr[i];
}
System.out.println(err);
}
// arr中,有两种数,出现奇数次
public static void printOddTimesNum2(int[] arr) {
int err=0;
for(int i=0;i<arr.length;i++){
err^=arr[i];
}
// a 和 b是两种数
// eor != 0
// eor最右侧的1,提取出来
// eor : 00110010110111000
// rightOne :00000000000001000
int rightone=err&(~err+1);//提取最右的1;
int onlyone=0;
for(int i=0;i<arr.length;i++){
if((arr[i]&rightone)==0){//把最右一位是1的找出来,所有异或就可以找出其中一个
onlyone^=arr[i];
}
}
System.out.println(onlyone+" "+(err^onlyone));
}
//二进制上面有多少个1
public static int bit1counts(int N){
int count=0;
while(N!=0){
int rightone=N&(~N+1);
count++;
N^=rightone;
}
return count;
}
public static void main(String[] args) {
int[] arr1 = { 3, 3, 2, 3, 1, 1, 1, 3, 1, 1, 1 };
printOddTimesNum1(arr1);
int[] arr2 = { 4, 3, 4, 2, 2, 2, 4, 1, 1, 1, 3, 3, 1, 1, 1, 4, 2, 2 };
printOddTimesNum2(arr2);
System.out.println(bit1counts(5));
}
}
/*
2
2 3
2