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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 树状大根堆 树状大根堆是一种二叉树,满足以下性质: 1. 每个节点的值都大于等于其子节点的值。 2. 树的最后一层节点都靠左排列。 在Python中,我们可以使用列表来表示二叉树,其中第i个元素的左子节点为2i,右子节点为2i+1,父节点为i//2。 以下是创建树状大根堆的代码: ```python class MaxHeap: def __init__(self, arr=None): self.heap = [0] if arr: self.heap.extend(arr) self._build_heap() def _build_heap(self): n = len(self.heap) - 1 for i in range(n // 2, 0, -1): self._heapify(i) def _heapify(self, i): n = len(self.heap) - 1 largest = i left = 2 * i right = 2 * i + 1 if left <= n and self.heap[left] > self.heap[largest]: largest = left if right <= n and self.heap[right] > self.heap[largest]: largest = right if largest != i: self.heap[i], self.heap[largest] = self.heap[largest], self.heap[i] self._heapify(largest) def push(self, val): self.heap.append(val) i = len(self.heap) - 1 while i > 1 and self.heap[i] > self.heap[i // 2]: self.heap[i], self.heap[i // 2] = self.heap[i // 2], self.heap[i] i //= 2 def pop(self): if len(self.heap) == 1: return None if len(self.heap) == 2: return self.heap.pop() root = self.heap[1] self.heap[1] = self.heap.pop() self._heapify(1) return root ``` 2. 堆排序 堆排序是一种排序算法,基于树状大根堆实现。其思路是先将数组构建成树状大根堆,然后将堆顶元素与最后一个元素交换,再将前面的元素重新构建成树状大根堆,重复此过程直到数组有序。 以下是堆排序的代码: ```python def heap_sort(arr): n = len(arr) heap = [0] + arr for i in range(n // 2, 0, -1): _heapify(heap, i, n) for i in range(n, 0, -1): heap[1], heap[i] = heap[i], heap[1] _heapify(heap, 1, i - 1) return heap[1:] def _heapify(heap, i, n): largest = i left = 2 * i right = 2 * i + 1 if left <= n and heap[left] > heap[largest]: largest = left if right <= n and heap[right] > heap[largest]: largest = right if largest != i: heap[i], heap[largest] = heap[largest], heap[i] _heapify(heap, largest, n) ``` 3. 取前k个值 在树状大根堆中,堆顶元素是最大的元素,可以通过不断取出堆顶元素来获得最大的k个元素。 以下是取前k个值的代码: ```python def top_k(arr, k): n = len(arr) heap = [0] + arr[:k] for i in range(k // 2, 0, -1): _heapify(heap, i, k) for i in range(k, n): if arr[i] > heap[1]: heap[1] = arr[i] _heapify(heap, 1, k) return heap[1:] ``` 以上是树状大根堆、堆排序和取前k个值的Python实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值