2012-01-24 8 views
6

Tôi có một cấu trúc dữ liệu trong đó bao gồm các cặp giá trị, là người đầu tiên trong số đó là một số nguyên và lần thứ hai trong số đó là một chuỗi chữ và số (có thể bắt đầu với chữ số):Cấu trúc dữ liệu C# nào cho phép tìm kiếm một cặp dây có hiệu quả nhất đối với các đoạn mã?

+--------+-----------------+ 
| Number | Name   | 
+--------+-----------------+ 
| 15  | APPLES   | 
| 16  | APPLE COMPUTER | 
| 17  | ORANGE   | 
| 21  | TWENTY-1  | 
| 291 | 156TH ELEMENT | 
+--------+-----------------+ 

Một bảng trong số này sẽ bao gồm tối đa 100.000 hàng.

Tôi muốn cung cấp chức năng tra cứu trong đó người dùng có thể tra cứu số đó (như thể đó là một chuỗi) hoặc các phần của chuỗi. Lý tưởng là tra cứu sẽ "sống" như kiểu người dùng; sau mỗi lần nhấn phím (hoặc có thể sau một khoảng thời gian trễ ngắn ~ 250-500 ms) một tìm kiếm mới sẽ được thực hiện để tìm các ứng cử viên có khả năng nhất. Vì vậy, ví dụ tìm kiếm trên

  • 1 sẽ trở lại 15 APPLES, 16 APPLE COMPUTER, 17 ORANGE, và 291 156TH ELEMENT
  • 15 sẽ thu hẹp tìm kiếm để 15 APPLES, 291 156TH ELEMENT
  • AP sẽ trở lại 15 APPLES16 APPLE COMPUTER
  • (lý tưởng , nhưng không bắt buộc) ELEM sẽ trả lại 291 156TH ELEMENT.

Tôi đã suy nghĩ về việc sử dụng hai Dictionary<string, string> s kể từ cuối cùng là int s đang được so sánh như string s - một di chúc chỉ mục bằng phần số nguyên và các khác do phần chuỗi.

Nhưng thực sự tìm kiếm theo chuỗi con không nên sử dụng hàm băm và có vẻ như lãng phí khi sử dụng gấp đôi bộ nhớ mà tôi cảm thấy mình cần.

Cuối cùng câu hỏi là, có cách nào hoạt động tốt để tìm kiếm văn bản hai danh sách lớn đồng thời cho các bản chất không?

Nếu không, làm thế nào về một SortedDictionary? Có thể tăng hiệu suất nhưng vẫn không giải quyết được vấn đề băm.

Nghĩ về việc tạo một regex khi đang bay, nhưng tôi nghĩ điều đó sẽ thực hiện khủng khiếp.

Tôi mới tham gia C# (có nguồn gốc từ thế giới Java) nên tôi chưa xem xét LINQ; đó có phải là câu trả lời không?

EDIT 18:21 EST: Không có chuỗi nào trong trường "Tên" sẽ dài hơn 12-15 ký tự, nếu điều đó ảnh hưởng đến giải pháp tiềm năng của bạn.

+0

Tôi nghĩ rằng việc triển khai một chút sửa đổi của thuật toán Knuth – Morris – Pratt] (http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm) sẽ có ích. – ChaosPandion

+0

Khi bạn nói "hiệu quả", bạn có nghĩa là "nhanh" hoặc ít nhất là bộ nhớ? Nói chung trong những tình huống này, bạn giao dịch tốc độ cho bộ nhớ, hoặc tìm một số cân bằng chấp nhận được của cả hai. Cũng có chuỗi 100k khá tĩnh, nghĩa là có ít doanh thu và chúng được tìm kiếm nhiều lần? – EBarr

+0

@EBarr: Trí nhớ không phải là một mối quan tâm lớn, nhưng tôi không muốn lãng phí. Tốc độ là quan trọng hơn ở đây. – Tenner

Trả lời

3

Tôi muốn xem xét sử dụng cấu trúc dữ liệu Trie.

Làm cách nào để đạt được điều đó? Lá sẽ đại diện cho "hàng" của bạn, nhưng bạn sẽ có "hai con đường" cho mỗi cá thể bộ nhớ của một "hàng" (một cho số và một cái khác cho tên).

