Hợp nhất mảng không có bản sao


15

Gần đây tôi đã thấy mã Javascript này trên StackOverflow để hợp nhất hai mảng và xóa các bản sao:

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

Trong khi mã này hoạt động, nó không hiệu quả khủng khiếp ( O(n^2)). Thách thức của bạn là tạo ra một thuật toán với độ phức tạp ít hơn.

Các tiêu chí chiến thắng là giải pháp có độ phức tạp ít nhất , nhưng các mối quan hệ sẽ bị phá vỡ bởi độ dài ngắn nhất trong các ký tự.

Yêu cầu :

Gói tất cả mã của bạn lại với nhau trong một hàm đáp ứng các yêu cầu sau về "tính chính xác:"

  • Đầu vào: Hai mảng
  • Đầu ra: Một mảng
  • Hợp nhất các phần tử của cả hai mảng với nhau - Bất kỳ phần tử nào trong một trong hai mảng đầu vào phải nằm trong mảng xuất ra.
  • Mảng xuất ra không có bản sao.
  • Thứ tự không quan trọng (không giống như bản gốc)
  • Bất kỳ ngôn ngữ nào
  • Không sử dụng các hàm mảng của thư viện chuẩn để phát hiện tính duy nhất hoặc hợp nhất các bộ / mảng (mặc dù những thứ khác từ thư viện chuẩn vẫn ổn). Hãy để tôi phân biệt rằng nối mảng là tốt, nhưng các hàm đã làm tất cả các điều trên thì không.

Làm thế nào chúng ta có nghĩa vụ phải tạo hoặc nối vào một mảng mà không sử dụng các hàm mảng?
Emil Vikström

@ EmilVikström Xem chỉnh sửa của tôi. Tôi có nghĩa là bạn không thể sử dụng các chức năng duy nhất mảng. Xin lỗi vì không rõ ràng.
hkk

Nếu một trong các mảng có trùng lặp trong đó, chúng ta có loại bỏ chúng không? Ví dụ, nên hợp nhất [1, 2, 2, 3][2, 3, 4]trả lại[1, 2, 2, 3, 4] hay [1, 2, 3, 4]?
OI

1
@OI Vâng, điều đó sẽ làm cho nó quá dễ dàng.
hkk

1
Tôi có thể hỏi: Mảng ? Chúng ta có thể giả sử đơn giản là số nguyên hoặc chuỗi hay chúng ta cũng phải cho phép những thứ phức tạp hơn như các đối tượng đa cấp?
jawns317

Câu trả lời:


8

Perl

27 nhân vật

Hack Perl đơn giản

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

Tôi chắc chắn ai đó có thể lót một thứ này .. và do đó (Cảm ơn Dom Hastings)

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

1
"Đừng sử dụng các hàm mảng của thư viện tiêu chuẩn để phát hiện tính duy nhất (mặc dù những thứ khác tạo thành thư viện chuẩn vẫn ổn)"
John Dvorak

1
Làm thế nào tôi vi phạm quy tắc đó? Tôi không sử dụng các chức năng duy nhất?
Zach Leighton

Làm thế nào nó hoạt động, sau đó? Xin lỗi, tôi không thể đọc perl. Nếu nó đọc các khóa của bản đồ băm - điều đó có được tính là OK với quy tắc đó không? Tôi sẽ không bỏ phiếu cho đến khi tin rằng nó là.
John Dvorak

1
Nó kết hợp các mảng, vòng lặp trên cả hai và thêm vào một hàm băm tăng giá trị mà khóa của ai là giá trị hiện tại trong vòng lặp mảng. Sau đó, nó lấy các khóa của hàm băm đó, tôi đã sử dụng nó trong một số công việc của mình .. Vì vậy, [1,1,2,3,4,4] trở thành {1 => 2, 2 => 1, 3 => 1 , 4 => 2}
Zach Leighton

