Reordenar uma lista mestre com base em um subconjunto reordenado


19

Recentemente, tive um problema para resolver no trabalho, onde tinha duas listas: uma lista principal e uma lista menor que contém um subconjunto dos itens da lista principal, potencialmente em uma ordem diferente. Eu precisava reordenar a lista mestre de forma que os itens do subconjunto aparecessem na mesma ordem sem alterar a ordem dos itens não encontrados na lista e mantendo os itens no mesmo local sempre que possível. Ok, isso provavelmente parece confuso, então eu vou detalhar:

  • A lista principal define a ordem padrão dos itens.
  • A lista de subconjuntos define a ordem relativa de determinados itens.
  • Onde a lista principal tiver dois elementos fora de ordem, de acordo com a lista de subconjuntos, o item anterior na lista principal deverá ser movido para o índice mais antigo, onde estiver no local correto em relação a outros itens da lista de subconjuntos. (ou seja, imediatamente após o item posterior)

Sua tarefa é implementar esse algoritmo de reordenação.

Casos de teste de exemplo

Master: [1, 2, 3]
Subset: []
Result: [1, 2, 3]

Master: [9001, 42, 69, 1337, 420]
Subset: [69]
Result: [9001, 42, 69, 1337, 420]

Master: [9001, 42, 69, 1337, 420, 99, 255]
Subset: [69, 9001, 1337]
Result: [42, 69, 9001, 1337, 420, 99, 255]

Master: [1, 2, 3, 4, 5]
Subset: [2, 5]
Result: [1, 2, 3, 4, 5]

Master: [apple, banana, carrot, duck, elephant]
Subset: [duck, apple]
Result: [banana, carrot, duck, apple, elephant]

Master: [Alice, Betty, Carol, Debbie, Elaine, Felicia, Georgia, Helen, Ilene, Julia]
Subset: [Betty, Felicia, Carol, Julia]
Result: [Alice, Betty, Debbie, Elaine, Felicia, Carol, Georgia, Helen, Ilene, Julia]

Master: [snake, lizard, frog, werewolf, vulture, dog, human]
Subset: [snake, werewolf, lizard, human, dog]
Result: [snake, frog, werewolf, lizard, vulture, human, dog]

Master: [Pete, Rob, Jeff, Stan, Chris, Doug, Reggie, Paul, Alex]
Subset: [Jeff, Stan, Pete, Paul]
Result: [Rob, Jeff, Stan, Pete, Chris, Doug, Reggie, Paul, Alex]

Master: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
Subset: [8, 1, 2, 12, 11, 10]
Result: [3, 4, 5, 6, 7, 8, 1, 2, 9, 12, 11, 10]

Master: [lol, rofl, lmao, roflmao, lqtm, smh, jk, wat]
Subset: [wat, lmao, rofl]
Result: [lol, roflmao, lqtm, smh, jk, wat, lmao, rofl]

Regras

  • Brechas padrão, yadda yadda, E / S conveniente, blá blá.
  • Embora os exemplos usem números e seqüências de caracteres, você só precisa oferecer suporte a um tipo de elemento, sejam números inteiros, seqüências de caracteres ou qualquer outra coisa com semântica de igualdade bem definida, incluindo listas heterogêneas, se for conveniente em seu idioma.
  • Você pode assumir que a lista principal e a lista de subconjuntos não contêm duplicatas
  • Você pode assumir que todos os itens encontrados na lista de subconjuntos são encontrados na lista principal
  • Qualquer lista pode estar vazia
  • Você deve, no mínimo, suportar matrizes de até 100 elementos.
  • A reordenação pode ser implementada no local ou através da criação de uma nova lista / matriz.

Golfe feliz!


1
Um bom e robusto problema.
Jonah

É 8 1 3 4 5 6 7 2 9 12 11 10uma solução válida para a penúltima?
Ven

