python算法——java版堆排序,最小堆排序topN

input爲數組,k爲求的top值

	// 初始化建堆的時間複雜度爲O(n),排序重建堆的時間複雜度爲nlog(n),所以總的時間複雜度爲O(n+nlogn)=O(nlogn)
	public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
		ArrayList<Integer> it = new ArrayList();
		// 臨界判斷
		if (k <= 0) {
			return it;
		}
		if (input.length == 0) {
			return it;
		}
		if (input.length < k) {
			return it;
		}
		if (input.length == 1) {
			it.add(input[0]);
			return it;
		}
		buildMinHeap(input);
		System.out.println("---第一次過後---" + new Gson().toJson(input));
		// 每次位置交換定位
		int place = input.length - 1;
		// 交換記錄暫存
		int save = 0;
		// 交換下沉
		for (int i = 0; i < k; i++) {
			place = input.length - 1 - i;
			int start = 0;
			// 交換樹頂和末節點,已排序位置左移
			while (start <= place) {
				//求出左右位置
				int left = (start << 1) + 1;
				int right = (start << 1) + 2;
				if (left > place) {
					break;
				}
				// 有雙節點
				if (right <= place) {
					//符合該位置規則,直接返回
					if (input[start] <= input[left] && input[start] <= input[right]) {
						break;
					} else {
						boolean smallL = false;
						if (input[left] <= input[right]) {
							smallL = true;
						}
						// 和最小的節點交換
						if (smallL) {
							change(input, left, start);
							start = left;
						} else {
							change(input, right, start);
							start = right;
						}
					}
				}
				// 只剩下左節點
				else {
					if (input[left] < input[start]) {
						change(input, left, start);
					}
					break;
				}
			}
			// 移除頂結點
			save = input[0];
			input[0] = input[input.length - 1 - i];
			input[input.length - 1 - i] = save;
			it.add(save);
		}
		System.out.println(new Gson().toJson(input));
		return it;
	}

	public void change(int[] input,int i,int j) {
		int c = input[i];
		input[i] = input[j];
		input[j] = c;
	}
	
	// build
	public void buildMinHeap(int[] input) {

		// 每次位置交換定位
		int place = input.length - 1;
		// 交換記錄暫存
		int save = 0;
		// 構建最小堆
		while (place > 0) {
			// 判斷奇數,偶數,奇數左指數,偶右指數,左樹開始下次跳一格,右樹開始,跳兩格,後面都以右結點開始
			boolean dou = false;
			if (place % 2 == 0) {
				dou = true;
			}
			// 找出父節點位置,偶數左移減一,奇數左移
			// 交換位置,調整指針,因爲排列完全二叉樹,定位位置是奇數,必定無右節點(只會在最開始出現單一節點)
			// 父節點位置
			int placeroot = 0;
			if (!dou) {
				placeroot = place >> 1;
				// 奇數只與父節點比較
				if (input[place] < input[placeroot]) {
					save = input[place];
					input[place] = input[placeroot];
					input[placeroot] = save;
				}
				place--;
			} else {
				placeroot = (place >> 1) - 1;
				// 偶數必有左兄弟節點,兩子節點都與父節點比較
				if (input[place] < input[placeroot]) {
					save = input[place];
					input[place] = input[placeroot];
					input[placeroot] = save;
				}
				if (input[place - 1] < input[placeroot]) {
					save = input[place - 1];
					input[place - 1] = input[placeroot];
					input[placeroot] = save;
				}
				// 跳兩格
				place -= 2;
			}
		}
	}

初始化建堆只需要對二叉樹的非葉子節點調用adjusthead()函數,由下至上,由右至左選取非葉子節點來調用adjusthead()函數。那麼倒數第二層的最右邊的非葉子節點就是最後一個非葉子結點。
  假設高度爲k,則從倒數第二層右邊的節點開始,這一層的節點都要執行子節點比較然後交換(如果順序是對的就不用交換);倒數第三層呢,則會選擇其子節點進行比較和交換,如果沒交換就可以不用再執行下去了。如果交換了,那麼又要選擇一支子樹進行比較和交換;高層也是這樣逐漸遞歸。
  那麼總的時間計算爲:s = 2^( i - 1 ) * ( k - i );其中 i 表示第幾層,2^( i - 1) 表示該層上有多少個元素,( k - i) 表示子樹上要下調比較的次數。
  S = 2^(k-2) * 1 + 2(k-3)2……+2(k-2)+2(0)*(k-1) ===> 因爲葉子層不用交換,所以i從 k-1 開始到 1;
  S = 2^k -k -1;又因爲k爲完全二叉樹的深度,而log(n) =k,把此式帶入;
  得到:S = n - log(n) -1,所以時間複雜度爲:O(n)

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