🥳每日一练-二叉排序的构建-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);

总结

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

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

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

相关推荐
深邃-1 小时前
【数据结构与算法】-二叉树(2):实现顺序结构二叉树(堆的实现),向上调整算法,向下调整算法,堆排序,TOP-K问题
数据结构·算法·二叉树·排序算法·堆排序··top-k
We་ct4 小时前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·javascript·算法·leetcode·typescript
王老师青少年编程8 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·合并果子
叼烟扛炮9 小时前
C++第二讲:类和对象(上)
数据结构·c++·算法·类和对象·struct·实例化
天疆说9 小时前
【哈密顿力学】深入解读航天器交会最优控制中的Hamilton函数
人工智能·算法·机器学习
wuweijianlove10 小时前
关于算法设计中的代价函数优化与约束求解的技术7
算法
leoufung10 小时前
LeetCode 149: Max Points on a Line - 解题思路详解
算法·leetcode·职场和发展
样例过了就是过了10 小时前
LeetCode热题100 最长公共子序列
c++·算法·leetcode·动态规划
HXDGCL11 小时前
矩形环形导轨:自动化循环线的核心运动单元解析
运维·算法·自动化
谭欣辰11 小时前
C++ 排列组合完整指南
开发语言·c++·算法