一,問題描述
給定兩個整型數組,找出這兩個數組中的最大的公共元素。注意條件:①公共元素 ②最大的公共元素
比如:arr1={8,2,9,6,18,7,25,28} arr2={6,39,4,9,25,18,36,12}.假設 arr1 的長度為M,arr2的長度為N
這兩個數組的最大公共元素是:25
二,思路
①對 arr1 中的每個元素arr1[i],去 arr2 查找是否也存在 該元素,若存在則標記起來,因為它雖然是公共的,但不一定是最大的。
直到掃描完arr1中的所有元素,這種方式的時間復雜度為O(MN),空間復雜度為O(1)
②先對數組 arr1 排序,再對 arr2 排序。再定義兩個指針 i, j 分別指向 arr1 和 arr2 中的最后一個元素。比較這兩個數組中的最后一個元素,若相等則找到了所求的元素;若不相等,將指向較大的那個元素的指針 前移一位(減1)。
排序的時間復雜度為O(MlogM + NlogN),最壞情況下指針遍歷的時間復雜度為O(M+N),故總的時間復雜度為O(MlogM+NlogN)
③采用 堆 來實現
對兩個數組分別構造兩個大頂堆,若堆頂元素相同,則堆頂元素就是公共最大的元素。否則,刪除較大的那個堆頂元素,進行堆調整,繼續比較。
數組1建堆的時間復雜度為O(M),數組2建堆的時間復雜度為O(N)
一般對於刪除堆頂元素,進行堆調整而言,平均情況下的時間復雜度為O(logN)。故平均情況下,時間復雜度應該要比 方法② 中的小。
另外,可以直接在原數組上進行建堆操作,此時空間復雜度為O(1)
三,方法③代碼實現
核心代碼如下:
1 int len_1 = arr1.length - 1; 2 int len_2 = arr2.length - 1; 3 while(len_1 >= 0 && len_2 >=0) 4 { 5 int max1 = arr1[0];//獲取大頂堆的根元素,即數組中的最大值 6 int max2 = arr2[0]; 7 8 if(max1 > max2)//如果arr1的堆頂元素要大,則刪除arr1的堆頂元素 9 { 10 swap(arr1, 0, len_1);//delete arr1's root 11 percDown(arr1, 0, len_1);//進行堆調整, 刪除了最后一個元素,剛好堆調整的元素個數為 len_1 12 len_1--; 13 } 14 else if( max1 < max2)//如果arr2的堆頂元素要大,則刪除arr2的堆頂元素 15 { 16 swap(arr2, 0, len_2); 17 percDown(arr2, 0, len_2); 18 len_2--; 19 } 20 else//arr1的堆頂元素與 arr2的堆頂元素相等了. 21 return max1; 22 }
當建立了兩個大頂堆后,比較這兩個大頂堆的堆頂元素,誰大,則刪除誰。當然,刪除了堆頂元素之后,需要進行堆調整以保證堆的性質。
第10行的swap方法就表示 刪除堆頂元素,第11行的percDown方法表示 堆調整。
不斷地刪除堆頂元素,直到:①某個堆中的元素都被刪除了(此時 while循環條件不成立了)這表明:兩個數組中沒有公共元素。
②若兩個堆的堆頂元素相同了(第20-21行),則表明找到了最大公共元素。
算法的正確性說明:因為使用的是大頂堆。堆頂元素一定是當前數組中最大的元素,而通過比較兩個堆頂元素,若不相等,則刪除較大的堆頂元素,這樣總能保證:優先找到兩個堆中目前相同且最大的元素。
完整代碼實現:

//給定兩個整形數組,尋找這兩個數組的公有的且最大的元素 public class MaxCommonEle { public static int findCommMax(int[] arr1, int[] arr2) { if(arr1 == null || arr2 == null) throw new NullPointerException(); if(arr1.length == 0 || arr2.length == 0) throw new IllegalArgumentException(); //build heap--大頂堆 , time complex: O(M) for(int i = arr1.length / 2 -1; i >= 0; i--) percDown(arr1, i, arr1.length); //build heap, O(N) for(int i = arr2.length / 2 - 1; i >= 0; i--) percDown(arr2, i, arr2.length); int len_1 = arr1.length - 1; int len_2 = arr2.length - 1; while(len_1 >= 0 && len_2 >=0) { int max1 = arr1[0];//獲取大頂堆的根元素,即數組中的最大值 int max2 = arr2[0]; if(max1 > max2)//如果arr1的堆頂元素要大,則刪除arr1的堆頂元素 { swap(arr1, 0, len_1);//delete arr1's root percDown(arr1, 0, len_1);//進行堆調整, 刪除了最后一個元素,剛好堆調整的元素個數為 len_1 len_1--; } else if( max1 < max2)//如果arr2的堆頂元素要大,則刪除arr2的堆頂元素 { swap(arr2, 0, len_2); percDown(arr2, 0, len_2); len_2--; } else//arr1的堆頂元素與 arr2的堆頂元素相等了. return max1; } return -1;// -1 means there are no common element } private static void percDown(int[] arr, int i, int n) { int tmp; int child; // int k = leftChild(i); for(tmp = arr[i]; leftChild(i) < n; i = child) { child = leftChild(i); if(child != n-1 && arr[child] < arr[child+1]) child = child + 1; if(tmp < arr[child]) arr[i] = arr[child]; else break; } arr[i] = tmp; } private static int leftChild(int i){ return (i << 1 ) + 1; } private static void swap(int[] arr ,int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } //hapjin test public static void main(String[] args) { int[] arr1 = {8,2,9,6,18,7,25,28}; int[] arr2 = {6,39,4,9,25,18,36,12}; // int[] arr1 = {4,2,8}; // int[] arr2 = {10,4,6}; // int[] arr1 = {4,2,8}; // int[] arr2 = {5,7,9}; int res = findCommMax(arr1, arr2); System.out.println(res); } }
四,參考資料