优先队列
-普通队列:先进先出
-出队顺序和入队顺序无关,和优先级相关
-操作系统划分时间片执行任务采用的就是动态优先队列
优先队列的实现
- | 入队 | 出队 |
---|---|---|
普通数组 | O(1) | O(n) |
顺序数组 | O(n) | O(1) |
堆 | O(lgn) | O(lgn) |
-使用堆实现优先队列
对于总共N个请求
使用普通数组或者顺序数组,最差的情况:O(n^2)
使用堆:O(nlgn)
用数组存储二叉堆
最大堆
-代码实现
public class MaxHeap<E extends Comparable<? super E>> {
private static final int DEFAULT_CAPACITY = 10;
private int currentSize;
private E[] array;
private int capacity;
// 构造函数
public MaxHeap() {
this(DEFAULT_CAPACITY);
}
public MaxHeap(int capacity) {
currentSize = 0;
// 数组内容从1开始,故+1
array = (E[]) new Comparable[capacity + 1];
this.capacity = capacity;
}
// 获取堆大小
public int size() {
return currentSize;
}
// 按最大堆方式插入元素
public void insert(E x) {
// 避免数组越界
assert (currentSize + 1 <= capacity);
array[currentSize + 1] = x;
currentSize++;
shiftUp(currentSize);
}
// 元素上移直到满足最大堆定义
private void shiftUp(int k) {
while (k > 1 && array[k / 2].compareTo(array[k]) < 0) {
E temp;
temp = array[k / 2];
array[k / 2] = array[k];
array[k] = temp;
k /= 2;
}
}
// 判断是否为空
public boolean isEmpty() {
return currentSize == 0;
}
// 重新构建最大堆,并返回根元素
public E extractMax() {
assert (currentSize > 0);
E ret = array[1];
// 将最后的元素放到堆顶
E temp;
temp = array[1];
array[1] = array[currentSize];
array[currentSize] = temp;
currentSize--;
shiftDown(1);
return ret;
}
public E printElement(int i) {
E ret = array[i + 1];
return ret;
}
// 将堆顶元素下移到合适的位置
private void shiftDown(int k) {
while (k * 2 <= currentSize) {
// 在此轮循环中,data[k]和data[j]交换位置
int j = 2 * k;
// 判断左右孩子大小
if (j + 1 <= currentSize && array[j + 1].compareTo(array[j]) > 0)
j += 1;
if (array[k].compareTo(array[j]) >= 0)
break;
E temp;
temp = array[k];
array[k] = array[j];
array[j] = temp;
k = j;
}
}
public static void main(String[] args) {
MaxHeap<Integer> maxHeap = new MaxHeap<Integer>(100);
// 随机添加元素
System.out.println("=插入元素=");
for (int i = 0; i < 15; i++) {
int a = (int) (Math.random() * 100);
maxHeap.insert(a);
System.out.print(a + " ");
}
System.out.println("");
System.out.println("=打印元素=");
for (int i = 0; i < 15; i++)
System.out.print(maxHeap.printElement(i) + " ");
System.out.println("");
while (!maxHeap.isEmpty()) {
System.out.print(maxHeap.extractMax() + " ");
}
System.out.println("");
System.out.println("元素个数" + maxHeap.size());
}
}
-输出结果
=插入元素=
72 9 42 51 4 35 38 88 15 5 0 87 13 16 40
=打印元素=
88 72 87 51 5 42 40 9 15 4 0 35 13 16 38
88 87 72 51 42 40 38 35 16 15 13 9 5 4 0
元素个数0
堆排序
基础堆排序
-代码实现
// 堆排序算法1
public <E extends Comparable<? super E>> void heapSort1(MaxHeap<E> maxHeap, E[] arr,int n) {
// 将数据插入堆
for (int i = 0; i < n; i++)
maxHeap.insert(arr[i]);
// 倒序插入最大数据完成从小到大的排序
for (int i = n-1; i >= 0; i--) {
arr[i] = maxHeap.extractMax();
}
System.out.println("=打印元素=");
for (int i = 0; i < 15; i++)
System.out.print(arr[i] + " ");
}
-输出结果
=插入元素=
20 57 81 86 28 21 57 51 12 92 50 84 46 14 91
=打印元素=
92 86 91 51 81 57 84 20 12 28 50 21 46 14 57
进行堆排序
=打印元素=
12 14 20 21 28 46 50 51 57 57 81 84 86 91 92
-算法复杂度
Nlog(N)
从建堆(heapify)角度优化排序
// heapify
public MaxHeap(E[] arr, int n) {
array = (E[]) new Comparable[n + 1];
this.capacity = n;
for (int i = 0; i < n; i++)
array[i + 1] = arr[i];
currentSize = n;
// 从第一个不是叶子节点的点开始进行下移操作,将数组整合成堆
for (int i = currentSize / 2; i >= 1; i--)
shiftDown(i);
}
// 堆排序算法2
// 效率比第一种高,将N个元素逐一插到空堆中算法复杂度为O(nlogn) heapify建堆过程为O(n)
public <E extends Comparable<? super E>> void heapSort2( E[] arr, int n) {
MaxHeap<E> maxHeap = new MaxHeap<>(arr,n);
// 倒序插入最大数据完成从小到大的排序
for (int i = n - 1; i >= 0; i--) {
arr[i] = maxHeap.extractMax();
}
System.out.println("=打印元素=");
for (int i = 0; i < n; i++)
System.out.print(arr[i] + " ");
}
效率比第一种高,主要差别在于两者建堆的方式不同。将N个元素逐一插到空堆中算法复杂度为O(nlogn) heapify建堆过程为O(n)
原地堆排序
-不需要开辟额外的空间
算法思想
-通过heapify构建最大堆
-由最大堆的性质可知数组第一个元素是最大的
-将最大的元素放到最后的位置,第二大的元素会被放到倒数第二的位置,以此类推
-当v和w换位置时,不在满足最大堆,需要进行相应的shitfdown操作重新构建最大堆
相应代码
// 堆排序算法3
public <E extends Comparable<? super E>> void heapSort3(E[] arr, int n) {
// 算法1和2中的堆从1开始索引,算法3从0开始索引
for (int i = (n - 1) / 2; i >= 0; i--)
shiftDown(arr, n, i);
for (int j = n - 1; j > 0; j--) {
// 把当前堆中最大的数放到合适的位置中
E temp;
temp = arr[0];
arr[0] = arr[j];
arr[j] = temp;
shiftDown(arr, j, 0);
}
System.out.println("=打印元素=");
for (int i = 0; i < n; i++)
System.out.print(arr[i] + " ");
}
private <E extends Comparable<? super E>> void shiftDown(E[] arr, int n, int k) {
while (k * 2 + 1 < n) {
// 在此轮循环中,arr[k]和arr[j]交换位置
int j = 2 * k + 1;
// 判断左右孩子大小
if (j + 1 < n && arr[j + 1].compareTo(arr[j]) > 0)
j += 1;
if (arr[k].compareTo(arr[j]) >= 0)
break;
E temp;
temp = arr[k];
arr[k] = arr[j];
arr[j] = temp;
k = j;
}
}