Một yếu tố khác nhau trong hai mảng. Làm thế nào để tìm thấy nó hiệu quả?


22

Tôi đang chuẩn bị cho một cuộc phỏng vấn mã hóa và tôi thực sự không thể tìm ra cách hiệu quả nhất để giải quyết vấn đề này.

Giả sử chúng ta có hai mảng bao gồm các số chưa được sắp xếp. Mảng 2 chứa một số mà Mảng 1 không có. Cả hai mảng có số được định vị ngẫu nhiên, không nhất thiết phải theo cùng một thứ tự hoặc tại cùng một chỉ số. Ví dụ:

Mảng 1 [78,11, 143, 84, 77, 1, 26, 35 .... n]

Mảng 2 [11,84, 35, 25, 77, 78, 26, 143 ... 21 ... n + 1]

Thuật toán nhanh nhất để tìm số khác nhau là gì? Thời gian chạy của nó là gì? Trong ví dụ này, con số chúng tôi sẽ tìm kiếm là 21.

Ý tưởng của tôi là chạy qua Mảng 1 và xóa giá trị đó khỏi mảng 2. Lặp lại cho đến khi bạn hoàn thành. Điều này sẽ có khoảng thời gian chạy , phải không?O(nlogn)


@Jandvorak Cảm ơn các bạn đã phản hồi. Tôi thức dậy muộn và ngủ thiếp đi sau khi đăng bài này. Mảng không được sắp xếp và tất cả các Mục xuất hiện ở các chỉ mục ngẫu nhiên trong cả hai mảng.
Konstantino Sparakis

@KonstantinoSparakis: việc làm rõ này làm mất hiệu lực các câu trả lời cho rằng cả hai mảng đều chứa các phần tử trong cùng một vị trí.
Mario Cervera

Đăng bài chéo được tán thành khi phần
paparazzo

@Paparazzi Chỉ đơn giản là tìm kiếm một giải pháp mà tôi đã đọc trong kỹ thuật phần mềm meta là nơi để tìm giải pháp nhưng tại thời điểm đó tôi không biết về diễn đàn CS. Tôi đã thông báo cho các mod, để dọn dẹp nó.
Konstantino Sparakis

@Paparazzi có một meta post sao lưu không? Cá nhân tôi không thấy cách nào để thực hiện tốt chính sách đó.
djechlin

Câu trả lời:


30