@ZachLeighton bạn có thể rút ngắn mã xuống còn 27 ký tự sub x{$_{$_}++for@_;keys%_}(trong trường hợp nó bị ràng buộc!) Và sử dụng như sau:z((1,2,3,4),(2,3,4,5,6))
Dom Hastings

10

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

Phiên bản chơi gô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}

Phiên bản golf có thể đọc được của con người:

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
}

tôi có thể dùng concat như vậy và làm điều đó trong 86 ký tự:

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

Nhưng tôi không chắc liệu nó có còn là O (N) hay không dựa trên JsPerf này: http://jsperf.com/unique-array-merging-concat-vs-looping vì phiên bản concat nhanh hơn một chút với các mảng nhỏ hơn nhưng chậm hơn với mảng lớn hơn (Chrome 31 OSX).

Trong thực tế làm điều này (golf có đầy những thực hành xấu):

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

Tôi không giỏi về tính phức tạp của máy tính nhưng tôi tin rằng đây là O(N) . Sẽ yêu nếu ai đó có thể làm rõ.

Chỉnh sửa: Đây là một phiên bản mà mất bất kỳ số lượng mảng và kết hợp chúng.

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

Đây gần như chính xác là những gì tôi sẽ đăng trong vài giây :-( Vâng, đó là thời gian tuyến tính được khấu hao nếu các bảng băm được thực hiện với thời gian không đổi được khấu hao để chèn và tìm kiếm (phổ biến trong nhiều ngôn ngữ, không biết cụ thể về JS).
Emil Vikström

@ EmilVikström Cảm ơn vì tôi tin rằng JavaScript có nhưng không có bằng chứng về nó. Xin lỗi vì có ngón tay nhanh, làm chậm bản thân với bình luận: P
George Reith

Đây là một cách tiếp cận tuyệt vời. Tuy nhiên, bạn cũng có thể cung cấp giải pháp kiểu "code-golf" ngoài phiên bản được định dạng độc đáo không? Thấy rằng nhiều người đã nghĩ rằng đây là cách tiếp cận phù hợp, có lẽ sẽ có một sự ràng buộc O(N).
hkk

@ cloudcoder2000 Ok, tôi muốn in một phiên bản đầy đủ vì phiên bản code-golf có thể sẽ kém hiệu quả hơn trong thực tế.
George Reith

1
@ cloudcoder2000 Chúng không hoàn toàn độc lập nên trường hợp xấu nhất là không O(A*B)(Không sử dụng Nvì nó khó hiểu). Sẽ là nếu mọi mảng đầu vào (mọi A) có cùng số lượng phần tử (B ) như thực tế O(SUM(B) FOR ALL A), có thể được viết lại như O(N)khi xác định Nlà số lượng phần tử của tất cả các đầu vào mảng.
meiamsome

4

Python 2.7, 38 ký tự

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

Nên là O (N) giả sử hàm băm tốt.

Việc setthực hiện 8 ký tự của Wasi's là tốt hơn, nếu bạn không nghĩ rằng nó vi phạm các quy tắc.


Đẹp! Hiểu biết trong Python có thể rất thanh lịch và mạnh mẽ.
OI

3

PHP, 69/42 68/41 ký tự

Bao gồm khai báo hàm là 68 ký tự:

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

Không bao gồm khai báo hàm là 41 ký tự:

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

3

Một cách trong Ruby

Để tuân thủ các quy tắc được nêu ở trên, tôi sẽ sử dụng một chiến lược tương tự như giải pháp JavaScript và sử dụng hàm băm làm trung gian.

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

Về cơ bản, đây là những bước tôi sẽ thực hiện trong dòng trên.

  1. Xác định một biến merged_arr sẽ chứa kết quả
  2. Khởi tạo hàm băm trống, chưa được đặt tên làm trung gian để đặt các phần tử duy nhất vào
  3. Sử dụng Object#tapđể điền vào hàm băm (được tham chiếu như hashtrong tapkhối) và trả lại cho chuỗi phương thức tiếp theo
  4. Nối arr1arr2thành một mảng duy nhất, chưa được xử lý
  5. Đối với mỗi phần tử eltrong mảng được nối, đặt giá trị elvào hash[el]nếu không có giá trị hash[el]hiện tại. Sự ghi nhớ ở đây ( hash[el] ||= el) là những gì đảm bảo tính duy nhất của các yếu tố.
  6. Lấy các khóa (hoặc giá trị, vì chúng giống nhau) cho hàm băm hiện được phổ biến

Điều này sẽ chạy trong O(n) thời gian. Vui lòng cho tôi biết nếu tôi đã đưa ra bất kỳ tuyên bố không chính xác nào hoặc nếu tôi có thể cải thiện câu trả lời ở trên về hiệu quả hoặc khả năng đọc.

Cải tiến có thể

Sử dụng ghi nhớ có lẽ không cần thiết vì các khóa của hàm băm sẽ là duy nhất và các giá trị không liên quan, vì vậy điều này là đủ:

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

Tôi thực sự yêu thích Object#tap, nhưng chúng ta có thể hoàn thành cùng một kết quả bằng cách sử dụng Enumerable#reduce:

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

Bạn thậm chí có thể sử dụng Enumberable#map:

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

Làm thế nào tôi sẽ làm điều đó trong thực tế

Đã nói tất cả, nếu tôi được yêu cầu hợp nhất hai mảng arr1arr2kết quả merged_arrcó các phần tử duy nhất và có thể sử dụng bất kỳ phương thức Ruby nào theo ý của tôi, tôi chỉ cần sử dụng toán tử hợp nhất được thiết kế để giải quyết vấn đề chính xác này:

merged_arr = arr1 | arr2

Tuy nhiên, một cái nhìn nhanh về nguồn của Array#|dường như xác nhận rằng sử dụng hàm băm làm trung gian dường như là giải pháp chấp nhận được để thực hiện hợp nhất duy nhất giữa 2 mảng.


"Đừng sử dụng các hàm mảng của thư viện tiêu chuẩn để phát hiện tính duy nhất (mặc dù những thứ khác tạo thành thư viện chuẩn vẫn ổn)"
John Dvorak

Làm thế nào tôi vi phạm quy tắc đó trong ví dụ thứ hai? Ghi nhớ đang được thực hiện trên một hàm băm. Điều đó cũng không được phép?
OI

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

Một hàm có n mảng có thể là như sau:

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

Chơi gôn, tôi nghĩ cái này sẽ hoạt động (117 ký tự)

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

Cập nhật Nếu bạn muốn giữ loại ban đầu, bạn có thể

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
}

