Tần số từ với độ phức tạp trong O (n)


11

Trong một cuộc phỏng vấn cho vị trí nhà phát triển Java, tôi đã được hỏi như sau:

Viết hàm có hai tham số:

  1. Chuỗi đại diện cho một tài liệu văn bản và
  2. một số nguyên cung cấp số lượng các mặt hàng để trả lại.

Thực hiện chức năng sao cho nó trả về một danh sách các Chuỗi được sắp xếp theo tần số từ, từ xuất hiện thường xuyên nhất trước tiên. Giải pháp của bạn sẽ chạy trong thời gian trong đó là số lượng ký tự trong tài liệu.nO(n)n

Sau đây là những gì tôi đã trả lời (bằng mã giả), đó không phải là thời gian mà là vì sắp xếp. Tôi không thể tìm ra làm thế nào để làm điều đó thời gian. O ( n log n ) O ( n )O(n)Ôi(nđăng nhậpn)Ôi(n)

wordFrequencyMap = new HashMap<String, Integer>();
words = inputString.split(' ');

for (String word : words) {
  count = wordFrequencyMap.get(word);
  count = (count == null) ? 1 : ++count;
  wordFrequencyMap.put(word, count);
}

return wordFrequencyMap.sortByValue.keys

Có ai biết hoặc ai đó có thể cho tôi một số gợi ý?


1
Sử dụng bảng băm.
Yuval Filmus

Sử dụng hashtable không giải quyết được vấn đề. Hơn nữa, hashtable là Java kế thừa.
dùng2712937

Các bảng băm thường là mẹo để giảm độ phức tạp từ xuống O ( n ) . Ngay cả khi chúng là Java kế thừa, bất kể điều đó có nghĩa là gì. Tôi đã không kiểm tra trường hợp cụ thể này, vì vậy bạn có thể đúng. Ôi(nđăng nhậpn)Ôi(n)
Yuval Filmus

@YuvalFilmus. Cảm ơn nhưng bảng băm khá giống với bản đồ băm mà tôi đang sử dụng (sự khác biệt lớn giữa 2 cấu trúc dữ liệu là đồng bộ hóa, không áp dụng ở đây). Nhật ký (n) trong tôi xuất phát từ việc sắp xếp các giá trị trong bản đồ băm.
dùng2712937

3
Nhân tiện, trang web này tập trung vào các khái niệm và thuật toán, không phải về mã. Do đó, thông thường, chúng tôi sẽ yêu cầu bạn xóa mã Java và đưa ra mô tả khái niệm về cách tiếp cận của bạn (có thể bằng mã giả cấp cao ngắn gọn nếu cần). Ngoài ra, trên trang web này, câu hỏi liên quan là sử dụng cấu trúc dữ liệu và thuật toán nào; API Java cụ thể không có chủ đề cho trang web này (nhưng bạn có thể hỏi về nó trên StackOverflow) và tương tự, liệu HashtableJava có hợp pháp hay không thực sự không liên quan cho mục đích của trang web này.
DW

Câu trả lời:


10

Tôi đề nghị một biến thể của đếm phân phối:

  1. Đọc văn bản và chèn tất cả các từ gặp phải vào một Trie , duy trì trong mỗi nút đếm, từ đại diện bởi nút này đã xảy ra như thế nào thường xuyên. Ngoài ra theo dõi số lượng từ cao nhất nói maxWordCound. - Ôi(n)
  2. Khởi tạo một mảng kích thước maxWordCount. Kiểu nhập là danh sách các chuỗi. - , vì số lượng không thể cao hơn.Ôi(n)
  3. Di chuyển qua bộ ba và cho mỗi nút thêm chuỗi tương ứng vào mục nhập mảng được chỉ định bởi số đếm. - , vì tổng chiều dài của chuỗi được giới hạn bởi n .Ôi(n)n
  4. Di chuyển mảng theo thứ tự giảm dần và xuất ra số chuỗi mong muốn. - , vì đó là một ràng buộc về cả kích thước và lượng dữ liệu trong mảng.Ôi(n)

Bạn có thể có thể thay thế trie bằng các cấu trúc dữ liệu khác trong giai đoạn đầu tiên.


