🥳每日一练-二叉排序树的节点删除-JS简易版

上篇文章分享了如何创建一个二叉排序树, 这篇文章来分享如何删除二叉排序树的节点

其实逻辑比较简单,

  1. 如果删除的是叶子,就直接删除;
  2. 如果删除的是只含有一个子树,那么将子树的第一个节点代替当前节点;
  3. 如果删除的节点有两个子树,那么就用当前节点中序遍历的下一个节点。

这样做的目的是为了,删除节点之后,整棵树仍然是二叉排序树。下面来看代码实现

准备数据

javascript 复制代码
const treeData = [5, 7, 6, 3, 8, 7, 4, 1, 2];

const insertNode = (tree, node) => {
	if (node.value <= tree.value) {
		if (tree.left) insertNode(tree.left, node);
		else tree.left = node;
	} else {
		if (tree.right) insertNode(tree.right, node);
		else tree.right = node;
	}
};

const generateTree2 = (data) => {
	const root = {};
	root.value = data[0];
	for (let i = 1; i < data.length; i++) {
		insertNode(root, { value: data[i], left: null, right: null });
	}
	return root;
};

const tree = generateTree2(treeData);

这是上篇文章分享的创建排序二叉树的代码,将一个无序数组转成排序二叉树。

下面是这个排序二叉树的 json 结构

javascript 复制代码
{
  value: 5,
  right: {
    value: 7,
    left: {
      value: 6,
      left: null,
      right: {
        value: 7,
        left: null,
        right: null,
      },
    },
    right: {
      value: 8,
      left: null,
      right: null,
    },
  },
  left: {
    value: 3,
    left: {
      value: 1,
      left: null,
      right: {
        value: 2,
        left: null,
        right: null,
      },
    },
    right: {
      value: 4,
      left: null,
      right: null,
    },
  },
}

了解更多创建二叉树的内容,可以看这篇文章:🥳每日一练-二叉排序的构建-JS简易版 - 掘金。这里就不讲解了

删除节点

删除节点有三种情况:

  1. 如果删除的是叶子,就直接删除;
  2. 如果删除的是只含有一个子树,那么将子树的第一个节点代替当前节点;
  3. 如果删除的节点有两个子树,那么就用当前节点中序遍历的下一个节点。

说实话,这个代码我一开始没写出来,奔着三种情况,还特意写了三个处理不同情况的函数。最后问了 GPT 这个代码怎么写,它给出了非常漂亮的回答

javascript 复制代码
function deleteNode(root, key) {
	if (root === null) return null;

	if (key < root.value) {
		root.left = deleteNode(root.left, key);
	} else if (key > root.value) {
		root.right = deleteNode(root.right, key);
	} else {
		// 当前节点是要删除的节点
		// 如果左子树为空,返回右子树
		if (root.left === null) return root.right;
		// 如果右子树为空,返回左子树
		if (root.right === null) return root.left;

		// 找到右子树中最小的节点
		let minNode = root.right;
		while (minNode.left) {
			minNode = minNode.left;
		}

		// 删除右子树中最小的节点
		root.right = deleteNode(root.right, minNode.value);

		// 用右子树中最小的节点替换当前节点
		minNode.left = root.left;
		minNode.right = root.right;
		root = minNode;
	}

	return root;
}

代码的开头会有两个判断,判断要删除的节点和当前遍历到的节点的大小。要么小于,就继续往左子树方向遍历;要么大于,就继续往右子树方向遍历;

当要删除的节点和当前遍历到的节点的大小相等的时候,就开始执行删除操作了。删除的过程也是依据上面的三种情况来的。

如果左子树为空,就返回右子树;如果右子树为空,就返回左子树;这其中的逻辑就包含了如果左右子树都为空的情况。

如果两个子树都不为空,就找到右子树中最小的节点 minNode,并且将 minNode 替代当前节点的位置返回上一层调用。

为什么需要是右子树中最小的节点呢,因为一个节点要替代当前节点的话,必须要满足两个要求。第一,那就是这个节点的值要大于当前节点的左子树所有节点;第二,并且小于右子树的所有节点。共两个

那右子树中最小的节点就满足这个要求,首先因为是在右子树,所以必然大于左子树,满足第一个要求。又因为是右子树的最小的,所以满第二个要求

而右子树中最小的节点又是当前节点中序遍历的下一个节点。论证完美😍


那么要怎么找到这个 minNode,就要从右子树的第一个节点,不断往左遍历,直到遍历到叶子节点,就找到了 minNode

找到了 minNode,下一步就是将 minNode 取代当前节点的位置。代码中处理地很妙。先将 minNode 从二叉排序树删除,然后将当前节点的左右子树分别交给 minNode,然后 root 直接变成 minNode,最后 return;

代码中root.right = deleteNode(root.right, minNode.value)很妙,它完美地处理了 minNode 就是当前节点的右子树的情况,避免了指向自我的。属实学到了。

测试代码实际效果:

javascript 复制代码
const printNode = (tree) => {
	if (!tree) return null;
	printNode(tree.left);
	console.log(tree.value);
	printNode(tree.right);
};

const newTree = deleteNode(tree, 3);
printNode(newTree);
// 1
// 2
// 4
// 5
// 6
// 7
// 7
// 8

const newTree2 = deleteNode(tree, 5);
printNode(newTree2);
// 1
// 2
// 3
// 4
// 6
// 7
// 7
// 8

printNode是一个以中序遍历输出二叉树的方法。

代码第二个测试用例,删除的是二叉排序树的根节点,也成功。这也是我对这个代码赞不绝口的地方。

总结

这篇文章分享了如何删除二叉排序的节点。文中给出的代码不是我想出来的,是 GPT 帮我想出来的,属实很妙

喜欢就关注一下吧❤️,你的点赞和关注是我不断分享的动力

相关推荐
Microsoft Word11 分钟前
c++基础语法
开发语言·c++·算法
天才在此24 分钟前
汽车加油行驶问题-动态规划算法(已在洛谷AC)
算法·动态规划
小镇程序员1 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
莫叫石榴姐1 小时前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
疯狂的沙粒1 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪1 小时前
AJAX的基本使用
前端·javascript·ajax
力透键背2 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript
程楠楠&M2 小时前
node.js第三方Express 框架
前端·javascript·node.js·express
weiabc2 小时前
学习electron
javascript·学习·electron
想自律的露西西★2 小时前
用el-scrollbar实现滚动条,拖动滚动条可以滚动,但是通过鼠标滑轮却无效
前端·javascript·css·vue.js·elementui·前端框架·html5