+7

Một bài toán xử lý mảng tưởng chừng đơn giản!

Xuân sang, Quang đang lang thang trang facebook thì bắt gặp một bạn hỏi bài toán này:

Mặc dù có rất nhiều comment nhưng có điểm sai mà tuyệt nhiên mình không thấy có ai phát hiện ra đó là chủ thớt viết sai "chính tã" : "giúp đỡ" chứ không phải "giúp đở", "mảng" chữ không phải "mãng". Mà thôi bỏ qua vấn đề này đi, cùng tập trung vào vấn đề chính nào!

Để giải bài này chắc cũng không có gì khó mình thấy cũng khá nhiều comment giải giống nhau, mình tóm gọn lại thành các phương pháp chung như sau : (Đây là diễn đàn về Javascript nên lời giải hoàn toàn dùng javascript)

  1. Gán giá trị check ban đầu là true. Lọc qua từng phần tử mảng array1, mỗi vòng lặp lọc qua mảng array2, nếu có phần tử nào ở màng 2 có giá trị bằng mảng 1 thì lập tức gán check = false và return . Viết thì khá là dễ hiểu nhưng cách này thuật toán quá phức tạp, lọc qua 2 mảng thì performance tăng theo cấp số nhân, nghĩa là 2 mảng càng dài thì thời gian thực thi càng lâu.
array2.some(item => array1.some(item1 => item1 === item))

Cách này thì không ai cãi được

  1. Cách 2 có vẻ đơn giản hơn, lọc qua array2 và sau đó dùng hàm includes() để xem array1 có chứa phần tử của mảng 2 không. Performance hiệu quả hơn hẳn vì chỉ chạy vòng lặp 1 lần.
arr1.some((x) => arr2.includes(x));

Ngoài ra thì còn 1 số cách sáng tạo khác nhưng nhìn chung là sai.

Nhưng cách giải khiến mình để ý nhất vẫn là cách giải của bạn này, nhận được vô số reaction và comment tán thưởng, thậm chí nhiều người còn thừa nhận rằng đây là cách giải tốt nhất vì không cần dùng bất kì vòng lặp nào :

*set loại bỏ phần tử trùng nhau trong mảng (Javascript), tuy nhiên các phần tử của set nằm trong ngoặc nhọn , set không phải là mảng

Đúng là cách giải sáng tạo thật nhưng mình cứ ngờ ngợ làm sao á. Cái cảm giác này thường xuất hiện khi mình làm task xong mà không dám chắc mình có làm đúng hay không (rồi đúng là có bug thật)

Và cách giải trên cũng có bug. Trường hợp 2 mảng là [1,2,3,3][4,5] => Trả về true trong khi đáp án phải là false.

Chỉ cần Fix con bug trên là ngon lành cành đào. Không biết có bug nào nữa không nhỉ ? (Vẫn còn ngờ ngợ)

    const uniq_a = new Set(a)
    const uniq_b = new Set(b)
    uniq_a.add(uniq_b).size < uniq_a.size + uniq_b.size

Website của tôi: https://www.websitegiare.co

https://3000vocab.com

https://zreview.vn


All rights reserved

Bình luận

Đăng nhập để bình luận
Avatar
@nguyentuan239
thg 4 8, 2021 6:49 CH

Mình thấy code logic sao dễ đọc, dễ hiểu nhất có thể là được còn performance thì tính sau. Nếu mình code thì loop array 2 rồi dùng indexOf

Avatar
@khangnd
thg 4 9, 2021 3:36 SA

thậm chí nhiều người còn thừa nhận rằng đây là cách giải tốt nhất vì không cần dùng bất kì vòng lặp nào

Mình không giỏi thuật toán nhưng mình đoán Set vẫn được implement từ vòng lặp phía dưới mới có thể loại bỏ các phần tử trùng lặp, nên khẳng định này không đúng lắm 🙂

Xem thêm (5)
Avatar
@dhvan85
thg 4 13, 2021 8:23 SA

@hoangtq160798 Haiz, cái mình muốn nói là đánh giá khi chạy nhiều lần và đang bàn lượng dữ liệu như ví dụ. Khi chạy 1 lần cách tạo Set tốt hơn, mảng lớn thì Set cũng tốt hơn. Tại sao? Vì giai đoạn check key có hay chưa nó rất nhanh và nó bù lại cho khoảng thời gian tạo Set, thời gian cấp phát bộ nhớ. Vậy khi đánh giá lựa chọn một cách nào đó thì ta cần cân nhắc cái gì? Ý mình là tránh nhận xét cách tạo Set là hay nhất như trong bài viết, mà cần coi ngữ cảnh chạy nhiều hay ít, data nhiều không, cần tối ưu bộ nhớ hay cpu vì RAM và CPU thường phải đánh đổi... ứng với mỗi ngữ cảnh mà chọn cách phù hợp, tránh nhớ đúng một cách và làm một cách máy móc. Phần mình up là muốn nói tác dụng ngoài của Set khi chạy nhiều lần, chi phí tạo Set nó bị tăng nhanh theo số lần lặp và ăn luôn lợi thế check key, không câu nào mình bác bỏ nó. Như vậy mình có thể tóm tắt thế này:

  • Chạy ít lần, data nhiều hoặc ít: nên dùng Set (chấp nhận tốn thêm RAM khi data nhiều, vì hàng triệu thì dùng loop có O(N2) là không khả thi)
  • Chạy nhiều lần, data ít: nên dùng loop
  • Chạy nhiều lần, data nhiều: chưa rõ nên dùng cái nào, có time thì nghiên cứu thêm thuật toán khác.
