前言
本文为本小白🤯学习数据结构的笔记,将以算法题为导向,向大家更清晰的介绍数据结构相关知识(算法题都出自🙌B站马士兵教育------左老师的课程,讲的很好,对于想入门刷题的人很有帮助👍)
上面写完了归并排序,快速排序,排序它本身整个算法并没有什么,重要的是他的算法,它怎么提升时间复杂度,以及这种思想的应用,这是我们更要去关注的。下面来看一个新的排序------堆排序。
下面来介绍一下数据结构中的"堆"(Heap)。
1.堆 (Heap) 概述
堆结构就是用数组 实现的完全二叉树结构
-
堆是一棵完全二叉树。这意味着除了最后一层,其他层都被完全填满,且最后一层的节点都尽可能地靠左排列。
-
堆的数组表示 :由于堆是完全二叉树,可以用数组 heap[0...n-1] 来存储(通常索引从 0 开始):
- 根节点:heap[0]
- 节点 i 的左子节点:heap[2*i + 1]
- 节点 i 的右子节点:heap[2*i + 2]
- 节点 i 的父节点:heap[(i-1) / 2] (整数除法)
这种表示法非常节省空间,且父子节点的访问是 O(1) 的。
java
索引: 0 (值: 50)
/ \
索引: 1 (值: 30) 索引: 2 (值: 40)
/ \ / \
索引:3 索引:4 索引:5 索引:6
(值:20) (值:10) (值:35) (值:25)
java
数组索引: 0 1 2 3 4 5 6
+-----+-----+-----+-----+-----+-----+-----+
数组值: | 50 | 30 | 40 | 20 | 10 | 35 | 25 |
+-----+-----+-----+-----+-----+-----+-----+
2.堆主要有两种类型:
大根堆 (Max Heap) :对于树中的任意节点,其值大于或等于其子节点的值。因此,根节点(堆顶)是整个堆中最大的元素。
实现大根堆:
java
package DiGui;
public class Dui {
public static void heapInsert(int [] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
public static void swap(int [] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
}
大根堆应用: 去掉堆中的最大值,其结构仍保持大根堆
实现思路:
1.找到堆中最大值:肯定是数组中,下标为0的数,这个很简单。
2.去掉最大值保持大根堆:将数组中下标最大的数,赋值给下标为0位置,在用heapify方法往下调整堆结构。
(heapify方法):
java
//构建堆,index:构建堆结构下表,一下全是堆结构,heapSize:整个arr数组长度防止越界
public static void heapify(int [] arr. int index, int heapSize){
//index 左孩子
int left = index * 2 + 1;
while (left < heapSize) {
//index右孩子,与左孩子比较,取最大值下标
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
//判断arr[largest] 与 arr[index] 大小取最大值下标
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap( arr, largest, index);
index = largest;
}
}
public static void swap(int [] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
时间复杂度O(logN)
最小堆 (Min Heap) :对于树中的任意节点,其值小于或等于其子节点的值。因此,根节点(堆顶)是整个堆中最小的元素。
堆排序
先来看代码吧:
java
package DiGui;
public class Dui {
public static void heapSort(int [] arr) {
if (arr == null || arr.length < 2) {
return;
}
//先把整个数组变成大根堆
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
//再使整个大根堆有序
int heapSize = arr.length;
//将数组中最后一个数跟第一个数交换,再heapify,之后一个一个交换,使其完全有序
swap(arr, 0, --heapSize);
while (heapSize > 0) {
heapify(arr, 0, heapSize);
swap(arr, 0, --heapSize);
}
}
//
public static void heapInsert(int [] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
public static void heapify(int [] arr, int index, int heapSize) {
int left = index * 2 + 1;
while (left < heapSize) {
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, largest, index);
index = largest;
}
}
public static void swap(int [] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
}
其实就是把上面两个问题结合起来
时间复杂度O(N*logN)
小白啊!!!写的不好轻喷啊🤯如果觉得写的不好,点个赞吧🤪(批评是我写作的动力)
...。。。。。。。。。。。...
...。。。。。。。。。。。...