@Ven Não. Mesmo que isso se enquadre nas restrições de manter os itens do subconjunto na mesma ordem relativa, eu queria ter certeza de que havia apenas uma resposta correta; portanto, o item fora de ordem anterior deve ser movido para ficar após o item fora de ordem posterior.
Beefster 05/04

Por que importa que haja mais de uma resposta correta? Por favor, adicione a restrição às regras do desafio, por favor.
Ven

Respostas:


4

Retina 0.8.2 , 51 bytes

+`(\b(\w+),(\w+)\b.*¶.*\b)\3,(.*\b\2\b)
$1$4,$3
1A`

Experimente online! Recebe entrada como uma lista de subpalavras separada por vírgula na primeira linha e como uma lista principal de palavras separada por vírgula na segunda linha. Explicação:

(\b(\w+),(\w+)\b.*¶.*\b)\3,(.*\b\2\b)

Encontre duas subpalavras adjacentes onde a segunda palavra precede a primeira na lista principal.

$1$4,$3

Mova a segunda palavra para aparecer após a primeira palavra na lista principal.

+`

Repita até que nenhuma palavra apareça fora de ordem.

1A`

Exclua as subpalavras.


4

JavaScript (ES6),  96 89 74  71 bytes

Isso começou como uma bagunça volumosa e acabou sendo reduzido a uma forma bastante concisa e elegante. Gostaria de agradecer ao .splice () método por sua colaboração frutífera nesse. ;)

Toma entrada como (master)(subset). Saídas atualizando a lista principal.

m=>s=>s.map(p=x=>m.splice(p,0,...m.splice(i=m.indexOf(x),p>i||!(p=i))))

Experimente online!

Quão?

Eup

m.splice(p, 0, ...m.splice(i, condition))

1

  • Eu[eeuement]
  • p

0 0

  • o .splice interno () não remove nada e retorna uma matriz vazia
  • como resultado, o .splice externo () recebe indefinido como seu terceiro argumento e nada é inserido

Comentado

m => s =>                 // m[] = master list, s[] = subset list
  s.map(                  //
    p =                   // p = position in the master list of the last element from
                          //     the subset list (initialized to a non-numeric value)
    x =>                  // for each element x in the subset list:
    m.splice(             //   insert in the master list:
      p,                  //     at position p
      0,                  //     without removing any element
      ...m.splice(        //     remove from the master list and flatten:
        i = m.indexOf(x), //       i = position of x in the master list
        p > i             //       if p is greater than i, remove x from its current
                          //       position and insert it at position p
        || !(p = i)       //       otherwise, set p to i and don't remove/insert anything
      )                   //     end of inner splice()
    )                     //   end of outer splice()
  )                       // end of map()

1
"Gostaria de agradecer ao método .splice () por ..." Cue PPCG Oscar's Music ... :)
Chas Brown

Mais corretamente, a chamada de emenda externa recebe 3 ou 2 argumentos respectivamente, o que faz a coisa certa.
Neil

2

Haskell, 79 bytes

(m:n)#u@(s:t)|m==s=m:n#t|all(/=m)u=m:n#u|(x,_:z)<-span(/=s)n=(x++s:m:z)#u
m#_=m

Experimente online!

(m:n)#u@(s:t)                 -- m: head of master list
                              -- n: tail of master list
                              -- s: head of subset
                              -- t: tail of subset
                              -- u: whole subset
   |m==s                      -- if m==s
        =m:n#t                -- return 'm' and append a recursive call with 'n' and 't'
   |all(/=m)u                 -- if 'm' is not in 'u'
             =m:n#u           -- return 'm' and append a recursive call with 'n' and 'u'
   |                          -- else (note: 's' is element of 'n')
    (x,_:z)<-span(/=s)n       -- split 'n' into a list 'x' before element 's' and
                              -- a list 'z' after element 's' and
       = (x++s:m:z)#u         -- make a recursive call with
                              -- x++s:m:z as the new master list (i.e. 'm' inserted into 'n' after 's') 
                              -- and 'u'
