Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Решение проблемы переполнения стека в AVL-дереве при вставке и удалении

Delphi , Синтаксис , Деревья

Статья: Решение проблемы переполнения стека в AVL-дереве при вставке и удалении

Если вы столкнулись с ошибкой переполнения стека при работе с AVL-деревом в Pascal, то эта статья поможет вам решить эту проблему.

Описание проблемы

При использовании AVL-дерева для хранения и поиска данных могут возникать ошибки переполнения стека. Это происходит из-за бесконечной рекурсии при балансировке дерева. Ошибка #202 (stack overflow) возникает, когда стек переполняется и больше не может выделить память для новых функций.

Пример кода, вызывающий ошибку

Вот пример кода, который может вызвать ошибку переполнения стека при работе с AVL-деревом:

program Avl_generator; uses Crt;

type
  p_Avl = ^Avl_node;
  Avl_node = record
    key: integer;
    l, r, par: p_Avl;
    bal, h: integer
  end;

procedure init(var root: p_Avl);
begin
  new(root);
  root:=nil
end;

function get_height(var n: p_Avl): integer;
begin
  if(n=nil) then get_height:=-1 else get_height:=n^.h;
end;

procedure reheight(var n: p_Avl);
begin
  if(n<>) then
  begin
    if(get_height(n^.r)>get_height(n^.l)) then n^.h:=1+get_height(n^.r)
    else n^.h:=1+get_height(n^.l);
  end;
end;

procedure set_balance(var n: p_Avl);
begin
  reheight(n);
  n^.bal:=get_height(n^.r)-get_height(n^.l);
end;

function rotate_l(var a: p_Avl): p_Avl;
var
  b: p_Avl;
begin
  b := a^.r;
  b^.par := a^.par;
  a^.r := b^.l;
  if(a^.r<>) then a^.r^.par := a;
  b^.l := a;
  a^.par := b;
  if(b^.par<>) then
  begin
    if(b^.par^.r=a) then b^.par^.r := b
    else b^.par^.l := b;
  end;
  set_balance(a);
  set_balance(b);
  rotate_l := b;
end;

function rotate_r(var a: p_Avl): p_Avl;
var
  b: p_Avl;
begin
  b := a^.l;
  b^.par := a^.par;
  a^.l := b^.r;
  if(a^.l<>) then a^.l^.par := a;
  b^.r := a;
  a^.par := b;
  if(b^.par<>) then
  begin
    if(b^.par^.r=a) then b^.par^.r := b
    else b^.par^.l := b;
  end;
  set_balance(a);
  set_balance(b);
  rotate_r := b;
end;

function rotate_l_r(var a: p_Avl): p_Avl;
begin
  a^.l := rotate_l(a^.l);
  rotate_l_r := rotate_r(a);
end;

function rotate_r_l(var a: p_Avl): p_Avl;
begin
  a^.r := rotate_r(a^.r);
  rotate_r_l := rotate_l(a);
end;

procedure rebalance(var root: p_Avl; var n: p_Avl);
begin
  set_balance(n);
  if(n^.bal=-2) then
  begin
    if(get_height(n^.l^.l)>=get_height(n^.l^.r)) then n:=rotate_r(n)
    else n:=rotate_l_r(n);
  end
  else if(n^.bal=2) then
  begin
    if(get_height(n^.r^.r)>=get_height(n^.r^.l)) then n:=rotate_l(n)
    else n:=rotate_r_l(n);
  end;
  if(n^.par<>) then rebalance(root, n^.par) else root:=n;
end;

procedure insert(var root: p_Avl; what: integer);
var
  found: boolean;
  pre_tmp, tmp: p_Avl;
begin
  found:=false; tmp:=root; pre_tmp:= nil;
  while(tmp<>) and not found do
  begin
    if(tmp^.key=what) then found:=true
    else if(tmp^.key>what) then begin pre_tmp:=tmp; tmp:=tmp^.l end
    else begin pre_tmp:=tmp; tmp:=tmp^.r end;
  end;
  if not found then
  begin
    new(tmp); tmp^.key:=what;
    tmp^.l:=nil; tmp^.r:=nil; tmp^.par:=pre_tmp; tmp^.h:=0; tmp^.bal:=0;
    if(pre_tmp=nil) then root:=tmp
    else
    begin
      if(pre_tmp^.key>what) then pre_tmp^.l:=tmp else pre_tmp^.r:=tmp;
      rebalance(root, pre_tmp);
    end;
  end;
