js实现的大根堆算法(基于链式的m叉树)

本文介绍了一种基于m叉树的堆排序算法实现,包括数据生成、m叉树构建、堆调整、堆排序等核心步骤,并通过面向过程和面向对象两种方式对比展示了算法细节。此外,还提供了算法正确性和性能的验证方法。

面向过程版本

var COUNT = 3;
/*
	获取待排序数据,数据的个数和值随机生成
*/
function getData() {
	var arr = [];
	var i = 0;
	var len = Math.max(10, ~~(Math.random() * 30 ));
	while(i <  len) {
		arr.push(~~(Math.random() * 100 ));
		i++;
	}	
	return arr;
}
/*
	节点构造函数
	@param val {any} 节点的值
	@param position {int} 节点的位置,即第几个节点
*/
function Node(val, position) {
	// 叉数,例如二叉树
	var count = COUNT;
	// 初始化节点的孩子为null
	while(count) {
		this[count--] = null;
	}
	this.position = position;
	// 父节点、前继节点、后继节点
	this.parent = null;
	this.pre = null;
	this.next = null;
	// 孩子个数
	this.childs = 0;
	this.val = val;
}
/*
	构造COUNT叉树
*/
function makeTree(arr) {
	var i = 1;
	var root = new Node(arr[0], 0);
	var position = 1;
	var count = COUNT;
	// 初始化队列
	var queue = [root];
	// 保存上一个遍历的节点,用于前后节点的连接
	var preNodePtr;
	// 构造m叉树完成后,从哪个节点开始遍历这棵树,从最后一个非叶子节点开始
	var neededNode;
	// 是否已经找到了最后一个非叶子节点
	var isFind = false;
	// 用于寻找最后的非叶子节点
	var preNode = root;
	// 利用队列进行广度遍历
	while(queue.length) {
		var node = queue.shift();
		i = 1;
		// 给当前节点配置孩子数据,个数为COUNT
		while(i <= count) {
			// 判断当前的节点数是否已经超过数据的长度
			if (position < arr.length) {
				var newNode = new Node(arr[position], position);
				node[i] = newNode;
				newNode.parent = node;
				newNode.pre = preNode;
				preNode.next = newNode;
				node.childs++;
				queue.push(newNode);
				preNode = newNode;
				i++;
				position++;
			} 
			// 全部数据赋值完成
			else {
				break;
			}
		}
		// 是否找到了最后的非叶子节点
		if (!isFind) {
			// 当前节点的孩子数为0,说明前一节点为最后一个非叶子节点
			if (node.childs === 0) {
				neededNode = preNodePtr;
				isFind = true;
				return {
					root: root,
					startNode: neededNode
				};
			} 
			// 当前节点的孩子数小于分支数COUNT,说明当前节点为最后的非叶子节点
			else if (node.childs < COUNT){
				neededNode = node;
				isFind = true;
				return {
					root: root,
					startNode: neededNode
				};
			}
			// 保存前一个节点
			else {
				preNodePtr = node;
			}
		}
	}
	return root;
}
// 找出一棵树的最后非叶子节点,已经在makeTree里实现
function findNode(root) {
	var queue = [root];
	var node;
	var count = COUNT;
	var i = 1;
	var preNodePtr;

	while(queue.length) {
		node = queue.shift();
		if (node.childs === 0) {
			return preNodePtr;
		} else if (node.childs < COUNT) {
			return node;
		}
		preNodePtr = node;
		while(i <= count) {
			queue.push(node[i++]);
		}
		count = COUNT;	
	}
}
// 根据一颗无序的m叉树构建m叉大堆树
function buildHeapTree(root, startNode) {
	var node = startNode;
	// 从最后一个非叶子节点开始,到根节点结束,遍历其中的所有节点,调整成m叉堆的形式,最后根节点为值最大的节点
	while(node) {
		adjustHeapTree(node);
		node = node.pre;
	}
	return root;
}
// 以node节点为根节点,endNode为结束节点。把node子树调整成m叉堆,
function adjustHeapTree(node, endNode) {
	var i = 1;
	var max = node.val;
	var position = 0;
	var isEnd = false;
	while(i <= COUNT) {
		// 是否已经到达了结束节点,是则结束所有步骤
		if (endNode && node[i] && node[i].position >= endNode.position) {
			isEnd = true;
			break;
		}
		// 找到node节点和孩子中的最大值。
		if (node[i] && node[i].val > max) {
			max = node[i].val;
			position = i;
		}
		i++;
	}
	var temp;
	// 如果node节点的值不是最大的,那么和值最大的孩子节点进行值的互换
	if (position !== 0) {
		temp = node.val;
		node.val = node[position].val;
		node[position].val = temp;
		/*
			是否已经到达结束节点,是的话不需要再进行调整后续的子树,因为孩子节点的position和父节点的大
			以被置换值的孩子节点为根节点,调整成m叉树	
		*/
		!isEnd && adjustHeapTree(node[position], endNode);
	}	
}

