调试平衡二叉搜索树的过程

问题描述 投票:0回答:0

我已经实现了红黑搜索树,目前正在研究以下功能:

以下功能目前有效:

  • 向树添加新节点

  • 打印树:

    -- 对于每个节点,打印如下信息:值,颜色,父元素的值

    -- 树的每个“层”都打印在单独的一行上

  • 树木平衡:

    -- 节点重新着色

    -- 左转重新着色

    -- 右转重新着色

我写了一个左转的程序:

procedure LeftRotation(var p: NodePtr);
var
    grandpa, dad, son: NodePtr;
begin
    son := p^.left;
    dad := p^.prev;
    grandpa := p^.prev^.prev;
    grandpa^.left := p;
    p^.prev := grandpa;
    dad^.prev := p;
    dad^.right := son;
    if son <> nil then
        son^.prev := dad;
    p^.left := dad;
end;

我遇到了运行时错误 216.

我通过调试器运行程序:

55      dad^.prev := p;
1: p = 0x7ffff7ff1140
2: p^.value = 65
(gdb) 
56      dad^.right := son;
1: p = 0x7ffff7ff1140
2: p^.value = 65
(gdb) 
57      if son <> nil then
1: p = 0x0
2: p^.value = <error: Cannot access memory at address 0x18>

我发现在程序的第 11 行之后,指向当前节点的指针中放置了一个 nil 值:

dad^.right := son;

我不确定发生这种情况的原因,因为通过重新着色实现左右旋转的过程更加复杂并且使用相同的逻辑,但它们可以正常工作。这些过程允许在新元素按升序或降序排序时平衡树:

procedure RightRotationAndRecolor(var p: NodePtr);
var
    GreatGrandpa, grandpa, dad, bro: NodePtr;
begin
    dad := p^.prev;
    bro := p^.prev^.right;
    grandpa := p^.prev^.prev;
    GreatGrandpa := p^.prev^.prev^.prev;
    if GreatGrandpa <> nil then begin
        if GrandpaIsRightSon(grandpa) then
            GreatGrandpa^.right := dad
        else
            GreatGrandpa^.left := dad;
    end;
    if bro <> nil then
        bro^.prev := grandpa;
    grandpa^.left := bro;
    dad^.right := grandpa;
    dad^.prev := GreatGrandpa;
    grandpa^.prev := dad;
    dad^.color := black;
    grandpa^.color := red;
end;

节目全文如下:

program RBTreeDemo;
 
type
    col = (red, black);
 
    NodePtr = ^Node;
    Node = record
        left, right, prev: NodePtr;
        value: integer;
        color: col;
    end;
 
    NodePosition = ^NodePtr;
 
function TreeHeight(p: NodePtr): word;
var
    CurrHeight: word = 0;
begin
    if p = nil then begin
        TreeHeight := 0;
        exit;
    end;
    if TreeHeight(p^.left) >= TreeHeight(p^.right) then
        CurrHeight := TreeHeight(p^.left)
    else
        CurrHeight := TreeHeight(p^.right);
    TreeHeight := CurrHeight + 1;
end;
 
function CurrentRootIs(p: NodePtr): NodePosition;
begin
    if p^.prev = nil then
        CurrentRootIs := @p
    else
        CurrentRootIs := CurrentRootIs(p^.prev);
end;
 
function GrandpaIsRightSon(p: NodePtr): boolean;
begin
    if p^.value = p^.prev^.right^.value then
        GrandpaIsRightSon := true
    else
        GrandpaIsRightSon := false;
end;
 
procedure LeftRotation(var p: NodePtr);
var
    grandpa, dad, son: NodePtr;
begin
    son := p^.left;
    dad := p^.prev;
    grandpa := p^.prev^.prev;
    grandpa^.left := p;
    p^.prev := grandpa;
    dad^.prev := p;
    dad^.right := son;
    if son <> nil then
        son^.prev := dad;
    p^.left := dad;
end;
 
