[LeetCode] -Búsqueda binaria

prefacio

Registre las preguntas relacionadas con la búsqueda binaria encontradas al cepillar LeetCode

704.Búsqueda binaria

Los requisitos previos para la búsqueda binaria son: matriz ordenada + sin elementos duplicados. Generalmente, la búsqueda binaria se utiliza para preguntas de búsqueda que cumplen estas dos condiciones. Tablero de dos partes: el límite izquierdo es -1, el límite derecho es la longitud de la matriz, la condición del bucle es izquierda + 1 <derecha, la posición media mid = (izquierda + derecha) >> 1

public static int search(int[] nums, int target) {
    
    
    int length = nums.length;
    int left = -1;
    int right = length;
    while (left + 1 < right){
    
    
        int mid = (left + right) >> 1;
        if(nums[mid] == target){
    
    
            return mid;
        }else if (nums[mid] > target){
    
    
            right = mid;
        }else{
    
    
            left = mid;
        }
    }
    return -1;
}

35.Buscar posición de inserción

La misma idea que 704, excepto que si no se encuentra al final, devolverá el subíndice de la posición donde se inserta el valor objetivo en la matriz en orden.

public static int searchInsert(int[] nums, int target) {
    
    
    int length = nums.length;
    int left = -1;
    int right = length;
    while (left + 1 < right){
    
    
        int mid = (left + right) >> 1;
        if(nums[mid] == target){
    
    
            return mid;
        }else if (nums[mid] > target){
    
    
            right = mid;
        }else{
    
    
            left = mid;
        }
    }
    //最后没有查找到的话,此时left和right应该是left + 1 == right的状态,按顺序插入,就是插在right的位置
    return left + 1;
}

34. Encuentra la primera y última posición de un elemento en una matriz ordenada.

Todavía se basa en la idea de búsqueda binaria, excepto que si se encuentra, es necesario encontrar la posición inicial y final del elemento.

public int[] searchRange(int[] nums, int target) {
    
    
    int length = nums.length;
    int left = -1;
    int right = nums.length;
    while (left + 1 < right){
    
    
        int mid = (left + right) >> 1;
        if(nums[mid] == target){
    
    
        	//如果找到了,从mid开始向左向右遍历该元素出现的位置
            int l = mid,r = mid;
            while(nums[l] == nums[mid] && l > 0){
    
    
                l--;
            }
            if(nums[l] != nums[mid]){
    
    
                l++;
            }
            while (nums[r] == nums[mid] && r < length - 1){
    
    
                r++;
            }
            if(nums[r] != nums[mid]){
    
    
                r--;
            }
            return new int[]{
    
    l,r};
        }else if (nums[mid] > target){
    
    
            right = mid;
        }else{
    
    
            left = mid;
        }
    }
    return new int[]{
    
    -1,-1};
}

367. Números cuadrados perfectos válidos

Al igual que la pregunta 69, la raíz del valor máximo de int es 46340, y los límites izquierdo y derecho de la plantilla de búsqueda binaria a la que estoy acostumbrado son el límite izquierdo del intervalo de valores menos uno y el límite derecho más uno, por lo que la izquierda es 0 y a la derecha es 46341

public static boolean isPerfectSquare(int num) {
    
    
    int left = 0;
    int right = 46341;
    while (left + 1 < right){
    
    
        int mid = (left + right) >> 1;
        int multi = mid * mid;
        if(multi == num){
    
    
            return true;
        }else if(multi > num){
    
    
            right = mid;
        }else{
    
    
            left = mid;
        }
    }
    return false;
}

153.Encontrar el valor mínimo en una matriz ordenada rotada

Utilice la búsqueda binaria. Intente simular el proceso de varias rotaciones y podrá encontrar que si el elemento del medio es más pequeño que el elemento más a la derecha del intervalo de búsqueda actual, significa que el valor mínimo debe estar a la izquierda del elemento del medio (incluido este elemento del medio). , tal vez sea él); si el elemento del medio es mayor que el actual Encuentre el valor del elemento más a la derecha en el intervalo, entonces el valor mínimo debe estar a la izquierda del elemento del medio (después de todo, este elemento del medio no está incluido , sabes que hay un elemento más pequeño que él). No hay elementos repetidos , por lo que solo existen estas dos condiciones de juicio y luego puede escribir el código de dos partes:

public int findMin(int[] nums) {
    
    
    int len = nums.length;
    int left = 0;
    int right = len - 1;
    while(left < right){
    
    
        int mid = (left + right) >> 1;
        //根据条件收缩区间
        if(nums[mid] < nums[right]){
    
    
            right = mid;
        }else{
    
    
            left = mid + 1;
        }
    }
    return nums[left];
}

154.Encontrar el valor mínimo en una matriz ordenada rotada II

Seguimos la idea de la pregunta 153. En este momento, al juzgar la relación de tamaño entre el elemento medio y el elemento más a la derecha del intervalo, surge una tercera posibilidad: los dos son iguales, en este momento no se puede juzgar de inmediato. que el valor mínimo está en el elemento del medio. El lado izquierdo sigue siendo el lado derecho, como se muestra en la siguiente figura (de la solución oficial de LeetCode), por lo que solo podemos elegir
Insertar descripción de la imagen aquí
la forma más segura de reducir el intervalo: desde nums[ mid] == nums[right], entonces podemos descartar completamente nums[right], porque al menos nums[mid] puede reemplazarlo (solo necesitamos encontrar el valor mínimo del intervalo, y puede haber más de uno elemento con un valor igual al valor mínimo. Solo necesitamos uno, los demás se pueden descartar a voluntad), por lo que el intervalo La forma de reducir es reducir a la derecha en uno

public int findMin(int[] nums) {
    
    
    int len = nums.length;
    int left = 0;
    int right = len - 1;
    while(left < right){
    
    
        int mid = (left + right) >> 1;
        if(nums[mid] < nums[right]){
    
    
            right = mid;
        }else if(nums[mid] > nums[right]){
    
    
            left = mid + 1;
        }else{
    
     //第三个判断条件
            right--;
        }
    }
    return nums[left];
}

34. Encuentre la primera y última posición del elemento en una matriz ordenada

Originalmente quería leer la solución directamente, pero descubrí que para reutilizar el código, las dos operaciones de encontrar la primera posición de un elemento y encontrar la última posición de un elemento se colocaron en un método. Pensé que sería más claro. para separarlos, así que decidí escribir la respuesta yo mismo. :

class Solution {
    
    
    public int[] searchRange(int[] nums, int target) {
    
    
        return new int[]{
    
    findFirstTarget(nums,target),findLastTarget(nums,target)};
    }
    int findFirstTarget(int[] nums,int target){
    
    
        int l = 0,r = nums.length - 1,mid;
        //当l跟r重叠的时候,重叠的位置可能就是满足条件的位置,所以用 <=
        while(l <= r){
    
    
            mid = (l + r) >> 1;
		    //查找元素出现的第一个位置,就是查找这么一个位置,满足这个位置上的数等于 target,
		    //而前一个数小于 target。还要注意这个位置可能就在下标为 0 处
            if( (nums[mid] == target && mid == 0) || (nums[mid] == target && nums[mid - 1] < target)){
    
    
                return mid;
            }else if(nums[mid] >= target){
    
    
            	//初始l跟r分别是0跟nums.length-1,说明l跟r的位置也可能是符合条件的位置,也即后续可能要查找的位置,那么这里mid已经查找过了,后面也不用再查找了,所以更新l跟r时不更新为mid
                r = mid - 1;
            }else{
    
    
                l = mid + 1;
            }
        }
        return -1;
    }

    int findLastTarget(int[] nums,int target){
    
    
        int l = 0,r = nums.length - 1,mid;
        while(l <= r){
    
    
            mid = (l + r) >> 1;
            //查找元素出现的最后一个位置,就是查找这么一个位置,满足这个位置上的数等于 target,
		    //而后一个数大于 target。还要注意这个位置可能就在下标为 nums.length - 1 处
            if( (nums[mid] == target && mid == nums.length - 1) || (nums[mid] == target && nums[mid + 1] > target) ){
    
    
                return mid;
            }else if(nums[mid] <= target){
    
    
                l = mid + 1;
            }else{
    
    
                r = mid - 1;
            }
        }
        return -1;
    }
}

528. Selección aleatoria por peso (suma de prefijo + búsqueda binaria)