hoặc đánh gôn 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}

Điều này vẫn có thể gây ra một số nghi ngờ, nếu bạn muốn phân biệt 123'123' , thì điều này sẽ không hoạt động ..


Cảm ơn câu trả lời. Nó ngắn một cách ấn tượng, tuy nhiên điều này chỉ làm một nửa vấn đề. Bạn cũng cần đưa vào giải pháp phần sáp nhập thực tế (ngay cả khi nó giống như trong ví dụ ban đầu) và đặt tất cả lại với nhau trong một hàm. Ngoài ra, bạn có thể cung cấp phiên bản "đánh gôn" ngoài phiên bản này O(N)không?
hkk

Điều này ép tất cả các thành viên thành chuỗi. ví dụ: m([1,2,3,4,5],[2,3,4,5,6],[2,3,4,5,6,7])trở thành["1", "2", "3", "4", "5", "6", "7"]
George Reith

2

trăn, 46

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

Hoặc, sử dụng thao tác thiết lập đơn giản

trăn, 8

set(a+b)

1
Xin lỗi nó không rõ ràng, sử dụng các thao tác thiết lập cũng là gian lận.
hkk

Mã thứ 1 của bạn sẽ có các bản sao nếu có các bản sao trong a hoặc nếu có các bản sao trong b và phần tử đó không nằm trong a.
Vedant Kandoi

