Yinelenmeyen Dizi Birleştirme


15

Son zamanlarda iki dizileri birleştirmek ve yinelenenleri kaldırmak için StackOverflow bu Javascript kodunu gördüm :

Array.prototype.unique = function() {
    var a = this.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }
    return a;
};

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = array1.concat(array2).unique(); 

Bu kod çalışırken korkunç bir şekilde verimsizdir ( O(n^2)). Zorluğunuz daha az karmaşıklığa sahip bir algoritma oluşturmaktır.

Kazanan kriterler en az karmaşıklığa sahip çözümdür , ancak bağlar karakterlerde en kısa uzunlukta kırılacaktır.

Gereksinimler :

Tüm kodunuzu, "doğruluk" için aşağıdaki gereksinimleri karşılayan bir işlevde paketleyin:

  • Girdi: İki dizi
  • Çıktı: Bir dizi
  • Her iki dizinin öğelerini bir araya getirir- Her iki giriş dizisindeki herhangi bir öğe, çıkış dizisinde olmalıdır.
  • Çıktı dizisinin yinelenmesi olmamalıdır.
  • Sipariş önemli değil (orijinalin aksine)
  • Herhangi bir dil önemlidir
  • Tekliği veya kümeleri / dizileri birleştirmek için standart kitaplığın dizi işlevlerini kullanmayın (standart kitaplıktaki diğer şeyler iyi olsa da). Dizi birleştirmenin iyi olduğunu ayırt edeyim, ancak yukarıdakilerin tümünü zaten yapan işlevler iyi değil.

Dizi işlevlerini kullanmadan nasıl bir dizi oluşturmamız veya ona bir dizi eklememiz gerekir?
Emil Vikström

@ EmilVikström Düzenlememe bakın. Dizi tekliği işlevlerini kullanamayacağınız anlamına geliyordu. Belirsiz olduğum için üzgünüm.
14:12 hkk

Dizilerden birinde yinelenenler varsa, bunları da kaldırır mıyız? Örneğin, birleştirme gerektiğini [1, 2, 2, 3]ve [2, 3, 4]iade [1, 2, 2, 3, 4]ya [1, 2, 3, 4]?
OI

1
@OI Evet, bu çok kolay olur.
14:24 hkk

1
Sorabilir miyim: Ne dizileri ? Yalnızca tamsayıları veya dizeleri varsayabilir miyiz, yoksa çok düzeyli nesneler gibi daha karmaşık şeylere de izin vermek zorunda mıyız?
jawns317

Yanıtlar:


8

Perl

27 Karakter

Basit Perl Hack

my @vals = ();
push @vals, @arr1, @arr2;
my %out;
map { $out{$_}++ } @vals;
my @unique = keys %out;

Eminim birisi bunu bir astar olabilir .. ve böylece (Teşekkürler Dom Hastings)

sub x{$_{$_}++for@_;keys%_}

1
"Benzersizliği algılamak için standart kütüphanenin dizi işlevlerini kullanmayın (standart kütüphaneden başka şeyler iyi olsa da)"
John Dvorak

1
Bu kuralı nasıl ihlal ediyorum? Benzersiz işlevler kullanmıyorum?
Zach Leighton

Öyleyse nasıl çalışır? Üzgünüm, perl okuyamıyorum. Bir karma haritanın anahtarlarını okursa - bu kuralda TAMAM olarak sayılır mı? Buna ikna olana kadar oy vermeyeceğim.
John Dvorak

1
Dizileri birleştirir, her ikisini de döngüler ve anahtarın dizi döngüsünde geçerli değer olan değeri artıran bir karma ekler. Sonra bu karma anahtarları alır, bunu bazı çalışmalarımda kullandım .. Yani [1,1,2,3,4,4] {1 => 2, 2 => 1, 3 => 1 olur , 4 => 2}
Zach Leighton