end;

procedure delete(var root: p_Avl; what: integer);
var
  found: boolean;
  tmp, pre_tmp, act, pre_act: p_Avl;
begin
  found:=false; tmp:=root; pre_tmp:=nil;
  while(tmp<>) and not found do
  begin
    if(tmp^.key=what) then found:=true
    else if(tmp^.key>what) then begin pre_tmp:=tmp; tmp:=tmp^.l end
    else begin pre_tmp:=tmp; tmp:=tmp^.r end;
  end;
  if found then
  begin
    if(tmp^.l=nil) then
    begin
      if(pre_tmp=nil) then root:=tmp^.r
      else if(pre_tmp^.key>what) then pre_tmp^.l:=tmp^.r
      else pre_tmp^.r:=tmp^.r;
      dispose(tmp);
      rebalance(root,pre_tmp);
    end
    else if(tmp^.r=nil) then
    begin
      if(pre_tmp=nil) then root:=tmp^.l
      else if(pre_tmp^.key>what) then pre_tmp^.l:=tmp^.l
      else begin pre_tmp^.r:=tmp^.l end;
      dispose(tmp);
      rebalance(root,pre_tmp);
    end
    else
    begin
      act:=tmp^.l; pre_act:=nil;
      while(act^.r<>) do begin pre_act:=act; act:=act^.r end;
      tmp^.key:=act^.key;
      if(pre_act=nil) then begin tmp^.l:=act^.l; dispose(act); rebalance(root,tmp) end
      else begin pre_act^.r:=act^.l; dispose(act); rebalance(root,pre_act) end;
    end;
  end;
end;

var
  Avl_tree: p_Avl;
begin
  init(Avl_tree);
  insert(Avl_tree,1);
  insert(Avl_tree,2);
  insert(Avl_tree,3);
  insert(Avl_tree,4);
  insert(Avl_tree,5);
  writeln(get_path(Avl_tree, 5));
  repeat until KeyPressed;
end.

При добавлении или удалении элементов из дерева может возникнуть ошибка переполнения стека в процедурах rotate_l, rotate_r, rebalance, insert или delete.

Решение проблемы

Чтобы решить проблему переполнения стека при работе с AVL-деревом, нужно правильно реализовать процедуры балансировки дерева и корректно обрабатывать рекурсию.

  1. Измените процедуры балансировки дерева

При балансировке дерева нужно правильно обрабатывать рекурсию, чтобы избежать бесконечной рекурсии и переполнения стека. В процедурах rotate_l, rotate_r, rotate_l_r и rotate_r_l нужно передавать параметры по значению, а не по ссылке. Это предотвратит ошибку, когда указатель на родительский узел станет недействительным после изменения указателя на дочерний узел.

Измените процедуры балансировки дерева следующим образом:

function rotate_l(a: p_Avl): p_Avl;
var
  b: p_Avl;
begin
  b := a^.r;
  b^.par := a^.par;
  a^.r := b^.l;
  if(a^.r<>) then a^.r^.par := a;
  b^.l := a;
  a^.par := b;
  if(b^.par<>) then
  begin
    if(b^.par^.r=a) then b^.par^.r := b
    else b^.par^.l := b;
  end;
  set_balance(a);
  set_balance(b);
  rotate_l := b;
end;

function rotate_r(a: p_Avl): p_Avl;
var
  b: p_Avl;
begin
  b := a^.l;
  b^.par := a^.par;
  a^.l := b^.r;
  if(a^.l<>) then a^.l^.par := a;
  b^.r := a;
  a^.par := b;
  if(b^.par<>) then
  begin
    if(b^.par^.r=a) then b^.par^.r := b
    else b^.par^.l := b;
  end;
  set_balance(a);
  set_balance(b);
  rotate_r := b;
end;

function rotate_l_r(a: p_Avl): p_Avl;
begin
  a^.l := rotate_l(a^.l);
  rotate_l_r := rotate_r(a);
end;

function rotate_r_l(a: p_Avl): p_Avl;
begin
  a^.r := rotate_r(a^.r);
  rotate_r_l := rotate_l(a);
end;
  1. Измените процедуру ребалансировки дерева

При ребалансировке дерева нужно правильно обрабатывать рекурсию, чтобы избежать бесконечной рекурсии и переполнения стека. В процедуре rebalance нужно передавать параметры по значению, а не по ссылке. Это предотвратит ошибку, когда указатель на родительский узел станет недействительным после изменения указателя на дочерний узел.

