Dia 10 de teste de algoritmo: recursão/retrocesso - 1

Índice

1. Mesclar duas listas vinculadas ordenadas

1. Recursão

Ideias

Análise de complexidade

2. Iteração

Ideias

Análise de complexidade

1. Mesclar duas listas vinculadas ordenadas

21. Mesclar duas listas vinculadas ordenadas - LeetCode https://leetcode.cn/problems/merge-two-sorted-lists/?plan=algorithms&plan_progress=gzwnnxs

1. Recursão

Ideias

Podemos definir recursivamente a operação de mesclagem em duas listas vinculadas da seguinte maneira (ignorando casos extremos, como listas vinculadas vazias, etc.):


list1[0]+merge(list1[1:],list2) list1[0]<list2[0]
list2[0]+merge(list1,list2[1:]) caso contrário
Em outras palavras
  , dois cabeçalhos de lista vinculados O o nó com o valor menor é mesclado com o resultado da operação de mesclagem dos elementos restantes.

algoritmo

Modelamos diretamente o processo recursivo acima e precisamos considerar casos limite.

Se l1 ou l2 for uma lista vinculada vazia desde o início, nenhuma operação precisará ser mesclada, portanto, só precisamos retornar uma lista vinculada não vazia. Caso contrário, temos que determinar qual nó principal da lista vinculada tem um valor menor, l1 ou l2, e então determinar recursivamente o próximo nó a ser adicionado ao resultado. Se uma das duas listas vinculadas estiver vazia, a recursão termina.

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == nullptr) {
            return l2;
        } else if (l2 == nullptr) {
            return l1;
        } else if (l1->val < l2->val) {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        } else {
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
    }
};

Análise de complexidade

Complexidade de tempo: O(n+m), onde n e m são os comprimentos das duas listas vinculadas, respectivamente. Como cada chamada recursiva removerá o nó principal de l1 ou l2 (até que pelo menos uma lista vinculada esteja vazia), a função mergeTwoList chamará cada nó recursivamente apenas uma vez, no máximo. Portanto, a complexidade do tempo depende do comprimento da lista vinculada mesclada, que é O(n+m).

Complexidade espacial: O(n+m), onde nn e mm são os comprimentos das duas listas vinculadas, respectivamente. Chamar recursivamente a função mergeTwoLists requer espaço de pilha. O tamanho do espaço de pilha depende da profundidade da chamada recursiva. Ao finalizar a chamada recursiva, a função mergeTwoLists é chamada no máximo n+m vezes, portanto a complexidade do espaço é O(n+m).

2. Iteração


Ideias

Podemos implementar o algoritmo acima usando um método iterativo. Quando nem l1 nem l2 são uma lista vinculada vazia, determine qual nó principal da lista vinculada tem um valor menor e adicione o nó com o valor menor ao resultado. Quando um nó for adicionado ao resultado, adicione a lista vinculada correspondente ao resultado. O nó é movido uma posição para trás.

algoritmo

Primeiro, definimos um pré-cabeçalho do nó sentinela, o que nos permite retornar a lista vinculada mesclada mais facilmente no final. Mantemos um ponteiro anterior e o que precisamos fazer é ajustar o próximo ponteiro. Em seguida, repetimos o seguinte processo até que l1 ou l2 aponte para nulo: se o valor do nó atual de l1 for menor ou igual a l2, conectamos o nó atual de l1 à parte de trás do nó anterior e movemos l1 ponteiro um pouco para trás. Caso contrário, fazemos o mesmo para l2. Não importa qual elemento conectamos na parte de trás, precisamos mover o anterior para trás.

Quando o loop termina, pelo menos um de l1 e l2 não está vazio. Como as duas listas vinculadas de entrada são ordenadas, não importa qual lista vinculada não esteja vazia, todos os elementos que ela contém são maiores do que todos os elementos nas listas vinculadas anteriormente mescladas. Isso significa que simplesmente acrescentamos a lista vinculada não vazia à lista mesclada e retornamos a lista mesclada.

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* preHead = new ListNode(-1);

        ListNode* prev = preHead;
        while (l1 != nullptr && l2 != nullptr) {
            if (l1->val < l2->val) {
                prev->next = l1;
                l1 = l1->next;
            } else {
                prev->next = l2;
                l2 = l2->next;
            }
            prev = prev->next;
        }

        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev->next = l1 == nullptr ? l2 : l1;

        return preHead->next;
    }
};

Análise de complexidade

Complexidade de tempo: O(n+m), onde n e m são os comprimentos das duas listas vinculadas, respectivamente. Como apenas um elemento de l1 e l2 será colocado na lista vinculada mesclada em cada iteração do loop, o número de loops while não excederá a soma dos comprimentos das duas listas vinculadas. Todas as outras operações têm complexidade de tempo constante, portanto a complexidade de tempo total é O(n+m).

Complexidade espacial: O(1). Precisamos apenas de espaço constante para armazenar diversas variáveis.

Acho que você gosta

Origin blog.csdn.net/m0_63309778/article/details/126753779
Recomendado
Clasificación