@ZachLeighton ile kodu 27 karakter kısaltabilirsiniz sub x{$_{$_}++for@_;keys%_}(bir kravat aşağı gelirse!) Ve şu şekilde kullanın:z((1,2,3,4),(2,3,4,5,6))
Dom Hastings

10

JavaScript O (N) 131 124 116 92 (86)

Golf versiyonu:

function m(i,x){h={};n=[];for(a=2;a--;i=x)i.map(function(b){h[b]=h[b]||n.push(b)});return n}

İnsan tarafından okunabilir golf versiyonu:

function m(i,x) {
   h = {}
   n = []
   for (a = 2; a--; i=x)
      i.map(function(b){
        h[b] = h[b] || n.push(b)
      })
   return n
}

Ben böyle kullanmak concatve 86 karakterle yapabiliriz:

function m(i,x){h={};n=[];i.concat(x).map(function(b){h[b]=h[b]||n.push(b)});return n}

Ama hala bu JsPerf dayalı O (N) emin değilim: http://jsperf.com/unique-array-merging-concat-vs-looping gibi concat sürümü küçük diziler ile marjinal olarak daha hızlı ama daha yavaş daha büyük diziler (Chrome 31 OSX).

Pratikte bunu yapın (golf kötü uygulamalarla doludur):

function merge(a1, a2) {
   var hash = {};
   var arr = [];
   for (var i = 0; i < a1.length; i++) {
      if (hash[a1[i]] !== true) {
        hash[a1[i]] = true;
        arr[arr.length] = a1[i];
      }
   }
   for (var i = 0; i < a2.length; i++) {
      if (hash[a2[i]] !== true) {
        hash[a2[i]] = true;
        arr[arr.length] = a2[i];
      }
   }
   return arr;
}
console.log(merge([1,2,3,4,5],[1,2,3,4,5,6]));

Karmaşıklığı hesaplamada çok iyi değilim ama bunun olduğuna inanıyorum O(N). Birisi açıklığa kavuşturabilseydi çok isterdim.

Düzenleme: İşte herhangi bir dizi dizi alır ve bunları birleştiren bir sürüm.

function merge() {
   var args = arguments;
   var hash = {};
   var arr = [];
   for (var i = 0; i < args.length; i++) {
      for (var j = 0; j < args[i].length; j++) {
        if (hash[args[i][j]] !== true) {
          arr[arr.length] = args[i][j];
          hash[args[i][j]] = true;
        }
      }
    }
   return arr;
}
console.log(merge([1,2,3,4,5],[1,2,3,4,5,6],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7,8]));

