Seleccione ordenar y ordenar en montón
1. Orden de selección
1. Principio
La selección y ordenación es relativamente simple, es decir, después de cada recorrido, se intercambia un valor máximo (mínimo) del área desordenada con el primer o último valor del área desordenada. De esta manera, el área desordenada se ordena gradualmente, y finalmente Complete la clasificación. Su complejidad de tiempo también es O (n 2) O (n ^ 2)O ( n2 )
2. Proceso
3. Código
int selectsort(int arry[], int len)
{
int min, i, j, temp;
for(i = 0; i < len - 1; ++i)
{
min = i;
for (j = i; j < len; ++j)
{
if (arry[j] < arry[min])
{
min = j;
}
}
temp = arry[i];
arry[i] = arry[min];
arry[min] = temp;
}
}
2. Orden de montón
1. El concepto de montón
Suponga que la secuencia k 1, k 2, ⋅ ⋅ ⋅, kn (k_1, k_2, \ cdot \ cdot \ cdot, k_n)k1,k2,⋅⋅⋅ ,knHay y solo cuando se cumplen las siguientes relaciones, lo llamamos un montón:
{ki ≤ k 2 i, ki ≤ k 2 i + 1 o {ki ≥ k 2 i, ki ≥ k 2 i + 1 donde (i = 1, 2, ⋅ ⋅ ⋅, n 2) \ begin {cases} k_i ≤ k_ {2i}, \\ k_i ≤ k_ {2i + 1} \ end {cases} o \ begin {cases} k_i ≥ k_ {2i} , \ \ k_i ≥ k_ {2i + 1} \ end {cases} donde (i = 1,2, \ cdot \ cdot \ cdot, \ frac {n} {2}){
kyo≤k2 yo,kyo≤k2 yo + 1o{
kyo≥k2 yo,kyo≥k2 yo + 1Que el ( yo=1 ,2 ,⋅⋅⋅ ,2n)
Tenga en cuenta que el número de secuencia comienza en 1.
El primero es menor o igual que la relación, por eso también lo llamamos: el 小根堆
segundo es mayor o igual que la relación, por eso también lo llamamos:大根堆
2. La relación entre el montón y el árbol binario completo
Luego, si simulamos un árbol binario completo a través de esta secuencia (matriz), debajo de la estructura del árbol, el montón tiene las siguientes propiedades:
cada nodo padre es más grande (menor que) su nodo hijo, vea la imagen:
suponga que existe esta raíz pequeña montón:
Para "convertirlo" en un árbol binario completo:
No es difícil ver que los nodos secundarios del nodo n son 2n y 2n + 1
3. El proceso de clasificación de pilas
Ahora sabemos que el nodo raíz de este árbol binario de montón (simulado por secuencia secuencial) debe estar 最大(小)
, entonces el elemento debe estar en un estado ordenado. Entonces nuestra idea de clasificación debería ser:
- Construye un montón
- Excluir el elemento del nodo raíz (excluir el elemento superior o intercambiar con el último elemento y excluir)
- Reajuste la estructura para que los elementos restantes formen una pila.
- Repita 2 y 3 hasta que todos los elementos estén en orden
Por supuesto, en el segundo punto, para evitar que se destruya la estructura básica del árbol, elegimos colocar el elemento superior y el elemento final 交换
después del elemento superior del pilote , y clasificamos el elemento final como 有序区
, es decir, excluir y no participar en el reajuste de la estructura del punto 3..
Por lo tanto, sobre esta base, generalmente usamos el orden ascendente 大根堆
y el orden descendente 小根堆
.
Después de una derivación complicada, su complejidad de tiempo es O (nlog 2 n) O (nlog_2n)O ( n l o g2n )
4. Construye un montón
Para inicializar el montón, necesitamos los siguientes pasos:
Realice las siguientes 调整堆
operaciones en todos los nodos principales :
- Si los nodos secundarios son todos más pequeños que el nodo principal, finaliza.
- En el caso de no 1: se intercambia el valor máximo del nodo padre y el nodo hijo, y las operaciones de 1 y 2 se realizan en el nodo hijo después del intercambio.
Sabemos que asumiendo un árbol binario completo lleno de hojas, supongamos que tiene n nodos, entonces el número de nodos de hojas es (n + 1) 2 \ frac {(n + 1)} {2}2( N + 1 ), Entonces el nodo padre tiene (n - 1) 2 \ frac ((n-1)) (2)2( N - 1 )Uno
Luego, suponga que inicializamos el montón, comenzamos desde el último nodo padre y avanzamos (de derecha a izquierda, de abajo hacia arriba) para construir el montón en un bucle.
Desde el punto de vista del código:
// 从最后一个父结点开始,将所有结点给调整一次。
for (i = len/2; i > 0; --i)
{
HeapAdjust(heapArry, i, len);
}
5. Ajustar el montón
Ajustamos el montón, pensamientos y dijimos:
- Si los nodos secundarios son todos más pequeños que el nodo principal, finaliza.
- En el caso de no 1: se intercambia el valor máximo del nodo padre y el nodo hijo, y las operaciones de 1 y 2 se realizan en el nodo hijo después del intercambio.
el código se muestra a continuación:
int HeapAdjust(int arry[], int index, int len)
{
int i;
arry[0] = arry[index]; // arry[0] 没被使用,刚好可以拿来当交换时的临时变量用
for (i = 2*index; i <= len; i*=2) // 从该结点的左孩子开始,且每次循环都直接到孩子的孩子,且i肯定不能超过树的大小
{
if (i < len && arry[i] < arry[i+1]) // 这里是判断i当前孩子是左孩子大还是右孩子大,将i指向最大的孩子
{
++i;
}
// 把上者<改成>,下者>=改为<=,则该堆排序变成降序
if (arry[0] >= arry[i]) // 如果我们的处理的结点大于了孩子结点,那么就必须交换。
{
break;
}
else
{
arry[index] = arry[i]; // 我们处理的结点被孩子顶替
index = i; // 我们新处理的结点变成了被顶替的孩子位置
}
}
arry[index] = arry[0]; // 一直到了最后,孩子一直顶替,直到没法顶替了,就是我们最开始待处理结点的位置
}
Leyenda:
Supongamos que solo tenemos el nodo raíz sin ajustar, ahora necesitamos ajustar el nodo con índice = 1:
primero ingrese y determine el estado de nuestro elemento: 5 se debe ajustar, 7 es el punto actual para el hijo:
en este tiempo, [0] <[i], es decir, 5 <7, porque debería haber un ajuste, por lo que [1] debería ser igual a [2], y luego el valor a ajustar ha cambiado de [1] a [2], y el hijo puntiagudo ha cambiado a [4]]:
En este momento, [0] <[i], es decir, 5 <6, porque debería producirse el ajuste, por lo que [2] debería ser igual a [4 ], y luego el ajuste que se va a ajustar ha cambiado de [2] a [4], apuntando a El hijo de ha cambiado a [8] (no existe):
Luego, después de salir del bucle, hemos determinado la posición que se va a ajustar index = 4, por lo que temp es el valor de arry [0] a arry [4]:
Esto completa un ajuste de montón.
6. Código
Para el proceso específico, puede generar HeapSort a la mitad para ver el proceso de clasificación y construcción del montón.
#include <stdio.h>
#include <stdlib.h>
int HeapAdjust(int arry[], int index, int len)
{
int i;
arry[0] = arry[index]; // arry[0] 没被使用,刚好可以拿来当交换时的临时变量用
for (i = 2*index; i <= len; i*=2) // 从该结点的左孩子开始,且每次循环都直接到孩子的孩子,且i肯定不能超过树的大小
{
if (i < len && arry[i] < arry[i+1]) // 这里是判断i当前孩子是左孩子大还是右孩子大,将i指向最大的孩子
{
++i;
}
// 把上者<改成>,下者>=改为<=,则该堆排序变成降序
if (arry[0] >= arry[i]) // 如果我们的处理的结点大于了孩子结点,那么就必须交换。
{
break;
}
else
{
arry[index] = arry[i]; // 我们处理的结点被孩子顶替
index = i; // 我们新处理的结点变成了被顶替的孩子位置
}
}
arry[index] = arry[0]; // 一直到了最后,孩子一直顶替,直到没法顶替了,就是我们最开始待处理结点的位置
}
int HeapSort(int arry[], int len)
{
int i = 1;
int *heapArry = (int*)malloc(sizeof(int) * (len+1)); // 构造一个从下标1开始的序列。
for (i = 1; i <= len; ++i)
{
heapArry[i] = arry[i - 1];
}
// 从最后一个父结点开始,将所有结点给调整一次。
for (i = len/2; i > 0; --i)
{
HeapAdjust(heapArry, i, len);
}
// 堆排序。
for (i = len; i > 0; --i)
{
arry[i-1] = heapArry[1]; // 堆顶是我们的最大元素,赋值给原数组
heapArry[1] = heapArry[i]; // 因为是交换,所以要把最后一个元素给堆顶,堆顶给最后一个元素(有序),但我们可以舍弃这个保存,因为存到了老数组里
HeapAdjust(heapArry, 1, i - 1); // i之后的结点是有序的(尽管没有赋值),所以不参与堆的构造
}
}
int main()
{
int a[7] = {
5,6,3,7,2,1,4};
int i;
HeapSort(a, 7);
for (i = 0; i < 7; ++i)
{
printf("%d ", a[i]);
}
return 0;
}