树的遍历分为深度优先搜索和广度优先搜索。深度优先搜索有先序遍历、中序遍历、和后序遍历三种方式。广度优先搜索是层次遍历。
先序遍历算法:先访问根节点,然后访问左节点,最后访问右节点。
preTraversal() {
this._pre(this.root)
}
_pre(node) {
if(node) {
console.log(node.value)
this._pre(node.left)
this._pre(node.right)
}
}
中序遍历算法:先访问左节点,然后访问根节点,最后访问右节点。
midTraversal() {
this._mid(this.root)
}
_mid(node) {
if(node) {
this._mid(node.left)
console.(node.value)
this._mid(node.right)
}
}
后序遍历算法:先访问左节点,然后访问右节点,最后访问根节点。
backIraversal() {
this._back(this.root)
}
_bcak(node) {
if(node) {
this._back(node.left)
this._back(node.right)
console.log(node.value)
}
}
层次遍历,使用队列结构完成。
breadthTraversal() {
if(!this.root) return null
let q = new Queue()
q.enQueue(this.root)
while (!q.isEmpty()) {
let n = q.deQueue()
if(n.left) q.enQueue(n.left)
if(n.right) q.enQueue(n.right)
}
}
最复杂的是第三种情况,解决办法为:找到需要删除节点p的直接前缀p(或直接后缀)s,用s来替换节点p,然后再删除此节点s。
delect(v) {
this.root = this._delect(this.root, v)
}
_delect(node, v){
if(!node) return null
// 判断在左子树还是在右子树
if(node.value < v) {
node.right = this._delect(node.right.v)
} eles if(node.value > v) {
node.left = this._delect(node.left.v)
} else {
if (!node.left) return node.right
if (!node.right) return node.left
// _getMin()获取右树上最小的节点 _delectMin()删除最小节点
//
let min = this._getMin(node.right)
min.right = this._delectMin(node.right)
min.left = node.left
node = min
}
// 维护size
node.size = this._getSize(node.left) + this._getSize(node.right) + 1
return node
}
构建有序序列,扫描已排序序列,将数据插入到有序序列中合适的位置。
function checkArray(array) {
return Array.isArray(array)
}
function swaps(array, left, right) {
let rightValue = array[right]
array[right] = array[left]
array[left] = rightValue
}
function insertion(array) {
if (!checkArray(array)) return
for (let i = 1; i < array.length; i++) {
for (let j = i - 1; j >= 0 && array[j] > array[j + 1]; j--)
swap(array, j, j +1)
}
return array;
}
在序列中找出最小的元素,放到排序序列的起始位置,然后再从剩下的序列中再找出最小的元素,重复这个过程。
function select(array) {
if (!checkArray(array)) return
for (let i = 0; i < array.length - 1; i++) {
let minIndex = i;
for(let j = i + 1; j < array.length; j++) {
minIndex = array[j] < array[minIndex] ? j : minIndex;
}
swap(array, i, minIndex);
}
return array;
}
比较两个元素,将较大的元素放在后边,当一次循环完成后,最大的元素被放在最后的位置,循环这个过程,知道排序完成。
代码实现:
funciton bubble(array) {
checkArray(array);
for (let i = array.legth - 1; i > 0; i--){
for (let j = 0; j < i; j++) {
if (array[j] > array[j + 1]) swap(array, j, j+1)
}
}
}
在序列中选取一个值,将比该值小的元素放在左边,比该值大的放在右边,在分别对左右两侧的元素做相同的操作,直到排序完成。
function sort(array) {
if (!checkArray(array)) return
quickSort(array, 0, array.length - 1);
return array;
}
function quickSort(array, left, right) {
if (left < right) {
swap(array, , right)
// 随机取值,然后和末尾交换,这样做比固定取一个位置的复杂度略低
let indexs = part(array, parseInt(Math.random() * (right - left + 1)) + left, right);
quickSort(array, left, indexs[0]);
quickSort(array, indexs[1] + 1, right);
}
}
function part(array, left, right) {
let less = left - 1;
let more = right;
while (left < more) {
if (array[left] < array[right]) {
// 当前值比基准值小,`less` 和 `left` 都加一
++less;
++left;
} else if (array[left] > array[right]) {
// 当前值比基准值大,将当前值和右边的值交换
// 并且不改变 `left`,因为当前换过来的值还没有判断过大小
swap(array, --more, left);
} else {
// 和基准值相同,只移动下标
left++;
}
}
// 将基准值和比基准值大的第一个值交换位置
// 这样数组就变成 `[比基准值小, 基准值, 比基准值大]`
swap(array, right, more);
return [less, more];
}
将序列的元素分成多个具有两个元素的子序列,将子序列排序后再将两个子序列合并,再排序再合并,直到合并为一个数组。
function sort(array) {
if (!checkArray(array)) return
mergeSort(array, 0, array.length - 1);
return array;
}
function mergeSort(array, left, right) {
// 左右索引相同说明已经只有一个数
if (left === right) return;
// 等同于 `left + (right - left) / 2`
// 相比 `(left + right) / 2` 来说更加安全,不会溢出
// 使用位运算是因为位运算比四则运算快
let mid = parseInt(left + ((right - left) >> 1));
mergeSort(array, left, mid);
mergeSort(array, mid + 1, right);
let help = [];
let i = 0;
let p1 = left;
let p2 = mid + 1;
while (p1 <= mid && p2 <= right) {
help[i++] = array[p1] < array[p2] ? array[p1++] : array[p2++];
}
while (p1 <= mid) {
help[i++] = array[p1++];
}
while (p2 <= right) {
help[i++] = array[p2++];
}
for (let i = 0; i < help.length; i++) {
array[left + i] = help[i];
}
return array;
}