最近在做Android开发的时候遇到这样一个问题。
服务器给客户端推送应用的时候会发送一个apk的列表serviceApkList,其中有n个apk的包名(com.xxx),类似于这个样子的。
因为客户端要到服务器端进行下载,然后可能出现下载失败的情况(网络不好,url错误等情况),本地也有一个apk的列表clientApkList
于是要在客户端上做比较,serviceApkList与clientApkList中是否包含相同的包名。
那么,问题来了。用什么方法进行比较
既能实现目标,又能最大限度地优化时间复杂度呢?
最简单的方法莫过于冒泡排序,一个个比,
可以实现,但时间复杂度为o(n^2)
然后又想到优化冒泡排序,当比对相同时,删除clientApkList中的相同包名
可以实现,但时间复杂度依然为o(n^2/2)
在应用比较少的时候用这个方法可以,因为是字符串,所以不能用什么二分查找啊,归并排序啊。
什么,等等,在同学的提醒下,可以先对字符串进行归并排序,然后使用二分查找的方法。
理论上来说,归并排序的时间复杂度:O(n log 2n)+二分查找的时间复杂度:O(log n)
当应用比较多的时候用这个方法就可以大大减少时间复杂度了。
但是用什么规则进行比较呢?
因为Andoird apk的包名大多数以com开头,所以比较开头是没有意义的,所以可以从结尾开始比较。
Java中是提供归并排序的方法的,只要定义好比较的算法就可以了。
所以可以写出如下的比较算法:
private static class MyComparator implements Comparator<String> {
@Override
public int compare(String lhs, String rhs) {
if (rhs == null) {
throw new NullPointerException("the ComparableStr is null!");
}
if (lhs.equals(rhs)) {
return 0;
}
int length = lhs.length() - 1;
int anotherLength = rhs.length() - 1;
// 比较
int minLength = Math.min(length, anotherLength);
for (int i = minLength - 1; i >= 0; i--) {
int value = (int) (lhs.charAt(length--));
int antherValue = (int) (rhs.charAt(anotherLength--));
if (value != antherValue) {
return value - antherValue;
}
}
if (length != 0) {
return lhs.charAt(length);
} else if (anotherLength != 0) {
return rhs.charAt(anotherLength);
}
return 0;
}
}
然后就可以直接调用排序和比较方法了。
public static void sort(List<String> list, MyComparator comparator) {
Collections.sort(list, comparator);
}
public static boolean binaryFind(List<String> list, String target) {
boolean result = Collections.binarySearch(list, target) > 0;
return result;
}
经过不大严密的单元测试,如下测试:
List<String> list1 = new ArrayList<String>();
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = new String("bc");
String str4 = new String("etyys");
String str5 = new String("...");
String str6 = new String("com.abv");
String str7 = new String("com.abva");
String str8 = new String("com.vvv");
String str9 = new String("com.aav");
list1.add(str1);
list1.add(str2);
list1.add(str3);
list1.add(str4);
list1.add(str5);
list1.add(str6);
list1.add(str7);
list1.add(str8);
list1.add(str9);
sort(list1, new MyComparator());
assertTrue(binaryFind(list1, "bc"));
结果:
通过!
总结:很多工作上的问题可以通过以前学过的知识解决,关键不是遇到足够多的案例,而是遇到新问题的时候能不能通过知识的转移来解决。
这可能是区别码农跟攻城狮的标准之一。