중복없는 배열 병합


15

최근에 두 배열병합 하고 중복을 제거하는 StackOverflow 에서이 Javascript 코드를 보았습니다 .

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(); 

이 코드는 작동하지만 끔찍하게 비효율적입니다 ( O(n^2)). 복잡성이 적은 알고리즘을 만드는 것이 과제입니다.

당첨 기준은 복잡성 이 가장 적은 솔루션 이지만 문자의 길이가 가장 짧아서 관련성이 떨어집니다.

요구 사항 :

다음 "정확성"요구 사항을 충족하는 함수로 모든 코드를 함께 패키지하십시오.

  • 입력 : 2 개의 배열
  • 출력 : 하나의 배열
  • 두 배열의 요소를 함께 병합-입력 배열의 모든 요소는 출력 배열에 있어야합니다.
  • 출력 된 배열에는 중복이 없어야합니다.
  • 순서는 중요하지 않습니다 (원래와 달리)
  • 모든 언어 수
  • 고유성을 감지하거나 세트 / 배열을 병합하기 위해 표준 라이브러리의 배열 함수를 사용하지 마십시오 (표준 라이브러리의 다른 항목은 괜찮음). 배열 연결은 훌륭하지만 위의 모든 기능을 수행하는 기능은 그렇지 않다는 점을 구별하겠습니다.

배열 함수를 사용하지 않고 어떻게 배열을 만들거나 추가해야합니까?
Emil Vikström

@ EmilVikström 내 편집 내용을 참조하십시오. 배열 고유성 함수를 사용할 수 없음을 의미했습니다. 불분명해서 죄송합니다.
hkk

배열 중 하나에 중복이있는 경우 제거할까요? 예를 들어, 병합해야[1, 2, 2, 3][2, 3, 4]반환 [1, 2, 2, 3, 4]또는 [1, 2, 3, 4]?
OI

1
@OI 예, 너무 쉬울 것입니다.
hkk

1
물어봐도 될까요 ? ? 단순히 정수 또는 문자열을 가정 할 수 있습니까? 아니면 다중 레벨 객체와 같은보다 복잡한 것을 허용해야합니까?
jawns317

답변:


8

27 자

간단한 펄 해킹

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

누군가가 이것을 한 줄로 확신 할 수 있다고 확신합니다. 따라서 (Dom Hastings에게 감사드립니다)

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

1
"독 특성을 감지하기 위해 표준 라이브러리의 배열 함수를 사용하지 마십시오 (표준 라이브러리를 구성하는 것은 괜찮지 만)"
John Dvorak

1
그 규칙을 어떻게 위반합니까? 고유 한 기능을 사용하지 않습니까?
Zach Leighton

그렇다면 어떻게 작동합니까? 죄송합니다. 펄을 읽을 수 없습니다. 해시 맵의 키를 읽는 경우 해당 규칙에서 OK로 간주됩니까? 동의 할 때까지 투표하지 않습니다.
John Dvorak

1
배열을 결합하고 둘 다 반복하며 해시에 추가하여 키가 배열 루프의 현재 값인 값을 증가시킵니다. 그런 다음 해당 해시의 키를 사용하여 일부 작업에 사용했습니다. [1,1,2,3,4,4]는 {1 => 2, 2 => 1, 3 => 1이됩니다. , 4 => 2}
Zach Leighton

@ZachLeighton 당신은 27 문자로 코드를 줄일 수 있습니다 sub x{$_{$_}++for@_;keys%_}(동점에 온 경우!) :z((1,2,3,4),(2,3,4,5,6))
Dom Hastings

10

자바 스크립트 O (N) 131 124 116 92 (86)

골프 버전 :

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}

인간이 읽을 수있는 골프 버전 :

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
}

사용할 수 있습니다 concat 그렇게 하고 86 자로 .

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

:이 JsPerf에 따라 여전히 O (N) 인 경우 그러나 나는 확실하지 않다 http://jsperf.com/unique-array-merging-concat-vs-looping CONCAT 연 버전이 소폭 빠른 작은 배열은 있지만 느린와 같이 더 큰 배열 (Chrome 31 OSX).

실제로이 작업을 수행하십시오 (골프에는 나쁜 습관이 가득합니다).

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]));

나는 컴퓨팅 복잡도는 좋지 않지만 이것이 믿습니다. O(N) . 누군가가 명확히 할 수 있다면 사랑할 것입니다.