2

Perl

23 byte, nếu chúng ta chỉ đếm khối mã bên trong chương trình con. Có thể là 21, nếu ghi đè các giá trị toàn cầu được cho phép (nó sẽ xóa mykhỏi mã). Nó trả về các phần tử theo thứ tự ngẫu nhiên, vì thứ tự không thành vấn đề. Về độ phức tạp, trung bình là O (N) (phụ thuộc vào số lần va chạm băm, nhưng chúng khá hiếm - trong trường hợp xấu nhất có thể là O (N 2 ) (nhưng điều này không nên xảy ra, vì Perl có thể phát hiện băm bệnh lý và thay đổi hạt giống hàm băm khi phát hiện hành vi đó)).

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;

Đầu ra (cũng hiển thị ngẫu nhiên):

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

2

Pháo đài: 282 252 233 213

Phiên bản chơi gôn:

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

Cái mà không chỉ trông tốt hơn vô cùng mà còn thực sự biên dịch (một dòng quá dài ở dạng golf) với dạng có thể đọc được:

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

Điều này nên được O(n)tôi sao chép avào cvà sau đó kiểm tra từng cái bđối với tất cả c. Bước cuối cùng là loại bỏ rác csẽ chứa vì nó chưa được khởi tạo.


2

Toán học 10 ký tự

Union[a,b]

Thí dụ:

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

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

Mathicala2 43 Chars

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

8
Tôi nghĩ rằng điều này sẽ đi trong thể loại sử dụng các phương pháp mảng thư viện tiêu chuẩn.
hkk

Xin chào @ cloudcoder2000. Không cần phải gọi một số thư viện cụ thể để sử dụng Union trong Mathematica.
Murta

5
Theo tôi, sử dụng hàm dựng sẵn để thực hiện chính xác những gì câu hỏi đang yêu cầu là gian lận.
Konrad Borowski

ok ok .. mã thứ hai không sử dụng Union.
Murta

1
Tôi đoán Tally[Join[a, b]][[;; , 1]]cũng sẽ gian lận ;-) BTW bạn có thể lưu ký tự bằng cách sử dụng các biến đơn.
Yves Klett

1

Javascript 86

Phiên bản chơi gôn:

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

Phiên bản dễ đọc:

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

1
Điều này bỏ qua các giá trị falsey ... m([1,0,0,0,0],[0,1,0])trả về [1].
George Reith

1
Thay đổi h[v]=vthành h[v]=1.
George Reith

Phát hiện tốt @GeorgeReith! Chúng tôi đã đi từ 86 đến 84 :)
Bertrand

Vẫn là 86, tôi nghĩ bạn đã nhầm lẫn vì bạn đã xóa 2 ký tự khỏi phiên bản có thể đọc được chứ không phải là golf.
George Reith

1

JavaScript 60

Tôi đang sử dụng trình tạo ES6.
Sau đây có thể kiểm tra bằng cách sử dụng REPL của Google .

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

0

Nếu bạn đang tìm kiếm một triển khai dựa trên JavaScript dựa trên các Đối tượng cơ bản đằng sau khung để có hiệu quả, tôi sẽ chỉ sử dụng Set. Thông thường trong một triển khai, đối tượng Set vốn đã xử lý các đối tượng duy nhất trong khi chèn với một số loại lập chỉ mục tìm kiếm nhị phân. Tôi biết trong Java nó là mộtlog(n) tìm kiếm, sử dụng tìm kiếm nhị phân dựa trên thực tế là không có tập hợp nào có thể chứa một đối tượng nhiều lần.


Mặc dù tôi không biết điều này có đúng với Javascript hay không, nhưng điều đơn giản như đoạn mã sau đây có thể đủ cho n*log(n)việc triển khai:

