Статистика динамического порядка: получить k-й элемент в постоянное время?
Итак, я пытаюсь реализовать структуру данных для обработки статистики динамического порядка. Структура данных имеет следующие операции:
- add( x): вставляет новый элемент со значением x
- get(k): возвращает K-й наименьший элемент: k = потолок (n/a), где n = количество элементов в структуре данных и A = постоянный коэффициент.
- reset: сбрасывает весь datastructuer, т. е. структура данных " пуста после него
я реализовал моя структура данных с использованием сбалансированного дерева AVL. Используя это, операции имеют следующую временную сложность:
- добавить(x): O(log(n))
- get (k): O(log(n))
вот моя имплементация для get (k), которая использует время O(log(n)):
public static int get(Node current, int k) {
int l = tree.sizeLeft(current) + 1;
if(k == l) {
return current.value;
} else if(k < l) {
if(current.left == null) {
return current.value;
}
return get(current.left, k);
} else {
if(current.right == null) {
return current.value;
}
return get(current.right, k);
}
}
и вот моя реализация для класса node:
class Node {
int height, value, bal, size; // bal = balanceFactor, size = amount of nodes in tree
rooted at current node
Node leftChild = null;
Node rightChild = null;
public Node(int val) {
value = val;
height = 1;
size = 1;
}
}
однако, моя задача-реализовать структуру данных, которая может обрабатывать вышеуказанные операции и только принимая O (1) (постоянное) время для операции get (k). (И добавить(x) все еще принимая O (log (n)) время). Кроме того, мне не разрешено использовать hashmap.
можно ли изменить мою реализацию, чтобы получить постоянное время? Или какая структура данных может обрабатывать операцию get (k) в постоянное время?
2 ответов
насколько я понимаю, параметр k в основном растет с размером элементов, а это означает, что для каждого n вы знаете точное значение k.
Если это так, то мое предложение-использовать max-heap и min-heap. Max-heap организует элементы (smallerequals, чем n / a-й элемент) в структуре кучи, позволяя получить доступ к самому большому элементу (корню) в постоянное время. Соответственно, мин-куча организует элементы (больше, чем n/a-й элемент) в куче структура, позволяющая получить доступ к наименьшему элементу (корню) в постоянное время.
когда появляются новые элементы (добавить), вы помещаете их в соответствующую кучу в O(log n). Если max-куча становится больше или меньше, чем (n / a), вы перебалансируете между двумя кучами в O(log n)
ваша функция get () теперь просто должна вернуть корневой элемент max-heap в O (1).
в Java вы можете использовать очередь приоритетов для max-heap (и min-heap)
PriorityQueue<Integer> heap = new PriorityQueue<>(10, Collections.reverseOrder());
класс может выглядеть так
import java.util.Collections;
import java.util.PriorityQueue;
public class DOS
{
double a;
PriorityQueue<Integer> heap;
PriorityQueue<Integer> heap_large_elements;
public DOS(double a) {
this.a = a;
this.heap = new PriorityQueue<>(10, Collections.reverseOrder());
this.heap_large_elements = new PriorityQueue<>();
}
public void add(int x){
if(heap.size() == 0 || x < heap.peek())
heap.add(x); // O(log n/a)
else
heap_large_elements.add(x); // O(log n)
//possible rebalance operations
int n = heap.size() + heap_large_elements.size();
if(heap.size() > Math.ceil(n/a)){
heap_large_elements.add(heap.poll()); //O(log n)
}else if(heap.size() < Math.ceil(n/a)) {
heap.add(heap_large_elements.poll()); //O(log n)
}
}
public int get(){
return heap.peek(); //O(1)
}
public static void main(String[] args)
{
DOS d = new DOS(3);
d.add(5);d.add(6);d.add(2);d.add(3);d.add(8);d.add(12);d.add(9);
System.out.println(d.get());
}
}
редактировать (Cheaty McCheatFace):
еще одна идея, которая позволяет использовать ваш код, но несколько обманчива, заключается в следующем. Всякий раз, когда вы добавляете элемент в AVL-дерево, вы вычисляете K (=n/a) самый большой элемент (как это сделано в вашем коде) и сохраняете его. Таким образом, функция add()-по-прежнему имеет среду выполнения O(log n). Функция get()-функция просто retrives сохраненное значение и в O (1).
если вы хотите использовать деревья, поддерживайте порядок от 1 до максимума a
сбалансированные деревья. Для каждого дерева держите указатель на самый маленький и самый большой элемент и размер дерева. Всякий раз, когда элемент добавляется, вставьте его в соответствующее дерево. Если дерево растет дальше ceiling(n / a)
элементы, реорганизуйте деревья, перемещая соответствующий самый низкий или самый высокий в соседнее дерево, чтобы сохранить их все между floor(n / a)
и ceiling(n / a)
элементы в размер. The k
th элемент всегда будет самое маленькое или самое высокое из деревьев.
Add
примет O(log a + log(n/a) * a) = O(log n)
времени.Get
примет O(log a) = O(1)
времени.