편집 : 여기에 여러 배열을 가져 와서 병합하는 버전이 있습니다.

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]));

이것은 거의 2 초 안에 게시 할 내용입니다. 에 대하여)
Emil Vikström

@ EmilVikström JavaScript가 그렇다고 생각하지만 이에 대한 증거는 없습니다. 빠른 손가락을 가진 사과는 의견으로 자신을 느리게 : P
George Reith

이것은 훌륭한 접근법입니다. 그러나 멋진 형식의 버전 외에도 "코드 골프"스타일 솔루션을 제공 할 수 있습니까? 여러 사람들이 이것을 올바른 접근 방식으로 생각한 것을 보았을 때 아마도에 묶여있을 것 O(N)입니다.
hkk

@ cloudcoder2000 자, 코드 골프 버전이 실제로 덜 효율적일 수 있으므로 정식 버전을 인쇄하고 싶었습니다.
George Reith

1
@ cloudcoder2000 그것들은 완전히 독립적이지 않기 때문에 최악의 경우는 아닙니다 O(A*B)( N혼동하기 때문에 사용 하지 마십시오 ). 모든 입력 배열 (모든 A)이 실제와 동일한 양의 요소 ( B)를 가진 경우 모든 배열 입력의 요소 수로 정의 할 때 O(SUM(B) FOR ALL A)다시 작성할 수 있습니다 . O(N)N
meiamsome

4

파이썬 2.7, 38 자

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

좋은 해시 함수를 가정하면 O (N)이어야합니다.

set규칙을 위반한다고 생각하지 않으면 Wasi의 8 자 구현이 더 좋습니다.


좋은! 파이썬의 이해는 매우 우아하고 강력 할 수 있습니다.
OI

3

PHP, 69/42 68/41 자

함수 선언을 포함하면 68 자입니다.

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

함수 선언을 포함하지 않으면 41 자입니다.

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

3

루비의 편도

위에서 설명한 규칙을 유지하기 위해 JavaScript 솔루션과 유사한 전략을 사용하고 해시를 중개자로 사용합니다.

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

본질적으로, 이것은 위의 줄에서 진행하는 단계입니다.

  1. 변수 정의 merged_arr결과를 포함 를
  2. 이름없는 빈 해시를 중개자로 초기화하여 고유 한 요소를 넣습니다.
  3. 사용 Object#tap으로 참조 해시 (웁니다 hash의를tap 블록) 및 후속 메소드 체인에 대한 반환
  4. 연결 arr1하고arr2 단일 배열로 미처리
  5. 각 요소 el연접 배열의 값을 넣어 el에서 hash[el]의 값이 있으면 hash[el]현재 존재하지 않는다. 여기 메모는hash[el] ||= el )는 요소의 고유성을 보장하는 것입니다.
  6. 현재 채워진 해시의 키 (또는 동일한 값)를 가져옵니다.

이것은 실행되어야합니다 O(n)시간에 . 내가 부정확 한 진술을했거나 효율성이나 가독성을 위해 위의 답변을 향상시킬 수 있는지 알려주십시오.

가능한 개선

해시의 키가 고유하고 값이 관련이 없기 때문에 메모를 사용하는 것은 아마도 불필요합니다.

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

나는 정말로 사랑 Object#tap하지만 다음을 사용하여 동일한 결과를 얻을 수 있습니다 Enumerable#reduce.

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

당신은 사용할 수 있습니다 Enumberable#map:

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

내가 실제로 어떻게 할 것인가

데 내가 두 배열을 병합했다 경우 모든 말했다 arr1arr2같은 결과가 있다는 merged_arr독특한 요소를 가지고 내 처분에 어떤 루비 방법을 사용할 수 있습니다, 나는 단순히이 정확한 문제를 해결하기위한 것입니다 합집합 연산자를 사용합니다 :

merged_arr = arr1 | arr2

Array#|그러나 의 소스를 빠르게 살펴보면 해시를 중개자로 사용하는 것이 2 개의 배열 사이에서 고유 한 병합을 수행하는 데 적합한 솔루션 인 것으로 보입니다.


"독 특성을 탐지하기 위해 표준 라이브러리의 배열 함수를 사용하지 마십시오 (표준 라이브러리를 구성하는 것은 괜찮지 만)"
John Dvorak

