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

总结

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

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

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

相关推荐
小牛itbull8 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
请叫我欧皇i16 分钟前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
533_19 分钟前
[vue] 深拷贝 lodash cloneDeep
前端·javascript·vue.js
GIS瞧葩菜28 分钟前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript
带多刺的玫瑰36 分钟前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
爱敲代码的憨仔38 分钟前
《线性代数的本质》
线性代数·算法·决策树
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
yigan_Eins1 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
zhang-zan1 小时前
nodejs操作selenium-webdriver
前端·javascript·selenium
ZBY520311 小时前
【Vue】 npm install amap-js-api-loader指南
javascript·vue.js·npm