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)

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