Avatar

@dhvan85 oke, nghe hợp lý

Avatar
@xuanvupham96
thg 4 9, 2021 7:25 SA

Bài viết ngắn quá bạn ơi, hụt hẫng

Avatar
@haunguyenn
thg 4 11, 2021 9:35 SA

array.png

Nếu add một array vào Set thì Set đó sẽ thêm 1 element mới chính là array mà ta? Vậy thì đâu có loại bỏ phần tử trùng nhau 🤔 set2.png

Avatar
@KudouSterain
thg 4 11, 2021 6:26 CH

bác lag rồi đó 😃 bên dưới có bác giải thích kìa

Avatar
@AwakenKayz
thg 4 11, 2021 9:42 SA

Set cũng là 1 dạng hash table. Chỉ khác là các phần tử trong Set được lưu trữ dưới dạng các cặp key-value(K,V) với các value là hằng số. Ví dụ: 1 Set có các phần tử {1, 2, 3} thì bản chất đằng sau là 1 hash table có thể biểu thị dưới dạng object như sau: {1: true, 2: true, 3: true} Do đó, để khởi tạo 1 Set ta cần phải lặp qua array (O(N)) và sử dụng phương thức Object.hasProperty() để kiểm tra phần tử có tồn tại chưa trước khi thêm vào.

Thế nên time complexity vẫn là O(N).

Avatar
@KudouSterain
thg 4 11, 2021 6:17 CH

Mình không tán thành với cách suy nghĩ của host, bạn có chắc là sử dụng include , set v.v rằng ở tầng bên dưới của javascript không sử dụng for loop không , mà chắc nịt rằng việc sử dụng tụi kia cho performace tốt hơn trong khi lại không có 1 bài test nào xảy ra cả? đúng là sử dụng những hàm có sẳn là nhanh nhất , nhưng best performance thì mình lại không nghĩ vậy ?

  • Thậm chí rằng việc dùng set chậm hơn rất nhiều, nếu nó nhanh hơn thì em mời bác đề xuất cách kiểm tra mảng có phần tử trùng nhau mà không phải for ít nhất 1 lần
Avatar
@sjIv3r
thg 4 11, 2021 7:37 CH

chắc bác chưa biết barebone - xương sống của mọi loại ngôn ngữ, chỉ gồm vòng lặp và phép tăng 1 (++). Cho dù Set có nhanh hơn thì bởi vì không chạy bằng vòng lặp của JS mà là vòng lặp của C++ (Java tuỳ engine) nên trông có vẻ nhanh hơn thôi.

Avatar
@sax1476
thg 4 12, 2021 12:14 SA

Cho mình hỏi ngu xíu, hàm some và hàm includes trong js đều là duyệt mảng để tìm phần tử, vậy cách giải 2 bản chất vẫn là 2 vòng lặp lồng nhau, độ phức tạp O(N^2) chứ. Đúng là với trường hợp số phần tử nhỏ thế này thì code cho rõ ràng là ok, còn nếu mẫu rất lớn thì mình nghĩ cách giải là: chuyển mảng 1 thành Set trước -> lặp qua từng phần tử trong mảng 2 và xem phần tử đó có trong mảng 1 không, nếu có thì return true, lặp hết mảng 2 mà k có thì return false. Do Set thuộc dạng hash table nên việc tìm 1 phần tử trong Set là O(1), tổng kết thuật toán là O(N)

Avatar
@mrhoang
thg 4 12, 2021 12:51 SA

Nghĩ đơn giản thôi các bác, lấy giao của 2 mảng là được

a = [1,2,3,4]
b = [2,5]
(a & b).length > 0
Avatar
Avatar
@nhoxtwi
thg 4 13, 2021 3:35 SA

Mình thuộc trường phái clean code, nên viết cái nào cho rõ ràng, dễ hiểu nhất, ai đọc vào cũng hiểu code làm gì, performance thì từ từ tính sau Chứ code cho cao siêu vào, hoặc viết hằm bà lằng tỏ ra cao thủ, hoặc viết code cụt ngủn kèm 7x7 49 cái comment để người đọc khỏi wtf thì mình thấy cũng không được :v

Avatar
@cr_ronaldo16396
thg 5 28, 2021 8:22 SA

cái array có dăm 3 cái phần tử mà cứ nghĩ phức tạp lên làm gì.

Avatar
+7