Измените процедуру ребалансировки дерева следующим образом:

procedure rebalance(root: p_Avl; n: p_Avl);
begin
  set_balance(n);
  if(n^.bal=-2) then
  begin
    if(get_height(n^.l^.l)>=get_height(n^.l^.r)) then n:=rotate_r(n)
    else n:=rotate_l_r(n);
  end
  else if(n^.bal=2) then
  begin
    if(get_height(n^.r^.r)>=get_height(n^.r^.l)) then n:=rotate_l(n)
    else n:=rotate_r_l(n);
  end;
  if(n^.par<>) then rebalance(root, n^.par) else root:=n;
end;
  1. Измените процедуры вставки и удаления элементов

При вставке и удалении элементов из дерева нужно правильно обрабатывать рекурсию, чтобы избежать бесконечной рекурсии и переполнения стека. В процедурах insert и delete нужно правильно вызывать процедуру ребалансировки дерева.

Измените процедуры вставки и удаления элементов следующим образом:

procedure insert(root: p_Avl; what: integer);
var
  found: boolean;
  pre_tmp, tmp: p_Avl;
begin
  found:=false; tmp:=root; pre_tmp:= nil;
  while(tmp<>) and not found do
  begin
    if(tmp^.key=what) then found:=true
    else if(tmp^.key>what) then begin pre_tmp:=tmp; tmp:=tmp^.l end
    else begin pre_tmp:=tmp; tmp:=tmp^.r end;
  end;
  if not found then
  begin
    new(tmp); tmp^.key:=what;
    tmp^.l:=nil; tmp^.r:=nil; tmp^.par:=pre_tmp; tmp^.h:=0; tmp^.bal:=0;
    if(pre_tmp=nil) then root:=tmp
    else
    begin
      if(pre_tmp^.key>what) then pre_tmp^.l:=tmp else pre_tmp^.r:=tmp;
      rebalance(root, pre_tmp);
    end;
  end;
end;

procedure delete(root: p_Avl; what: integer);
var
  found: boolean;
  tmp, pre_tmp, act, pre_act: p_Avl;
begin
  found:=false; tmp:=root; pre_tmp:=nil;
  while(tmp<>) and not found do
  begin
    if(tmp^.key=what) then found:=true
    else if(tmp^.key>what) then begin pre_tmp:=tmp; tmp:=tmp^.l end
    else begin pre_tmp:=tmp; tmp:=tmp^.r end;
  end;
  if found then
  begin
    if(tmp^.l=nil) then
    begin
      if(pre_tmp=nil) then root:=tmp^.r
      else if(pre_tmp^.key>what) then pre_tmp^.l:=tmp^.r
      else pre_tmp^.r:=tmp^.r;
      dispose(tmp);
      rebalance(root,pre_tmp);
    end
    else if(tmp^.r=nil) then
    begin
      if(pre_tmp=nil) then root:=tmp^.l
      else if(pre_tmp^.key>what) then pre_tmp^.l:=tmp^.l
      else begin pre_tmp^.r:=tmp^.l end;
      dispose(tmp);
      rebalance(root,pre_tmp);
    end
    else
    begin
      act:=tmp^.l; pre_act:=nil;
      while(act^.r<>) do begin pre_act:=act; act:=act^.r end;
      tmp^.key:=act^.key;
      if(pre_act=nil) then begin tmp^.l:=act^.l; dispose(act); rebalance(root,tmp) end
      else begin pre_act^.r:=act^.l; dispose(act); rebalance(root,pre_act) end;
    end;
  end;
end;

Вывод

При работе с AVL-деревом в Pascal могут возникать ошибки переполнения стека из-за бесконечной рекурсии при балансировке дерева. Чтобы решить эту проблему, нужно правильно реализовать процедуры балансировки дерева и корректно обрабатывать рекурсию. В этой статье мы рассмотрели, как изменить процедуры балансировки дерева, процедуру ребалансировки дерева и процедуры вставки и удаления элементов, чтобы избежать ошибки переполнения стека при работе с AVL-деревом в Pascal.

Создано по материалам из источника по ссылке.

Контекст: Статья описывает решение проблемы переполнения стека, возникающей при работе с AVL-деревом в Pascal при вставке и удалении элементов.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Деревья ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-19 13:29:56
2024-11-21 13:26:39/0.0057888031005859/1