/*
	对排序的主流程
*/
function heapSort(root, startNode) {
	if (!root.childs) {
		return;
	}
	
	var node = startNode;
	// 获取整个树的最后一个节点,即最后一个非叶子节点的最后一个节点
	var lastChild = getNodeLastChild(node);
	var endNode = lastChild;
	var temp;
	// 首先构建m叉堆
	buildHeapTree(root, startNode);
	while(endNode !== root) {
		// 把m叉堆中值最大的节点,即根节点的值和最后一个孩子的值互换
		temp = root.val;
		root.val = endNode.val;
		endNode.val = temp;
		// 以根节点为起点,endNode为终点,调整子树为m叉堆
		adjustHeapTree(root, endNode);
		// 终点根节点方向,往前挪一位,此时,endNode后面的节点都是有序的
		endNode = endNode.pre;
	}
}
// 或许某个节点的最后一个孩子节点
function getNodeLastChild(node) {
	var count = COUNT;
	while(count) {
		if (node[count]) {
			return node[count];
		}
		count--;
	}
	return null;
}
// 广度遍历root为根节点的m叉树
function echo(root) {
	var queue =[root];
	var result = [];
	var i = 1;
	while(queue.length) {
		i = 1;
		var node = queue.shift();
		result.push(node.val);
		while(i <= node.childs) {
			queue.push(node[i++]);
		}
	}
	console.log('sort: data',JSON.stringify(result),'\n');
	return result;
}
// 断言堆排序算法产生的数据是升序的
function assert(result) {
	var i = 0;
	while(i < result.length - 2) {
		if (result[i] > result[i+1]) {
			console.log('error',i, result,'\n');
			break;
		}
		i++;
	}
}
function start() {
	var data = getData();
	console.log('source data:',JSON.stringify(data));
	var {root, startNode} = makeTree(data);
	heapSort(root, startNode);
	assert(echo(root));
}
start();



粗糙的面向对象版本

function HeapSort(count) {
	this.data = [];
	this.COUNT = count || 2;
}