Sau đó bạn có thể hy sinh tình trạng của bạn:

(ideally, but not required) ELEM will return 291 156TH ELEMENT. 

Hoặc cung cấp thậm chí nhiều đường dẫn đến trường hợp hàng của bạn.

+0

Thú vị; Tôi chắc chắn sẽ xem xét việc thực hiện điều này và xem nó hoạt động tốt như thế nào. Tôi đã không bao gồm thực tế này trong bài gốc nhưng tôi có thể làm việc tạo cây ban đầu khi bắt đầu chương trình; nếu mất thêm một chút thời gian đó chắc chắn không phải là kết thúc của thế giới. Cảm ơn! – Tenner

+0

Phát hiện tại đây. Đánh bại tôi với cú đấm ;-) – EBarr

+0

Đó là giải pháp "độc ác" hơn "giải pháp tối ưu về sử dụng bộ nhớ". Đó là một trong đó làm cho bạn khóc như một đứa trẻ khi bạn thực hiện nó :) Như đã đề cập bởi Phil, Lucene.Net là một giải pháp tốt, nhưng nó thực sự phụ thuộc vào trường hợp sử dụng cụ thể của bạn. 100k các chuỗi như vậy ... đó là ~ 1MB. Không có nhiều thực sự nếu bạn có chúng sẵn có trong bộ nhớ, nhưng bạn sẽ cần phải kéo chúng từ cơ sở dữ liệu nhiều lần theo yêu cầu và xây dựng một trie đầu tiên, sau đó đó là một câu chuyện khác. – doblak

6

Nếu có thể, tôi sẽ tránh tải tất cả 100.000 mục nhập vào bộ nhớ. Tôi sẽ sử dụng cơ sở dữ liệu hoặc Lucene.Net để lập chỉ mục các giá trị. Sau đó, sử dụng cú pháp truy vấn thích hợp để tìm kiếm kết quả một cách hiệu quả.

+2

Điều đó có tất cả những niềm vui ra khỏi nó mặc dù .... – ChaosPandion

+0

Những gì tôi đã nêu ở trên là một phần rất nhỏ của sản phẩm, và tôi thực sự thích giải pháp trọng lượng nhẹ nhất có thể. Điều đó nói rằng, tôi chắc chắn sẽ xem xét Lucene.net trong bộ nhớ nếu tôi không thể đến với bất cứ điều gì khác mà thực hiện tốt. Cảm ơn! – Tenner

1

Vì bạn đang tìm kiếm từ đầu, bộ sưu tập dựa trên khóa sẽ không hoạt động, trừ khi bạn lưu trữ tất cả các từ có thể có, như "a", "ap", "app", "appl", "apple ".

Đề xuất của tôi là sử dụng System.Collections.Generic.List<T> kết hợp với tìm kiếm nhị phân. Bạn sẽ phải cung cấp cho riêng mình IComparer<T>, cũng tìm thấy sự bắt đầu của các từ. Bạn sẽ sử dụng hai cấu trúc dữ liệu.

Một List<KeyValuePair<string,int>> giữ các từ đơn lẻ hoặc số làm khóa và số làm giá trị.

One Dictionary<int,string> giữ nguyên tên.

Bạn sẽ tiến hành như thế này:

  1. Tách câu của bạn (cả tên) vào những từ đơn lẻ.

  2. Thêm chúng vào danh sách có từ là khóa và số làm giá trị của KeyValuePair.

  3. Thêm số vào danh sách dưới dạng khóa và làm giá trị của KeyValuePair.

  4. Khi danh sách đầy, hãy sắp xếp danh sách để cho phép tìm kiếm nhị phân.

Tìm kiếm một sự khởi đầu của một từ:

  1. Tìm kiếm trong danh sách bằng cách sử dụng BinarySearch kết hợp với IComparer<T> của bạn.

  2. Chỉ mục bạn nhận được từ tìm kiếm có thể không phải là trường hợp đầu tiên áp dụng, vì vậy hãy quay lại danh sách cho đến khi bạn tìm thấy mục nhập đầu tiên phù hợp.

  3. Sử dụng số được lưu làm giá trị trong danh sách, tra cứu toàn bộ tên trong từ điển bằng cách sử dụng số này làm khóa.