2013-01-12 28 views
5

Tôi đang cố gắng triển khai cây Kd để thực hiện hàng xóm gần nhất và gần đúng tìm kiếm lân cận gần nhất trong C++. Cho đến nay tôi đã bắt gặp 2 phiên bản của cây Kd cơ bản nhất.Kd tree: dữ liệu được lưu trữ chỉ trong lá vs được lưu trữ trong lá và các nút

  1. Một, nơi dữ liệu được lưu trữ trong các nút và trong lá, chẳng hạn như here
  2. Một, nơi dữ liệu được lưu trữ chỉ trong lá, chẳng hạn như here

Họ dường như là căn bản giống nhau, có cùng tính chất tiệm cận.

Câu hỏi của tôi là: có một số lý do tại sao chọn cái khác?

I figured hai lý do cho đến nay:

  1. Cây mà lưu trữ dữ liệu trong các nút quá là nông bởi 1 mức.
  2. Cây mà các cửa hàng dữ liệu chỉ trong lá có dễ dàng hơn để thực hiện delete data chức năng

Có một số lý do khác tôi nên xem xét trước khi quyết định cái nào để thực hiện?

+0

@Boris Strandjev, Cảm ơn bạn! –

+0

Tại sao lý do thứ hai? Tôi cho rằng ngay cả với phương pháp thứ hai bạn lưu trữ một số dữ liệu khoảng cách trong các nút trung gian? –

+0

@BorisStrandjev Trong phương pháp 1. st, nếu bạn xóa một nút, bạn cần phải tìm một nút thay thế. Điều này có thể được thực hiện bằng cách tìm kiếm cây con bắt nguồn từ nút đó. Trong cách tiếp cận thứ 2. bạn chỉ có thể xóa lá –

Trả lời

4

Bạn chỉ có thể các nút đánh dấu là đã xóa và trì hoãn bất kỳ thay đổi cấu trúc nào đối với việc xây dựng lại cây tiếp theo. k-d-cây phân hủy theo thời gian, vì vậy bạn sẽ cần phải thực hiện xây dựng lại cây thường xuyên. k-d-cây là tuyệt vời cho các tập dữ liệu chiều thấp không thay đổi, hoặc nơi bạn có thể dễ dàng đủ khả năng để xây dựng lại một cây tối ưu (xấp xỉ).

Để triển khai cây, tôi khuyên bạn nên sử dụng cấu trúc tối giản. Tôi thường làm các nút sử dụng không phải. Tôi sử dụng một mảng tham chiếu đối tượng dữ liệu. Trục được xác định bởi độ sâu tìm kiếm hiện tại, không cần lưu trữ nó ở bất kỳ đâu. Hàng xóm bên trái và bên phải được đưa ra bởi cây tìm kiếm nhị phân của mảng. (Nếu không, chỉ cần thêm một mảng của byte, một nửa kích thước của tập dữ liệu của bạn, để lưu trữ các trục bạn đã sử dụng). Tải cây được thực hiện bởi một QuickSort chuyên biệt. Về mặt lý thuyết nó là O(n^2) trường hợp xấu nhất, nhưng với một heuristic tốt như trung bình-of-5 bạn có thể nhận được O(n log n) khá đáng tin cậy và với chi phí liên tục tối thiểu.

Mặc dù không giữ được nhiều cho C/C++, bằng nhiều ngôn ngữ khác, bạn sẽ trả một mức giá khá lớn để quản lý nhiều đối tượng. A type*[] là cấu trúc dữ liệu rẻ nhất bạn sẽ tìm thấy, và đặc biệt nó không đòi hỏi nhiều nỗ lực quản lý. Để đánh dấu một phần tử là đã xóa, bạn có thể null nó và tìm kiếm cả hai bên khi bạn gặp phải một số null. Đối với chèn, tôi đầu tiên thu thập chúng trong một bộ đệm. Và khi bộ đếm sửa đổi đạt đến ngưỡng, hãy xây dựng lại.

Và đó là toàn bộ quan điểm của nó: nếu cây của bạn thực sự rẻ để xây dựng lại (rẻ như sử dụng một mảng gần như sắp xếp trước!) Thì không hại đến việc thường xuyên xây dựng lại cây. Quét tuyến tính qua danh sách chèn "ngắn" rất thân thiện với bộ nhớ cache CPU. Bỏ qua null s cũng rất rẻ.

Nếu bạn muốn có cấu trúc động hơn, tôi khuyên bạn nên xem xét R * -trees.Họ đang thực sự desinged để cân bằng trên chèn và xóa, và tổ chức các dữ liệu trong một cấu trúc khối theo định hướng đĩa. Nhưng ngay cả đối với R-cây, đã có báo cáo rằng việc giữ một bộ đệm chèn vv để trì hoãn thay đổi cấu trúc cải thiện hiệu suất. Và tải hàng loạt trong nhiều tình huống cũng giúp ích rất nhiều!

+0

Cảm ơn bạn rất nhiều vì đã giải thích chi tiết. Tuy nhiên có một vài điểm không rõ ràng với tôi. 1. những gì bạn có nghĩa là bạn không sử dụng các nút? 2. Bạn có thể cụ thể hơn đối với câu hỏi của tôi không? So sánh hai cây –

+1

Bạn thực sự có thể thực hiện một cây kd mà không có kiểu dữ liệu 'nút'. Tổng chi phí bộ nhớ: 'n' con trỏ. Và mã của bạn càng đơn giản, thường nhanh hơn. Những gì tôi cũng đang cố gắng truyền đạt là: bạn có thể thực hiện tốt hoặc chậm. Không có quy tắc nào tốt hơn, nhưng nó phụ thuộc vào việc bạn có thể thực hiện tốt như thế nào cho các nhu cầu cụ thể của bạn. –

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