Tôi thấy bốn cách chính để giải quyết vấn đề này, với thời gian chạy khác nhau:

  • nÔi(n2)Giải pháp : đây sẽ là giải pháp mà bạn đề xuất. Lưu ý rằng, vì các mảng không được sắp xếp, việc xóa mất thời gian tuyến tính. Bạn thực hiện xóa; do đó, thuật toán này mất thời gian bậc hai.n

  • O ( nÔi(ntôiogn) : sắp xếp các mảng trước; sau đó, thực hiện tìm kiếm tuyến tính để xác định phần tử riêng biệt. Trong giải pháp này, thời gian chạy bị chi phối bởi hoạt động sắp xếp, do đó giới hạn trên của .Ôi(ntôiogn)

Khi bạn xác định một giải pháp cho một vấn đề, bạn nên luôn tự hỏi: tôi có thể làm tốt hơn không? Trong trường hợp này, bạn có thể, sử dụng thông minh các cấu trúc dữ liệu. Lưu ý rằng tất cả những gì bạn cần làm là lặp lại một mảng và thực hiện tra cứu lặp đi lặp lại trong mảng khác. Cấu trúc dữ liệu nào cho phép bạn thực hiện tra cứu trong thời gian không đổi (dự kiến)? Bạn đoán đúng: một bảng băm .

  • Ôi(n)Giải pháp (dự kiến): lặp lại mảng đầu tiên và lưu trữ các phần tử trong bảng băm; sau đó, thực hiện quét tuyến tính trong mảng thứ hai, tìm kiếm từng phần tử trong bảng băm. Trả về phần tử không tìm thấy trong bảng băm. Giải pháp thời gian tuyến tính này hoạt động cho bất kỳ loại phần tử nào mà bạn có thể chuyển đến hàm băm (ví dụ: nó sẽ hoạt động tương tự cho các chuỗi của chuỗi).

Nếu bạn muốn các bảo đảm giới hạn trên và các mảng được cấu tạo chặt chẽ bởi các số nguyên, thì giải pháp tốt nhất có lẽ là giải pháp được đề xuất bởi Tobi Alafin (mặc dù giải pháp này sẽ không cung cấp cho bạn chỉ số của phần tử khác trong mảng thứ hai) :

  • Ôi(n)Giải pháp (được bảo đảm): tổng hợp các phần tử của mảng đầu tiên. Sau đó, tổng hợp các phần tử của mảng thứ hai. Cuối cùng, thực hiện cơ chất. Lưu ý rằng giải pháp này thực sự có thể được tổng quát hóa cho bất kỳ loại dữ liệu nào có giá trị có thể được biểu diễn dưới dạng chuỗi bit có độ dài cố định, nhờ toán tử XOR bitwise . Điều này được giải thích cặn kẽ trong câu trả lời của Ilmari Karonen .

Cuối cùng, một khả năng khác (theo cùng một giả định của mảng số nguyên) sẽ là sử dụng một thuật toán sắp xếp thời gian tuyến tính, chẳng hạn như sắp xếp đếm. Điều này sẽ giảm thời gian chạy của giải pháp dựa trên sắp xếp từ sang .O ( n )Ôi(ntôiogn)Ôi(n)


4
tổng cộng không phải là tuyến tính nếu số lượng đủ lớn, mặc dù.
Sange Borsch

9
Một điều thú vị về thuật toán tổng hợp là nó hoạt động với bất kỳ nhóm abelian nào, không chỉ với các số nguyên (Đáng chú ý nhất là uint64; cc @sarge).
John Dvorak

6
@Abdul điều là nếu số nguyên của bạn rất lớn, bạn không còn có thể giả vờ rằng họ lấy để thêm. Tôi tin rằng sự phức tạp tăng lên thành O ( n ln n ) nếu bạn tính đến điều đó. Tuy nhiên, sử dụng XOR thay vì bổ sung thông thường sẽ giải quyết được điều đó, trong khi vẫn cho phép số lượng lớn tùy ý trong đầu vào. Ôi(n)Ôi(nlnn)
John Dvorak

2
@JanDvorak Không, không. Bạn đang giả định rằng hoạt động được xác định trên nhóm abelian mất thời gian không đổi. Điều đó không thể được giả định.
UTF-8

2
@ UTF-8 Tôi không cho rằng điều đó. Nhưng nó làm như vậy trong các nhóm hữu hạn (uint64) và phép cộng số tại chỗ (bổ sung trong ) có kích thước tuyến tính của toán hạng ngoài vị trí. Vì vậy, tính toán tổng trong các nhóm như vậy thời gian tuyến tính trong tổng kích thước của toán hạng. Znd
John Dvorak

16

Các sự khác biệt-of-tiền giải pháp được đề xuất bởi TobiMario trong thực tế có thể được khái quát hóa cho bất kỳ loại dữ liệu khác mà chúng ta có thể định nghĩa một (không đổi theo thời gian) hoạt động nhị phân đó là:Θ(n)

  • tổng , như vậy mà cho bất kỳ giá trị b , một b được định nghĩa và cùng loại (hoặc ít nhất là một số siêu kiểu thích hợp của nó, mà các nhà điều hành vẫn được xác định);abab
  • kết hợp , sao cho ;a(bc)=(ab)c
  • giao hoán , như vậy mà ; vàab=ba
  • cancellative , chẳng hạn rằng có tồn tại một nhà điều hành ngược thỏa mãn ( một b ) b = một . Về mặt kỹ thuật, thao tác nghịch đảo này thậm chí không nhất thiết phải là thời gian không đổi, miễn là "trừ" hai tổng số n phần tử, mỗi phần tử không mất nhiều thời gian hơn O ( n ) .(ab)b=anO(n)

(Nếu loại chỉ có thể có một số lượng hữu hạn các giá trị riêng biệt, thì các thuộc tính này đủ để biến nó thành một nhóm Abel ; ngay cả khi không, ít nhất nó sẽ là một nửa nhóm hủy giao hoán .)

Sử dụng như một hoạt động , chúng ta có thể xác định "tiền" của một mảng một = ( một 1 , một 2 , ... , một n ) như ( a=(a1,a2,,an) Với một mảng b = ( b 1 , b 2 , ... , b n , b n + 1 ) chứa tất cả các yếu tố của một cộng với một yếu tố thêm x , chúng ta nên có (

(a)=a1a2an.
b=(b1,b2,,bn,bn+1)ax , và vì vậy chúng tôi có thể tìm thấy yếu tố phụ này bằng cách tính toán: x = ( (b)=(a)x
x=(b)(một).

Ví dụ, nếu các giá trị trong mảng là các số nguyên, sau đó số nguyên Ngoài (hoặc modul nữa cho số nguyên hữu hạn có độ dài các loại) có thể được sử dụng như các nhà điều hành , với phép trừ như các hoạt động ngược . Ngoài ra, đối với bất kỳ kiểu dữ liệu có giá trị có thể được biểu diễn dưới dạng chuỗi bit chiều dài cố định, chúng ta có thể sử dụng Bitwise XOR như cả .

Tổng quát hơn, chúng ta thậm chí có thể áp dụng phương pháp XOR bitwise cho các chuỗi có độ dài thay đổi, bằng cách đệm chúng lên cùng một độ dài cần thiết, miễn là chúng ta có một số cách để loại bỏ phần đệm ở cuối.

Trong một số trường hợp, điều này là tầm thường. Ví dụ, các chuỗi byte kết thúc null kiểu C hoàn toàn mã hóa độ dài của riêng chúng, do đó, áp dụng phương thức này cho chúng là không quan trọng: khi XORing hai chuỗi, đệm chuỗi ngắn hơn với byte rỗng để làm cho độ dài của chúng khớp với nhau kết quả cuối cùng. Tuy nhiên, xin lưu ý rằng các chuỗi tổng XOR trung gian có thể chứa byte rỗng, do đó, bạn sẽ cần lưu trữ độ dài của chúng một cách rõ ràng (nhưng bạn sẽ chỉ cần tối đa một hoặc hai trong số chúng).

Tổng quát hơn, một phương thức hoạt động cho các chuỗi bit tùy ý sẽ là áp dụng đệm một bit , trong đó mỗi chuỗi bit đầu vào được đệm với bit đơn và sau đó có càng nhiều 0 bit cần thiết để khớp với độ dài (đệm) của chuỗi đầu vào dài nhất. (Tất nhiên, phần đệm này không cần phải được thực hiện trước một cách rõ ràng; chúng ta chỉ có thể áp dụng nó khi cần trong khi tính toán tổng XOR.) Cuối cùng, chúng ta chỉ cần tách bất kỳ bit 0 nào1 bit cuối cùng từ kết quả. Ngoài ra, nếu chúng ta biết rằng các chuỗi là ví dụ nhiều nhất là 2 321001232dài byte, chúng ta có thể mã hóa độ dài của mỗi chuỗi dưới dạng một số nguyên 32 bit và thêm nó vào chuỗi. Hoặc thậm chí chúng ta có thể mã hóa độ dài chuỗi tùy ý bằng cách sử dụng một số mã tiền tố và thêm chúng vào chuỗi. Các mã hóa khác có thể tồn tại là tốt.

Trong thực tế, do bất kỳ loại dữ liệu nào có thể biểu diễn trên máy tính, theo định nghĩa, có thể được biểu diễn dưới dạng chuỗi bit có độ dài hữu hạn, phương pháp này mang lại giải pháp cho vấn đề.Θ(n)

Phần khó khăn tiềm năng duy nhất là, để hủy bỏ hoạt động, chúng ta cần chọn một đại diện chuỗi bit chính tắc duy nhất cho mỗi giá trị, điều này có thể khó khăn (thực sự, thậm chí có thể tính toán được) nếu các giá trị đầu vào trong hai mảng có thể được đưa ra trong các đại diện tương đương khác nhau. Tuy nhiên, đây không phải là điểm yếu cụ thể của phương pháp này; bất kỳ phương pháp nào khác để giải quyết vấn đề này cũng có thể được thực hiện để thất bại nếu đầu vào được phép chứa các giá trị có tính tương đương là không thể giải quyết được.


Wow rất thú vị về điều này. Cảm ơn bạn @IlmariKaronen
Konstantino Sparakis

14

Tôi sẽ đăng bài này như một bình luận về câu trả lời của Tobi, nhưng tôi chưa có tiếng tăm.

Thay thế cho việc tính tổng của từng danh sách (đặc biệt nếu chúng là danh sách lớn hoặc chứa số lượng rất lớn có thể tràn loại dữ liệu của bạn khi được tính tổng), bạn có thể sử dụng xor thay thế.

Chỉ cần tính xor-sum (tức là x [0] ^ x [1] ^ x [2] ... x [n]) của mỗi danh sách và sau đó xor hai giá trị đó. Điều này sẽ cung cấp cho bạn giá trị của mục không liên quan (nhưng không phải là chỉ mục).

Đây vẫn là O (n) và tránh mọi vấn đề với tràn.


3
Tôi cũng sử dụng XOR, vì nó có vẻ gọn gàng hơn một chút, nhưng công bằng mà nói, tràn không thực sự là một vấn đề miễn là ngôn ngữ bạn thực hiện điều này trong hỗ trợ tràn bằng cách gói.
Martin Ender

14

Phần tử = Tổng (Mảng 2) - Tổng (Mảng1)

Tôi thực sự nghi ngờ đây là thuật toán tối ưu nhất. Nhưng đó là một cách khác để giải quyết vấn đề, và là cách đơn giản nhất để giải quyết nó. Hy vọng nó giúp.

Nếu số lượng phần tử được thêm nhiều hơn một, thì phần tử này sẽ không hoạt động.

Câu trả lời của tôi có độ phức tạp thời gian chạy tương tự cho trường hợp tốt nhất, xấu nhất và trung bình,

EDIT
Sau một số suy nghĩ, tôi nghĩ rằng câu trả lời của tôi là giải pháp của bạn.

nn-11= =n-12= =n+1-1= =n

2n-12-1= =1

2n-1+1= =2n

Θ(n)

EDIT:
Do một số vấn đề với các loại Dữ liệu, tổng XOR theo đề xuất của reffu sẽ có nhiều khả năng hơn.


Lưu ý rằng phương pháp này có thể không mang lại câu trả lời chính xác nếu giá trị của bạn là số float, vì tổng các số có thể đưa ra lỗi vòng. Tuy nhiên, nó sẽ hoạt động cho các giá trị số nguyên với điều kiện là a) loại số nguyên của bạn có hành vi bao quanh được xác định rõ khi tràn, hoặc b) bạn lưu trữ tổng trong các biến của một loại đủ rộng để chúng không thể tràn.
Ilmari Karonen

Lớp "BigNum" của Ruby có thể xử lý việc này.
Tobi Alafin

Nó hoàn toàn không hoạt động nếu mảng của bạn chứa các chuỗi ví dụ hoặc bất kỳ thứ gì không thể thêm vào một cách có ý nghĩa.
gnasher729

Yup, tôi nhận ra. Còn việc sử dụng 'XOR' thì sao? Nó sẽ làm việc cho phao?
Tobi Alafin

Có và cả con trỏ và nói chung bất cứ thứ gì bao gồm một số bit cố định. Nhiều ngôn ngữ không hỗ trợ điều đó, nhưng đó không phải là vấn đề cơ bản. Bổ sung / trừ mô-đun sẽ làm việc trong cùng một trường hợp.
harold

1

Giả sử rằng mảng 2 được tạo bằng cách lấy mảng 1 và chèn một phần tử vào vị trí ngẫu nhiên hoặc mảng 1 được tạo bằng cách lấy mảng 2 và xóa một phần tử ngẫu nhiên.

Nếu tất cả các phần tử mảng được đảm bảo là khác biệt, thời gian là O (ln n). Bạn so sánh các yếu tố tại vị trí n / 2. Nếu chúng bằng nhau, phần tử phụ là từ n / 2 + 1 đến cuối mảng, nếu không thì từ 0 đến n / 2. Và như vậy.

Nếu các thành phần mảng không được đảm bảo là khác biệt: Bạn có thể có n lần số 1 trong mảng 1 và số 2 được chèn ở bất kỳ đâu trong mảng 2. Trong trường hợp đó, bạn không thể biết số 2 ở đâu mà không nhìn vào tất cả các phần tử mảng. Do đó O (n).

Tái bút Vì các yêu cầu thay đổi, hãy kiểm tra thư viện của bạn để biết những gì có sẵn. Trên macOS / iOS, bạn tạo NSCountedset, thêm tất cả các số từ mảng 2, xóa tất cả các số khỏi mảng 1 và những gì còn lại là ở mảng 2 nhưng không phải trong mảng 1, mà không phụ thuộc vào tuyên bố rằng có thêm một mục.


Câu trả lời này là tại chỗ, nhưng câu hỏi đã được chỉnh sửa với một yêu cầu mới làm mất hiệu lực giả định của bạn.
Mario Cervera

Câu trả lời mới của bạn có vẻ đúng. Thời gian phức tạp là gì.
Tobi Alafin

Vâng, đầu tiên thời gian cần thiết để viết mã là gì. Thật tầm thường. NSCountedSet sử dụng băm, vì vậy độ phức tạp thời gian là "thường tuyến tính".
gnasher729

-1

var ngắn nhất, dài nhất;

Chuyển đổi ngắn nhất thành bản đồ để tham khảo nhanh và vòng lặp dài nhất cho đến khi giá trị hiện tại không có trong bản đồ.

Một cái gì đó như thế này trong javascript:

if (Array1.length> Array2.length) {shortest = Array2; dài nhất = mảng1; } other {shortest = Array1; dài nhất = mảng2; }

var map = shortest.reduce (function (obj, value) {obj [value] = true; return obj;}, {});

var differ = longest.find (function (value) {return !!! map [value];});


Mã mà không giải thích không được tính là một câu trả lời tốt ở đây. Ngoài ra tại sao bạn sẽ sử dụng !!! ?
Ác quỷ

-1

Giải pháp O (N) về độ phức tạp thời gian O (1) về độ phức tạp không gian

Báo cáo vấn đề: Giả sử mảng2 chứa tất cả các phần tử của mảng1 cộng với một phần tử khác không có trong mảng1.

Giải pháp là: Chúng tôi sử dụng xor để tìm phần tử không có trong mảng1 nên các bước là: 1. Bắt đầu từ mảng1 và thực hiện xor tất cả các phần tử và lưu trữ chúng trong một biến. 2. Lấy mảng2 và thực hiện xor của tất cả các phần tử với biến lưu trữ xor của mảng1. 3. Sau khi thực hiện thao tác, biến của chúng ta sẽ chứa phần tử chỉ có trong mảng2. Thuật toán trên hoạt động vì thuộc tính sau của xor "a xor a = 0" "a xor 0 = a" Tôi hy vọng điều này sẽ giải quyết vấn đề của bạn. Ngoài ra các giải pháp đề xuất ở trên cũng tốt

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.