LINUX.ORG.RU

Помогите в реализации контейнера MultiMap на Си


0

1

Помогите в реализации контейнера MultiMap на Си (не Си++) (unsigned, unsigned), на двоичных деревьях или по другому. чтобы работал быстро и с понятным исходным кодом. реализовал на обычных двоичных деревьях но работает медленно. особено если на нем ведется учет выделленной памяти через обертку xmalloc. Т.к. malloc выделяет адресса последовательно а это наихудщий вариант для обычных двоичных деревьев. Нужны map_insert(), map_delete(), и желательно map_balance() Дополнение #1 10.10.2010 20:50:15 смотрел реализацию в glib, мне он показалась оч. страшной, и как я понял в структуре узла двоичного дерева нет ссылки на родителя -> я не понимаю как можно обойти все элементы дерева без этого. например struct btree * elem = bt_first( root_elem ); while( elem ) { printf( «%u %u\n», elem -> key, elem -> value ); elem = bt_next( elem ); } - возможно только если есть родитель у узла

есть реализация в исходниках ядра линукс lib/rb_tree.c но там нет delete ? ( красно-черное дерево с указателем на родителя, признак R/B упакован в указателе на родителя )

Интересная формулировка вопроса. Не «помогите найти библиотеку контейнер - надо для работы», а реализация, да ещё с понятным кодом. Студент делает лабу по информатике?

anonymous
()

Дерево без балансировки есть в K&R.

KblCb ★★★★★
()
Ответ на: комментарий от anonymous

Нет я не студент, я создаю свою библеотеку подобную glib, но glib мне не нравится

peter_t
() автор топика

А Кнута почитать на эту тему слабо? После прочтения соответствующей главы, красно-черное дерево пишется за полчаса.

Reset ★★★★★
()
Ответ на: комментарий от Reset

Зачем Кнута? Роберт Седжвик - его ученик - гораздо понятнее пишет и ближе к практике, чем заумный теоретик Кнут.

anonymous
()
Ответ на: комментарий от peter_t

Кстати функция balance не нужна, надо делать автобалансировку на insert/delete. delete кажется у Кнута не описана, но пишется аналогично insert'у. next/prev/find вообще тривиальны и пишутся за минуту.

Reset ★★★★★
()
Ответ на: комментарий от Reset

Скажи ты сможешь реализовать выше перечисленные функции за пол часа, сразу без попыток ?? если бы ты писал, ты бы понял что это не просто

peter_t
() автор топика
Ответ на: комментарий от Reset

Нет всетаки нужна, RB-деревья не являются идеально сбалансироваными, гарантируется что висота дерева не более чем в 2 раза больше чем у идеально сбалансированого. Часто это очень полезно.

peter_t
() автор топика
Ответ на: комментарий от Reset

видишь ты не знаешь про delete, она не аналогична insert если бы ты писал ты бы знал, тогда зачем ты пишешь если не понимаешь ?

peter_t
() автор топика
Ответ на: комментарий от Reset

next тривиальна ?? ты ее писал ?? напеши мне ее код, если это так, я ее осилил и знаю в чем ее сложность, говорить легко а сделать ...

peter_t
() автор топика
Ответ на: комментарий от peter_t

Так это же от задачи зависит. Одно дело постоянные вставки и удаления. Другая задача - создание дерева, доведение его до идеального состояния и потом только поиск...

anonymous
()
Ответ на: комментарий от peter_t

Писал я вообще сразу делал STL-like интерфейс.

Вот тебе next

template < class T >
Tree_iter < T > & Tree_iter<T>::operator ++ () {	
	if (node_ == 0) {
		;
	} else if (node_->right_) {
		node_ = Min(node_->right_);
	} else {
		prev_ = node_;
		node * P;
		//идем до верха справа налево
		while ((P = node_->parent_) != 0 && node_ == P->right_)
		{
			node_ = P;
		}

		node_ = P;
		/*if (node_ == prev_) {
		node_ = 0;
		}*/
	}
	return *this;
}

А вот тебе delete