Bu neredeyse birkaç saniye içinde yayınlayacağım şeydi :-( Evet, karma tabloları ekleme ve arama için itfa edilmiş sabit süre ile uygulandığında doğrusal amortisman süresidir (birçok dilde yaygındır, özellikle bilmiyorum ) JS hakkında.
Emil Vikström

@ EmilVikström Bunun için JavaScript'in işe yarayıp kanıt olmadığına inanıyorum. Hızlı parmaklara sahip olduğun için özür dilerim, yorumlarla kendinizi yavaşlattı: P
George Reith

Bu harika bir yaklaşım. Ancak, güzel biçimlendirilmiş sürümünüze ek olarak bir "kod golf" tarzı çözüm de sağlayabilir misiniz? Birden fazla insanın bunu doğru yaklaşım olarak düşündüğünü görünce, muhtemelen bir kravat olacaktır O(N).
14:21 hkk

@ cloudcoder2000 Tamam, tam sürüm yazdırmak istedim çünkü code-golf sürümü pratikte daha az verimli olacak.
George Reith

1
@ cloudcoder2000 Tamamen bağımsız değiller, bu yüzden en kötü durum değil O(A*B)( NKafa karıştırıcı olduğu için kullanmıyor ). Her giriş dizisi (every A) B, gerçekte olduğu gibi aynı miktarda elemente ( ) sahipse , tüm dizi girişlerinin eleman sayısı olarak tanımlanırken O(SUM(B) FOR ALL A)yeniden yazılabilir . O(N)N
14:17

4

Python 2.7, 38 karakter

F=lambda x,y:{c:1 for c in x+y}.keys()

İyi bir hash fonksiyonu olduğu varsayılarak O (N) olmalıdır.

Wasi'nin 8 karakterlik setuygulaması daha iyi, eğer kuralları ihlal ettiğini düşünmüyorsanız.


Güzel! Python'daki anlayışlar çok zarif ve güçlü olabilir.
OI

3

PHP, 69/42 68/41 karakter

İşlev bildirimi dahil 68 karakterdir:

function m($a,$b){return array_keys(array_flip($a)+array_flip($b));}

İşlev bildirimi dahil edilmemesi 41 karakterdir:

array_keys(array_flip($a)+array_flip($b))

3

Ruby'de bir yol

Yukarıda belirtilen kurallara uymak için JavaScript çözümüne benzer bir strateji kullanır ve aracı olarak karma kullanırdım.

merged_arr = {}.tap { |hash| (arr1 + arr2).each { |el| hash[el] ||= el } }.keys

Esasen, bunlar yukarıdaki satırda attığım adımlar.

  1. Sonucu merged_arriçerecek bir değişken tanımlayın
  2. Benzersiz öğeleri koymak için boş, adlandırılmamış bir karma aracı olarak başlatın
  3. Kullanım Object#tap(olarak gösterilen karma doldurmak için hashde tapblok) ve takip eden bir yöntem zincirleme için geri
  4. Birleştirin arr1ve arr2işlenmemiş tek bir diziye dönüştürün
  5. Her bir element için elbirleştirilmiş bir dizide, değeri koymak eliçinde hash[el]herhangi bir değer ise hash[el], şu anda mevcuttur. Buradaki not ( hash[el] ||= el) öğelerin benzersizliğini sağlayan şeydir.
  6. Şimdi doldurulmuş karma için anahtarları (veya aynı oldukları gibi değerleri) getir

Bu O(n)zaman içinde çalışmalıdır. Herhangi bir yanlış beyanda bulunup bulunmadığımı veya yukarıdaki yanıtı verimlilik veya okunabilirlik açısından iyileştirip iyileştiremeyeceğimi lütfen bize bildirin.

Olası iyileştirmeler

Karıştırmanın anahtarlarının benzersiz olacağı ve değerlerin alakasız olduğu göz önüne alındığında, not kullanmak muhtemelen gereksizdir, bu yüzden bu yeterlidir:

merged_arr = {}.tap { |hash| (arr1 + arr2).each { |el| hash[el] = 1 } }.keys

Gerçekten seviyorum Object#tap, ama aynı sonucu kullanarak yapabiliriz Enumerable#reduce:

merged_arr = (arr1 + arr2).reduce({}) { |arr, val| arr[val] = 1; arr }.keys

Hatta Enumberable#mapşunları kullanabilirsiniz :

merged_arr = Hash[(arr1 + arr2).map { |val| [val, 1] }].keys

Pratikte nasıl yaparım

Tüm bunları söyledikten sonra, iki diziyi birleştirmem istendi arr1ve arr2sonucun merged_arrbenzersiz unsurları olacak ve elimdeki herhangi bir Ruby yöntemini kullanabilseydim, sadece bu tam problemi çözmek için ayarlanan sendika operatörünü kullanırdım:

merged_arr = arr1 | arr2

Bununla birlikte, kaynağın hızlı bir göz atması, Array#|bir karma değerini aracı olarak kullanmanın, 2 dizi arasında benzersiz bir birleştirme gerçekleştirmek için kabul edilebilir bir çözüm gibi göründüğünü doğrulamaktadır.


"Benzersizliği algılamak için standart kütüphanenin dizi işlevlerini kullanmayın (standart kütüphaneden başka şeyler olsa da)"
John Dvorak

İkinci örnekte bu kuralı nasıl ihlal ederim? Memolaştırma bir karma üzerinde yapılır. Buna da izin verilmiyor mu?
OI

2
Array.prototype.unique = function()
{
  var o = {},i = this.length
  while(i--)o[this[i]]=true
  return Object.keys(o)
}

N dizisi alacak bir işlev aşağıdakiler olabilir:

function m()
{
  var o={},a=arguments,c=a.length,i;
  while(c--){i=a[c].length;while(i--)o[a[c][i]] = true} 
  return Object.keys(o);
}

Golf, bunun çalışması gerektiğini düşünüyorum (117 karakter)

function m(){var o={},a=arguments,c=a.length,i;while(c--){i=a[c].length;while(i--)o[a[c][i]]=1}return Object.keys(o)}

Güncelleme Orijinal türü korumak istiyorsanız,

function m()
{
  var o={},a=arguments,c=a.length,f=[],g=[];
  while(c--)g.concat(a[c])
  c = g.length      
  while(c--){if(!o[g[c]]){o[g[c]]=1;f.push(g[c])}}
  return f
}

veya golf oynadı 149:

function m(){var o={},a=arguments,c=a.length,f=[],g=[];while(c--)g.concat(a[c]);c= g.length;while(c--){if(!o[g[c]]){o[g[c]]=1;f.push(g[c])}}return f}

Bu hala bazı şüpheler yaratabilir, ayırt etmek istiyorsanız 123ve '123'o zaman bu işe yaramaz ..


Cevap için teşekkürler. Etkileyici derecede kısadır, ancak bu sadece sorunun yarısını yapar. Ayrıca, çözümde gerçek birleştirme parçasını (orijinal örnekteki ile aynı olsa bile) eklemeniz ve hepsini bir işlevde bir araya getirmeniz gerekir. Ayrıca, buna ek olarak "olduğu gibi" "golfed" sürümünü sağlayabilir misiniz O(N)?
14:16 hkk

Bu, tüm üyeleri dizgilere çevirir. örneğin m([1,2,3,4,5],[2,3,4,5,6],[2,3,4,5,6,7])olur["1", "2", "3", "4", "5", "6", "7"]
George Reith

2

piton, 46

def A(a,b):print[i for i in b if i not in a]+a

Veya ayar işlemini basitçe kullanarak

piton, 8

set(a+b)

1
Maalesef net değil, set işlemlerini kullanmak da hile yapıyor.
hkk

A'da kopyalar varsa veya b'de kopyalar varsa ve bu öğe a'da değilse 1. kodunuzda kopyalar olacaktır.
Vedant Kandoi

2

Perl

23 byte, eğer kod bloğunu sadece altyordam içinde sayarsak. Genel değerlerin üzerine yazmaya izin verilirse 21 olabilir ( mykoddan kaldırılır ). Öğeleri rastgele sırada döndürür, çünkü düzen önemli değildir. Karmaşıklığa gelince, ortalama olarak O (N) (karma çarpışma sayısına bağlıdır, ancak oldukça nadirdir - en kötü durumda O (N 2 ) olabilir (ancak bu olmamalıdır, çünkü Perl patolojik hashları tespit edebilir , ve bu tür bir davranış algıladığında hash işlevi tohumunu değiştirir)).

use 5.010;
sub unique{
    my%a=map{$_,1}@_;keys%a
}
my @a1 = (1, 2, 3, 4);
my @a2 = (3, 4, 5, 6);
say join " ", unique @a1, @a2;

Çıktı (rasgeleliği de gösterir):

/tmp $ perl unique.pl 
2 3 4 6 1 5
/tmp $ perl unique.pl 
5 4 6 2 1 3

2

Fortran: 282 252 233 213

Golf versiyonu:

function f(a,b,m,n) result(d);integer::m,n,a(m),b(n),c(m+n);integer,allocatable::d(:);j=m+1;c(1:m)=a(1:m);do i=1,n;if(.not.any(b(i)==c(1:m)))then;c(j)=b(i);j=j+1;endif;enddo;allocate(d(j-1));d=c(1:j-1);endfunction

Sadece sonsuza kadar daha iyi görünmekle kalmaz, aynı zamanda (okunabilir formda çok uzun bir çizgi) insan tarafından okunabilir formla derlenir:

function f(a,b,m,n) result(d)
  integer::m,n,a(m),b(n),c(m+n)
  integer,allocatable::d(:)
  j=m+1;c(1:m)=a(1:m)
  do i=1,n
     if(.not.any(b(i)==c(1:m)))then
        c(j)=b(i);j=j+1
     endif
  enddo
  allocate(d(j-1))
  d=c(1:j-1)
end function

Bu olmalı O(n)kopyalarım olarak aiçine cve daha sonra her kontrol bbiriminin tümü karşısında c. Son adım c, başlatılmadığı için içerecek çöpleri ortadan kaldırmaktır .


2

Mathematica 10 Karakterleri

Union[a,b]

Misal:

a={1,2,3,4,5};
b={1,2,3,4,5,6};
Union[a,b]

{1, 2, 3, 4, 5, 6}

Mathematica2 43 Karakter

Sort@Join[a, b] //. {a___, b_, b_, c___} :> {a, b, c}

8
Bu standart kütüphane dizi yöntemleri kullanma kategorisinde gider düşünüyorum.
hkk

Merhaba @ cloudcoder2000. Mathematica'da Union'ı kullanmak için belirli bir kütüphaneyi aramanıza gerek yoktur.
Murta

5
Bence, sorunun tam olarak ne yapmasını istediğini yapmak için yerleşik bir işlev kullanmak hile yapmaktır.
Konrad Borowski

tamam tamam .. ikinci kod Union kullanmıyor.
Murta

1
Ben Tally[Join[a, b]][[;; , 1]]de hile olurdu ;-) BTW Eğer tek harfli değişkenleri kullanarak chars kaydedebilirsiniz.
Yves Klett

