关于堆的7种基本排序方式及相关面试题:【JAVA】数据结构——堆的排序及相关面试题
目录
(4)放元素val:首先考虑数组是否满了,再放入元素(放末尾),然后重新建堆
(6)出元素:保证每次出最大值(大根堆)或者最小值(小根堆)
3.java中的优先级队列:PriorityQueue implements Queue
1.二叉树的顺序存储
1.1存储方式:用层序遍历的方式将二叉树放入数组中,保存二叉树的结构(也即是用堆表示)
1.2下标关系:
已知孩子child节点下标,则parent = (child-1)/2
已知双亲parent节点下标,则左孩子:left = 2*parent+1; 右孩子:right = 2*parent +2;
2.堆的概念及应用
堆逻辑上是一颗完全二叉树,物理上是保存在数组中的;
堆的基本作用是快速找到集合中的最值;
优先级队列(堆)实现方式:常见的是使用堆来构建
前提:左右子树必须已经是堆(大小堆),才能进行调整;
(1)向下调整:根据parent向下创建堆
时间复杂度:最坏:O(log(N))
public class testHeap {
public int[] elem;
public int usedSize;
public testHeap() {
this.elem = new int[10];
}
//parent:每棵树的根节点,len:每棵树调整的结束位置
//向下调整注意,child下标不能超出数组长度
public void shiftDown(int parent,int len) {
int child = 2*parent+1; //孩子下标
//当孩子下标小于数组长度,则需进行判断是否符合大根堆
while (child < len) { //进入循环,至少有一个孩子(左孩子)
if (child+1 < len && elem[child] < elem[child+1]) { //孩子下标没超出数组范围,并且左孩子<右孩子,则继续向后进行,向下调整
child++;//保证当前左右孩子最大值的下标
}
if (elem[child] > elem[parent]) { //如果孩子节点值》父亲节点则进行交换
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
parent = child; //交换结束,继续向下移动,parent= child 向下调整,再求解其子树下标
child = 2*parent+1; //左孩子下标
}else { //如果elem[child] 《 elem[parent],则 已经是大根堆了,只需退出即可
break;
}
}
}
(2)建堆:创建大根堆
时间复杂度:理论:O(n*log(n));实际:每一层节点个数*每个节点调整的高度O(N)
public void createHeap(int[] array) { //创建一个大根堆函数,也即是将数组元素放入大根堆
for (int i = 0; i < array.length; i++) {
elem[i] = array[i];
usedSize++;
}
//parent = (child-1)/2,child = usedsize-1
for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) { //此处parent下标是最后一个父亲节点下标
shiftDown(parent,usedSize);
}
}
(3)向上调整:根据child向上创建大根堆
public void shiftUp(int child) {
int parent = (child-1)/2;
while (child > 0) { //进入循环,至少有一个孩子(左孩子),孩子下标是>0的
//如果孩子节点值》父亲节点则进行交换
if (elem[child] > elem[parent]) { //还是创建大根堆,孩子节点大则进行交换
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
//交换结束,说明当前子二叉树满足大堆要求,则向上调整;当parent= child 时,是向下调整,child = 2*parent+1
child = parent;
parent = (child-1)/2;
}else { //如果elem[child] 《 elem[parent],则 已经是大根堆了,只需退出即可
break;
}
}
}
(4)放元素val:首先考虑数组是否满了,再放入元素(放末尾),然后重新建堆
public void offer(int val) {
if (isFull()) { //首先判断数组满不满,满了则需扩容
//扩容
elem = Arrays.copyOf(elem,2*elem.length); //2倍扩容
}
//放元素,则有效数据多了一个
elem[usedSize++] = val; //扩容结束放元素,在位置elem[usedSize++]放val
shiftUp(usedSize-1); // 因为已知最后一个下标所以向上调整: usedSize-1为下标
}
public boolean isFull() {
return usedSize == elem.length;
}
(5)获取队头元素:也即是根节点
//获取队头元素,也即是elem[0]根节点元素
public int peek() {
if (isEmpty()) {
throw new RuntimeException("优先级队列为空");
}
return elem[0];
}
public boolean isEmpty() {
return usedSize == 0;
}
(6)出元素:保证每次出最大值(大根堆)或者最小值(小根堆)
//交换根节点和最后一个元素的位置,变成了出最后一个元素,而此时已不符合大小堆的要求,因此需要重新创建大小根堆。
public int poll() {
if (isEmpty()) {
throw new RuntimeException("优先级队列为空");
}
int tmp = elem[0]; //交换0下标元素和最后一个元素
elem[0] = elem[usedSize-1];
elem[usedSize-1] = tmp;
usedSize--;
shiftDown(0,usedSize); //usedSize结束位置
return tmp;
}
3.java中的优先级队列:PriorityQueue implements Queue
错误处理 | 抛出异常 | 返回特殊值 |
入队列 | add(e) | offer(e) |
出队列 | remove() | poll() |
队首元素 | element() | peek() |