栈
先进后出,可以想象为碟子一个个叠起来,拿走时从上面拿,也就是最先叠进来的在最下面,最后拿出去
/*
栈
先进后出,可以想象为碟子一个个叠起来,拿走时从上面拿,也就是最先叠进来的在最下面,最后拿出去
*/
class Stack {
constructor() {
this.stack = []
}
push (item) {
this.stack.push(item)
}
pop () {
return this.stack.pop()
}
print () {
console.log(this.stack)
}
}
const stack = new Stack()
stack.push(1)
stack.push(2)
stack.push(3)
stack.print() // [1, 2, 3]
stack.pop()
stack.print() // [1, 2]
队列
先进先出,可以想象为排队打饭,先排的人先打饭
/*
队列
先进先出,可以想象为排队打饭,先排的人先打饭
*/
class Queue {
constructor() {
this.queue = []
}
enqueue (item) {
this.queue.push(item)
}
dequeue () {
return this.queue.shift()
}
print () {
console.log(this.queue)
}
}
const queue = new Queue()
queue.enqueue(1)
queue.enqueue(2)
queue.enqueue(3)
queue.print() // [1, 2, 3]
queue.dequeue()
queue.print() // [2, 3]
优先队列
设置优先级,按优先级插入,先插先出,可以想象为银行排队时,vip 可以插在前面普通用户,vvip 又可以插在 vip 前面
/*
优先队列
设置优先级,按优先级插入,先插先出,可以想象为银行排队时,vip 可以插在前面普通用户,vvip 又可以插在 vip 前面
*/
class PriorityQueue {
constructor() {
this.priorityQueue = []
}
enqueue(value, priority) {
const enqueueItem = { value, priority }
// 队列为空直接 push
if (!this.priorityQueue.length) {
this.priorityQueue.push(enqueueItem)
} else {
// 找到第一个 priority 比当前 priority 大的元素的 index ,插入此位置
const index = this.priorityQueue.findIndex(item => enqueueItem.priority < item.priority)
if (index > -1) {
this.priorityQueue.splice(index, 0, enqueueItem)
} else {
this.priorityQueue.push(enqueueItem)
}
}
}
dequeue() {
return this.priorityQueue.shift()
}
print() {
console.log(this.priorityQueue)
}
}
const priorityQueue = new PriorityQueue()
priorityQueue.enqueue('小明', 1)
priorityQueue.enqueue('小强', 3)
priorityQueue.enqueue('小花', 1)
priorityQueue.enqueue('小杰', 2)
priorityQueue.print() // [{"value":"小明","priority":1},{"value":"小花","priority":1},{"value":"小杰","priority":2},{"value":"小强","priority":3}]
链表
链表是指每个元素包含 值 和 指向下一个元素的指针。数组的缺点在于插入一个元素时,其后的元素都要向后移一位,而链表插入一个元素时,不需要按顺序插进去,只需指针指向正确就行,但是链表查找元素时需要循环
/*
链表
链表是指每个元素包含 值 和 指向下一个元素的指针。数组的缺点在于插入一个元素时,其后的元素都要向后移一位,而链表插入一个元素时,不需要按顺序插进去,只需指针指向正确就行,但是链表查找元素时需要循环
*/
// 一个节点包含 值 和 链接下一个节点的指针
class Node {
constructor(value) {
this.value = value
this.next = null
}
}
class LinkedList {
constructor() {
// 设置 头 和 尾 节点 ,设置 尾 可以更容易 append
this.head = null
this.tail = this.head
this.length = 0
}
// 尾部插入
append(value) {
const newNode = new Node(value)
// 如果 头 是空的 直接赋值给头,否则赋值给 现有的尾的next 并且 尾节点更新为插入节点
if (!this.head) {
this.head = newNode
this.tail = newNode
} else {
this.tail.next = newNode
this.tail = newNode
}
this.length++
}
// 头部插入
prepend(value) {
const newNode = new Node(value)
newNode.next = this.head
this.head = newNode
this.length++
}
// 插入 当前元素 就是找到插入位置 之前 和 之后 的元素,将 之前的元素next 指向 当前元素,将 当前元素next 指向 之后的元素
insert(value, index) {
const node = new Node(value)
const { prevNode, nextNode } = this.getPrevNextNodes(index)
prevNode.next = node
node.next = nextNode
this.length++
}
getPrevNextNodes(index) {
let count = 0;
let prevNode = this.head;
let nextNode = prevNode.next;
// 其实就是从 0 开始向前找,找到 目标index 之前那个元素作为 prevNode ,prevNode.next 作为之后那个元素,将目标元素插入这俩元素之间
while (count < index - 1) {
prevNode = prevNode.next;
nextNode = prevNode.next;
count++;
}
return {
prevNode,
nextNode
}
}
// 不断查找 prevNode 和 curNode ,让 curNode 的 next 指向 prevNode
reverse() {
let prevNode = null;
let curNode = this.head;
this.tail = curNode;
while (curNode !== null) {
let nextNode = curNode.next;
curNode.next = prevNode;
prevNode = curNode
curNode = nextNode
}
this.head = prevNode
}
// 当前元素 之前的元素 的 next 改为 当前元素之后的元素
remove(index) {
let { prevNode, nextNode: currentNode } = this.getPrevNextNodes(index)
prevNode.next = currentNode.next
this.length--
}
getNode(index) {
let curNode = this.head
let count = 0
while (count < index) {
curNode = curNode.next
count++
}
return curNode
}
}
const linkedList = new LinkedList()
linkedList.append('小明')
linkedList.append('小强')
linkedList.insert('小一', 1)
linkedList.remove(1)
console.log(linkedList) // {"head":{"value":"小明","next":{"value":"小强","next":null}},"tail":{"value":"小强","next":null},"length":2}
console.log(linkedList.getNode(1)) // { "value": "小强", "next": null }
linkedList.reverse()
console.log(linkedList)
/* 双向链表 */
/* 循环链表 */
集合
无重复数据的数组,es6 已实现,可通过对象模拟实现
/*
集合
无重复数据的数组,es6 已实现,可通过对象模拟实现
*/
class Set {
constructor() {
this.set = {}
}
add(v) {
if (!this.has(v)) {
this.set[v] = v
}
}
remove() {
if (this.has(v)) {
delete this.set[v]
}
}
has(v) {
return this.set.hasOwnProperty(v)
}
size() {
return this.value.length
}
get values() {
return Object.keys(this.set)
}
// 并集
unionSet(otherSet) {
const unionSet = new Set()
Array.prototype.concat(otherSet.values, this.values).forEach(v => {
unionSet.add(v)
})
return unionSet.values
}
// 交集
intersection(otherSet) {
const intersection = new Set()
otherSet.values.forEach(v => {
if (this.has(v)) {
intersection.add(v)
}
})
return intersection.values
}
// 差集
difference(otherSet) {
const difference = new Set()
this.values.forEach(v => {
if (!otherSet.has(v)) {
difference.add(v)
}
})
return difference.values
}
// 子集
subset(otherSet) {
if (this.size > otherSet.size) return false
return !this.values.some(v => !otherSet.has(v))
}
}
const set = new Set()
set.add(1)
set.add(1)
set.add(2)
set.add(3)
console.log('set', set.values) // [1, 2, 3]
const set2 = new Set()
set2.add(3)
set2.add(4)
set2.add(4)
console.log('set2', set2.values) // ["3","4"]
const union = set.unionSet(set2)
console.log('并集', union) // ["1","2","3","4"]
const intersection = set.intersection(set2)
console.log('交集', intersection) // ["3"]
const difference = set.difference(set2)
console.log('差集', difference) // ["1","2"]
const set3 = new Set()
set3.add(1)
set3.add(2)
set3.add(3)
set3.add(4)
console.log('set 是否 set2 子集', set.subset(set2)) // false
console.log('set 是否 set3 子集', set.subset(set3)) // true
二叉树
二叉树是节点最多只能有两个的树结构,一个左侧一个右侧,左侧存比父节点小的值,右侧存比父节点大的值 二叉树删除很麻烦,如果涉及频繁删除不适合使用
1. 先序遍历
对于二叉树中的任意一个节点,先打印该节点,然后是它的左子树,最后右子树
2. 中序遍历
对于二叉树中的任意一个节点,先打印它的左子树,然后是该节点,最后右子树
3. 后序遍历
对于二叉树中的任意一个节点,先打印它的左子树,然后是右子树,最后该节点
/*
二叉树
二叉树是节点最多只能有两个的树结构,一个左侧一个右侧,左侧存比父节点小的值,右侧存比父节点大的值
二叉树删除很麻烦,如果涉及频繁删除不适合使用
*/
class Node {
constructor(key) {
this.key = key
this.left = null
this.right = null
}
}
class BinarySearchTree {
constructor() {
this.root = null
}
insert(key) {
const newNode = new Node(key)
const insertNode = (node, newNode) => {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode
} else {
insertNode(node.left, newNode)
}
} else {
if (node.right === null) {
node.right = newNode
} else {
insertNode(node.right, newNode)
}
}
}
if (!this.root) {
this.root = newNode
} else {
insertNode(this.root, newNode)
}
}
// 遍历有点难理解,主要是方法执行完才会继续向下执行,debugger 看看调用栈会比较清楚
// 中序遍历
inOrderTraverse(callback) {
const inOrderTraverseNode = (node, callback) => {
if (node !== null) {
inOrderTraverseNode(node.left, callback)
callback(node.key)
inOrderTraverseNode(node.right, callback)
}
}
inOrderTraverseNode(this.root, callback)
}
// 先序遍历
preOrderTraverse(callback) {
const preOrderTraverseNode = (node, callback) => {
if (node !== null) {
callback(node.key)
preOrderTraverseNode(node.left, callback)
preOrderTraverseNode(node.right, callback)
}
}
preOrderTraverseNode(this.root, callback)
}
// 后序遍历
postOrderTraverse(callback) {
const postOrderTraverseNode = (node, callback) => {
if (node !== null) {
postOrderTraverseNode(node.left, callback)
postOrderTraverseNode(node.right, callback)
callback(node.key)
}
}
postOrderTraverseNode(this.root, callback)
}
// 有 left 节点 则继续找下去,没有则返回自身
min(node) {
const minNode = node => {
return node ? (node.left ? minNode(node.left) : node) : null
}
return minNode(node || this.root)
}
// 有 right 节点 则继续找下去,没有则返回自身
max(node) {
const maxNode = node => {
return node ? (node.right ? maxNode(node.right) : node) : null
}
return maxNode(node || this.root)
}
search(key) {
const searchNode = (node, key) => {
if (node === null) return false
if (node.key === key) return node
return searchNode((key < node.key) ? node.left : node.right, key)
}
return searchNode(root, key)
}
// 删除叶节点,或者只有一个子节点的节点,有两个子节点的情况不实现
/* 删除节点的思路:
删除叶节点:
叶节点比其父节点小,则将父节点的 left 赋值为 null;叶节点比父节点大,则将父节点的 right 赋值为 null
删除只有一个子节点的节点:
该节点比父节点小:则将父节点的 left 赋值为该节点唯一的子节点;比父节点大,则将父节点的 right 赋值为该节点唯一的子节点
*/
remove(key) {
const removeNode = (node, key) => {
if (node === null) return null
if (key === node.key) {
if (node.left === null && node.right === null) {
return null
}
if (node.left === null) {
return node.right
}
if (node.right === null) {
return node.left
}
} else if (key < node.key) {
node.left = removeNode(node.left, key)
return node
} else {
node.right = removeNode(node.right, key)
return node
}
}
removeNode(this.root, key)
}
}
var bst = new BinarySearchTree();
bst.insert(5);
bst.insert(3);
bst.insert(7);
bst.insert(2);
bst.remove(3)
bst.inOrderTraverse((v) => {
console.log(v)
}) // 2 5 7