【问题标题】:Implementing an insert function onto array implementation of Binary Search Tree在二叉搜索树的数组实现上实现插入函数
【发布时间】:2023-03-09 10:07:02
【问题描述】:

我正在尝试使用二叉搜索树的功能,而无需实际创建 Node 对象并为它们提供左/右子对象,而是在三个并行数组中使用二叉搜索树的基本思想:左、数据和右。在这些数组中的特定索引处,left 保存当前数据的左孩子所在的数据的索引,而 right 保存当前数据的右孩子所在的数据的索引。这张表给出了一个更好的例子来说明我在说什么:

-1 值表示节点没有左或右子节点的位置。在插入任何节点之前,所有数组的值都为0,每次插入一个节点时,它的左右子索引值都设置为-1(表示我们刚刚插入的是叶子)。我正在努力弄清楚的是如何递归地执行此操作而不会意外访问-1的索引。我在下面看到的当前尝试遇到了这个问题:

public void insert(int d) {
//PRE: the tree is not full
//if d is not in the tree insert d otherwise the tree does not change
    if(root == -1) {
        root = d;
    }
    insert(d, 0);
}

private void insert(int d, int index) {
    if(data[index] == d) {
        return;
    }
    if(data[index] == 0) {
        data[index] = d;
        right[index] = -1;
        left[index] = -1;
    }
    if(data[index] > d) {
        if(left[index] == 0) {
            data[index] = d;
            right[index] = -1;
            left[index] = -1;
        } else {
            insert(d, left[index]);
        }
    }
    if(data[index] < d) {
        if(right[index] == 0) {
            data[index] = d;
            right[index] = -1;
            left[index] = -1;
        } else {
            insert(d, right[index]);
        }
    }
    return;
}

我很好奇如何防止访问索引值为 -1 的数组,同时仍然能够指示节点在特定一侧没有子节点。

我理解这样的概念,每次插入一个节点,我就是在插入一个叶子,所以当一个节点被放置在特定的索引处时,它的左右可以自动设置为-1,但是我当前的递归调用最终会在某一时刻传入 -1。即使我将此值更改为 0 或其他值,也不一定能帮助我在递归中取得任何进展。

【问题讨论】:

  • 为什么?您可以使用单个数组实现相同的目的。

标签: java binary-search-tree parallel-arrays


【解决方案1】:

对您的代码的一些评论:

  • 不应将root 变量分配给d,而是将存储d索引,只有这样才用root 编码空树才有意义等于 -1(意识到 d 可能是 -1)。

  • 您的代码没有逻辑来确定在哪个索引处存储新节点。这真的是你的问题。一个简单的解决方案是维护一个size 变量。这是存储下一个节点的索引,之后size 成员应该递增。

  • 那么就没有理由将 0 视为某种特殊指标,您的代码应该只检查 -1 引用,而不是 0。

  • 您可以通过创建一个“创建”节点的方法来避免一些代码重复:它将使用size 作为其索引,并将一个值作为参数。

这是建议的代码:

class BinaryTree {
    public static final int MAXSIZE = 100;
    int left[] = new int[BinaryTree.MAXSIZE]; 
    int right[] = new int[BinaryTree.MAXSIZE]; 
    int data[] = new int[BinaryTree.MAXSIZE]; 
    int root = -1;
    int size = 0;

    private int createNode(int value) {
        data[size] = value;
        left[size] = -1;
        right[size] = -1;
        return size++;
    }

    public void insert(int value) {
        if (root == -1) {
            root = createNode(value);
        } else {
            insert(value, 0);
        }
    }

    private void insert(int value, int index) {
        if (data[index] == value) {
            return;
        }
        if (data[index] > value) {
            if (left[index] == -1) {
                left[index] = createNode(value);
            } else {
                insert(value, left[index]);
            }
        } else {
            if (right[index] == -1) {
                right[index] = createNode(value);
            } else {
                insert(value, right[index]);
            }
        }
        return;
    }
}

此代码可以进一步扩展:

  • 验证树已达到最大大小,
  • 节点删除
  • “内存管理”用于重用已删除节点的索引(通过维护“空闲列表”)
  • 自平衡(如 AVL 或红黑树)

自己进行“内存管理”(数组槽管理)实际上会模仿 Java 在使用类实例时提供的强大的堆内存管理。因此,我建议以 OOP 方式实现树。

【讨论】:

  • 在这种情况下,您提到的前三件事的代码会是什么样子? (验证最大大小、删除节点和维护空闲列表)。我想您可以将空闲列表存储在数据为-1 的索引处的左数组或右数组中,但是如果删除发生在非叶子并且必须取而代之,您将如何操作数组?
  • 对于空闲列表,您只需要一个变量来跟踪 last 已释放的索引,然后使用 left(或 right - 不问题)链接到前面释放的索引,...等。如果你不能让它发挥作用,那就问一个关于它的新问题,展示你的工作,以及哪里出了问题。
猜你喜欢
  • 2017-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多