template < class T1, class Comp >
void Tree < T1, Comp> ::erase(node * n, bool del)
{
	stack * st = 0;
	bool bal = flags_ & BALANCE;

	if (bal) {
		stack * tmp = stack_alloc(height_, sizeof(node *));;
		st = stack_alloc(height_, sizeof(node *));
		node * cur = n->parent_;
		while (cur) {
			stack_push(tmp, &cur);
			cur = cur->parent_;
		}

		//перевертываем стек
		while (!stack_empty(tmp)) {
			cur = *(node**)stack_pop(tmp);
			stack_push(st, &cur);
		}
		stack_free(tmp);
	}

	int a;

	if (n->left_ == 0 && n->right_ == 0) {
		if (n->parent_->left_ == n) {
			n->parent_->left_  = 0;
			a = 1;
		} else {
			n->parent_->right_ = 0;
			a = -1;
		}
	} else if (n->left_ == 0 && n->right_ != 0) {
		if (n->parent_->left_ == n) {
			relink(-1, n->parent_, n->right_);
			a = 1;
		} else {
			relink(1, n->parent_, n->right_);
			a = -1;
		}
	} else if (n->left_ != 0 && n->right_ == 0) {
		if (n->parent_->left_ == n) {
			relink(-1, n->parent_, n->left_);
			a = 1;
		} else {
			relink(1, n->parent_, n->left_);
			a = -1;
		}
	} else {
		//оба указателя не нули
		node * cur = 0;
		if (n->B_) {
			//правая ветвь больше, займем элемент из неё
			cur = n->right_;
			while (cur->left_) {
				cur = cur->left_;
			}
		} else {
			//левая ветвь больше, займем элемент из неё
			cur = n->left_;
			while (cur->right_) {
				cur = cur->right_;
			}
		}

		//удаляем элемент (без вызова delete) с одной ссылкой
		//попадем в два верхних случая
		//глубина рекурсии = 2
		erase(cur, false);

		if (n->parent_->left_ == n) {
			n->parent_->left_  = cur;
			a = 1;
		} else {
			n->parent_->right_ = cur;
			a = -1;
		}
		
		cur->right_ = n->right_;
		cur->left_  = n->left_;

		if (cur->parent_->left_ == cur) {
			cur->parent_->left_  = 0;
		} else {
			cur->parent_->right_ = 0;
		}		
	}

	if (del) {
		delete n;
	}
	
	if (bal) {
		while (!stack_empty(st)) {
			node * S = *(node**)stack_pop(st);
			if (S->B_ == 0) {
				S->B_ = a;
				break;
			} else if (S->B_ == -a) {
				//высота уменьшилась
				S->B_ = 0;
				--height_;
				break;
			} else if (S->B_ == a) {
				//вращаем вокруг P в направлении -a
				node * R = *link(a, S);
				node * P = R;
				node * T = S->parent_;
				if (R->B_ == a) {
					//однократный поворот
					P = R;
					relink(a, S, *link(-a, R));
					relink(-a, R, S);

					S->B_ = R->B_ = 0;
				} else if (R->B_ == -a) {
					//двукратный поворот
					P = *link(-a, R);
					relink(-a, R, *link(a, P));
					relink(a, P, R);
					relink(a, S, *link(-a, P));
					relink(-a, P, S);

					if (P->B_ == a) {
						S->B_ = -a; R->B_ = 0;
					} else if (P->B_ == 0) {
						S->B_ = 0; R->B_ = 0;
					} else if (P->B_ == -a) {
						S->B_ = 0; R->B_ = a;
					}
					P->B_ = 0;
				}

				if (T) {
					if (S == T->right_) {
						relink(1, T, P);
					} else {
						relink(-1, T, P);
					}
				} else {
					root_          = P;
					root_->parent_ = 0;
				}
				P->parent_ = T;
			}
		}
		stack_free(st);
	}

	--size_;
}

В insert идет аналогичная портянка. Как оно работает не знаю ибо писалось года 4 назад, но то, что оно работает это факт. Всякие извраты с софтварным стеком навешивались, естественно, потом.

Reset ★★★★★
()
Ответ на: комментарий от anonymous

Да верно это зависит от задачи. Иногда нужно предельное быстродействие и в этом поможет balance()

peter_t
() автор топика
Ответ на: комментарий от peter_t

Без косметики и оптимизации оно пишется очень быстро, но, естественно, если уже есть опыт в работе со сложными структурами данных.

Reset ★★★★★
()

Любое сбалансированное дерево, типа rb-tree подойдет, если по части алгоритма и структуры данных.
Основные сложности - сделать удобный(ну, как минимум приличный, а не как в glib том же) интерфейс к нему и оптимизировать выделение памяти(да, malloc - говно).

Love5an
()
Ответ на: комментарий от Sorcerer

> я создаю свою библеотеку подобную glib, но glib мне не нравится

Если тебе не нравится glib то зачем ты создаёшь подобную библиотеку? Чтобы была ещё одна библиотека которая тебе не нравится?

ACR
()
Ответ на: комментарий от ACR

в glib мне не нравится реализация двоичных деревьев

anonymous
()
Ответ на: комментарий от Sorcerer

За libdict спасибо, то что надо !!

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.