Sea total la suma de la matriz w. Dado que el peso de cada w [i] es w [i] / total, el intervalo [1, total] se puede dividir en varias partes, la longitud de cada parte es w [i] y luego en [1, total] Seleccione aleatoriamente un número en el intervalo, vea a qué parte pertenece el número y luego devuelva el subíndice de w [i] correspondiente a esta parte.

Por ejemplo, para w = [3,1,2,4], total = 10, puede hacer que [1,3] corresponda a 3, [4,4] corresponda a 1, [5,6] corresponda a 2, y [7,10] corresponden a 4. Si el número aleatorio generado es 8, significa que el número correspondiente es 4 y el subíndice a devolver es 3.

Entonces, ¿cómo asignar el número aleatorio a su parte correspondiente? La respuesta es usar la suma del prefijo para
generar la suma del prefijo para la matriz w para obtener sumaprefijo = [3,4,6,10]. Se puede encontrar que si i se encuentra tal que i > 0 y prefixSum[ i] >= x && prefixSum[i - 1] < x, o i == 0 y prefixSum[i] >= x, entonces este i es el subíndice que se devolverá

Entonces el prefijo y la matriz son crecientes, por lo que puede usar la búsqueda binaria para buscar

class Solution {
    
    
    int[] prefixSum;
    int total;
    public Solution(int[] w) {
    
    
        prefixSum = new int[w.length];
        prefixSum[0] = total = w[0];
        for (int i = 1; i < w.length; ++i) {
    
     //计算w数组总和以及前缀和
            prefixSum[i] = prefixSum[i - 1] + w[i];
            total += w[i];
        }
    }
    public int pickIndex() {
    
    
        int x = (int) (Math.random() * total) + 1; //获取一个[1,total]中的数
        return binarySearch(x);
    }
    private int binarySearch(int x) {
    
     //二分查找x所属的部分对应的是哪个数
        int low = 0, high = prefixSum.length - 1;
        while (low < high) {
    
    
        	/*
        	为了便于理解可以加入这段代码
            int mid = (high + low) >> 1;
            if( (prefixSum[mid] >= x && mid == 0) || (prefixSum[mid] >= x && mid > 0 && prefixSum[mid - 1] < x) ){
                return mid;
            }
        	*/
            int mid = (high + low) >> 1;
            if (prefixSum[mid] < x) {
    
    
                low = mid + 1;
            } else {
    
    
                high = mid;
            }
        }
        return high; //return low 也是一样的,最终low跟high一定会相等
    }
}

33. Buscando una matriz ordenada rotada

public int search(int[] nums, int target) {
    
    
    int len = nums.length;
    if (len == 1) {
    
    
        return nums[0] == target ? 0 : -1;
    }
    int l = 0, r = len - 1;
    while (l <= r) {
    
    
        int mid = (l + r) >> 1;
        if (nums[mid] == target) return mid;
        if (nums[mid] > nums[0]) {
    
     //此时左边肯定是升序区间
            if (nums[0] <= target && target < nums[mid]) {
    
    
                r = mid - 1;
            } else {
    
    
                l = mid + 1;
            }
        } else {
    
      //此时右边肯定是升序区间
            if (nums[mid] < target && target <= nums[n - 1]) {
    
    
                l = mid + 1;
            } else {
    
    
                r = mid - 1;
            }
        }
    }
    return -1;
}

81. Búsqueda de matriz ordenada rotada II

En comparación con 33. Al buscar en la matriz ordenada rotada, la matriz en la pregunta 33 está en estricto orden ascendente, mientras que la matriz en esta pregunta está en orden no descendente, por lo que habrá elementos duplicados; otra diferencia es que la pregunta 33 devuelve el subíndice del elemento específico, y esta pregunta solo necesita devolver un valor booleano que indique si el objetivo existe, lo que significa que podemos eliminar elementos duplicados en la matriz.

Luego, debido a la existencia de elementos repetidos, podemos encontrar nums [mid] == nums [l] durante la dicotomía. En este momento, no podemos juzgar la monotonicidad de los dos intervalos alrededor de la mitad, y no podemos juzgar a qué intervalo ir. Dividido en dos, puedes "eliminar" números [l] en este momento, es decir, l ++