procedure RightRotationAndRecolor(var p: NodePtr);
var
    GreatGrandpa, grandpa, dad, bro: NodePtr;
begin
    dad := p^.prev;
    bro := p^.prev^.right;
    grandpa := p^.prev^.prev;
    GreatGrandpa := p^.prev^.prev^.prev;
    if GreatGrandpa <> nil then begin
        if GrandpaIsRightSon(grandpa) then
            GreatGrandpa^.right := dad
        else
            GreatGrandpa^.left := dad;
    end;
    if bro <> nil then
        bro^.prev := grandpa;
    grandpa^.left := bro;
    dad^.right := grandpa;
    dad^.prev := GreatGrandpa;
    grandpa^.prev := dad;
    dad^.color := black;
    grandpa^.color := red;
end;
 
procedure LeftRotationAndRecolor(var p: NodePtr);
var
    GreatGrandpa, grandpa, dad, bro: NodePtr;
begin
    dad := p^.prev;
    bro := p^.prev^.left;
    grandpa := p^.prev^.prev;
    GreatGrandpa := p^.prev^.prev^.prev;
    if GreatGrandpa <> nil then begin
        if GrandpaIsRightSon(grandpa) then
            GreatGrandpa^.right := dad
        else
            GreatGrandpa^.left := dad;
    end;
    if bro <> nil then
        bro^.prev := grandpa;
    grandpa^.right := bro;
    dad^.left := grandpa;
    dad^.prev := GreatGrandpa;
    grandpa^.prev := dad;
    dad^.color := black;
    grandpa^.color := red;
end;
 
function DadIsLeftSon(p: NodePtr): boolean;
begin
    if p^.prev^.prev^.left = nil then begin
        DadIsLeftSon := false;
        exit;
    end;
    if p^.prev^.prev^.left^.value = p^.prev^.value then
        DadIsLeftSon := true
    else
        DadIsLeftSon := false;
end;
 
function DadIsRightSon(p: NodePtr): boolean;
begin
    if p^.prev^.prev^.right = nil then begin
        DadIsRightSon := false;
        exit;
    end;
    if p^.prev^.prev^.right^.value = p^.prev^.value then
        DadIsRightSon := true
    else
        DadIsRightSon := false;
end;
 
function ImLeftSon(p: NodePtr): boolean;
begin
    if p^.prev^.left = nil then begin
        ImLeftSon := false;
        exit;
    end;
    if p^.prev^.left^.value = p^.value then
        ImLeftSon := true
    else
        ImLeftSon := false;
end;
 
function ImRightSon(p: NodePtr): boolean;
begin
    if p^.prev^.right = nil then begin
        ImRightSon := false;
        exit;
    end;
    if p^.prev^.right^.value = p^.value then
        ImRightSon := true
    else
        ImRightSon := false;
end;
 
procedure SetGrandpaColorRed(var p: NodePtr);
begin
    if p^.prev^.prev^.prev = nil then
        exit
    else
        p^.prev^.prev^.color := red;
end;
 
procedure SetDadAndUncleColorBlack(var p: NodePtr);
begin
    if (p^.prev^.prev^.left = nil) or (p^.prev^.prev^.right = nil) then
        exit;
    p^.prev^.prev^.left^.color := black;
    p^.prev^.prev^.right^.color := black;
end;
 
procedure recolor(var p: NodePtr);
begin
    SetDadAndUncleColorBlack(p);
    SetGrandpaColorRed(p);
end;
 
function UncleIsBlack(p: NodePtr): boolean;
begin
    if (p^.prev^.prev^.left = nil) or (p^.prev^.prev^.right = nil) then begin
        UncleIsBlack := true;
        exit;
    end;
    if (p^.prev^.prev^.left^.color = black) or
        (p^.prev^.prev^.right^.color = black)
    then begin
        UncleIsBlack := true
    end else
        UncleIsBlack := false;
end;
 
