2017-11-19 70 views
7

std::set là một cây được sắp xếp. Nó cung cấp các phương thức beginend để tôi có thể tối thiểu và tối đa và lower_boundupper_bound để tìm kiếm nhị phân. Nhưng điều gì sẽ xảy ra nếu tôi muốn đưa trình vòng lặp trỏ đến phần tử ở giữa (hoặc một trong số chúng nếu có cả số phần tử ở đó)?Cách hiệu quả để lấy trung bình (trung bình) của một tiêu chuẩn :: set?

Có cách nào hiệu quả (O(log(size)) không O(size)) để làm điều đó không?

{1} => 1 
{1,2} => 1 or 2 
{1,2,3} => 2 
{1,2,3,4} => 2 or 3 (but in the same direction from middle as for {1,2}) 
{1,312,10000,14000,152333} => 10000 

PS: Same question in Russian.

+0

Sắp xếp nhị phân cây có thể được và thường là chi tiết thực hiện của std :: thiết lập nhưng đó là không cần thiết. Nếu bạn cần sắp xếp mảng hoặc cây nhị phân thì tốt hơn là sử dụng những gì bạn cần. –

+0

@ ÖöTiib, tôi cần chèn động các phần tử và đặt giữa phần. Sắp xếp mảng/vectơ sẽ gây ra chèn là 'O (n)', nhưng tôi muốn cả chèn và truy vấn để làm việc 'O (lb (n))'. Tôi biết rằng cây Decart với khóa ngầm cho phép làm điều đó, nhưng tôi không muốn thực hiện nó và hy vọng rằng 'std :: set' đủ tốt để đạt được điều đó. – Qwertiy

+0

@Qwertiy trong hầu hết các trường hợp sử dụng chèn vào một vectơ sẽ rất nhanh do địa phương bộ nhớ cache. 'std :: set', cũng như các danh sách liên kết, sử dụng con trỏ đến các phần tử con nằm rải rác ở khắp mọi nơi, vì vậy nó có thể chậm hơn trong nhiều trường hợp. Đọc [Tại sao bạn không bao giờ, bao giờ, EVER sử dụng danh sách liên kết trong mã của bạn một lần nữa] (https://kjellkod.wordpress.com/2012/02/25/why-you-should-never-ever-ever-use- liên kết-danh sách-trong-của bạn-code-một lần nữa /), [Bjarne Stroustrup: Tại sao bạn nên tránh Danh sách liên kết] (https://youtu.be/YQs6IC-vgmo), [Có danh sách điều ác?] (https: // isocpp.org/blog/2014/06/stroustrup-lists) –

Trả lời

9

Tùy thuộc vào mức độ thường xuyên bạn chèn/xoá các mục so với nhìn lên giữa/trung bình, một giải pháp có thể hiệu quả hơn so với cái rõ ràng là để giữ một iterator dai dẳng đến phần tử ở giữa và cập nhật nó bất cứ khi nào bạn chèn/xóa các mục khỏi tập hợp. Có một loạt các trường hợp cạnh sẽ cần xử lý (số lẻ so với số mục, loại bỏ mục giữa, bộ trống, v.v.), nhưng ý tưởng cơ bản sẽ là khi bạn chèn một mục nhỏ hơn mục giữa hiện tại , trình vòng lặp giữa của bạn có thể cần giảm dần, trong khi nếu bạn chèn một cái lớn hơn, bạn cần tăng thêm. Đó là cách khác để loại bỏ.

Tại thời điểm tra cứu, tất nhiên là O (1), nhưng cũng có chi phí O (1) tại mỗi lần chèn/xóa, tức là O (N) sau N chèn, cần được phân bổ trên một đủ số lượng tra cứu để làm cho nó hiệu quả hơn việc ép buộc.

5

Nó sẽ là O (size) để có được giữa một cây tìm kiếm nhị phân. Bạn có thể lấy nó với std::advance() như sau:

std::set<int>::iterator it = s.begin(); 
std::advance(it, s.size()/2); 
+0

Đẹp, nhưng tôi muốn logarit thay vì tuyến tính ... – Qwertiy

+0

Tôi nghĩ Martin có nghĩa là O (chiều cao), nơi chiều cao của một cây nhị phân * cân bằng là logarit trong kích thước của cây. – chepner

+1

@chepner, nope, 'std :: advance' chỉ gọi' ++ 'số lần tương ứng trong trường hợp này. – Qwertiy

0

Nếu dữ liệu của bạn là tĩnh, những người bạn có thể precalcate nó và không chèn các yếu tố mới - đó là đơn giản hơn để sử dụng vector, sắp xếp nó, và trung bình truy cập chỉ bằng cách chỉ số trong thời gian O (1)

vector<int> data; 
// fill data 
std::sort(data.begin(), data.end()); 
auto median = data[data.size()/2]; 
Các vấn đề liên quan