m # _ = m                     -- if either list is emtpy, return the master list

2

Ruby , 73 68 bytes

->a,b{0while b.zip(a&b).find{|m,n|m!=n&&a=a[0..a.index(m)]-[n]|a};a}

Experimente online!

Quão?

  • A interseção entre ae bcontém todos os elementos de b, mas na mesma ordem em que os encontraríamos ema
  • Portanto, se iterarmos na binterseção paralelamente, assim que encontrarmos uma diferença, podemos realocar um único elemento.
  • A realocação é feita cortando aa posição do elemento em que encontramos b, removendo o elemento que encontramos na interseção e adicionando o restante de a.
  • Repita desde o início, até que todos os elementos de bestejam na ordem correta ema

O que o 0 está fazendo 0while?
Jonah

É apenas um NOP.
GB

por que é necessário?
Jonah

1
Como a comparação e a manipulação são feitas em um único bloco, para evitar declarar uma variável antes de iniciar o loop. Significa: "não faça nada enquanto a operação retornar verdadeira.", O código é mais curto que "faça a operação enquanto o resultado for verdadeiro"
GB


1

Perl 6 , 40 bytes

{*.permutations.first(*.grep(.any)eq$_)}

Experimente online!

Bloco de código anônimo que recebe a entrada com curry (como f(subList)(masterList)e encontra a primeira permutação lexográfica dos índices da lista principal) em que os elementos da sub-lista estão na ordem correta.

Intuitivamente, a primeira permutação satisfatória deixará os elementos ordenados corretamente na ordem original, enquanto move os posicionados incorretamente a distância mínima necessária para a frente, a fim de tê-los na ordem correta, que os coloca diretamente após o elemento anterior no subconjunto.

Explicação:

{*                                     } # Anonymous code block that returns a lambda
  .permutations                          # In all permutations of the master list
               .first(                )  # Find the first permutation
                     (*.grep(.any)       # Where the order of the subset
                                  eq$_   # Is the same as the given order


1

Geléia , 9 bytes

Œ!iⱮṢƑ¥ƇḢ

Experimente online! ou suíte de teste

Ineficiente, especialmente com grandes listas mestras. Gera todas as permutações possíveis, filtra aquelas onde o subconjunto está na ordem errada e depois retorna a primeira.

Explicação

Œ!        | Generate all permutations of the master list
      ¥Ƈ  | Filter including only those where...
  iⱮ      |   the index of each sublist item in this permutation...
     Ƒ    |   is...
    Ṣ     |   in order. 
        Ḣ | Finally take the first item

Isso não parece que ele estaria de acordo com a regra "Se a lista principal tem dois elementos fora de ordem de acordo com a lista subconjunto, o item que é anterior na lista mestre deve ser movido para o mais antigo índice onde é no local correto em relação a outros itens da lista de subconjuntos. (ou seja, imediatamente após o item posterior) "
Beefster

@ Beefster funciona nos que eu tentei até agora. Penso que a ordenação das permutações é tal que este é o resultado correto. Feliz por provar que está errado, se houver um contra-exemplo.
Nick Kennedy

@ Beefster Agora eu tentei todos os seus exemplos, exceto os nomes femininos e o 1..12 e a ordem do resultado está correta.
Nick Kennedy

2
@Beefster Minha resposta tem uma explicação parcial para o porquê disso funciona
Jo King

1

J , 49 bytes

[:(<@({:+i.@>:@-/)@i.~C.])^:(>/@i.~)&.>/]|.@;2<\[

Experimente online!

explicação

Tomamos o subconjunto como o argumento esquerdo e a entrada completa como o direito.

Vamos trabalhar com o código com um exemplo específico para maior clareza:

5 2 4 f 1 2 3 4 5

Veja os infixes in a box do tamanho dois do subconjunto:

2 <\ [

produzindo:

┌───┬───┐
│5 2│2 4│
└───┴───┘

anexá-los à entrada original e inverter a coisa toda:

] |.@;

Nós temos:

┌───┬───┬─────────┐
│2 4│5 2│1 2 3 4 5│
└───┴───┴─────────┘

Resolver o problema torna-se uma redução da direita para a esquerda acima. Precisamos apenas encontrar o verbo certo para inserir/ entre os itens.

Cada iteração da redução atualiza a caixa mais à direita (a entrada completa, que estamos transformando), para que ela esteja de acordo com a restrição de pedido representada pelo par à sua esquerda. Quando a redução terminar, a entrada respeitará a ordem completa do subconjunto.

Se a ordem do par for igual à ordem na entrada, o seguinte será avaliado como 0 e não faremos nada:

^:(>/@i.~)

Caso contrário, ele será avaliado como 1 e aplicaremos o verbo à esquerda de ^:

   {: + i.@>:@-/)@i.~ C. ]

que move o item esquerdo para a direita do item direito. Esse movimento é simplesmente uma permutação cíclica de todos os itens entre (e incluindo) os dois elementos em questão.

J tem primitivo para aplicar essa permutação cíclica:

<cyclic permutation definition> C. ]

