2011-01-14 27 views
5

Tôi có một mảng chứa tên các mục. Tôi muốn cung cấp cho người dùng tùy chọn tạo mục mà không chỉ định tên của họ, vì vậy chương trình của tôi sẽ phải cung cấp tên mặc định duy nhất, như "Mục 1".Thuật toán hiệu quả để tìm tên có sẵn đầu tiên

Thách thức là tên phải là duy nhất vì vậy tôi phải kiểm tra tất cả các mảng cho tên mặc định đó, và nếu có một mục có cùng tên tôi phải đổi tên thành "Mục 2" và cho đến khi tôi tìm thấy một tên có sẵn.

Các giải pháp hiển nhiên sẽ là một cái gì đó như thế:

String name = "Item "; 
for (int i = 0; !isAvailable(name + i) ; i++); 

Vấn đề của tôi với thuật toán đó là nó chạy ở O (N^2).

Tôi tự hỏi nếu có một thuật toán được biết đến (hoặc mới) hiệu quả hơn để giải quyết trường hợp này.

Nói cách khác, câu hỏi của tôi là: Có bất kỳ thuật toán nào tìm thấy số lớn hơn 0 không có liều KHÔNG tồn tại trong một mảng nhất định và chạy ít hơn N^2 không?

+0

Tên có phải là duy nhất không, hoặc nó có phải là chuỗi đầu tiên có sẵn trong chuỗi "Mục 1", "Mục 2", v.v ... không? Bạn có thể giảm đáng kể thời gian chạy dự kiến ​​bằng cách chọn một tên mới một cách ngẫu nhiên ("Item xF3g7a"). Bạn cũng có thể cải thiện trên một mảng cho cấu trúc dữ liệu: nếu thứ tự không quan trọng không sử dụng một mảng, và nếu nó quan trọng có lẽ giữ một chỉ số thứ cấp của tên cũng như mảng. –

+0

+1 câu hỏi về Ace! –

Trả lời

4

Bạn chắc chắn có thể làm điều đó trong thời gian O (N) thời gian, N là số các mục trong mảng của bạn:

  • Một trong những "khoản 1", "khoản 2", ... "Item N +1 "phải miễn phí, vì vậy hãy tạo một mảng cờ N + 1.
  • Traverse các mục và cho mỗi tên nếu nó có dạng "Mục k" với 0 < k < = N + 1, đặt cờ đó.
  • Quét mảng cờ cho cờ rõ ràng đầu tiên.

Yêu cầu bộ nhớ bổ sung là N + 1 bit, chắc chắn sẽ đánh bại bất kỳ cấu trúc dữ liệu nào thực sự lưu trữ tất cả các tên N.

+0

+1 Tôi thích cái này rất nhiều. –

+0

Tôi cũng xem xét điều này, nhưng điều này giả định rằng các con số trong các mục này đủ nhỏ để được sử dụng làm chỉ mục trong một vectơ. Nếu có một mục được gọi là "Item 123456789132456789" thì mảng của bạn sẽ rất lớn. Mặt khác, nếu giá trị lớn thì có lẽ bạn không quan tâm đến nó. – Patrick

+0

@Patrick: mảng cờ sẽ chỉ lớn nếu có 123456789132456788 mục trong mảng mục. Nếu chỉ có 4 mục trong mảng, và một trong số chúng xảy ra được gọi là "Item 123456789132456789", điều đó không quan trọng vì mảng cờ của tôi chỉ chiếm 5 bit. "Mục 123456789132456789", không phải là một ứng cử viên cho tên ít được sử dụng hơn bất kỳ "Fred", vì vậy tôi không cần phải ghi lại một trong hai. Một yêu cầu cho N + 1 bit của bộ nhớ là gây phiền nhiễu, nhưng trong thực tế hiếm khi khó khăn để đáp ứng vì đó là một tỷ lệ rất nhỏ của không gian đã bị chiếm đóng bởi các mục mình. –

3

Có, có.

Đầu tiên sắp xếp mảng. Sau đó chạy qua nó và trả về phần tử đầu tiên có giá trị không bằng với chỉ mục của nó (cộng 1). Sắp xếp là O (n log n), bước cuối cùng là O (n), do đó toàn bộ điều là O (n log n).

Nếu bạn đặt tất cả các mục vào bảng băm, bạn có thể làm điều đó bằng O (n) với chi phí của một số không gian và một bước O (1) bổ sung khi tạo mục mới. Vì mỗi phần tử cần được truy cập, O (n) rõ ràng là tối ưu.