Siempre que nums[mid] no sea igual a nums[l], si nums[l] < nums[mid], se puede determinar que el lado izquierdo de mid es un intervalo no decreciente; si nums[l] > nums [mid], se puede determinar que mid El lado derecho es el intervalo no decreciente. Si puede juzgar la monotonicidad del intervalo, puede dividirlo en dos partes.

public boolean search(int[] nums, int target) {
    
    
    if(nums == null || nums.length == 0) {
    
    
        return false;
    }
    int l = 0,r = nums.length - 1,mid = 0;
    while(l <= r) {
    
    
        mid = (l + r) >> 1;
        if(nums[mid] == target) {
    
    
            return true;
        }
        if(nums[l] == nums[mid]) {
    
     //此时影响区间单调性的判断,删除掉nums[l]
            l++;
            continue;
        }
        if(nums[l] < nums[mid]) {
    
     //此时能判断出区间单调性:mid左边为非递减区间
            if (nums[mid] > target && nums[l] <= target) {
    
     //那么就可以判断target是否在mid左边
                r = mid - 1;
            }else {
    
    
                l = mid + 1;
            }
        }else{
    
     //此时mid右边为非递减区间
            if(nums[mid] < target && nums[r] >= target) {
    
     //判断target是否在mid右边
                l = mid + 1;
            }else {
    
     
                r = mid - 1;
            }
        }
    }
    return false;
}

162. Encuentra picos

Código visto en el área de comentarios de la solución oficial . Para tomar prestado un dicho de la solución al problema: "La gente va a lugares más altos y el agua fluye a lugares más bajos." Comenzamos desde una determinada posición, y mientras avancemos en cada paso del camino, eventualmente llegaremos a el pico.

Supongamos que la posición actual es i y nums[i] < nums[i + 1], entonces el siguiente paso será a la posición de i + 1; y luego de llegar a i + 1, ya que nums[i] < nums[i + 1], por lo que no volverá a la posición de i en el futuro, y mucho menos a la posición a la izquierda de i, y todas las posiciones a la izquierda de i pueden eliminarse directamente. Si resulta que i es el punto medio de la matriz original, ¿no equivale a excluir directamente la mitad de las posiciones de la matriz? Es muy similar a la bisección. Entonces, partiendo de esta idea, existe la siguiente dicotomía:

public int findPeakElement(int[] nums) {
    
    
    int l = 0, r = nums.length - 1;
    while (l < r) {
    
    
        int mid = (l + r) >> 1;
        if (nums[mid] < nums[mid + 1]) {
    
    
            l = mid + 1;             //1
        } else {
    
    
            r = mid;                 //2
        }
    }
    return l;                        //3
}

No es difícil entender la división binaria, pero hay algunos detalles que pueden confundir a las personas, como cómo actualizar los límites izquierdo y derecho cada vez que se actualiza el límite y si el valor de retorno final es el puntero izquierdo l. o el puntero derecho r

Cómo actualizar los límites izquierdo y derecho: l y r inicialmente apuntan a 0 y nums.length - 1, por lo que apuntan a valores que pueden convertirse en la respuesta. Después de juzgar que nums [mid] <nums [mid + 1], tenemos que mirar a la derecha de mid, para ver si l se actualiza a mid o mid + 1. Como queremos seleccionar un valor mayor, nums[mid] definitivamente no se seleccionará más adelante, y l apunta a un valor que puede convertirse en la respuesta, por lo que l debería apuntar a mid + 1; y si es la rama else
, Necesito actualizar r. De la misma manera, dado que nums[mid] >= nums[mid + 1], mid + 1 definitivamente no se seleccionará en el futuro, por lo que r se actualiza a mid

Para el valor de retorno final, dado que la condición del bucle while es l < r, también podríamos suponer que lyr finalmente se actualizan a l + 1 == r. En este momento, aún se ingresará al bucle while. Si nums[mid] < nums[mid + 1], l se actualizará a la posición de r y ambos apuntarán a la respuesta final en este momento; de lo contrario, r se actualizará a la posición de l y ambos seguirán apunta a la respuesta final. La respuesta devuelta, en resumen, en realidad puede devolver l o r al final, porque ambos deben apuntar al mismo valor al saltar del bucle, y este valor es la respuesta final.

Supongo que te gusta

Origin blog.csdn.net/Pacifica_/article/details/125247365
Recomendado
Clasificación