e o restante do verbo não faz nada além de escolher os índices que precisamos dar um ciclo:

{: + i.@>:@-/)@i.~

que parece mais longo do que deveria ser, mas não pude jogar mais essa frase.

Finalmente, recolocamos o resultado <@e terminamos.


0

Gelatina , 24 bytes

i@€MƤFṬœṗƲḊ;JḟF}W€ʋ@¥ṢFị

Experimente online! ou suíte de teste

Explicação

Um link diádico que leva o subconjunto como a esquerda e a lista principal como argumentos certos. O exemplo abaixo usa 9001, 42, 69, 1337, 420, 99, 255 como mestre e 69, 9001, 1337 como subconjunto.

i@€                      | Find the index of each subset item in the master list [3, 1, 4]
         Ʋ               | Previous 4 links as a monad
   MƤ                    | Find the index of the maximum for each prefix of this list [1, 1, 3]
     F                   | Flatten (because the previous result are actually each length one lists)
      Ṭ                  | Convert to a boolean list [1,0,1]
       œṗ                | Partition the [3, 1, 4] list before each 1 [[], [3, 1], [4]]
          Ḋ              | Remove the empty first list [[3, 1], [4]]
                    ¥    | Previous two links as a dyad
                  ʋ@     | Previous 4 links as a dyad with reversed arguments
            J            | Sequence along the master list [1, 2, 3, 4, 5, 6, 7]
             ḟF}         | Filter out items in the flattened [3, 1, 4] list
                W€       | Wrap each item as a list [[2], [5], [6], [7]]
           ;             | Concatenate rhis to the [[3, 1], [4]] list
                     Ṣ   | Sort (effectively by first item in each list) [[2], [3, 1], [4], [5], [6], [7]]
                      F  | Flatten
                       ị | Look up in original master list (and implicitly output)

0

C # (compilador interativo do Visual C #) , 118 bytes

a=>b=>{for(int j;b.Any();)foreach(var e in b.Intersect(a.Take(j=a.IndexOf(b.Dequeue())))){a.Remove(e);a.Insert(j,e);}}

Experimente online!

Aproveitando algumas classes no System.Collections.Genericespaço para nome. O mestre é a List<T>e o subconjunto é a Queue<T>.

// a: master
// b: subset
a=>b=>{
  // continue until b is empty
  for(int j;b.Any();)
    // iterate over values that are out of order in a
    // per the head of b using loop variable e
    foreach(var e in
      // the out of order values are determined by
      // intersecting remaining values in b with
      b.Intersect(
        // values in a occurring before the current head of b
        // save the position in a to variable j and remove the head of b
        a.Take(j=a.IndexOf(b.Dequeue()))
      )
    ){
      // push back the out of order element in a
      a.Remove(e);
      a.Insert(j,e);
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.