+1, mặc dù tôi không chắc về điều này. Đó là O (n) vì số lượng từ cần trả về được giới hạn bởi n, số lượng ký tự, nhưng đây có phải là những gì câu hỏi yêu cầu? Hoặc một kết quả độc lập với số lượng từ trả về?
Nikos M.

@NikosM. Nó ; là một trường hợp xấu nhất nói chung về số lượng từ được trả về, không phải là giả định cần thiết. n
Raphael

@Raphael, yeap đúng tôi đang nghĩ về điều này vì nó đã được hỏi trong một cuộc phỏng vấn, những mánh khóe có thể có trong câu hỏi ..
Nikos M.

Tôi tự hỏi nếu có một thuật toán thời gian tuyến tính hiệu quả không gian.
saadtaame

3
@saadtaame, yup, đó là một câu hỏi thú vị. Có thể có giá trị gửi bài riêng như một câu hỏi riêng biệt. Đó không chỉ là hiệu quả không gian; giải pháp trie cũng sử dụng nhiều con trỏ, điều này có thể làm cho nó chậm hơn trong thực tế (được đưa ra cách thức phân cấp bộ nhớ hoạt động trong các máy thực). "Hiệu quả" khác với thời gian chạy trong trường hợp xấu nhất. Không có gì lạ khi thuật toán thời gian để đánh bại thuật toán thời gian O ( n ) chuyên sâu về con trỏ , vì vậy câu hỏi này dường như đã loại trừ một số thuật toán tiềm năng có thể là lựa chọn tốt hơn trong thực tế. Ôi(nlgn)Ôi(n)
DW

3

Tập hợp số lần xuất hiện là O (n), vì vậy mẹo thực sự chỉ là tìm số lần xuất hiện k hàng đầu.

