算法导论:堆排序

维护堆

主要思想

  • 比较 A[i],A[Left(i)]A[Right(i)] 的大小
  • 如果 A[i] 不是最大的,则将其与比较大的孩子节点进行交换
  • 在堆中继续向下比较和交换,直到 i 节点为根的子树是一个大顶堆

伪代码

Max-Heapify(A, i, n) {
	l = Left(i);
  r = Right(i);
  if(l <= n && A[l] > A[i]) largest = l;
  else largest = i;
  if(r <= n && A[r] > A[largest]) largest = r;
  if(largest != i) {
  	swap(A[i], A[largest]);
    Max-Heapify(A, largest, n);
  }
}

运行时间为 O(lgn),因为树的高度为 lgn,而将 A[i] 向下移动一层需要常数级时间。

建堆

主要思想

自底向上把一个无序的数组建成大顶堆。

  • 在建堆的过程中,只需要考虑非叶节点
  • 子数组 A[n/2+1,...,n] 中的元素对应的所有节点都是叶子结点

伪代码

Build-Max-Heap(A, n) {
	for(i = n/2; i >= 1; i--) {
  	Max-Heapify(A, i, n);
  }
}

效率分析

简单界:O(n) 调用 Max-Heapify,每次调用需要 O(lgn) 的时间,故建堆需要 O(nlgn) 时间。

准确界:堆的高度是 lgn

  • 最多有 n/2h+1 个高度为 h 的节点
  • 在高度为 h 的节点上运行 Max-Heapify 的时间是 O(h)

因此建堆的总代价是:

h=0lgnn2h+1O(h)=O(nh=0lgnn2h)O(nh=0)h(12)hk=0xk=11xfor|x|<1k=0kxk1=1(1x)2k=0kxk=x(1x)2O(nh=0h(12)h)=O(2n)=O(n)

堆排序

主要思想

  • 在数组上建立大顶堆
  • 从根节点开始,将根节点与数组最后一个元素进行交换
  • “去掉”数组中最后一个元素,然后在新的根节点上调用 Max-Heapify
  • 重复上面两个步骤,直到只剩下一个节点

伪代码

HeapSort(A, n) {
	Build-Max-Heap(A, n);
  for(i = n; i > 1; i--) {
  	swap(A[1], A[i]);
    Max-Heapify(A, 1, i-1);
  }
}

效率分析

  • 建堆:O(n)
  • for 循环 n1
    • swapO(1)
    • Max-HeapifyO(lgn)

故总时间为 O(nlgn)

优先队列

IncreaseKey(A,i,key):增加元素 ikey

  • 确保 A[i]key
  • 更新 A[i]key
  • 向上遍历树,比较 A[i] 和它的父节点,有需要就交换值,直到 A[i]key 比它的父节点小
Increase-Key(A, i, key) {
	if(key < A[i]) return;
  A[i] = key;
  while(i > 1 && A[Parent(i)] < A[i]) {
  	swap(A[i], A[Parent(i)]);
    i = Parent(i);
  }
}

时间:O(lgn)

Insert(A,key,n):将元素 key 插入集合 A

  • 增加堆的大小
  • 在堆的最后一个位置增加一个 key 的节点
  • 增加 key,调用 IncreaseKey
Insert(A, key, n) {
	n = n + 1;
  A[n] = -inf;
  Increase-Key(A, n, key)
}

时间:O(lgn)

ExtractMax(A,n):去掉并返回集合 Akey 最大的元素

  • 确保堆不空
  • 复制最大元素(根节点)
  • 把树中最后一个节点变成新的根节点
Extract-Max(A, n) {
	if(n < 1) return;
  max = A[1];
  A[1] = A[n];
  n = n - 1;
  Max-Heapify(A, 1, n);
  return max;
}

时间:O(lgn)

posted @   FireOnFire  阅读(59)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示