一個面試題html
一個數組 找出這樣的三個元素 它們的和與目標值最接近
如
原始數組: [15, 27, 31, 33, 39, 44, 50, 57, 86, 91]
目標值: 98
這樣的三個元素:15,33,50 (15+33+50=98)面試
沒有想到什麼好的算法 能夠快捷的找到這樣的三個元素
只想到了窮舉法 即算法
找出全部的任意三元素 C(數組長度,3)數組
放到優先隊列中 按三個元素的和與目標值的差值(絕對值)進行排序測試
第一個便是與目標值最接近的三元素code
// 獲得全部的三元素組合列表 如["1,2,3", "4,5,6" , ...] List<String> allUniqueThreeElements = getAllUniqueThreeElements(a,3); // 三元素列表轉成對象 對象中提供了這樣的方法:getDiff (計算三元素的和與目標值的差值(絕對值)) List<ThreeElements> threeElementsList = new ArrayList<>(); for (String s : allUniqueThreeElements) { elementsList.add(new ThreeElements(s)); } // 構造優先隊列 按三元素的和與目標值的差值(絕對值)進行排序 優先隊列默認大小爲三元素組合列表大小 PriorityQueue<Elements> pq = new PriorityQueue<>(threeElementsList.size(),comparingInt(o -> o.getDiff(target))); // 將三元素組合對象 逐一放到優先隊列中 for (ThreeElements e : threeElementsList) { pq.offer(e); } ThreeElements poll = pq.poll(); // 優先隊列中 第一個即爲要找的三元素
即C(n,m)htm
如 數組 [1,2,3,4,5]
找出全部的三
元素組合對象
index | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
copy1 | 1 | 2 | 3 | 4 | 5 |
copy2 | 1 | 2 | 3 | 4 | 5 |
copy3 | 1 | 2 | 3 | 4 | 5 |
至關於將同一數組複製三份 每一份中 取一個元素 不重複便可排序
這樣的取法遞歸
copy1 | copy2 | copy3 | - |
---|---|---|---|
0 | 1 | 2 | 1,2,3 |
0 | 1 | 3 | 1,2,4 |
0 | 1 | 4 | 1,2,5 |
0 | 1 | 5 | 超過最大索引值 從前一位開始遞增 同時逐個更新後面的值 即後一位的值 = 前一位 + 1 |
0 | 2 | 3 | 1,3,4 |
0 | 2 | 4 | 1,3,5 |
0 | 2 | 5 | 超過最大索引值 從前一位開始遞增 |
0 | 3 | 4 | 1,4,5 |
0 | 3 | 5 | 超過最大索引值 從前一位開始遞增 |
0 | 4 | 5 | 超過最大索引值 從更一位開始遞增 |
1 | 2 | 3 | 2,3,4 |
1 | 2 | 4 | 2,3,5 |
1 | 2 | 5 | 超過最大索引值 從前一位開始遞增 |
1 | 3 | 4 | 2,4,5 |
1 | 3 | 5 | 超過最大索引值 從前一位開始遞增 |
1 | 4 | 5 | 超過最大索引值 從更一位開始遞增 |
2 | 3 | 4 | 3,4,5 |
2 | 3 | 5 | 超過最大索引值 從前一位開始遞增 |
2 | 4 | 5 | 超過最大索引值 從更一位開始遞增 |
3 | 4 | 5 | 超過最大索引值 且此時不存在更前一位了 退出 |
/** * * @param indexArray 索引數組 * @param maxIndexValue 最大索引值 * @param startIndex indexArray中今後位開始遞增 * @return */ private boolean next(final int[] indexArray, final int maxIndexValue, int startIndex){ // System.out.println("indexArray: "+Arrays.toString(indexArray)); // System.out.println("startIndex: "+startIndex); indexArray[startIndex]++; // 今後位開始遞增 if(indexArray[startIndex] > maxIndexValue){ // 超過最大索引值 從前一位開始遞增 return next(indexArray,maxIndexValue,startIndex-1); }else{ // 同時逐個累加以後的元素 後一位的值 = 前一位+1 for (int i = startIndex+1; i < indexArray.length; i++) { indexArray[i] = indexArray[i-1]+1; if(indexArray[i] > maxIndexValue){ if(startIndex -1 < 0){ // 若是是從第一位開始遞增的 即不存在更前一位了 則退出遞歸 return false; } return next(indexArray,maxIndexValue,startIndex-1); } } return true; } }
測試代碼
@Test public void test_next(){ // 測試從一個數組中獲得全部的三元素組合 int[] a = {1, 2, 3, 4, 5}; // 數組 int maxIndexValue = a.length-1; // 最大索引值 int[] indexArray = {0,1,2}; // 初始化索引數組 int startIndex = indexArray.length-1; // 從末位開始遞增 // 驗證 [0,1,2] --> [0,1,3] boolean next = next(indexArray, maxIndexValue, startIndex); assertTrue(next); assertArrayEquals(new int[]{0,1,3}, indexArray); // 驗證 [0,1,4] --> [0,2,3] indexArray = new int[]{0, 1, 4}; next = next(indexArray, maxIndexValue, startIndex); assertTrue(next); assertArrayEquals(new int[]{0,2,3}, indexArray); // 驗證 [0,3,4] --> [1,2,3] indexArray = new int[]{0, 3, 4}; next = next(indexArray, maxIndexValue, startIndex); assertTrue(next); assertArrayEquals(new int[]{1,2,3}, indexArray); // 驗證 [2,3,4] --> X indexArray = new int[]{2, 3, 4}; next = next(indexArray, maxIndexValue, startIndex); assertFalse(next); }
當驗證其餘一些極端狀況的時候 如從一個數組中獲得全部一個元素的組合 即C(n,1) 測試沒有經過
@Test public void test_next_and_only_choose_one_element(){ // 測試一些更極端的狀況 如 一個數組中選出全部1個元素的組合(C(n,1)) final int[] a = {1, 2, 3, 4, 5}; int maxIndexValue = a.length-1; int[] indexArray = {0}; int startIndex = indexArray.length-1; //驗證 [0] -> [1] boolean next = next(indexArray, maxIndexValue, startIndex); assertTrue(next); assertArrayEquals(new int[]{1},indexArray); System.out.println(); // 驗證 [4] -> X indexArray = new int[]{4}; next = next(indexArray, maxIndexValue, startIndex); assertFalse(next); }
修復代碼 將判斷最後沒有下一組的代碼給提取出來了
private boolean next(final int[] indexArray, final int maxIndexValue, int startIndex){ if(startIndex < 0){ // 若是不存在更前一位了 則退出遞歸 return false; } indexArray[startIndex]++; // 今後位開始遞增 if(indexArray[startIndex] > maxIndexValue){ // 超過最大索引值 從前一位開始遞增 return next(indexArray,maxIndexValue,startIndex-1); }else{ // 同時逐個累加以後的元素 後一位的值 = 前一位+1 for (int i = startIndex+1; i < indexArray.length; i++) { indexArray[i] = indexArray[i-1]+1; if(indexArray[i] > maxIndexValue){ return next(indexArray,maxIndexValue,startIndex-1); } } return true; } }