從一組無序的整數中找出前N大的數

    經常會遇見這樣的問題,如何從一組序列中找出最大的N個數,比如從一個班級的成績中找出總成績的前三名。可能會有一個比較簡單的做法就是先將這組序列排序,然後前N個值自然而然就得到了。這對於比較少的序列,是可行的,比如前面說的一個班的前三名,但是對於數據量特別龐大的現實應用中,就不太現實了,例如我們經常用到的搜索引擎,它應該不會對她搜到的所有的頁面先進行排序然後再返回前N個搜索結果吧,這樣的話花費在排序上的時間消耗的也太大了。


    對於這樣一個問題應該怎麼做呢,可以使用一種數據結構--堆。假如問題是求一個很大的整數序列中的前M個,我們就可以建立一個最多包含M + 1個元素的堆,然後逐個將序列中的元素插入堆中,元素插入堆後堆的元素個數大於M,就執行刪除最小元素的操作。


    那麼應該建立一個什麼樣的堆呢,是大根堆還是小根堆呢,當然是小跟堆了,要不怎麼刪除最小的元素呢。有了上面的分析,我們就可以明確一些問題了,那就是這個小程序的api了。

public class TopM{
	private class MinHeap{
		private int M; //堆中需要保存多少個元素
		private int N; //堆中實際存在的元素個數
		private int[] heap = null; 
		//heap[0]作爲哨兵, heap[M +1]作爲最後最後插入的元素
		public MinHeap(int M){this.M = M; heap = new int[M + 2];}
		private void insert(int x){}//向堆中插入一個元素x
		private int delMin(){} //刪除堆中最小的元素,並返回最小元素的值
		private void swim(int k){} //上浮操作,從k處開始向上將堆調整成小根堆
		private void sink(int k){}//下沉操作,從k處向下將堆調整成小根堆
		private int size(){} //返回元素的堆中元素的個數
		private String toString(){}//返回堆內元素的情況
		private void exch(int i, int j){} //交換i和j位置的元素
	}
	public void topM(int[] a, int M){} //a代表大數組, M表示去前多少個元素, 找到前M的元素,然後打印輸出
	public static int[] generateArray(int length){} //隨機生成長度爲length的整形數組
	public static void main(String[] args){
		int[] a = generateArray(length);
		new TopM().topM(a, M);
	}
}

 

api有了,咱們就開始一步步實現吧。
1.首先是小根堆的插入函數的實現,很簡單,先將元素插入N後,然後再從N向上調整堆,如果插入元素後N到達堆數組的最後一個位置,即N == M + 1,先將堆調整好後,再刪除最小的元素

private void insert(int x){
	heap[++N] = x;
	swim(N);
	if(N > M)
		delMin();
}


2.刪除最小元素的操作,delMin,先將堆頂元素返回,然後用堆底元素代替堆頂元素,將N-1,從底部開始向下調整堆,

private int delMin(){
	int min = heap[1];
	heap[1] = heap[N];
	N--;
	sink(1);
	return min;
}


3.向上調整堆

private void swim(int k){
	while(k > 1 && heap[k] < heap[k / 2]){
		exch(k, k/2);
		k = k/2;
	}
}

 

4.向下調整堆。

private void sink(int k){
	while(2 * k <= N){
		int j = 2 * k;
		if(j < N && heap[j] > heap[j + 1]) j++;
		if(heap[k] < heap[j]) break;
		exch(k, j);
		k = j;
	}
}

 

5.exch的實現

private void exch(int i, int j){
	if(i < 0 || j < 0 || i > heap.length || j > heap.length || i == j)
		return;
	int t = heap[i];
	heap[i] = heap[j];
	heap[j] = t;
}

 

6.返回堆內元素的個數

private int size(){
	return N;
}


7.toString的實現

public String toString(){
	String str = N + ":[";
	for(int i = 1; i <= N; i++){
		str += heap[i] + ",";
	}
	str +="]";
	return str;
}

 

8.topM的實現

public void topM(int[] a, int M){
	MinHeap minHeap = new MinHeap(M);
	for(int i = 0; i < a.length; i++){
		minHeap.insert(a[i]);
	}
	System.out.println(minHeap);
}

 

9.generateArray的實現

public static int[] generateArray(int length){
	Random rand = new Random(47);
	int[] a = new int[length];
	for(int i = 0; i < length; i++){
		a[i] = rand.nextInt(length);
	}
	return a;
}

 

好了,將上面的程序模塊組合在一塊,就是一個完整的程序了,程序的功能很清楚了,就是輸出一個隨機序列的前M個值。最後整個程序的樣子就出來了。

import java.util.Arrays;
import java.util.Random;

public class TopM {
	
	private class MinHeap {
		private int M; // 堆中需要保存多少個元素
		private int N; // 堆中實際存在的元素個數
		private int[] heap = null; 
		public MinHeap(int M) {
			this.M = M;
			heap = new int[M + 2];
		}

		private void insert(int x){
			heap[++N] = x;
			swim(N);
			if(N > M)
				delMin();
		}

		private int delMin(){
			int min = heap[1];
			heap[1] = heap[N];
			N--;
			sink(1);
			return min;
		}

		private void swim(int k) {
			while (k > 1 && heap[k] < heap[k / 2]) {
				exch(k, k / 2);
				k = k / 2;
			}
		}

		private void sink(int k) {
			while (2 * k <= N) {
				int j = 2 * k;
				if (j < N && heap[j] > heap[j + 1])
					j++;
				if (heap[k] < heap[j])
					break;
				exch(k, j);
				k = j;
			}
		}

		private int size() {
			return N;
		}

		private void exch(int i, int j){
			if(i < 0 || j < 0 || i > heap.length || j > heap.length || i == j)
				return;
			int t = heap[i];
			heap[i] = heap[j];
			heap[j] = t;
		}

		public String toString() {
			String str = N + " : [";
			for (int i = 1; i <= N; i++) {
				str += heap[i] + ",";
			}
			str += "]";
			return str;
		}
	}

	public void topM(int[] a, int M) {
		MinHeap minHeap = new MinHeap(M);
		for (int i = 0; i < a.length; i++) {
			minHeap.insert(a[i]);
		}
		System.out.println(minHeap);
	}

	public static int[] generateArray(int length) {
		Random rand = new Random(47);
		int[] a = new int[length];
		for (int i = 0; i < length; i++) {
			a[i] = rand.nextInt(length);
		}
		return a;
	}

	public static void main(String[] args) {
		int length = 20;
		int M = 3;
		int[] a = generateArray(length);
		System.out.println(Arrays.toString(a));
		new TopM().topM(a, M);
	}
}


 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章