二叉树
树形结构
-
像数组、栈、队列、默认都是线性结构类型。常见的树形结构有二叉树和多叉树(大于两个叉的树)。
-
开发中常见的树形结构有: 文件夹目录、DOM结构、路由的配置...... (树的数据结构是非常重要的)
常见概念
-
节点 (根节点、父节点、字节点、兄弟节点)
-
子树 (左子树、右子树),子树的个数称之为度
-
叶子节点 (度为0的节点) 非叶子节点 (度不为0的节点)
-
节点的深度 (从根节点到当前节点所经过的节点总数)
-
节点的高度 (从当前节点到最远叶子节点经过的节点总数)
-
树的层数 (树的高度、树的深度)
-
有序树( 节点按照顺序排列)、无序树
二叉树
二叉树是每个结点最多有两个子树的树结构 ,每个节点的度最多为2。 通常子树被称作“左子树”(left subtree)和“右子树”(right subtree) ,左子树和右子树是有顺序的
二叉树的常见概念
- 真二叉树: 不含一度节点的二叉树称作真二叉树(proper binary tree)
- 满二叉树:满二叉树也是真二叉树,且所有的叶子节点都在最后一层
- 完全二叉树: 深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
.二叉搜索树
-
1.什么是二叉搜索树? 一般情况下存储数据我们可以采用数组的方式,但是从数组中检索数据的时间复杂度是O(n),如果数据存储有序,则可以采用二分查找的方式来检索数据,复杂度为:O(logn),但是如果操作数组中的数据像增加、删除默认数组会产生塌陷。时间复杂度为O(n)
-
二叉搜索树中查询、增加、删除复杂度最坏为O(logn),特性是当它的左子树不空,则左子树上所有结点的值均小于它的根结点的值,当右子树不空,则右子树上所有结点的值均大于它的根结点的值。
class Node {
constructor(element, parent) {
this.element = element
this.parent = parent
this.left = null
this.right = null
}
}
class BST {
constructor() {
// 树有跟节点,和节点个数
this.root = null
this.size = 0
}
add(element, parent = null) {
// 构建二叉树
if (this.root === null) {
// 根节点
this.root = new Node(element, null)
} else {
// 叶子节点
let current = this.root // 找到当前要添加的父元素,从根节点开始找
let compare = 0
let parent = null
while(current) {
// 判断是在当前元素的左边还是右边
parent = current
compare = element - current.element
// 如果> 0,在右侧子树,如果小于0,在左侧子树
if (compare > 0) {
current = current.right
} else {
current = current.left
}
}
let node = new Node(element, parent)
if (compare > 0) {
parent.right = node
} else {
parent.left = node
}
}
this.size++
return this.root
}
preorderTravesal(visitor) {
const travesal = (node) => {
if(node === null) return
visitor.visit(node.element)
travesal(node.left)
travesal(node.right)
}
// 先序遍历, 根左右
travesal(this.root)
}
inorderTravesal(visitor) {
// 中序遍历, 左根右
const travesal = (node) => {
if(node === null) return
travesal(node.left)
visitor.visit(node.element)
travesal(node.right)
}
// 先序遍历, 根左右
travesal(this.root)
}
postorderTravesal(visitor) {
// 后序遍历,左右根
const travesal = (node) => {
if(node === null) return
travesal(node.left)
travesal(node.right)
visitor.visit(node.element)
}
// 先序遍历, 根左右
travesal(this.root)
}
levelordertravesal(visitor) {
// 层序遍历, 一层一层的来,使用栈
if (!this.root || !visitor) return null
let stack = [this.root]
let index = 0
let current = null
while(current = stack[index]) {
visitor.visit(current.element)
if(current.left) {
stack.push(current.left)
}
if (current.right) {
stack.push(current.right)
}
index++ // 指针右移
}
stack = []
}
reverse() {
// 二叉树的反转,在遍历过程中将左右孩子交换,其实是一个冒泡排序的过程
if (!this.root) return
let stack = [this.root]
let index = 0
let current = null
while(current = stack[index++]) {
// 遍历树
let temp = current.left
current.left = current.right
current.right = temp
if (current.left) {
stack.push(current.left)
}
if (current.right) {
stack.push(current.right)
}
}
return this.root
}
}
let bst = new BST()
bst.add(10)
bst.add(8)
bst.add(19)
bst.add(6)
bst.add(15)
bst.add(22)
console.log(bst.root)
console.log(bst.reverse())
单向链表
- 各个节点数据通过指针的方法串联起来,构成链表。(单向指针)
创建链表
class LinkNode {
constructor(element, next) {
this.element = element
this.next = next
}
}
class LinkList {
constructor() {
this.head = null
this.size = 0
}
getNode(index) {
// 从头开始找
let current = this.head
for(let i = 0; i < index; i++) {
current = current.next
}
return current
}
add(index, element) {
// 实现函数的重载,如果参数只有一个,是从后追加一个,
if (arguments.length === 1) {
element = index
index = this.size
}
// element 是本身的值,index是索引
if (index > this.size || index < 0) throw new Error('越界')
if(index === 0) {
// 说明是头指针
let oldHead = this.head
this.head = new LinkNode(element, oldHead)
} else {
// 找到当前位置老的元素,将next指向老的元素
let previousNode = this.getNode(index -1)
previousNode.next = new LinkNode(element, previousNode.next)
}
this.size++
}
let links = new LinkList()
links.add(1)
links.add(2)
links.add(3)
console.dir(links, {depth: 1000})
反转链表
// 反转链表
reverseLink() {
/*
思路:
递归: 将头部的next.next指向当前头部,新的头部为老头的next
*/
const reverse = (head) => {
if (head === null || head.next === null) return head;
let newHead = reverse(head.next)
head.next.next = head
head.next = null
return newHead
}
this.head = reverse(this.head)
return this.head
}
reverseLink2() {
/*
非递归方式:
先创建一个新头,然后一个一个的往下拿
*/
let head = this.head //老头
if (head === null || head.next === null) return head;
let newHead = null
while(head) {
const temp = head.next // 保存下一个
head.next = newHead
newHead = head
head = temp
}
this.head = newHead
return this.head
}
}
console.dir(links.reverseLink2(), {depth: 1000})