leetcode task01-divide y conquista

1. Información general

La idea principal del algoritmo divide y vencerás es dividir el problema original de forma recursiva en varios subproblemas hasta que los subproblemas cumplan las condiciones de contorno y detengan la recursividad. Divida las subpreguntas una por una (generalmente el mismo método), combine las subpreguntas que se han resuelto y, finalmente, el algoritmo fusionará capa por capa para obtener la respuesta a la pregunta original.

Los pasos del algoritmo divide y vencerás:

  • Dividir: Descomponer de forma recursiva el problema en subproblemas (subproblemas de la misma naturaleza e independientes entre sí);
  • Solución: analice estos subproblemas más pequeños uno por uno;
  • Combinar: fusionar los subproblemas resueltos capa por capa, y finalmente obtener la solución del problema original;

2.Ejercicio del tema de Leetcode

2.1 Pregunta 169. La mayoría de los elementos

Inserte la descripción de la imagen aquí
Antes de dividir y conquistar, mi idea es filtrar las características de los elementos repetidos en la lista de manera eficiente a través del conjunto de Python, y luego el método incorporado cuenta para generar el modo único.

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        for i in set(nums):
            if nums.count(i)>len(nums)/2:
                return i
                break

Los resultados parecen ser bastante buenos.
Inserte la descripción de la imagen aquí
Utilice divide y vencerás (mayor complejidad de tiempo):

class Solution(object):
    def majorityElement2(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # 【不断切分的终止条件】
        if not nums:
            return None
        if len(nums) == 1:
            return nums[0]
        # 【准备数据,并将大问题拆分为小问题】
        left = self.majorityElement(nums[:len(nums)//2])
        right = self.majorityElement(nums[len(nums)//2:])
        # 【处理子问题,得到子结果】
        # 【对子结果进行合并 得到最终结果】
        if left == right:
            return left
        if nums.count(left) > nums.count(right):
            return left
        else:
            return right 

La solución óptima a este problema debería ser el método de votación de Moore (la complejidad del tiempo es O (n))

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        """
        :type nums: List[int]
        :rtype: int
        """
        major = 0
        count = 0
        for n in nums:
            if count == 0:
                major = n
            if n == major:
                count = count + 1
            else:
                count = count - 1

        return major

2.2 Problema 53: Suma máxima de suborden

Inserte la descripción de la imagen aquí
En primer lugar, utilizo el método de pseudo-retroceso, la temperatura es una suma continua, si la temperatura es menor que cero, significa que la corriente definitivamente no es la subcadena de suma máxima, y ​​el punto de partida de la suma se restablece al número actual. La subcadena de suma máxima se obtendrá en el valor máximo constante.
Complejidad temporal O (n)

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        
        temp=nums[0]
        max_=temp
        for i in range(1,len(nums)):
            if temp>0:                
                temp+=nums[i]
                max_=max(max_,temp)

            else:
                temp=nums[i]
                max_=max(max_,temp)
        return max_
        

Divide y conquista más sutil

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        if n == 1:
            return nums[0]

        # 【准备数据,并将大问题拆分为小的问题】
        left = self.maxSubArray(nums[:len(nums)//2])
        right = self.maxSubArray(nums[len(nums)//2:])

        # 【处理小问题,得到子结果】
        # 从右到左计算左边的最大子序和
        max_l = nums[len(nums)//2 -1] # max_l为该数组的最右边的元素
        tmp = 0 # tmp用来记录连续子数组的和
        
        for i in range( len(nums)//2-1 , -1 , -1 ):# 从右到左遍历数组的元素
            tmp += nums[i]
            max_l = max(tmp ,max_l)
            
        # 从左到右计算右边的最大子序和
        max_r = nums[len(nums)//2]
        tmp = 0
        for i in range(len(nums)//2,len(nums)):
            tmp += nums[i]
            max_r = max(tmp,max_r)
            
        # 【对子结果进行合并 得到最终结果】
        # 返回三个中的最大值
        return max(left,right,max_l+ max_r)

2.3 Problema 50 Pow (x, n)

Inserte la descripción de la imagen aquí

Algoritmo de potencia rápida (complejidad temporal de dividir y conquistar O (n))

class Solution:
    def myPow(self, x: float, n: int) -> float:
        if x == 0.0: return 0.0
        res = 1
        if n < 0: x, n = 1 / x, -n
        while n:
            if n & 1: res *= x
            x *= x
            n >>= 1
        return res

3. Resumen

En informática, divide y vencerás es un algoritmo muy importante. La explicación literal es "divide y vencerás", es decir, dividir un problema complejo en dos o más subproblemas idénticos o similares, y luego dividir los subproblemas en subproblemas más pequeños ... hasta que los subproblemas finales se puedan resolver simple y directamente. , La solución del problema original es la combinación de las soluciones de los subproblemas. Esta técnica es la base de muchos algoritmos eficientes, como los algoritmos de clasificación (clasificación rápida, clasificación por fusión), transformada de Fourier (transformada rápida de Fourier).
Si el problema original se puede dividir en k subproblemas, 1 <k≤n, y estos subproblemas se pueden resolver y las soluciones de estos subproblemas se pueden usar para encontrar la solución del problema original, entonces este método de dividir y vencer es factible. Los subproblemas generados por el método de divide y vencerás a menudo son modelos más pequeños del problema original, lo que brinda conveniencia para usar técnicas recursivas. En este caso, la aplicación repetida de métodos de divide y vencerás puede hacer que el subproblema sea coherente con el tipo de problema original, pero su escala continúa reduciéndose y, finalmente, el subproblema se reduce al punto en que es fácil encontrar su solución directamente. Esto naturalmente conduce a un proceso recursivo. Dividir y conquistar y la recursividad son como hermanos gemelos, que a menudo se utilizan en el diseño de algoritmos al mismo tiempo, y muchos algoritmos eficientes se producen a partir de ellos.

Los problemas que puede resolver el método divide y vencerás generalmente tienen las siguientes características:

1) 该问题的规模缩小到一定的程度就可以容易地解决

2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。

3) 利用该问题分解出的子问题的解可以合并为该问题的解;

4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

Algunos problemas clásicos que se pueden resolver usando divide y vencerás

(1) Búsqueda binaria
(2) Multiplicación de números enteros grandes
(3) Multiplicación de matrices de Strassen
(4) Cubierta de tablero de ajedrez
(5) Clasificación por fusión
(6) Clasificación rápida
(7) Selección de tiempo lineal

(8) Problema de pares de puntos más cercanos
(9) Programa de round robin
(10) Torre de Hanoi

referencia

Explicación detallada del algoritmo divide y vencerás (super detallada)

Este material de aprendizaje proviene de datawhale y LeetCode

Supongo que te gusta

Origin blog.csdn.net/hu_hao/article/details/108088795
Recomendado
Clasificación