Heap là một cách phổ biến để tổng hợp các giá trị k hàng đầu, mặc dù các phương pháp khác có thể được sử dụng (xem https://en.wikipedia.org/wiki/Partial_sorting ).

Giả sử k là tham số thứ hai ở trên và đó là hằng số trong báo cáo vấn đề (có vẻ như vậy):

  1. Xây dựng một bộ ba từ với số lần xuất hiện trên mỗi nút.
  2. Khởi tạo một đống kích thước k.
  3. Di chuyển ngang qua trie và min-thăm dò / chèn từng cặp (lá, số lần xuất hiện) trong heap top-k.
  4. Xuất ra k lá và số đếm trên cùng (đây thực sự là một nỗi đau vì bạn cần con trỏ cha mẹ để ánh xạ mỗi lá trở lại một từ).

Vì kích thước heap là một hằng số, các hoạt động của heap là O (1), vì vậy bước 3 là O (n).

Heap cũng có thể được duy trì linh hoạt trong khi xây dựng bộ ba.


2

Thuật toán của bạn thậm chí không chạy trong thời gian ; chèn Θ ( n ) điều trong một chi phí Hashtable thời gian Ω ( n 2 ) đã (trường hợp xấu nhất).Ôi(nđăng nhậpn)Θ(n)Ω(n2)


Điều gì sau là sai ; Tôi để nó ở đây trong thời gian này là cho mục đích minh họa.

Chạy sau thuật toán trong trường hợp xấu nhất thời gian (giả sử một bảng chữ cái Σ kích thước không đổi), n là số ký tự trong văn bản.Ôi(n)Σn

  1. Xây dựng một cây hậu tố của văn bản, ví dụ với thuật toán của Ukkonen .

    Nếu việc xây dựng chưa làm điều này, hãy thêm số lượng lá có thể tiếp cận vào mỗi nút (bên trong).

  2. Đi qua cây từ gốc và cắt tất cả các nhánh ở khoảng trống (màu trắng) đầu tiên.

  3. Đi qua cây và sắp xếp danh sách con của mỗi nút theo số lá của chúng.

  4. Năng suất của cây (lá từ trái sang phải) hiện là danh sách tất cả các từ, được sắp xếp theo tần số.

Về thời gian chạy:

  1. Thuật toán của Ukkonen (ở dạng nâng cao) chạy trong thời gian ; duy trì đếm lá không làm tăng Θ -cost của thuật toán.Ôi(n)Θ
  2. Chúng ta phải duyệt qua một nút trên mỗi ký tự của mỗi từ xuất hiện trong văn bản. Vì có nhiều nhất cặp từ-ký tự khác nhau, chúng tôi truy cập vào tối đa n nút.nn
  3. Chúng tôi ghé thăm nhiều nhất là lần chi tiêu hạch (cf 2.) và O ( | Σ |log | Σ | ) = O ( 1 ) mỗi node.nÔi(|Σ|đăng nhập|Σ|)= =Ôi(1)
  4. Chúng ta có thể thu được sản lượng (có kích thước khóa học ) bằng cách di chuyển đơn giản trong thời gian O ( n ) (cf 2.).Ôi(n)Ôi(n)

Giới hạn chính xác hơn có thể thu được bằng thời gian chạy tham số với số lượng từ khác nhau; nếu có ít thì cây nhỏ sau 2.


Thuật toán không chính xác (nó không sắp xếp). Tôi không còn chắc chắn thời gian tuyến tính thậm chí có thể.
Raphael

1

Sử dụng bảng băm (ví dụ HashMap:) để thu thập tất cả các từ và tần số của chúng. Sau đó, sử dụng sắp xếp đếm để sắp xếp các từ theo thứ tự tần số giảm dần. Vì tất cả các tần số là số nguyên trong phạm vi , nên việc sắp xếp sẽ mất thời gian O ( n ) . Tổng thời gian chạy dự kiến ​​là O ( n ) , nhiều hơn có thể là quá đủ cho tất cả các mục đích thực tế (trừ khi người phỏng vấn đề cập đến một cái gì đó bị bỏ qua câu hỏi của bạn). Hãy chắc chắn đề cập rằng đây là thời gian chạy dự kiến thay vì thời gian chạy trong trường hợp xấu nhất .1 ..nÔi(n)Ôi(n)

Ôi(n)Ôi(n)Ôi(n)

Ôi(n)Ôi(n)


Θ(n)Ω(n2)

Tôi không thể nói cho những người phỏng vấn, nhưng tôi ngần ngại sử dụng sự cẩu thả của họ làm lý do cho nhiều thứ tương tự. Ngoài ra, trang web này là về khoa học (như bạn đã nhận xét ở trên), chứ không phải về việc vẫy tay "làm thế nào tôi sẽ được trả tiền sớm hơn".
Raphael

Miễn là sự hiểu biết này được làm rõ ràng, tôi ổn với điều đó. Tôi đã thấy quá nhiều câu hỏi ở đây được thành lập trong sự nhầm lẫn bởi vì một số "hiểu" ngầm thúc đẩy các ý tưởng sai.
Raphael

0

Giải pháp dựa trên Hashtable

Ω(n2)n

nΩ(n)

Ôi(1)Ôi(n)Ôi(n2)n

Giả định là thuật toán băm là tuyến tính theo thời gian liên quan đến số lượng ký tự.

Giải pháp dựa trên cơ sở sắp xếp

Ôi(kN)kNnkÔi(n)

2nnÔi(n)

Một vài từ dài nhất trong tiếng Anh dài một cách lố bịch , nhưng sau đó người ta có thể giới hạn độ dài từ ở một số hợp lý (chẳng hạn như 30 hoặc nhỏ hơn) và cắt ngắn các từ chấp nhận lề lỗi có thể đi kèm với nó.


Θ(n)Θ(n)

Ôi(n+n)Ôi(n2)

(3) Dù bạn chọn hàm băm nào, tôi có thể đưa ra một đầu vào trong đó hàm cụ thể đó suy giảm. Và việc chọn hàm băm sau khi biết đầu vào thường không phải là một tùy chọn. (Và hãy nhớ rằng bình luận mà bạn có lẽ đang giải quyết là về trường hợp xấu nhất, không phải là trường hợp điển hình.)
FrankW

Ôi(n2)

Ôi(n2)Ôi(1)Ω(1)Ôi(1)Ôi(1)
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.