두 번째 예에서 해당 규칙을 어떻게 위반합니까? 해시에서 메모가 수행되고 있습니다. 그것도 허용되지 않습니까?
OI

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

n 개의 배열을 취하는 함수는 다음과 같습니다.

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);
}

골프, 나는 이것이 작동해야한다고 생각합니다 (117 자)

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)}

업데이트 원본 유형을 유지하려면

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
}

또는 골프 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}

당신이 구별하려는 경우 여전히 일부 의심을 캐스팅 할 수 123'123'다음이 작동하지 않을 것입니다 ..


답변 해주셔서 감사합니다. 매우 짧지 만 문제의 절반에 불과합니다. 또한 실제 병합 부분 (원래 예제에서와 동일하더라도)을 솔루션에 포함시키고 모든 기능을 하나의 함수로 묶어야합니다. 또한이 외에도 "골프"버전을 제공 할 수 O(N)있습니까?
hkk

모든 멤버를 문자열로 캐스팅합니다. 예 :m([1,2,3,4,5],[2,3,4,5,6],[2,3,4,5,6,7]) 이된다["1", "2", "3", "4", "5", "6", "7"]
조지 Reith는에게

2

파이썬, 46

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

또는 단순히 set 조작을 사용하여

파이썬, 8

set(a+b)

1
세트 작업을 사용하는 것도 부정 행위라는 것이 확실하지 않습니다.
hkk

a에 중복이 있거나 b에 중복이 있고 해당 요소가 a에없는 경우 첫 번째 코드는 중복됩니다.
Vedant Kandoi

2

서브 루틴 내부의 코드 블록 만 계산하는 경우 23 바이트 전역 값 덮어 쓰기가 허용되는 경우 21이 될 수 있습니다 ( my코드에서 제거됨). 순서는 중요하지 않기 때문에 임의의 순서로 요소를 반환합니다. 복잡성에 관해서는 평균적으로 O (N)입니다 (해시 충돌의 수에 따라 다르지만 다소 드문 경우입니다-최악의 경우 O (N 2) 일 수 있습니다 ) (그러나 Perl은 병리학 적 해시를 감지 할 수 있기 때문에 발생하지 않아야 함) 해시 함수 시드가 이러한 동작을 감지하면 변경됩니다)).

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;

출력 (임의 함수 표시) :

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

2

포트란 : 282 (252) 233 213

골프 버전 :

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

무한히 좋아 보일뿐만 아니라 실제로 사람이 읽을 수있는 형식으로 너무 긴 라인을 골프 형식으로 컴파일합니다.

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

이렇게해야 O(n)내가 복사 할 ac다음 각 검사 b의 모든 상대로을 c. 마지막 단계 c는 초기화되지 않았으므로 포함될 가비지를 제거하는 것입니다.


2

매스 매 티카 10 문자

Union[a,b]

예:

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

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

Mathematica2 43 문자

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

8
나는 이것이 표준 라이브러리 배열 방법을 사용하는 범주에 들어갈 것이라고 생각합니다.
hkk

@ cloudcoder2000 님 안녕하세요. Mathematica에서 Union을 사용하기 위해 특정 라이브러리를 호출 할 필요가 없습니다.
Murta

5
내 의견으로는, 내장 함수를 사용하여 질문이 정확히하는 일을하는 것은 부정 행위입니다.
Konrad Borowski

ok ok .. 두 번째 코드는 Union을 사용하지 않습니다.
Murta

1
Tally[Join[a, b]][[;; , 1]]또한 단일 문자 변수를 사용하여 문자를 저장할 수있는 ;-) BTW를 속이는 것 같습니다 .
Yves Klett

1

자바 스크립트 86

골프 버전 :

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

읽을 수있는 버전 :

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

1
이것은 잘못된 값을 무시하고 ...를 m([1,0,0,0,0],[0,1,0])반환합니다 [1].
George Reith

1
변경 h[v]=vh[v]=1.
George Reith

@GeorgeReith를 잘 발견했습니다! 우리는 86에서 84로 갔다 :)
Bertrand

여전히 86입니다. 골프가 아닌 읽을 수있는 버전에서 2 개의 문자를 제거했기 때문에 혼란 스러웠습니다.
George Reith

1

자바 스크립트 60