1

Javascript 86

Golf versiyonu:

function m(a,b){var h={};return a.concat(b).filter(function(v){return h[v]?0:h[v]=1})}

Okunabilir sürüm:

function merge(a, b) {
  var hash = {};
  return a.concat(b).filter(function (val) {
    return hash[val] ? 0 : hash[val] = 1;
  });
}

1
Bu falsey değerlerini yok sayar ... m([1,0,0,0,0],[0,1,0])döndürür [1].
George Reith

1
Değişim h[v]=viçin h[v]=1.
George Reith

İyi @GeorgeReith gördüm! 86'dan 84'e gittik :)
Bertrand

Hala 86, sanırım kafanız karıştı çünkü okunabilir versiyondan 2 karakteri golfe değil.
George Reith

1

JavaScript 60

ES6 jeneratör kullanıyorum.
Aşağıdakiler Google'ın Traceur REPL'i kullanılarak test edilebilir .

m=(i,j)=>{h={};return[for(x of i.concat(j))if(!h[x])h[x]=x]}

0

Verimli olması için çerçevenin altında yatan Nesneleri kullanan JavaScript tabanlı bir uygulama arıyorsanız, Set'i kullanmış olurdum. Genellikle bir uygulamada, Set nesnesi ekleme sırasında benzersiz nesneleri bir tür ikili arama dizine ekleme ile doğal olarak işler. Ben Java log(n)hiçbir arama bir kereden fazla tek bir nesne içerebilir gerçeğine dayalı ikili arama kullanarak bir arama biliyorum .