HeapSort.prototype = {
	getData: function() {
		var arr = [];
		var i = 0;
		var len = Math.max(10, ~~(Math.random() * 30 ));
		while(i <  len) {
			arr.push(~~(Math.random() * 100 ));
			i++;
		}	
		this.data = arr;
		return arr;
	},
	makeTree: function makeTree() {
		var arr = this.data;
		var i = 1;
		var root = this.getNode(arr[0], 0);
		var position = 1;
		var count = this.COUNT;
		var queue = [root];
		var preNodePtr;
		var neededNode;
		var isFind = false;
		var preNode = root;
		while(queue.length) {
			var node = queue.shift();
			i = 1;
			while(i <= count) {
				if (position < arr.length) {
					var newNode = this.getNode(arr[position], position);
					node[i] = newNode;
					newNode.parent = node;
					newNode.pre = preNode;
					preNode.next = newNode;
					node.childs++;
					queue.push(newNode);
					preNode = newNode;
					i++;
					position++;
				} else {
					break;
				}
			}

			if (!isFind) {
				if (node.childs === 0) {
					neededNode = preNodePtr;
					isFind = true;
					this.startNode = neededNode;
					this.root = root;
				} else if (node.childs < this.COUNT){
					neededNode = node;
					isFind = true;
					this.startNode = neededNode;
					this.root = root;
				} else {
					preNodePtr = node;
				}
			}
		}
		return root;
	},
	findNode: function findNode(root) {
		var queue = [root];
		var node;
		var count = COUNT;
		var i = 1;
		var preNodePtr;

		while(queue.length) {
			node = queue.shift();
			if (node.childs === 0) {
				return preNodePtr;
			} else if (node.childs < this.COUNT) {
				return node;
			}
			preNodePtr = node;
			while(i <= count) {
				queue.push(node[i++]);
			}
			count = this.COUNT;	
		}
	},
	buildHeapTree: function buildHeapTree() {
		var node = this.startNode;
		while(node) {
			this.adjustHeapTree(node);
			node = node.pre;
		}
	},
	adjustHeapTree: function adjustHeapTree(node, endNode) {
		var i = 1;
		var max = node.val;
		var position = 0;
		var isEnd = false;
		while(i <= this.COUNT) {
			if (endNode && node[i] && node[i].position >= endNode.position) {
				isEnd = true;
				break;
			}
			if (node[i] && node[i].val > max) {
				max = node[i].val;
				position = i;
			}
			i++;
		}
		var temp;
		if (position !== 0) {
			temp = node.val;
			node.val = node[position].val;
			node[position].val = temp;
			!isEnd && this.adjustHeapTree(node[position], endNode);
		}	
	},
	heapSort: function heapSort() {
		if (!this.root.childs) {
			return;
		}
		var root = this.root;
		var node = this.startNode;
		var lastChild = this.getNodeLastChild(node);
		var endNode = lastChild;
		var temp;
		this.buildHeapTree();
		while(endNode !== root) {
			temp = root.val;
			root.val = endNode.val;
			endNode.val = temp;
			this.adjustHeapTree(root, endNode);
			endNode = endNode.pre;
		}
	},
	getNodeLastChild: function getNodeLastChild(node) {
		var count = this.COUNT;
		while(count) {
			if (node[count]) {
				return node[count];
			}
			count--;
		}
		return null;
	},
	echo: function echo() {
		var queue =[this.root];
		var result = [];
		var i = 1;
		while(queue.length) {
			i = 1;
			var node = queue.shift();
			result.push(node.val);
			while(i <= node.childs) {
				queue.push(node[i++]);
			}
		}
		this.result = result;
		console.log('sort: data',JSON.stringify(result),'\n');
	},
	assert: function assert() {
		var result = this.result;
		var i = 0;
		while(i < result.length - 2) {
			if (result[i] > result[i+1]) {
				console.log('error',i, result,'\n');
				break;
			}
			i++;
		}
	},
	start: function start() {
		this.getData();
		console.log('source data:',JSON.stringify(this.data));
		this.makeTree();
		this.heapSort();
		this.echo();
		this.assert();
	},
	getNode(val, position) {
		var node = {};
		var count = this.COUNT;
		while(count) {
			node[count--] = null;
		}
		node.position = position;
		node.parent = null;
		node.pre = null;
		node.next = null;
		node.childs = 0;
		node.val = val;
		return node;
	}
}
new HeapSort().start();
```
算法正确性验证
```
var end = 10000;
var i = 0;
var time = setInterval(function() {
	if (i > end) {
		clearInterval(time);
	}
	i++;
	start();
	//new HeapSort().start();
},10)
```
算法性能比较
```
// 插入排序 
function insertSort(arr) {
for (var i = 1;i<arr.length;i++) {
var j=i-1;
var key = arr[i];
while (j>=0 && key<arr[j]) {
arr[j+1] = arr[j];
j--;
}
arr[j+1] = key;
}
}

// 希尔排序
function shellSortRecursion(arr,step) {
	if(step<1) {
		return;
	}
	for (var i = step;i<arr.length;i++) {
		var j = i-step;
		var key = arr[i];
		while (j>=0 && key<arr[j]) {
			arr[j+step] = arr[j];
			j-=step;
		}
		arr[j+step] = key;
	}
	step = Math.floor(step/2);
	shellSortRecursion(arr,step);
}

function start() {
	var data = getData();
	var start = new Date().getTime();
	var {root, startNode} = makeTree(data);
	heapSort(root, startNode);
	//insertSort(data)
	//shellSortRecursion(data,5)
	console.log((new Date().getTime() - start)/1000);
}
start();
```
复制代码


转载于:https://juejin.im/post/5c3e049f6fb9a049b22223fb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值