编程之美【02】_续
2、题目:从很多无序的数中(姑且假定各不相等),选出其中最大的K个数。
解法五、
思路:
1、采用快排的分治思想,首先取无序数中一随机数,将无序数分割成1、2两部分,其中第1部分数为小于该随机数集合,第2部分数为大于该随机数集合;
2、若恰好第1部分的数(加该随机数)个数为K,则直接返回前K个数即为所求;若第1部分的数个数大于K,则递归对查找第1部分数中最大K个数;若第1部分数个数小于K,则递归查找第2部分数中最大的K – length个数,length为第1部分数个数;
该算法时间复杂度为O(n * log n),同快排一样,该排序算法依赖于随机数的选取,具有不稳定性。
计算:
1、对整个数组进行分割算法(简单可直接取第一个数为分割随机数);
2、若分割后,第1部分数个数为K – 1,则数组前K个数即为所求;若分割后,第1部分数个数小于K – 1,则递归查找对第2部分数中K – split个最大数;若分割后,第1部分数大于K – 1,则递归查找第1部分数中K个最大数;
代码:
private int partition(int[] arr, int begin, int end) { int key = begin, tmp; for(int low = begin, high = end; low < high; ) { if(arr[low] < arr[high]) { tmp = arr[low]; //swap arr[low] and arr[high] arr[low] = arr[high]; arr[high] = tmp; if(key == low) { low++; key = high; } else { high--; key = low; } } else { if(key == low) { high--; } else { low++; } } } return key; } public void topK4(int []arr, int begin, int end, int k) { int split = partition(arr, begin, end); if((split - begin + 1) == k) { return; } else if(split - begin + 1 < k) { topK4(arr, split + 1, end, k - (split - begin) - 1); } else { topK4(arr, 0, split - 1, k); } }
思路:采用多路归并的思路,先将无序数划分成M等分,并对每等分内的数进行排序(可简单采用快排,其时间复杂度为Q * log Q,其中Q为每等分内数的个数),然后采用败者树进行归并排序,从每等分数中取出最大数作为叶节点进行归并排序,将节点与父节点进行比较,loser留在父节点,winner可继续进行更高层级比较;
计算:
1、对无序数进行M等分的划分;
2、依次取出每等分数中最大数,作为败者树叶节点进行sift up,对winner节点进行输出,并从该胜者节点所在的等分中取出目前最大数替换winner,直到输出的winner节点个数为K;
public class LoseTree { private int[] branches; private int[] nodes; public void getNumFromBuffer(List<Integer> buffer, int bufferIndex) throws Exception { //get number if(buffer.size() == 0) { throw new Exception("the buffer is null"); } branches[bufferIndex] = buffer.remove(0); } public int competitionBranch(int branchIndex) { int father = (nodes.length + branchIndex - 1) / 2; while(father >= 0) { if(branches[nodes[father]] > branches[branchIndex]) { //swap the nodes[father] and the branchIndex int tmp = nodes[father]; nodes[father] = branchIndex; branchIndex = tmp; } if(father == 0) break; else father = (father - 1) / 2; } return branchIndex; } public int getBranch(int branchIndex) { return branches[branchIndex]; } public int init(List<Integer>[] buffers) throws Exception { branches = new int[buffers.length]; for(int i = 0; i < branches.length; i++) { getNumFromBuffer(buffers[i], i); } nodes = new int[branches.length - 1]; int max = -1; for(int i = 0; i < branches.length; i++) { if(max < branches[i]) { max = i; } } for(int i = 0; i < nodes.length; i++) { nodes[i] = max; } int winnerIndex = 0; for(int i = 0; i < nodes.length; i++) { winnerIndex = competitionBranch(i); } return winnerIndex; } public LoseTree() { } } @SuppressWarnings({ "unchecked", "rawtypes"}) public List<Integer>[] createBuffer(int[] arr, int k) { List[] buffers = arr.length % k == 0 ? new List[arr.length / k] : new List[arr.length / k + 1]; for(int i = 0; i < buffers.length; i++) { buffers[i] = new ArrayList(); } for(int i = 0; i < arr.length; i++) { buffers[i / k].add(arr[i]); } int addition = arr.length % k; if(addition != 0) { int end = arr.length / k; for(int i = addition; i < k; i++) { buffers[end].add(0); } } for(int i = 0; i < buffers.length; i++) { Collections.sort(buffers[i], Collections.reverseOrder()); } return buffers; } public List<Integer> topK(int []arr, int k) throws Exception { List<Integer>[] buffers = createBuffer(arr, k); if(buffers.length == 1) { return buffers[0]; } LoseTree loseTree = new LoseTree(); int winnerIndex = loseTree.init(buffers); List<Integer> topKArr = new ArrayList<Integer>(); for(int i = 0; i < k - 1; i++) { winnerIndex = loseTree.competitionBranch(winnerIndex); topKArr.add(loseTree.getBranch(winnerIndex)); loseTree.getNumFromBuffer(buffers[winnerIndex], winnerIndex); } winnerIndex = loseTree.competitionBranch(winnerIndex); topKArr.add(loseTree.getBranch(winnerIndex)); return topKArr; }