最近在做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"));
結果:
通過!
總結:很多工作上的問題可以通過以前學過的知識解決,關鍵不是遇到足夠多的案例,而是遇到新問題的時候能不能通過知識的轉移來解決。
這可能是區別碼農跟攻城獅的標準之一。