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

总结

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

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

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

相关推荐
酷酷的阿云5 分钟前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
九圣残炎22 分钟前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode
lulu_gh_yu27 分钟前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
丫头,冲鸭!!!1 小时前
B树(B-Tree)和B+树(B+ Tree)
笔记·算法
Re.不晚1 小时前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
aPurpleBerry1 小时前
JS常用数组方法 reduce filter find forEach
javascript
ZL不懂前端2 小时前
Content Security Policy (CSP)
前端·javascript·面试
乐闻x2 小时前
ESLint 使用教程(一):从零配置 ESLint
javascript·eslint
为什么这亚子2 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
2 小时前
开源竞争-数据驱动成长-11/05-大专生的思考
人工智能·笔记·学习·算法·机器学习