ES6 생성기를 사용하고 있습니다.
다음은 Google의 Traceur REPL을 사용하여 테스트 할 수 있습니다.

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

0

프레임 워크 뒤에있는 기본 개체를 사용하는 효율적인 JavaScript 기반 구현을 찾고 있다면 Set을 사용했을 것입니다. 일반적으로 구현에서 Set 개체는 본질적으로 일종의 이진 검색 인덱싱을 사용하여 삽입하는 동안 고유 한 개체를 처리합니다. 나는 자바에서 그것을 알고있다.log(n) 단일 객체를 두 번 이상 포함 할 수 없다는 사실에 따라 이진 검색을 사용하여 검색 있습니다.


Javascript에서도 이것이 사실인지는 모르겠지만 다음 스 니펫과 같은 간단한 것이 n*log(n)구현에 충분할 수 있습니다 .

자바 스크립트 , 61 바이트

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

온라인으로 사용해보십시오!


위의 코드 조각을 사용하는 경우 a = [1,2,3]b = [1,2,3,4,5,6]다음 s=[1,2,3,4,5,6].

당신이의 복잡성 알고있는 경우 Set.add(Object)자바 스크립트의 기능을 알려, 이러한 복잡성은 n + n * f(O)어디 f(O)의 복잡성이다 s.add(O).


0

APL (Dyalog Unicode) , O (N), 28 바이트

익명의 암묵적 삽입 기능.

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

온라인으로 사용해보십시오!

, 인수를 연결하십시오. 의 위에)

() 다음의 익명의 암묵적 기능을 적용하십시오. O (1)

   ⍳⍨ 지표 셀카 (전체 배열에서 각 요소의 첫 번째 발생을 나타냄); 의 위에)

  = 요소 별 비교 의 위에):

   ⍳∘≢ 배열의 길이의 지표; 의 위에)

(/⍨) 이를 사용하여 필터링하십시오. 의 위에):

   수정되지 않은 주장; O (1)

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


-2

자바 스크립트, 131 자

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

4
PPCG에 오신 것을 환영합니다! 이 언어가 어떤 언어인지 알려 주시고 가독성을 높이기 위해 코드 형식으로 지정하십시오. (이것은 네 줄의 공백으로 코드 라인을 들여 쓰는 방식으로 작동합니다). 또한 귀하의 접근 방식에 대한 설명을 부탁드립니다.
Laikoni

그것은 단지 자바 스크립트 코드입니다.
deepak_pal

@techdeepak 게시물에 이러한 중요한 정보를 추가하고, 적절하게 형식을 지정하고, 구문 강조를 추가하고, 알고리즘의 복잡성에 대해 조금 더 작성할 수 있습니다. 알고리즘이 가장 빠릅니다 . 이 게시물의 품질은 상당히 낮습니다.
Jonathan Frech

-2

배열 변수와 결과 변수 예제를 제외한 PHP 약 28 자.

$ array1 = 배열 ​​(1, 2, 3); $ array2 = 배열 ​​(3, 4, 5);

$ result = array_merge ($ array1, $ array2);


질문에서 : 고유성을 감지하거나 세트 / 배열을 병합하기 위해 표준 라이브러리의 배열 함수를 사용하지 마십시오 . 또한 이것은 실제로 배열에서 중복을 제거하지 않습니다
Jo King

" 독 특성을 발견하거나 세트 / 배열을 병합하기 위해 표준 라이브러리의 배열 함수를 사용하지 마십시오 . "
Peter Taylor

예. 맞습니다. 지적 해 주셔서 감사합니다. 비판은 겸손하게 받아 들여졌다.
Endri

@ 조 왕. "표준 라이브러리를 사용하지 마십시오 ..."에 대해 절대적으로 옳습니다. 나머지는 잘못되었습니다. 중복을 제거합니다. php.net/manual/en/function.array-merge.php . PHP 문서를 완전히 읽는 것이 좋습니다. 나는 그것이 일을 100 % 확신합니다. 배열 중 하나를 복제본으로 간주하면주의해야합니다. 건배.
Endri

1
문자 그대로 변경 사항없이 제출물에서 코드를 실행했으며 출력에 중복이 있습니다. 설명서를 읽어야 것 같습니다 . 그러나 배열에 숫자 키가 포함 된 경우 나중에 값이 원래 값을 덮어 쓰지 않지만 추가됩니다
Jo King
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.