Topics:
强调稳定性时多关注的是多关键字排序。
插入排序
时间复杂度是 O ( n 2 ) O(n^2) O(n2),原地排序
归并排序
时间复杂度是 O ( n l g n ) O(nlgn) O(nlgn),需要辅助存储空间
时间复杂度是 Θ ( n l g n ) \Theta(nlgn) Θ(nlgn),原地排序
快排
平均情况的时间复杂度是 O ( n l g n ) O(nlgn) O(nlgn),原地排序
线性时间的排序
堆的使用:
{
栈内存:局部变量,方法和函数
堆内存:
m
a
l
l
o
c
n
e
w
静态方法区:全局变量
堆数据结构:
元素按照下标索引——堆数据结构的实现是一个一维数组
每个元素支持:PARENT
,LEFT
和RIGHT
操作
父子关系的映射逻辑为:
若数组下标从1开始,那么父子映射关系函数为:
P A R E N T ( i ) = ⌊ i / 2 ⌋ PARENT(i) = \lfloor i/2 \rfloor PARENT(i)=⌊i/2⌋
L E F T ( i ) = 2 i LEFT(i) = 2i LEFT(i)=2i
R I G H T ( i ) = 2 i + 1 RIGHT(i) = 2i+1 RIGHT(i)=2i+1
若数组下标从0开始,那么父子映射关系函数为:
P A R E N T ( i ) = ⌈ i / 2 ⌉ − 1 PARENT(i) = \lceil i/2 \rceil -1 PARENT(i)=⌈i/2⌉−1
L E F T ( i ) = 2 i + 1 LEFT(i) = 2i+1 LEFT(i)=2i+1
R I G H T ( i ) = 2 i + 2 RIGHT(i) = 2i+2 RIGHT(i)=2i+2
int getParent(int child) {
return ceil(child / 2.0) - 1;
}
int getLeft(int parent) {
return parent * 2 + 1;
}
int getRight(int parent) {
return parent * 2 + 2;
}
堆中父子的大小关系
{
大顶堆:
A
[
P
A
R
E
N
T
(
i
)
]
≥
A
[
i
]
小顶堆:
A
[
P
A
R
E
N
T
(
i
)
]
≤
A
[
i
]
堆中的第一个元素是 A [ 0 ] A[0] A[0]或者 A [ 1 ] A[1] A[1],对应两种不同的情况
结点的高度是从该结点到叶子结点的最长路径。
堆的高度是树根的高度。
叶子结点的高度为0,树根的高度为树的高度。
有n个结点的树的高度为 ⌊ l g n ⌋ \lfloor lgn\rfloor ⌊lgn⌋
MAX-HEAPIFY
:保证堆是一个大顶堆,时间复杂度是
O
(
l
g
n
)
O(lgn)
O(lgn)
Build-Max-Heap
:建堆,时间复杂度是
O
(
n
)
O(n)
O(n)
堆排序:堆排序时,我们首先要建成一个大顶堆,然后反复执行,取下结点后调用MAX-HEAPIFY
的过程,时间复杂度是
O
(
n
l
g
n
)
O(nlgn)
O(nlgn)
建堆两种方式:递归建堆和非递归建堆
递归建堆三个步骤:分、治、合并
MaxHeapify
void buildMaxHeap(int parent, vector<int>& vec) {
if (parent < vec.size()) {// 考虑是否还有孩子结点
// 分
int left = getLeft(parent);
int right = getRight(parent);
// 治
buildMaxHeap(left, vec);
buildMaxHeap(right, vec);
// 合并
maxHeapify(vec, parent);
}
}
非递归建堆过程采用循环的建堆方式,从最后一个非叶子结点开始,逐步建堆
最后一个非叶子结点的下标: ⌊ v e c . s i z e ( ) / 2 ⌋ \lfloor vec.size()/2\rfloor ⌊vec.size()/2⌋
void buildMaxHeap_iteration(vector<int>& vec) {
int last_non_leaf = vec.size() / 2;
for (int i = last_non_leaf; i >= 0; i--) {
maxHeapify(vec, i);
}
}
MAXHEAPIFY
MaxHeapify()
时间复杂度为
O
(
h
)
O(h)
O(h),其中
h
h
h是树的高度,$h = \lfloor lgn \rfloor $
写成递归处理的形式如下:
void maxHeapify(vector<int>& vec, int parent) {
int largest = parent;
int left = getLeft(parent);
int right = getRight(parent);
// 找到父子之间最大值的位置
if (left < vec.size() && vec[left] > vec[largest]) { // 注意短路判决
largest = left;
}
if (right < vec.size() && vec[right] > vec[largest]) { // 注意短路判决
largest = right;
}
// 更换位置
if (largest != parent) {
int temp = vec[largest];
vec[largest] = vec[parent];
vec[parent] = temp;
// 接着maxheapify处理子树
maxHeapify(vec, largest);
}
}
上述的写法是在结束的时候调用递归处理,这种写法很容易改成循环的形式。如下:
void maxHeapify(vector<int>& vec, int parent) {
int largest = parent;
bool ifChange = true;
while(ifChange) {
int left = getLeft(parent);
int right = getRight(parent);
// 找到父子之间最大值的位置
if (left < vec.size() && vec[left] > vec[largest]) { // 注意短路判决
largest = left;
}
if (right < vec.size() && vec[right] > vec[largest]) { // 注意短路判决
largest = right;
}
// 更换位置
if (largest != parent) { // 需要交换
int temp = vec[largest];
vec[largest] = vec[parent];
vec[parent] = temp;
ifChange = true;
}else{
ifChange = false;
}
}
}
若采取直接的方式分析运行时间,那么时间将会是:
T
(
n
)
=
O
(
n
/
2
)
×
O
(
l
g
n
)
=
O
(
n
l
g
n
)
T(n) = O(n/2)\times O(lgn) = O(nlgn)
T(n)=O(n/2)×O(lgn)=O(nlgn)
key observation:
T
(
n
)
=
2
T
(
n
/
2
)
+
O
(
l
g
n
)
T(n) = 2T(n/2) + O(lgn)
T(n)=2T(n/2)+O(lgn)
f
(
n
)
=
l
g
n
=
O
(
n
l
o
g
2
2
−
ϵ
)
=
O
(
n
1
−
ϵ
)
f(n) = lgn = O(n^{log_2^2-\epsilon})=O(n^{1-\epsilon})
f(n)=lgn=O(nlog22−ϵ)=O(n1−ϵ)
所以堆排序的时间复杂度为 Θ ( n ) \Theta(n) Θ(n)
堆排序策略:每次取出堆顶的元素,使用堆中最后一个元素与堆顶元素交换,然后调用MaxHeapify
void HeapSort(vector<int>& vec) {
int size = vec.size(); // 从0开始的
for (int i = size - 1; i >= 1; i--) { // 从最后一个结点开始
int temp = vec[i];
vec[i] = vec[0];
vec[0] = temp;
size = size - 1;
maxHeapify(vec, 0, size);
}
}
最大值优先队列的应用:
最小值优先队列应用:
操作:
插入一个元素Insert(S, x)
获得最大值MAXIMUM(S)
取出最大值EXTRACT-MAX(S)
INCREASE-KEYS(S, x, k)
将位置x的元素正大为key
MAXMUM(A)
return A[0];
int PriorityQueue::extract_max() {
if (vec.size() < 1) {
cout << "Error:HEAP UNDERFLOW" << endl;
return 0;
}
int max = vec[0]; // 需要返回的值
vec[0] = vec[vec.size() - 1]; // 与最后一个元素交换
vec.pop_back(); // 弹出最后一个元素
maxHeapify(vec.size(), 0);
return max;
}
void PriorityQueue::increase_key(int index, int key) {
if (key < vec[index]) {
cout << "Error:" <<key << "is smaller than the original number " << vec[index] << endl;
}
vec[index] = key;
while (index > 0 && vec[parent(index)] < vec[index]) {
int temp = vec[index];
vec[index] = vec[parent(index)];
vec[parent(index)] = temp;
index = parent(index);
}
}
void PriorityQueue::insert(int key) {
// 放到最后
vec.push_back(key);
// 向上比较调整
increase_key(vec.size()-1, key);
}
优先队列完整代码如下:
#include
#include
#include
using namespace std;
class PriorityQueue {
public:
// 构造函数
PriorityQueue () = default;
PriorityQueue (vector<int>& paraVec);
// 向队列中插入新的值
void insert (int key);
// 返回优先队列中的最大值
int maximum ();
// 去除优先队列中的最大值并且返回
int extract_max ();
// 增加index处的值,增加为key
void increase_key (int index, int key);
// 堆排序
void heapSort ();
// 打印出堆中的数据:
void queuePrint();
private:
vector<int> vec;
// 建堆函数
void buildMaxHeap (int root = 0);
// 初始建堆的递归处理子堆
void maxHeapify (int size, int root);
// 返回左孩子结点的坐标
int leftChild (int root);
// 返回右孩子结点的坐标
int rightChild (int root);
// 返回父亲结点的坐标
int parent (int child);
};
int PriorityQueue::leftChild(int root = 0) {
return root * 2 + 1;
}
int PriorityQueue::rightChild(int root = 0) {
return root * 2 + 2;
}
int PriorityQueue::parent(int child) {
return ceil(child / 2.0) - 1; // 默认向下取整
}
PriorityQueue::PriorityQueue (vector<int>& paraVec):vec(paraVec) {
buildMaxHeap(); // 初始时直接将输入的数组转换为大顶堆
};
void PriorityQueue::buildMaxHeap(int root) {
if (root < vec.size()) {
// 分
int left = leftChild(root);
int right = rightChild(root);
// 治
buildMaxHeap(left);
buildMaxHeap(right);
// 合并
maxHeapify(vec.size(), root);
}
}
void PriorityQueue::maxHeapify(int size, int root) {
int left = leftChild(root);
int right = rightChild(root);
int largest = root;
// 找到三者中较大的一个
if (left < size && vec[left] > vec[largest]) {
largest = left;
}
if (right < size && vec[right] > vec[largest]) {
largest = right;
}
if (largest != root) { // 需要将root的值与largest的值进行交换
int temp = vec[root];
vec[root] = vec[largest];
vec[largest] = temp; // 交换完毕
maxHeapify(size, largest); // largest处现在是换下来的root的值,需要对这个子树进行处理
}
}
void PriorityQueue::heapSort() {
buildMaxHeap();
int size = vec.size();
for (int i = size - 1; i >= 1; i--) { // 从最后一个结点开始
int temp = vec[i];
vec[i] = vec[0];
vec[0] = temp;
size = size - 1;
maxHeapify(size, 0);
}
}
void PriorityQueue::queuePrint() {
int k = 1, n = 1;
for (int i = 0; i < vec.size(); i++,k++) {
cout << vec[i] << " ";
if(k == n) {
cout << endl;
k = 0;
n = n * 2;
}
}
cout << endl;
}
int PriorityQueue::maximum() {
return vec[0];
}
int PriorityQueue::extract_max() {
if (vec.size() < 1) {
cout << "Error:HEAP UNDERFLOW" << endl;
return 0;
}
int max = vec[0]; // 需要返回的值
vec[0] = vec[vec.size() - 1]; // 与最后一个元素交换
vec.pop_back(); // 弹出最后一个元素
maxHeapify(vec.size(), 0);
return max;
}
void PriorityQueue::increase_key(int index, int key) {
if (key < vec[index]) {
cout << "Error:" <<key << "is smaller than the original number " << vec[index] << endl;
}
vec[index] = key;
while (index > 0 && vec[parent(index)] < vec[index]) {
int temp = vec[index];
vec[index] = vec[parent(index)];
vec[parent(index)] = temp;
index = parent(index);
}
}
void PriorityQueue::insert(int key) {
// 放到最后
vec.push_back(key);
// 向上比较调整
increase_key(vec.size()-1, key);
}
int main () {
vector<int> vec = {4,1,3,2,16,9,10,14,8,7};
PriorityQueue myQueue = PriorityQueue(vec);
// 打印出初始的堆
cout << "The original heap is:"<<endl;;
myQueue.queuePrint();
// 向堆中插入元素
myQueue.insert(11);
// 将堆中元素变大
myQueue.increase_key(4,15);
// 取出堆中最大元素
cout << "The max number after increasing in priority queue is:" << myQueue.maximum() << endl;
// 取出堆中的最大的元素
myQueue.extract_max();
// 打印取出后的结果
cout << "The result after the max is remove:"<<endl;
myQueue.queuePrint();
// 堆排序
myQueue.heapSort();
// 打印原地排序的结果
cout << "After HeapSort:"<<endl;
myQueue.queuePrint();
return 0;
}