Tôi muốn được xem liệu có cách O (n) để thực hiện việc này không, mà không cần bằng bất kỳ cấu trúc dữ liệu "liên tục" nào như bảng băm. (Và giả sử các số nguyên không bị chặn, nếu không, một loại nhóm có thể được sử dụng như một thuật toán phân loại O (n)).

+0

+1 Vâng, đây là cách để làm điều đó! –

+0

Và sau đó tôi thấy giải pháp của Steve tốt hơn! –

+0

Cảm ơn Thomas, Câu trả lời hay! trong thực tế, tôi nghĩ về một cái gì đó rất giống nhau nhưng tôi đã có một mảnh mất tích mà bạn chỉ cần đưa ra. Mặc dù tôi đang viết chương trình của tôi cho iPhone - vì vậy tôi có thể sử dụng thuật toán của bạn vì nó sử dụng ít bộ nhớ hơn thuật toán của Steve, tôi chấp nhận câu trả lời của Steve ở đây vì giải pháp của anh ấy hiệu quả hơn. –

1

Tại sao không chỉ theo dõi số lượng tối đa cho đến ngày và khi nào bạn cần số mới, hãy tăng số tiền đó?

+1

Sau đó, nếu bạn có Item1, Item2 và Item8 bạn sẽ thêm Item9. Nhưng người dùng có thể muốn Item3 tái sử dụng. –

+0

Có thể, nhưng "chống phân mảnh" các mục có thể không có ý nghĩa và/hoặc có thể không được yêu cầu. Nếu không, đây là thuật toán O (1) trong cả thời gian chạy * và *. – EmeryBerger

+0

Ngoài ra, nếu "chống phân mảnh" là bắt buộc, nó có thể được thực hiện một cách lười biếng và chỉ ở cuối chuỗi mục. Ví dụ: khi mục [max-1] không còn được sử dụng nữa, bạn có thể đặt lại tối đa thành max-1. – EmeryBerger

1

Chèn tất cả các tên hiện có vào bảng băm. Lặp lại vòng lặp của bạn, nhưng thực hiện isAvailable kiểm tra bảng băm. Giả sử một hash khá, đó là O (nh) trong đó h là chi phí để đánh giá băm.

1

Bạn có thể thử làm như sau:

đầu tiên:

  • vòng qua danh sách, và nhận được tất cả các mục được đánh số, đây là độ phức tạp N
  • cho tất cả các mục được đánh số, đặt mục trong một cái cây (trong C++: std :: map), đây là nhật ký phức tạp (N)

Vì vậy, bây giờ bạn đã xây dựng một bản đồ với số đã sử dụng, với độ phức tạp "N x log (N)"

Tiếp theo, lặp lại cây và ngay sau khi bạn nhìn thấy 'lỗ', hãy sử dụng số. Trường hợp xấu nhất là phức tạp N.

Vì vậy, tổng thể, độ phức tạp là N log (N) + N hoặc đơn giản: N log (N).

0

Nếu chỉ có một người dùng truy cập vào mảng này, tại sao không sử dụng số nanoseconds? Điều này, tất nhiên, giả định rằng người sử dụng là nhiều hơn chậm hơn so với một máy tính, mà có vẻ là một giả định an toàn.

Bằng cách đó, bạn có chi phí O (1) để xác định tên duy nhất.

+0

Tôi muốn ** Tên duy nhất ** đầu tiên có định dạng "Mục" + i –

+0

Sẽ không "mục" + nano giây thực hiện yêu cầu đó? – Davidann

+0

nó là duy nhất, nhưng nó không phải là tên có sẵn đầu tiên của định dạng được chỉ định. –

0

Một cách tiếp cận logarit thời gian, giả định rằng bạn không bao giờ rời khỏi "lỗ" bởi mục xóa:

// Inverse binary search for the last one. 
int upperBound = 1; 
while(isInUse(upperBound)) upperBound *= 2; 

// Standard binary search for the end once we have a ballpark. 
int lowerBound = upperBound/2; 
while(lowerBound < upperBound - 1) 
{ 
    int midpoint = (lowerBound + upperBound)/2; 
    if (isInUse(midpoint)) 
     lowerBound = midpoint; 
    else 
     upperBound = midpoint; 
} 
return upperBound; 

Nếu số mặt hàng có thể được giải phóng bằng cách xóa, không có gì ngắn của tìm kiếm tuyến tính sẽ làm việc trừ khi bạn cũng giữ một "danh sách miễn phí" và chọn từ đó.

Các vấn đề liên quan