堆排序

堆排序的最坏时间复杂度是O(nlogn),平均时间复杂度是O(nlogn)。但是堆排序的时间常数比较大,因此从平均来看堆排序的时间复杂度反而是最差的。

基本接口:
1.插入:插入一个新元素的时候,首先将这个新元素放在最后,因为新元素的插入操作可能会破坏堆的性质,因此执行上游操
作。

2.删除:删除一个新元素的时候,首先要将堆顶元素和最后一个元素交换。然后将堆的大小减一。删除操作同样也可能会破坏堆的性质,因此执行下沉操作来维护堆的性质。

3.构造堆的操作:堆的构造除了可以通过一个个插入元素来构造之外还可以在一个原始的序列上构建起一个堆。首先从这棵树的第二层开始,然后遍历所有内部结点(内部结点:除了叶子结点之外的结点),并执行下沉操作。

内置操作:
swim(上游):当插入一个新元素的时候要把这个新的元素与它的父结点比较,如果优先级比父结点高,就与父结点交换位置(上游),直到小于或等于父结点的优先级或者新插入的元素成为了根结点。

sink(下沉):当删除堆顶元素的时候堆顶元素要跟最后一个元素交换。然后将最后一个元素与它的两个孩子结点比较,并和两个孩子结点中的优先级较高的并且比该元素优先级高的孩子结点交换。直到该元素没有孩子结点或者它的所有孩子结点的优先级都没有它高。

头文件:
#ifndef HEAP_H
#define HEAP_H


#include <vector>


class Heap {
public :
  Heap();
  ~Heap();
  void Push(const int &e);
  void Pop();
  int &Top();
  const int &Top() const;
  bool Empty() const;
  void MakeHeap(const std::vector<int> &src);
private :
  void Sink(size_t pos);
  void Swim(size_t pos);
  bool Prior(const size_t &lhs, const size_t &rhs);
private :
  std::vector<int> _heap;
};


#endif  //HEAP_H

实现文件:
#include "heap.h"


Heap::Heap() {
  _heap.push_back(0);
}


Heap::~Heap() {}


void Heap::Push(const int &e) {
  _heap.push_back(e);
  ++_heap[0];
  Swim(_heap[0]);
}


void Heap::Pop() {
  if (_heap[0] <= 0) {
    throw std::exception("the heap is empty");
  }


  int temp = _heap[1];
  _heap[1] = _heap[_heap[0]];
  _heap[_heap[0]] = temp;
  --_heap[0];
  Sink(1);
}


int &Heap::Top() {
  if (_heap[0] <= 0) {
    throw std::exception("the heap is empty");
  }


  return _heap[1];
}


const int &Heap::Top() const {
  return static_cast<const int &>(const_cast<Heap *>(this)->Top());
}


bool Heap::Empty() const {
  return _heap[0] <= 0;
}


void Heap::MakeHeap(const std::vector<int> &src) {
  _heap.clear();
  _heap.push_back(0);
  for (std::vector<int>::const_iterator citer = src.cbegin();
    citer != src.cend();
    ++citer) {
    _heap.push_back(*citer);
    ++_heap[0];
  }
  size_t pos = _heap[0] >> 1;
  while (pos != 0) {
    Sink(pos);
    --pos;
  }
}




void Heap::Sink(size_t pos) {
  size_t prior_child = pos * 2;
  while (prior_child <= _heap[0]) {
    if (prior_child + 1 <= _heap[0] && Prior(prior_child + 1, prior_child)) {
      prior_child += 1;
    }
    if (Prior(prior_child, pos)) {
      int temp = _heap[pos];
      _heap[pos] = _heap[prior_child];
      _heap[prior_child] = temp;
      pos = prior_child;
      prior_child = pos * 2;
    }
    else {
      break;
    }
  }
}




void Heap::Swim(size_t pos) {
  size_t parent = pos >> 1;
  while (parent != 0) {
    if (Prior(pos, parent)) {
      int temp = _heap[pos];
      _heap[pos] = _heap[parent];
      _heap[parent] = temp;
      pos = parent;
      parent = pos >> 1;
    }
    else {
      break;
    }
  }
}


bool Heap::Prior(const size_t &lhs, const size_t &rhs) {
  return _heap[lhs] > _heap[rhs] ? true : false;
}

上面给出的例子是大顶堆。对于堆排序其实不难理解的关键点在Swim和Sink两个函数。实现也是很简单的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值