JavaScript , 61 byte

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

Hãy thử trực tuyến!


Nếu đoạn trích trên sử dụng a = [1,2,3]b = [1,2,3,4,5,6]sau đós=[1,2,3,4,5,6] .

Nếu bạn biết sự phức tạp của các Set.add(Object)hàm trong JavaScript cho tôi biết, sự phức tạp của việc này là n + n * f(O)nơi f(O)là sự phức tạp của s.add(O).


0

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

Chức năng ẩn danh ẩn danh.

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

Hãy thử trực tuyến!

, nối các đối số; TRÊN)

(... ) áp dụng các chức năng ngầm ẩn danh sau trên đó; Ô (1)

   ⍳⍨ chỉ số selfie (chỉ số xuất hiện đầu tiên của từng yếu tố trong toàn bộ mảng); TRÊN)

  = so sánh yếu tố theo yếu tố với; TRÊN):

   ⍳∘≢ các chỉ số về độ dài của mảng; TRÊN)

(/⍨) sử dụng để lọc; TRÊN):

   lập luận không sửa đổi; Ô (1)

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


-2

JavaScript, 131 ký tự

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

4
Chào mừng đến với PPCG! Vui lòng cho chúng tôi biết đây là ngôn ngữ nào và định dạng nó dưới dạng mã để dễ đọc hơn. (Điều này hoạt động bằng cách thụt dòng mã với bốn khoảng trắng). Ngoài ra một lời giải thích về phương pháp của bạn sẽ được đánh giá cao.
Laikoni

nó chỉ là một mã javascript.
deepak_pal

@techdeepak Bạn có thể thêm thông tin quan trọng như vậy vào bài đăng của mình, định dạng chính xác nó, thêm tô sáng cú pháp và viết thêm một chút về độ phức tạp của thuật toán của bạn, vì đây là thuật toán nhanh nhất . Vì nó đứng, bài này có chất lượng khá thấp.
Jonathan Frech

-2

PHP khoảng 28 ký tự [bỏ qua các biến mảng ví dụ và biến kết quả].

$ mảng1 = mảng (1, 2, 3); $ mảng2 = mảng (3, 4, 5);

$ result = mảng_merge ($ mảng1, $ mảng2);


Từ câu hỏi: Không sử dụng các hàm mảng của thư viện chuẩn để phát hiện tính duy nhất hoặc hợp nhất các bộ / mảng . Ngoài ra, điều này không thực sự loại bỏ các bản sao khỏi mảng
Jo King

Tôi nghĩ rằng bạn đã bỏ qua dòng quan trọng này từ câu hỏi: " Đừng sử dụng các hàm mảng của thư viện tiêu chuẩn để phát hiện tính duy nhất hoặc hợp nhất các bộ / mảng "
Peter Taylor

Đúng. Đúng rồi. Cảm ơn các bạn đã chỉ ra rằng. Phê bình khiêm tốn chấp nhận.
Endri

@jo vua. Bạn hoàn toàn đúng về "Đừng sử dụng thư viện tiêu chuẩn ...". Phần còn lại là sai. Nó không loại bỏ các bản sao. php.net/manual/en/feft.array-merge.php . Tôi khuyên bạn nên đọc đầy đủ tài liệu của PHP. Tôi chắc chắn 100% là nó làm được việc. Bạn chỉ cần cẩn thận một trong những mảng bạn coi là trùng lặp. Chúc mừng.
Endri

1
Tôi thực sự đã chạy mã trong trình của bạn mà không có thay đổi và đầu ra có trùng lặp. Có vẻ như bạn nên đọc tài liệu, cụ thể là , tuy nhiên, nếu các mảng chứa các khóa số, giá trị sau này sẽ không ghi đè lên giá trị ban đầu, nhưng sẽ được thêm vào
Jo King
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.