Bu Javascript için de geçerli olup olmadığını bilmiyorum, ancak aşağıdaki snippet kadar basit bir şey bir n*log(n)uygulama için yeterli olabilir :

JavaScript , 61 bayt

var s = new Set(a);      // Complexity O(a.length)
b.forEach(function(e) {  // Complexity O(b.length) * O(s.add())
  s.add(e);
}); 

Çevrimiçi deneyin!


Yukarıdaki pasajı kullanımları ise a = [1,2,3]ve b = [1,2,3,4,5,6]daha sonra s=[1,2,3,4,5,6].

Set.add(Object)JavaScript'teki işlevin karmaşıklığını biliyorsanız, bunun karmaşıklığı n + n * f(O)nerede f(O)olduğunu bana bildirin s.add(O).


0

APL (Dyalog Unicode) , O (N), 28 bayt

Anonim zımni infix fonksiyonu.

(⊢(/⍨)⍳∘≢=⍳⍨),

Çevrimiçi deneyin!

, argümanları birleştirin; O (N)

() Üzerine şu anonim zımni fonksiyonu uygulayın; O (1)

   ⍳⍨ selfie indeksleri (dizideki tüm elemanların ilk ortaya çıkış endeksleri); O (N)

  = öğeyi öğeye göre karşılaştır; O (N):

   ⍳∘≢ dizinin uzunluğunun endeksleri; O (N)

