Antes de comprender el algoritmo de selección, estamos familiarizados con un término
顺序统计量
. En el conjunto, el i-ésimo se数序统计量
refiere al i-ésimo elemento más pequeño del conjunto.
El problema que trata el algoritmo de selección es encontrar el i-ésimo顺序统计量
problema en el conjunto de n elementos . Sii=3
necesita encontrar el tercer elemento más pequeño del conjunto
Idea básica
Usando la idea de divide y vencerás, basada
快速排序
en el algoritmo de partición en, se顺序统计量
resuelve de forma recursiva según la ubicación que se busque .
- Descomposición del problema: utilice el algoritmo de partición (consulte el
快速排序
capítulo) para particionar la matriz, determine la posición de partición de i y proceda de forma recursiva. - Manejo de problemas: cuando la ubicación de la muestra de partición es igual a i, devuelve el resultado.
Código
La
快速排序
diferencia con el algoritmo de partición del medio es que el algoritmo de partición aquí agrega una función de muestreo aleatorio; consulte el código para obtener más detalles.
/**
* 获取数组任意位置的顺序统计量
* @param array 输入数组
* @param pos 查询位置
* @param start 起始位置
* @param end 结束位置
* @return 元素值
*/
private static int randomSelection(int[] array, int pos, int start, int end) {
//获取随机分区的样本位置
int mid = randomPartition(array, start, end);
//递归结束点,当要获取的 pos=mid 时
if (pos == mid) {
return array[mid];
}
System.out.println("start:" + start + "|end:" + end + "|pos:" + pos + "|mid:" + mid);
//递归情况
if (pos < mid) {
return randomSelection(array, pos, start, mid - 1);
} else {
return randomSelection(array, pos, mid + 1, end);
}
}
/**
* 随机分区
* @param array 分区数组
* @param start 起始位置
* @param end 结束位置
* @return 样本位置
*/
private static int randomPartition(int[] array, int start, int end) {
//计算随机样本位置
int samplePos = random(start, end);
//将随机样本放到数组尾部
int temp = array[end];
array[end] = array[samplePos];
array[samplePos] = temp;
int sample = array[end];
//左侧区域默认为空
int leftEnd = start - 1;
//检测到小于样本值的数据,放入左侧区域
for (int i = start; i < end; i++) {
if (array[i] <= sample) {
leftEnd++;
temp = array[leftEnd];
array[leftEnd] = array[i];
array[i] = temp;
}
}
//将样本中放到中间区域
temp = array[end];
array[end] = array[leftEnd + 1];
array[leftEnd + 1] = temp;
return leftEnd + 1;
}
Conclusión
La idea de dividir y conquistar es realmente buena, pero este código no está completamente
算法导论
implementado yStackOverflow
se encuentra una excepción en el medio Parece que la capacidad del algoritmo debe mejorarse.