🥳每日一练-二叉排序的构建-JS简易版

前言

排序一直是个算法中的重点,且无法回避的问题。排序算法必须要全部遍历每一个数据,才能保证数据是有序的,所以排序算法的复杂度一定是大于 O(n), 像经典的冒泡排序, 插入排序的复杂度是 O(n^2), 快速排序,归并排序的复杂要低些,是 O(nlogn)

而二叉排序树也是一个算法复杂低的排序算法,基于二叉排序树可以衍生很多快速查找的算法,像二叉平衡术,红黑树等。数据库查询数据之所以这么快,很大原因是用了红黑树这样的数据结果。让查询速度呈指数集下降。但是维护一个良好的红黑树需要消耗不小的性能。

回到正题,为什么二叉排序树是有序的呢

二叉排序树(Binary Sort Tree)是一种特殊的二叉树,它的每个结点的值都大于等于(或小于等于)其左子树中所有结点的值,小于等于(或大于等于)其右子树中所有结点的值。就像下面这样

很明显,二叉排序树的中序遍历结果是一个递增的序列。所以要以顺序输出数据,就直接输出二叉排序树的中序遍历结果就好了。

而且,二叉排序树插入、删除和查找等操作的时间复杂度均为O(log n),这就是它的厉害之处。为什么是 logn 呢,因为一个平衡的二叉树,节点数量和树的高度呈对数关系:log n 约等于 h

二叉排序树,下面看看代码如何实现吧

准备数据

这里准备一个乱序的是个小于 10 的自然数

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

构建二叉树

javascript 复制代码
const generateTree = (data) => {
	const root = {};
	root.value = data[0];
  
	for (let i = 1; i < data.length; i++) {
		let tree = root;
		while (tree) {
			if (data[i] <= tree.value) {
				if (tree.left) tree = tree.left;
				else {
					tree.left = { value: data[i] };
					break;
				}
			} else {
				if (tree.right) tree = tree.right;
				else {
					tree.right = { value: data[i] };
					break;
				}
			}
		}
	}
	return root;
};

generateTree函数,接受一个数组作为参数。然后将传入的数组转成一个排序二叉树。中间不断遍历数组,遍历到一个,就往构建好的二叉树中插入一个节点。直到所有的数据被遍历完成。

javascript 复制代码
const tree = generateTree(treeData);

构建好了一个二叉排序树,如何校验它的正确性呢?有两种方法,第一种就是在 IDE 中查看生成树的结构;

下面是编译器中查看到的 json 结构:

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

可以用二叉排序的性质解读这个 json ,看看是否正确。我是觉得没问题😋

第二种就是输出二叉排序树的中序遍历;

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

这是一段极其简单的中序遍历代码,下面用它来输出看看:

javascript 复制代码
printNodeIn(tree);
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 7
// 8

完全正确

构建二叉树的递归写法

上面是构建的循环写法,可能不太好写。相对于循环来讲,递归代码会更加容易懂些:

javascript 复制代码
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++) {
    // insert node
		insertNode(root, { value: data[i], left: null, right: null });
	}
	return root;
};

generateTree2和上面构建逻辑一致,也是接受一个数组作为参数,然后遍历这个数组,每遍历到一个节点,就插入一个节点。只是这个插入的过程这里专门写了一个函数来做。

insertNode作用是向二叉排序树中的插入一个新节点。如果要插入节点的值小于等于当前节点,那就插入当前节点的左节点。否则就插入又节点。

代码使用了递归写法,是不是更简单了

下面用printNodeIn测试写得对不对:

javascript 复制代码
const tree2 = generateTree2(treeData);
printNodeIn(tree2);
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 7
// 8

完全正确

完整代码

非递归写法

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

const generateTree = (data) => {
	const root = {};
	root.value = data[0];
	for (let i = 1; i < data.length; i++) {
		let tree = root;
		while (tree) {
			if (data[i] <= tree.value) {
				if (tree.left) tree = tree.left;
				else {
					tree.left = { value: data[i] };
					break;
				}
			} else {
				if (tree.right) tree = tree.right;
				else {
					tree.right = { value: data[i] };
					break;
				}
			}
		}
	}
	return root;
};

const tree = generateTree(treeData);

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

printNodeIn(tree);

递归写法

javascript 复制代码
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 tree2 = generateTree2(treeData);
printNodeIn(tree2);

总结

这篇还是比较简单的,就是介绍了二叉树排序的树的构建,也就相当于节点的插入操作。

下篇文章介绍二叉排序树节点删除操作如何来做?有人可能好奇,这个有什么好分享的,不就是直接删除吗?不是的,删除节点之后,还要保证整个二叉树满足二叉排序树

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

相关推荐
Yan.love几秒前
开发场景中Java 集合的最佳选择
java·数据结构·链表
AC使者6 分钟前
#B1630. 数字走向4
算法
冠位观测者10 分钟前
【Leetcode 每日一题】2545. 根据第 K 场考试的分数排序
数据结构·算法·leetcode
alikami1 小时前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
古希腊掌管学习的神1 小时前
[搜广推]王树森推荐系统笔记——曝光过滤 & Bloom Filter
算法·推荐算法
qystca1 小时前
洛谷 P1706 全排列问题 C语言
算法
浊酒南街1 小时前
决策树(理论知识1)
算法·决策树·机器学习
就爱学编程1 小时前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法
学术头条1 小时前
清华、智谱团队:探索 RLHF 的 scaling laws
人工智能·深度学习·算法·机器学习·语言模型·计算语言学
wakangda1 小时前
React Native 集成原生Android功能
javascript·react native·react.js