(/⍨) bunu filtrelemek için kullanın; O (N):

   değiştirilmemiş argüman; O (1)

O (N + 1 + N + N + N + N + 1) = O (N)


-2

JavaScript, 131 karakter

var array1 = ["Vijendra","Singh"];   
var array2 = ["Singh", "Shakya"];     
result = Array.from(new Set([...array1, ...array2]))

4
PPCG'ye Hoşgeldiniz! Lütfen bunun hangi dil olduğunu bize söyleyin ve daha iyi okunabilirlik için kod olarak biçimlendirin. (Bu, kod satırlarını dört boşlukla girintili olarak çalışır). Ayrıca yaklaşımınızın bir açıklaması da takdir edilecektir.
Laikoni

sadece bir javascript kodudur.
deepak_pal

@techdeepak Bu tür hayati bilgileri postanıza ekleyebilir, düzgün bir şekilde biçimlendirebilir, sözdizimi vurgulaması ekleyebilir ve en hızlı algoritma olduğu için algoritmanızın karmaşıklığı hakkında biraz daha yazabilirsiniz . Olduğu gibi, bu yazı oldukça düşük kalitededir.
Jonathan Frech

-2

PHP yaklaşık 28 karakter [örnek dizi değişkenlerini ve sonuç değişkenini dışarıda bırakarak].

$ array1 = dizi (1, 2, 3); $ array2 = dizi (3, 4, 5);

$ sonuç = dizi_merge ($ dizi1, $ dizi2);


Sorudan: Tekliği veya kümeleri / dizileri birleştirmek için standart kitaplığın dizi işlevlerini kullanmayın . Ayrıca, bu aslında diziden yinelenenleri kaldırmaz
Jo King

Bence şu önemli satırdan kaçtınız: " Tekilliği tespit etmek veya kümeleri / dizileri birleştirmek için standart kütüphanenin dizi işlevlerini kullanmayın "
Peter Taylor

Evet. Bu doğru. Bunu belirttiğiniz için teşekkürler çocuklar. Eleştiriler alçakgönüllülükle kabul edildi.
Endri

@jo kral. "Standart kütüphaneleri kullanma ..." konusunda kesinlikle haklısınız. Gerisi yanlış. Yinelemeleri kaldırır. php.net/manual/tr/function.array-merge.php . PHP'nin belgelerini tam olarak okumanızı tavsiye ederim. İşi yaptığından% 100 eminim. Tek yapmanız gereken dizilerden hangisinin kopya olduğunu düşündüğünüze dikkat etmek. Şerefe.
Endri

1
Kelimenin tam anlamıyla herhangi bir değişiklik olmadan gönderimi kodu çalıştırdı ve çıktı yinelenen vardır. Gibi görünüyor size yani belgeleri okumalı , ancak, diziler sayısal anahtarlar içeriyorsa, sonraki değer üzerine yazmaz, ama eklenecektir
Jo Kral
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.