Pular para o conteudo principal

Subárvore de outra árvore

Como procurar uma subarvore de verdade comparando forma e valores, não só nos que parecem bater.

Dadas duas árvores binarias root e subRoot, retorne true se existir um no em root cuja subarvore seja exatamente igual a subRoot.

Igual aqui significa:

  • mesmos valores
  • mesma estrutura

Exemplo:

Input:
  root = [3,4,5,1,2]
  subRoot = [4,1,2]

Output:
  true

O que perceber antes de codar

Erros comuns

Passo 1 - Entender o que "ser subarvore" realmente pede

Muita gente le isso como:

  • "tem um no com o mesmo valor?"

Não e isso.

O ponto e:

  • a partir de algum no de root
  • a estrutura inteira precisa bater com subRoot

Passo 2 - Escrever uma versão inicial bem direta

Uma versão inicial possível e:

  • serializar subRoot
  • serializar cada subarvore de root
  • comparar as strings

Funciona se você colocar marcadores de null.

Mas repete serialização demais.

Passo 3 - Separar as duas perguntas

Em qualquer no de root, existem duas perguntas diferentes:

  • esta árvore e exatamente igual a subRoot?
  • se não for aqui, a resposta esta na esquerda ou na direita?

Quando você separa isso, a recursão fica limpa.

Passo 4 - Criar uma função auxiliar de igualdade estrutural

A função de igualdade precisa checar:

  • os dois null ao mesmo tempo
  • valor atual
  • esquerda com esquerda
  • direita com direita

Depois disso, a busca principal só tenta essa comparação em cada candidato.

O editor interativo precisa de JavaScript. Voce ainda pode ler o desafio e copiar o codigo inicial abaixo.

Codigo inicial

export function isSubtree(root, subRoot) {
  // root and subRoot are objects like { val: number, left: ..., right: ... } or null
  // sua solução aqui
  return false
}
solution.ts
Travado? Dicas disponiveis.

Ainda não resolveu?

Ver a solução agora pode reduzir o aprendizado. Vale a pena tentar mais um pouco.

Abrir a solucao de referencia

Sem JavaScript, a solucao de referencia aparece inline em vez de um dialogo.

Solução

Complexidade final

Tempo: O(n * m)

Espaço: O(h)

Solução 1 - Serializar cada candidata e comparar

Boa versão inicial porque deixa visível que o problema pede estrutura, não só valores.

function serialize(node): string {
  if (node === null) {
    return "#"
  }

  return `${node.val},${serialize(node.left)},${serialize(node.right)}`
}

export function isSubtree(root, subRoot) {
  if (subRoot === null) {
    return true
  }

  const target = serialize(subRoot)
  const candidates: string[] = []

  function collect(node) {
    if (node === null) {
      return
    }

    candidates.push(serialize(node))
    collect(node.left)
    collect(node.right)
  }

  collect(root)

  return candidates.includes(target)
}

Funciona.

Mas reserializa várias subarvores inteiras sem necessidade.

Solução 2 - Comparação estrutural com busca em DFS por recursao

Agora cada no de root só vira um candidato de verdade quando a comparação faz sentido.

function isSameTree(a, b) {
  if (a === null && b === null) {
    return true
  }

  if (a === null || b === null) {
    return false
  }

  return (
    a.val === b.val &&
    isSameTree(a.left, b.left) &&
    isSameTree(a.right, b.right)
  )
}

export function isSubtree(root, subRoot) {
  if (subRoot === null) {
    return true
  }

  if (root === null) {
    return false
  }

  if (isSameTree(root, subRoot)) {
    return true
  }

  return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot)
}

O insight forte aqui e que a busca e sobre candidatos, mas a confirmação e sempre estrutural.

O que dizer na entrevista

Uma explicação curta e boa seria:

Eu separo o problema em duas funções. isSameTree responde se duas árvores sao exatamente iguais em valor e estrutura. isSubtree percorre a árvore maior procurando um ponto onde essa comparação completa passe. Isso evita tratar um valor igual como se ele já provasse a subarvore.

Isso mostra que você:

  • entendeu a diferença entre valor local e igualdade estrutural
  • decompos o problema em duas responsabilidades claras
  • explicou a recursão sem virar teatro

Quando esse padrão aparece de novo

Esse padrão reaparece quando você precisa:

  • comparar estruturas recursivas
  • distinguir candidato local de confirmação global
  • usar função auxiliar para igualdade de árvore ou lista
  • reconhecer quando “parece igual” ainda não prova nada

O ponto não e decorar isSubtree.

O ponto e aprender a separar busca de confirmação estrutural.

Proximo desafio K-ésimo menor elemento em uma árvore binária de busca Desafio anterior Clonar grafo

Desafios relacionados

Artigos relacionados