0.999的循环等于1
真空不是没有东西,是充满了东西
前言
在一次面试中,面试官让我做这题,我还以为他有什么更好的方法。他否定了我的想法,我不服,于是找他要的的解法,测试结果,他的方法消耗的时间是最多的。47w纳秒左右,然而网上找的回溯法才32w纳秒左右,而我做的只需3000纳秒。可惜我还是没有找到工作。
我做的
一个个试,用判断代替循环,用空间节省时间。应该没bug,平均用时3000纳秒
package math;
import java.util.ArrayList;
import java.util.List;
/**
* 1.1.2.3034 一开始就判断尾部是否<255,成立再判断是否0开头。
* 1.1.23.034 移动了最后这个点,就要判断这个点的两边的数据的合理性(突然发现你那段代码有这个bug,如果一段前面可以为0,那每个字符串都应该是12位)。
* 1.1.230.34 移动了最后这个点,就要判断这个点的两边的数据的合理性。
* 1.12.3.034 最后的点后移了两次,再移动就是四位数了,不合理,这时候开始移动第二个点,第三个点紧跟第二个点的后面。判断第二个点前,第三个点后的数的合理性。
* 1.12.30.34 然后再动第三个点,再判断移动点前后数据合理性。
* 1.12.303.4 然后再动第三个点,再判断移动点前后数据合理性。
* 1.123.0.34 移动第二个点,同时第三个点紧跟第三点后面,判断第二点前,第三点后的数合理性。
*
* @author zhan几次手动查找之后我发现了一些规律,有部分规律比较复杂,暂时没有用程序表达出来!
*
*/
public class OneByOneTest {
public static void main(String[] args) {
long startTime = System.nanoTime();
String ip = "0650114";
int length = ip.length();
//随便校验一下
if (length < 4 || length > 12) {
System.out.println("输入不合法");
} else {
long endTime = System.nanoTime();
System.out.println(search(ip, length).toString() + " 共用时间: " + (endTime - startTime)+"纳秒");
}
}
//另外定义一个方法实现逻辑
public static List<StringBuilder> search(String ip, int length) {
//转成StringBuilder效率高一些
StringBuilder newIp = new StringBuilder(ip);
//3点分割,定义最后一个点的位置,一定是从第a处空隙开始的
int a = 3;
//因为最后一段不可能超过三位数,如果给的字符串比较长,最后一点就从倒数第三个空隙开始
if (length - 3 > 3) {
a = length - 3;
//如果倒数第三、二个数是0,那么第三个点肯定不能在0前面的
while (a < length - 1 && Integer.valueOf(ip.substring(a, a + 1)) == 0) {
a++;
}
}
List<StringBuilder> ipList = new ArrayList<>();
for (int i = 1; i < 4 && i < length - 2; i++) {
//判断一下,如果第一个数时0,那么第一个点只能移动一次
String l = newIp.substring(0, 1);
if("0".equals(l) && i>1) {
break;
}
//判断一下,第一段的数值不能大于255
int m = Integer.valueOf(newIp.substring(0, i));
if(m>255) {
break;
}
//第二段是从第一段的后面以为开始尝试的
for (int j = i + 1; j < i + 4 && j < length - 1; j++) {
//判断一下,第二段的数值不能大于255,且第二段是2位以上的时候,开头不能是零
String e = newIp.substring(i, i + 1);
String h = newIp.substring(i, j);
int f = Integer.valueOf(h);
if (f > 255) {
continue;
}
if (!(e.equals(h)) && "0".equals(e)) {
continue;
}
//最后一段的开始位置一定在第二段的后面
for (int k = j >= a ? j + 1 : a; k < length; k++) {
//判断一下,最后一段被截出来后,两位数以上时,第一位不能是0,是0就把点后移
if (k < length - 1 && Integer.valueOf(newIp.substring(k, k + 1)) == 0) {
k++;
continue;
}
//判断一下倒数第二段的合理性和最后一段的值不大于255
int b = Integer.valueOf(newIp.substring(k, length));
String c = newIp.substring(j, j + 1);
String g = newIp.substring(j, k);
int d = Integer.valueOf(g);
if (b <= 255 && d <= 255) {
if (!(c.equals(g)) && "0".equals(c)) {
continue;
}
//合理则保存在集合中
ipList.add(new StringBuilder(ip).insert(k, ".").insert(j, ".").insert(i, "."));
}
}
}
}
return ipList;
}
}
面试官做的
用排列组合出所有的可能性,由很多冗余的for循环,然后遍历,并判断合理性。此代码有bug,而且平均要用47w纳秒
package math;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Test4 {
private static Map<Integer, List<IpPoint>> data = new HashMap<>();
static {
for (int a = 1 ; a < 4 ; a++){
for (int b = 1 ; b < 4 ; b++){
for (int c = 1 ; c < 4 ; c++){
for (int d = 1 ; d < 4 ; d++){
int length = a+b+c+d;
IpPoint ipPoint = new IpPoint(a,b,c,d);
if(data.containsKey(length)){
data.get(length).add(ipPoint);
}else{
List<IpPoint> list = new ArrayList<>();
list.add(ipPoint);
data.put(length,list);
}
}
}
}
}
}
public static void main(String[] args) {
long startTime = System.nanoTime();
String ip = "0650114";
List<String> ipList = new ArrayList<>();
List<IpPoint> list = data.get(ip.length());
if(list != null && list.size() > 0){
for (IpPoint ipPoint : list){
int a = Integer.valueOf(ip.substring(0,ipPoint.getA()));
if(a < 0 || a > 255){
continue;
}
int b = Integer.valueOf(ip.substring(ipPoint.getA(),ipPoint.getA()+ipPoint.getB()));
if(b < 0 || b > 255){
continue;
}
int c = Integer.valueOf(ip.substring(ipPoint.getA()+ipPoint.getB(),ipPoint.getA()+ipPoint.getB()+ipPoint.getC()));
if(c < 0 || c > 255){
continue;
}
int d = Integer.valueOf(ip.substring(ipPoint.getA()+ipPoint.getB()+ipPoint.getC(),ipPoint.getA()+ipPoint.getB()+ipPoint.getC()+ipPoint.getD()));
if(d < 0 || d > 255){
continue;
}
ipList.add(a+"."+b+"."+c+"."+d);
}
}
long endTime = System.nanoTime();
System.out.println(ipList+" Time taken:"+(endTime-startTime)+"纳秒");
}
/**
* ip组成
*/
public static class IpPoint{
private int a;
private int b;
private int c;
private int d;
public IpPoint(int a, int b, int c, int d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public int getA() {
return a;
}
public int getB() {
return b;
}
public int getC() {
return c;
}
public int getD() {
return d;
}
@Override
public String toString() {
return "IpPoint{" +
"a=" + a +
", b=" + b +
", c=" + c +
", d=" + d +
'}';
}
}
}
网上找的
回溯法,此代码比较严谨,平均用时30w纳米哦
package math;
import java.util.ArrayList;
import java.util.List;
/**
* 回溯法求数字回ip
* @author zhan
*
*/
public class Solution {
public static void main(String[] args) {
long startTime = System.nanoTime();
List<String> list = restoreIpAddresses("0650114");
long endTime = System.nanoTime();
System.out.println(list+"耗时:"+(endTime-startTime));
}
public static List<String> restoreIpAddresses(String s) {
if (null == s || s.length() == 0) {
return new ArrayList<String>();
}
List<String> list = new ArrayList<String>();
method(s, list, new ArrayList<String>(), 0, 4);
return list;
}
private static void method(String s, List<String> list, List<String> tmp, int index, int count) {
if(index == s.length() && tmp.size() == 4){
list.add(join(tmp));
return;
}
if (count <= 0 || tmp.size() >= 4) {
return;
}
for (int i = index; i < index + 3 && i < s.length(); i++) {
if (isNumValid(s, index, i + 1)) {
String sub = s.substring(index, i + 1);
tmp.add(sub);
method(s, list, tmp, i + 1, count - 1);
tmp.remove(tmp.size() - 1);
}
}
}
private static String join(List<String> tmp) {
StringBuilder sb = new StringBuilder();
sb.append(tmp.get(0)).append(".").append(tmp.get(1)).append(".").append(tmp.get(2)).append(".").append(tmp.get(3));
return sb.toString();
}
private static boolean isNumValid(String s, int begin, int end) {
if (begin > end || end - begin > 3) {
return false;
}
if(s.startsWith("0", begin) && end - begin > 1){
return false;
}
Integer value = Integer.valueOf(s.substring(begin, end));
return value >= 0 && value <= 255;
}
}
后话
解决算术问题最好的方式是找规律,或者用公式求解。学编程的时候求1到100的求和你们是不是用for循环,偏偏这种方法是最慢的,明明就有公式,为什么不用?然而,有些问题不是用我们学的数学去限定我们的思路,一定要知道计算机是怎么运行的,从而明白它会做多少的无用功。从而估算算法的性能。我是光信息科学技术专业的,我自豪。