function UncleIsRed(p: NodePtr): boolean;               {technically I check}
begin                                       {if both parent and uncle are red}
    if (p^.prev^.prev^.left = nil) or (p^.prev^.prev^.right = nil) then begin
        UncleIsRed := false;
        exit;
    end;
    if (p^.prev^.prev^.left^.color = red) and
        (p^.prev^.prev^.right^.color = red)
    then begin
        UncleIsRed := true
    end else
        UncleIsRed := false;
end;
 
function DadIsRed(p: NodePtr):boolean;
begin
    if p^.prev^.color = red then
        DadIsRed := true
    else
        DadIsRed := false
end;
 
function DadIsBlack(p: NodePtr):boolean;
begin
    if p^.prev^.color = black then
        DadIsBlack := true
    else
        DadIsBlack := false
end;
 
procedure SelfBalance(var p: NodePtr);
begin
    if (p^.prev = nil) or DadIsBlack(p) or (p^.color = black) then
        exit;
    if DadIsRed(p) then begin
        if UncleIsRed(p) then begin
            recolor(p);
            SelfBalance(p^.prev^.prev);
        end else
        if UncleIsBlack(p) and ImRightSon(p) and DadIsRightSon(p) then begin
            LeftRotationAndRecolor(p);
            SelfBalance(p^.prev);
        end else
        if UncleIsBlack(p) and ImLeftSon(p) and DadIsLeftSon(p) then begin
            RightRotationAndRecolor(p);
            SelfBalance(p^.prev);
        end else
        if UncleIsBlack(p) and ImRightSon(p) and DadIsLeftSon(p) then begin
            LeftRotation(p);
            RightRotationAndRecolor(p^.left);
            SelfBalance(p);
        end;
    end;
end;
 
procedure FindNode(
    n: integer; var p, previous: NodePtr; var position: NodePosition
);
begin
    if (p = nil) or (p^.value = n) then begin
        position := @p;
        exit;
    end;
    previous := p;
    if n < p^.value then
        FindNode(n, p^.left, previous, position)
    else
        FindNode(n, p^.right, previous, position);
end;
 
procedure AddNode(var p: NodePtr; var height: word);
var
    n: integer;
    previous: NodePtr = nil;
    position: NodePosition = nil;
begin
    read(n);
    FindNode(n, p, previous, position);
    if position^ <> nil then
        writeln('The node ', n, ' already exists')
    else begin
        new(position^);
        position^^.value := n;
        position^^.right := nil;
        position^^.left := nil;
        position^^.prev := previous;
        if position^^.prev <> nil then begin
            position^^.color := red;
            SelfBalance(position^);
            position := CurrentRootIs(position^);       {check if root changed}
            p := position^;                             {and set new root}
        end else
            position^^.color := black;
        height := TreeHeight(p);
    end;
end;
 
procedure PrintColor(p: NodePtr);
begin
    if p^.color = black then
        write(',blk(')
    else
        write(',red(');
end;
 
procedure PrintTreeLevel(p: NodePtr; height, CurrentHeight: word);
begin
    if p = nil then
        exit;
    if height = CurrentHeight then begin
        write(p^.value);
        PrintColor(p);
        if p^.prev = nil then
            write(' ) ')
        else
            write(p^.prev^.value, ') ');
    end else begin
        PrintTreeLevel(p^.left, height, CurrentHeight + 1);
        PrintTreeLevel(p^.right, height, CurrentHeight + 1);
    end;
end;
 
procedure PrintTree(root: NodePtr; height: word);
var
    i: word;
begin
    for i := 1 to height do begin
        PrintTreeLevel(root, i, 1);
        writeln;
    end;
end;
 
var
    root: NodePtr = nil;
    height: word = 0;
    c: char;
begin
    while true do begin
        while not SeekEoln do begin
            AddNode(root, height);
        end;
        readln;
        writeln;
        PrintTree(root, height);
        writeln('current tree height is ', height);
        write('Continue input? y/n ');
        readln(c);
        if c <> 'y' then
            halt;
    end;
end.
pointers freepascal red-black-tree
© www.soinside.com 2019 - 2024. All rights reserved.