题目:假如一个订单的编码规则是AAAAOrder2020-0000001,AAAAOrder2020-0000002,后面的数字是自增长,如果订单号码达到AAAAOrder2020-1000000(一百万)时,数据库中应该有100万条数据,此时我随机删除两条数据(物理删除,且不考虑备份和日志),请问怎么找到删除的数据编码?给出解题思路,答案要求在1秒内运行得到。
*今天看到群里有人问了一道这样的题目,想了想,给出了自己的答案。两种方法:方法一:比较愚蠢;方法二:二分查找法。觉得麻烦直接看方法二,会好理解很多。*
1. 方法一(愚蠢的方法)
package com.study;
import java.util.ArrayList;
import java.util.Scanner;
public class testnumber {
public static void main(String[] args) {
ArrayList<String> num = new ArrayList<>();
System.out.print("设置总数据量:");
int total = new Scanner(System.in).nextInt();
System.out.println("输入打算删除的两个数x,y");//模拟随机删除两条数据,0<x,y<=total,x!=y
Scanner input = new Scanner(System.in);
int x = input.nextInt();
int y = input.nextInt();
int[] arr = new int[total-2];
for(int i=0;i<(total-2);i++){//此处可以设置成存一百万条数据,用于模拟从数据库过来的
String str = "a-"+(i+1);//类似格式“AAAAOrde-0000001”
num.add(str);
}
if(x > y){
int temp = y;
y = x;
x = temp;
}
//删除指定的两个数
num.remove(x-1);
num.remove(y-2);
long start = System.currentTimeMillis();
for(int i=0;i<(total-2);i++){
arr[i] = Integer.parseInt(num.get(i).replace("a-",""));
}
find(arr,0,0);
long end = System.currentTimeMillis();
System.out.println("消耗时间:"+(end-start)+"毫秒");
}
public static void find(int[] arr,int start,int flag){
if(flag == 1){
for(;start<arr.length;start++){
if(arr[start] - start == 3){
System.out.println("删除的编号:"+(arr[start]-1));
return;
}
}
}else{
for(;start<arr.length;start++){
if(arr[start] - start == 3){
System.out.println("删除的编号:"+ (arr[start]-1)+"和"+(arr[start]-2));
return;
}else if(arr[start] - start == 2){
flag = 1;
System.out.println("删除的编号:"+(arr[start]-1));
find(arr,start+1,flag);
return;//此处一定要有return,不然还会继续执行代码
}
}
}
}
}
运行结果:
2. 方法二:二分查找法
package com.study;
import java.util.ArrayList;
import java.util.Scanner;
public class testnumber {
public static void main(String[] args) {
ArrayList<String> num = new ArrayList<>();
System.out.print("设置总数据量:");
int total = new Scanner(System.in).nextInt();
System.out.println("输入打算删除的两个数x,y");
Scanner input = new Scanner(System.in);
int x = input.nextInt();
int y = input.nextInt();
int[] arr = new int[total - 2];
for (int i = 0; i < total; i++) {//此处可以设置成存一百万条数据,用于模拟从数据库度过来的
String str = "a-" + (i + 1);//类似格式“AAAAOrde-0000001”
num.add(str);
}
if (x > y) {
int temp = y;
y = x;
x = temp;
}
//删除指定的两个数
num.remove(x - 1);
num.remove(y - 2);
long start = System.currentTimeMillis();
for (int i = 0; i < (total - 2); i++) {
arr[i] = Integer.parseInt(num.get(i).replace("a-", ""));
}
int flag =0;//用于标记找到了几个数,当flag=2时,退出循环,已经找到两个数。可以扩展到flag个数,删除flag个整数,然后进行查找
for(int i=1;i<=total && flag!=2;i++){
int res = find3(arr, i, flag);
if(res != -10){
System.out.println("删除的数字为:"+res);
}
}
long end = System.currentTimeMillis();
System.out.println("消耗时间:" + (end - start) + "毫秒");
}
public static int find3(int[] a, int i, int flag) {
int start = 0;
int end = a.length;
while (start <= end) {
int mid = (start + end) / 2;
if (i < a[mid]) {
end = mid - 1;
} else if (i > a[mid]) {
start = mid + 1;
} else {
return -10;//说明这个数存在
}
}
flag++;
return i;
}
}
明显方法二更好理解,也更简单。这里通过一个逆向思维来使用二分查找法。原本二分查找法是用于在一个有序数组中,查找某个值,查到则返回对应数组角标;若查不到则返回-1。但是这里我们反其道而行之:题目的意思是找到缺失的两个数字,那么只需要把查不到的情况,修改成返回这个缺少值,那么就可以找到我们所需要的缺少值。从而循环一百万次,我们在这里用flag作为标志,当flag==2时,便退出for循环。这里可以推广下去,你缺少x个数字,想把这x个数字找出来,那么对应把flag修改成x即可。