куча
博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收**藏 ^ _ ^ !
- В моем сердце куча https://blog.csdn.net/luo_boke/article/details/106928990
- В самом сердце есть дерево https://blog.csdn.net/luo_boke/article/details/106980011
- В моем сердце есть стек https://blog.csdn.net/luo_boke/article/details/106982563
Куча - это полное двоичное дерево , отсортированная древовидная структура данных, которая удовлетворяет следующим свойствам:
- Последовательность кучи: значение любого узла меньше (или больше) значения всех его потомков, а минимальный узел (или максимальный узел) находится в корне кучи.
- Структурный: куча всегда представляет собой полное двоичное дерево, то есть, за исключением нижнего слоя, узлы других слоев заполняются элементами, а нижний слой заполняется максимально слева направо.
Что такое бинарное дерево? Пожалуйста, проверьте мою другую запись в блоге "Дерево в основах сердца"
Маленькая корневая куча и большая корневая куча
Малая корневая куча: Куча, значение узла которой меньше значения узла-потомка, также называемая минимальной кучей, слева от рисунка
Большая корневая куча: Куча со значением узла, превышающим значение узла-потомка, также называемая самой большой кучей, справа от рисунка
Двоичная куча
Двоичная куча - это особый вид кучи, то есть каждый узел имеет не более двух дочерних узлов. Сортировка кучи - это используемая двоичная куча.
Хранилище кучи
обычно использует линейные структуры данных (например, массивы) для хранения кучи:
- Корневой узел хранится в 0-й позиции
- Левый дочерний элемент узла i хранится в 2 * i + 1
- Правый дочерний элемент узла i хранится в 2 * i + 2
Процесс сборки кучи
1) Нажмите на родительский узел в соответствии с дочерним узлом: (n-1) / 2
2) Нажмите на дочерний узел в соответствии с родительским узлом: левый дочерний узел (2n + 1), правый дочерний узел (2n + 2)
индексирует 0 начинает отсчет
Мы строим большую корневую сваю с 10 числами 9, 12, 5, 24, 0, 1, 99, 3, 10, 7 следующим образом:
- Прежде всего, мы рассматриваем текущую неупорядоченную последовательность как структуру кучи, двоичное дерево без правил и заполняем двоичное дерево значениями в последовательности сверху вниз и слева направо.
- Обойти родительский узел от последнего листа 7. Если родительский узел <максимального значения левого и правого дочерних узлов, значение родительского узла обменивается с максимальным дочерним узлом. Найдите, что 7 и 10 меньше 35, переключитесь на родительский узел 99 и 3, обнаружите, что 99> 24, поменяйте местами 24 и 99.
- Продолжайте сравнивать родительские узлы 0 и 1, потому что 0 <5, 1 <5, спуститесь вниз, чтобы сравнить родительские узлы 99 и 35, самый большой дочерний узел 99> 12, поменяйте местами позиции. После обмена родительский узел 12 должен быть отсортирован, и обнаруживается, что 24> 12, а позиции 12 и 24 необходимо поменять местами.
- Продолжайте перемещаться по родительскому узлу 9, и вам нужно изменить позиции на 99. Точно так же 9 нуждается в самом большом дочернем узле 35 для изменения позиций.
- Наконец-то получил много кода сборки root
/***
* 构建大根堆
* @param array 数据源
*/
private void buildHeap(int[] array) {
//从右向左,从下到上依次遍历父结点,建立大根堆,时间复杂度:O(n*log2n)
for (int i = (array.length - 1 - 1) / 2; i >= 0; i--) {
adjust(array, i, array.length - 1);
}
}
/**
* 将指定堆构建成大堆根函数
* 逻辑
* 1. 如果起始索引无子结点,则跳出该方法
* 2. 如果只有一个左子结点,进行大小比较并置换值
* 3. 如果有两个子结点,选择最大值与父结点比较,然后置换其位置。
* 如果子结点大于父结点,置换完成后,递归进行同样操作,其子结点索引即是函数的start值
*
* @param array 源数组
* @param start 起始索引
* @param end 结尾索引
*/
public void adjust(int[] array, int start, int end) {
// 左子结点的位置
int leftIndex = 2 * start + 1;
if (leftIndex == end) {
//只有一个左结点,进行比较并置换值
if (array[leftIndex] > array[start]) {
int temp = array[leftIndex];
array[leftIndex] = array[start];
array[start] = temp;
}
} else if (leftIndex < end) {
//有两个子结点
int temp = array[leftIndex];
int tempIndex = leftIndex;
if (array[leftIndex + 1] > array[leftIndex]) {
temp = array[leftIndex + 1];
tempIndex = leftIndex + 1;
}
if (temp > array[start]) {
array[tempIndex] = array[start];
array[start] = temp;
}
adjust(array, tempIndex, end);
}
}
Процесс сортировки кучи
Выше мы создали большую корневую кучу, и теперь мы сортируем кучу. Идея конструкции такая:
- Скомпилируйте в соответствии с характеристиками кучи, сначала создайте набор данных с n элементами в большую корневую кучу или маленькую корневую кучу (я ввожу его в соответствии с большой корневой кучей, и маленькая корневая куча - это та же идея).
- Затем поменяйте местами номер корневого узла с последней цифрой кучи.На данный момент n-я цифра является самым большим числом во всей последовательности.
- Затем сконструируйте первые n-1 как элементы, чтобы сформировать большую корневую кучу, а затем обменяйте корневой узел с данными n-1-го бита, чтобы получить вторые по величине данные.На данный момент последние два данных, несомненно, в порядке.
- Затем сформируйте первые n-2 данных в большую корневую кучу и выполняйте цикл до тех пор, пока не останется один элемент, указывая, что числа после первой цифры упорядочены и больше первой цифры, и сортировка завершена.
- 1) Замените 99 на 7 и перестройте большую корневую кучу для кучи, которая исключает 99
- 2) Заменить позиции 35 и 9 и восстановить корни оставшихся 8 чисел.
- 3) Поменяйте местами 24 и 3 и восстановите большую корневую стопку для оставшихся 7 чисел
- 4) Последующий процесс тот же, и, наконец, получается отсортированная куча 0, 1, 3, 5, 7, 9, 10, 12, 24, 35, 99
Сортировать код
/**
* 堆排序
*
* @param array 源数组
*/
public void heapSort(int[] array) {
buildHeap(array);
int tmp;
//要与root结点置换位置元素的索引
int end = array.length - 1;
//n个结点只用构建排序n-1次,最后只有1个元素不用在排序
for (int i = array.length - 1; i > 0; i--) {
tmp = array[0];
array[0] = array[end];
array[end] = tmp;
end--;
//头尾置换后,将堆重新构建为大堆根,置换尾部大元素不参加构建
//因为除了root结点,其他都是由大到小有序的,所以再次构建大根堆时,不用在进行adjust()前的那个循环
adjust(array, 0, end);
}
}
Элемент добавления кучи
При добавлении элементов новые элементы добавляются в конец массива, но после добавления элементов характер кучи может быть разрушен, и результат кучи необходимо скорректировать в сторону увеличения . Например, добавление элемента 100 в большую кучу корневых свай.
В это время структура сваи разрушена и ее нужно откорректировать снизу вверх. Поскольку 100 больше 0,5,99, новый корень
元素添加代码
/**
* 在 array 是大堆根的前提下添加元素然后重构大堆根
*
* @param array 大堆根数组
* @param value 添加的元素值
*/
private void addHeap(int[] array, int value) {
int[] arr = new int[array.length + 1];
System.arraycopy(array, 0, arr, 0, array.length);
arr[arr.length - 1] = value;
int currentIndex = arr.length - 1;
int parentIndex = (arr.length - 1) / 2;
while (parentIndex >= 0) {
if (value > arr[parentIndex]) {
int temp = arr[parentIndex];
arr[parentIndex] = value;
arr[currentIndex] = temp;
//如果最后一个元素的父结点还有父结点需要继续进行对比
currentIndex = parentIndex;
parentIndex = (currentIndex - 1) / 2;
} else {
break;
}
}
}
Элемент удаления кучи
Все элементы удаления кучи удаляются из узла, а затем последний элемент массива с этим узлом в качестве корневого узла перемещается в положение корневого узла, и структура кучи корректируется вниз, пока порядок кучи снова не будет соблюден. Если мы удалим узел 35, переместите 7 в позицию 35 и
сравните 7 с его дочерними узлами один за другим, пока он не будет соответствовать правилу большой корневой кучи.
Удалить код элемента
/**
* 在 array 是大堆根的前提下删除元素然后重构大堆根
*
* @param array 大堆根数组
* @param deleteIndex 删除元素的索引
*/
private int[] deleteHeap(int[] array, int deleteIndex) {
array[deleteIndex] = array[array.length - 1];
int[] arr = new int[array.length - 1];
System.arraycopy(array, 0, arr, 0, array.length - 1);
int lefeIndex = 2 * deleteIndex + 1;
while (lefeIndex >= arr.length - 1) {
int maxIndex = lefeIndex;
if (arr.length - 1 > lefeIndex) {
if (arr[lefeIndex + 1] > arr[lefeIndex]) {
maxIndex = lefeIndex + 1;
}
}
if (arr[maxIndex] > arr[deleteIndex]) {
int temp = arr[maxIndex];
arr[maxIndex] = arr[deleteIndex];
arr[deleteIndex] = temp;
lefeIndex = 2 * maxIndex + 1;
} else {
break;
}
}
return arr;
}
博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收**藏 ^ _ ^ !