记录 10 个数据结构

53 阅读10分钟

实现数据结构的时候, 数据结构中要实现数据的 增、删、改、查等操作, 判满、判空等操作

1.1 数组

定义: 相同类型的元素(element)的集合所组成的数据结构,分配一块连续的内存来存储。利用元素的索引(index)可以计算出该元素对应的存储地址

在js中这种数据结构,相较于其他的语言较为简单,js 的类型是松散类型,他可以存放多种类型,但是建议数组中还是存放一种类型的数据。同时他会自动扩容, 但是我们可以模拟出他扩容的场景, 一般是他原来的 1.5 倍或 2 倍


// 用到的位置全为索引值
class MyArray {
  length = 0;
  list = null;

  constructor(size) {
    this.list = new Array(size);
  }

  // 插入操作需要判断是否要扩容, 一般是 1.5 和 2 倍
  expandArray() {
    if (this.length === this.list.length) {
      const newLength = Math.floor(this.list.length * 1.5);
      const newArray = new Array(newLength);
      for (let i = 0; i < this.list.length; i++) {
        newArray[i] = this.list[i];
      }
      return newArray;
    } else {
      return this.list;
    }
  }

  // 在头部插入一个数据
  unshift(data) {
    this.list = this.expandArray();
    // 需要将所有的数据都往后移动一位
    for (let i = this.length; i > 0; i--) {
      this.list[i] = this.list[i - 1];
    }
    this.list[0] = data;
    this.length++;
    return this.length;
  }

  // 在尾部插入一个数据
  push(data) {
    this.list = this.expandArray();
    this.list[this.length] = data;
    this.length++;
    return this.length;
  }

  // 在中间插入一个元素
  insert(index, data) {
    this.list = this.expandArray();
    for (let i = this.length; i > index; i--) {
      this.list[i] = this.list[i - 1];
    }
    this.list[index] = data;
    this.length++;
    return this.length;
  }

  // 最后面弹出一个元素
  pop() {
    if (this.length === 0) {
      return undefined;
    }
    const temp = this.list[this.length - 1];
    delete this.list[this.length - 1];
    this.length--;
    return temp;
  }

  // 删除头部元素
  shift() {
    if (this.length === 0) {
      return undefined;
    }
    const temp = this.list[0];
    for (let i = 0; i < this.length - 1; i++) {
      this.list[i] = this.list[i + 1];
    }
    delete this.list[this.length - 1];
    this.length--;
    return temp;
  }

  // 指定位置删除一个
  del(index) {
    if (this.length === 0) {
      return undefined;
    }

    const temp = this.list[index];

    for (let i = index; i < this.length - 1; i++) {
      this.list[i] = this.list[i + 1];
    }
    // 移动后要将移动的最后一个元素删除
    delete this.list[this.length - 1];
    this.length--;
    return temp;
  }
}

// 测试
const arr = new MyArray(10);

console.log(arr);

arr.push(1);
arr.push(2);
arr.push(3);
console.log("🚀 ~ This is a result of console.log ~ ✨: ", arr.list);

console.log("🚀 ~ This is a result of console.log ~ ✨: ", arr.pop());
console.log("🚀 ~ This is a result of console.log ~ ✨: ", arr.list);

arr.unshift(99);
console.log("🚀 ~ This is a result of console.log ~ ✨: ", arr.list);

arr.shift();
console.log("🚀 ~ This is a result of console.log ~ ✨: ", arr.list);

arr.insert(2, 4);
console.log("🚀 ~ This is a result of console.log ~ ✨: ", arr.list);

arr.del(1);
console.log("🚀 ~ This is a result of console.log ~ ✨: ", arr.list);

测试结果

image.png

对于数组结构, 我们可以总结一下几点:

  • 更偏向于读, 他在计算机中是一串连续的地址, 寻址速度快, 如果是写的话, 他会移动大量的数据,影响性能
  • 他存储的是同一类型的数据的集合
  • 他分配的地址大小是固定的, 在 js 中他内部处理了大小, 如果是其他语言如果不扩容, 会有溢出的情况

1.2 链表

定义: 链表是一种线性数据结构,由一系列节点组成,节点之间通过指针关联,不要求是连续的内存空间

  • 1、 一个结点可以理解为是一个对象, 里面存放的数据, 和指向下一个结点的指针
  • 2、 要记录首结点和尾结点,方便定位和查找
  • 3、 链表是无序的, 要找到一个结点需要确定目标位置前后两个结点才能找到相应的位置

单链表

   // 节点
   
class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class SingleLink {
  // 头指针
  head = null;
  // 尾指针
  tail = null;
  // 数量
  size = 0;

  // 在末尾插入一个节点
  append(data) {
    const node = new Node(data);
    if (this.size === 0) {
      this.head = node;
      this.tail = node;
    } else {
      this.tail.next = node;
      this.tail = node;
    }

    this.size += 1;

    return this.size;
  }

  // 在头部插入一个节点
  prepend(data) {
    const node = new Node(data);
    if (this.size === 0) {
      this.head = node;
      this.tail = node;
    } else {
      node.next = this.head;
      this.head = node;
    }

    this.size += 1;
    return this.size;
  }

  // 在指定位置插入一个节点, index 不是索引, 是实际的位置
  insertIndex(index, data) {
    if (index < 0 || index > this.size) {
      return false;
    }
    const node = new Node(data);
    if (index === 0 || index === 1) {
      node.next = this.head;
      this.head = node;
    } else if (index === this.size) {
      this.tail.next = node;
      this.tail = node;
    } else {
      let current = this.head;
      let pre = this.head;
      for (let i = 1; i < index; i++) {
        pre = current;
        current = current.next;
      }
      pre.next = node;
      node.next = current;
    }
    this.size += 1;
    return this.size;
  }

  // 找到指定位置的节点数据
  findIndex(index) {
    if (index < 0 || index > this.size) {
      return false;
    }
    let current = this.head;
    for (let i = 1; i < index; i++) {
      current = current.next;
    }
    return current.data;
  }

  // 删除指定位置的节点
  delIndex(index) {
    if (index < 0 || index === 0 || index > this.size) {
      return false;
    }
    let temp;

    if (index === 1) {
      temp = this.head;
      this.head = this.head.next;
    } else {
      let current = this.head;
      let pre = null;

      for (let i = 1; i < index; i++) {
        pre = current;
        current = current.next;
      }
      temp = current.data;

      pre.next = current.next;
    }

    this.size -= 1;

    return temp;
  }

  // 获取链表长度
  getSize() {
    return this.size;
  }

  // 判断链表是否为空
  isEmpty() {
    return !!this.size;
  }

  // 遍历链表
  printLink() {
    if (this.size === 0) {
      return "当前链表为空";
    }
    let current = this.head;
    while (current) {
      console.log(current.data);
      current = current.next;
    }
  }
}

// 测试
const link = new SingleLink();

link.append(1);
link.prepend(2);

link.insertIndex(0, 0);
link.printLink();
console.log("🚀 ~ This is a result of console.log ~ ✨: ");
link.delIndex(1);
link.printLink();

console.log("🚀 ~ This is a result of console.log ~ ✨: ", link.findIndex(2));

测试结果

image.png

单向循环链表

class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class SingleCycleLink {
  head = null;
  size = 0;
  tail = null;

  // 在头部添加一个元素节点
  prepend(data) {
    const node = new Node(data);
    if (this.size === 0) {
      this.head = node;
      node.next = this.head;
      this.tail = node;
    } else {
      this.tail.next = node;
      node.next = this.head;
      this.head = node;
    }

    this.size++;
    return this.size;
  }

  // 在尾部添加一个节点
  append(data) {
    const node = new Node(data);

    if (this.size === 0) {
      this.head = node;
      node.next = this.head;
      this.tail = node;
    } else {
      this.tail.next = node;
      node.next = this.head;
      this.tail = node;
    }

    this.size++;
    return this.size;
  }

  // 在指定位置添加数据
  insertIndex(index, data) {
    const node = new Node(data);
    if (index < 0 || index > this.size) {
      return false;
    }

    if (index === 0 || index === 1) {
      this.prepend(data);
      return this.size;
    }

    let pre = null;
    let current = this.head;
    for (let i = 1; i < index; i++) {
      pre = current;
      current = current.next;
    }

    pre.next = node;
    node.next = current;
    this.size++;
    return this.size;
  }

  // 在指定位置删除数据
  delIndex(index) {
    if (index < 0 || index > this.size) {
      return false;
    }

    if (index === 0 || index === 1) {
      const val = this.head.data;
      this.tail.next = this.head.next;
      this.head = this.tail.next;

      this.size--;
      return val;
    }

    let pre = null;
    let current = this.head;
    for (let i = 1; i < index; i++) {
      pre = current;
      current = current.next;
    }

    if (current === this.tail) {
      this.tail = pre;
    }

    pre.next = current.next;

    this.size--;
    return current.data;
  }

  // 遍历所有元素
  printAll() {
    if (this.size === 0) {
      return undefined;
    }
    let node = this.head;
    while (node) {
      console.log(node.data);
      node = node.next;

      if (node === this.head) {
        return;
      }

    }
  }
}

// 测试
const singleCycleLink = new SingleCycleLink();

singleCycleLink.append(2);

singleCycleLink.prepend(1);

singleCycleLink.printAll();

console.log("===========");

singleCycleLink.insertIndex(0, 2);

singleCycleLink.insertIndex(3, 4);

singleCycleLink.printAll();

console.log("========");

singleCycleLink.delIndex(1);
singleCycleLink.printAll();

console.log("------");
singleCycleLink.insertIndex(3, 3);
singleCycleLink.printAll();

测试结果:

image.png

双向链表

class Node {
  constructor(data) {
    this.data = data;
    this.pre = null;
    this.next = null;
  }
}

class MyDoubleLink {
  size = 0;
  head = null;
  tail = null;

  // 在末尾添加一个元素
  append(data) {
    const node = new Node(data);
    if (this.size === 0) {
      this.head = node;
      this.tail = node;
    } else {
      this.tail.next = node;
      node.pre = this.tail;
      this.tail = node;
    }
    return ++this.size;
  }

  // 在指定位置添加一个元素
  insertIndex(index, data) {
    if (index < 0 || index > this.size) {
      return false;
    }

    const node = new Node(data);

    if (index === 0 || index === 1) {
      this.head.pre = node;
      node.next = this.head;
      this.head = node;
    } else {
      let pre = null;
      let current = this.head;

      for (let i = 1; i < index; i++) {
        pre = current;
        current = current.next;
      }

      pre.next = node;
      node.pre = pre;
      node.next = current;
      current.pre = node;
    }
    return ++this.size;
  }

  // 找到指定位置的数据
  findIndex(index) {
    if (index < 0 || index > this.size) {
      return undefined;
    }

    let current = this.head;
    for (let i = 1; i < index; i++) {
      current = current.next;
    }

    return current.data;
  }

  // 删除指定位置的数据
  delIndex(index) {
    if (index < 0 || index > this.size) {
      return false;
    }

    if (index === 1 || index === 0) {
      const val = this.head.data;
      this.head = this.head.next;
      this.size--;
      return val;
    }

    let pre = null;
    let current = this.head;

    for (let i = 1; i < index; i++) {
      pre = current;
      current = current.next;
    }

    if ((current = this.tail)) {
      this.tail = pre;
    }

    pre.next = current.next;
    current.next.pre = pre;

    this.size--;

    return current.data;
  }

  // 遍历
  printAll() {
    if (this.size === 0) {
      return undefined;
    }
    let node = this.head;
    while (node) {
      console.log(node.data);
      node = node.next;
    }
  }
}

// 测试
const doubleLink = new MyDoubleLink();

doubleLink.append(1);
doubleLink.insertIndex(0, 0);
doubleLink.printAll();

console.log("-------");

doubleLink.append(2);
doubleLink.printAll();
console.log("----=====");
doubleLink.delIndex(1);

doubleLink.printAll();

console.log(
  "🚀 ~ This is a result of console.log ~ ✨: ",
  doubleLink.findIndex(2)
);

测试结果:

image.png

双向循环链表

class Node {
  constructor(data) {
    this.data = data;
    this.pre = null;
    this.next = null;
  }
}

class MyDoubleCycleLink {
  head = null;
  tail = null;
  size = 0;

  // 末尾添加一个节点
  append(data) {
    const node = new Node(data);
    if (this.size === 0) {
      this.head = node;
      this.tail = node;
      node.next = this.tail;
      node.pre = this.heal;
    } else {
      this.tail.next = node;
      node.pre = this.tail;
      this.tail = node;
    }
    return ++this.size;
  }

  // 在指定位置插入数据
  insertIndex(index, data) {
    if (index < 0 || index > this.size) {
      return false;
    }

    const node = new Node(data);

    if (index === 1 || index === 0) {
      node.next = this.head;
      node.pre = this.tail;
      this.tail.next = node;
      this.head.pre = node;
      this.head = node;
    } else {
      let pre = null;
      let current = this.head;

      for (let i = 1; i < index; i++) {
        pre = current;
        current = current.next;
      }

      pre.next = node;
      node.pre = pre;
      node.next = current;
      current.pre = node;
    }
    return ++this.size;
  }

  // 在指定位置删除数据
  delIndex(index) {
    if (index < 0 || index > this.size) {
      return undefined;
    }

    if (index === 0 || index === 1) {
      const temp = this.head.data;
      this.tail.next = this.head.next;
      this.head.next.pre = this.tail;
      this.head = this.head.next;

      this.size--;
      return temp;
    }

    let pre = null;
    let current = this.head;

    for (let i = 1; i < index; i++) {
      pre = current;
      current = current.next;
    }

    if (current === this.tail) {
      this.tail = pre;
    }

    pre.next = current.next;
    current.next.pre = pre;

    this.size--;

    return current.data;
  }

  // 遍历
  printAll() {
    if (this.size === 0) {
      return undefined;
    }

    let node = this.head;

    while (node) {
      console.log(node.data);
      node = node.next;

      if (node === this.head) {
        return;
      }
    }
  }
}

// 测试

const doubleCycleLink = new MyDoubleCycleLink();

doubleCycleLink.append(0);
doubleCycleLink.append(1);
doubleCycleLink.append(2);
doubleCycleLink.delIndex(0);

doubleCycleLink.printAll();
console.log("------");

doubleCycleLink.insertIndex(0, 0);
doubleCycleLink.printAll();

测试结果:

image.png

1.3 栈

栈是一种先进先出的数据结构,在 js 中可以使用数组来实现,他内置来很多方法可以很容易的模拟出栈这种结构。但是数组他的定义是一段固定长的空间存储一类数据类型的集合,在其他语言中,需要考虑到这个数组长度以及类型等的限制(js中的数组内部作来处理),建议数组栈这种结构使用 java 或则是 c++ 来实现

用链表来实现也比较简单,尤其是双链表(他有指向前驱的指针),我们只要规定好谁是栈顶谁是栈底就行,一开始的时候都在栈底,previous 和 next 都为 null ,注意栈为空的时候他们两个都为 null

浏览器页面的前进后退就是一个很好的栈实现,它会有两个栈 A和B(简单比喻),当打开一个页面时,就一个入栈A,在这页面的基础上再打开就再入栈A,当后退的时候将栈顶的页面出栈,入栈B,前进和后退的逻辑都是互通的

数组栈

// 先进后出的结构问题都可以使用栈这种数据结构来解决

// 数组栈
class Stack {
    size = 0
    arr = []    

    // 入栈
    push(item) {
        if (!item) {
            return 
        }
        this.arr.push(item)   // js中数组可以直接实现入栈的需求
        this.size++     
        return this.size
     }

    //  出栈
    pop(){
        const item = this.arr.pop()
        this.size--
        return item
    }

    getSize(){
        return this.size
    }

    // 遍历
    print(){
        this.arr.forEach(i => console.log(i))
    }
}

const stack = new Stack()
stack.push(1)
stack.push(2)
stack.print()

console.log("当前栈的长度为:", stack.getSize());

console.log(stack.pop())
console.log("----------");

stack.print()

测试结果 image.png

单链表栈

class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class Stack {
  size = 0;
  head = null;
  tail = null;

  // 向栈中压入一个元素
  push(data) {
    const node = new Node(data);
    if (this.size === 0) {
      this.head = node;
      this.tail = node;
    } else if (this.size === 1) {
      node.next = this.tail;
      this.head = node;
    } else {
      node.next = this.head;
      this.head = node;
    }
    return ++this.size;
  }

  // 从栈中取出一个数据
  pop() {
    if (this.size === 0) {
      console.log("当前栈为空");
      return;
    } else if (this.size === 1) {
      const temp = this.head.data;
      this.head = null;
      this.tail = null;
      this.size--;
      return temp;
    } else {
      const temp = this.head.data;
      this.head = this.head.next;
      this.size--;
      return temp;
    }
  }

  // 遍历
  print() {
    if (this.size === 0) {
      console.log("当前的栈为空");
      return;
    }

    let node = this.head;
    while (node) {
      console.log(node.data);
      node = node.next;
    }
  }
}

// 测试
const stack = new Stack();

stack.push(1);
stack.print();
console.log("d----");
stack.push(2);
stack.push(3);
stack.print();
console.log("=======");
stack.pop();
stack.print();
stack.pop();
stack.pop();
console.log("====");
stack.print();

测试结果 image.png

双链表栈

//  双链表栈
class Node {
  constructor(value) {
    this.value = value;
    this.pre = null;
    this.next = null;
  }
}

class DoubleStack {
  head = null;
  tail = null;
  size = 0;

  // 插入一个元素
  insert(val) {
    const node = new Node(val);

    if (this.size === 0) {
      this.head = node;
      this.tail = node;
    } else {
      node.next = this.head;
      node.pre = this.tail;
      this.head = node;
      this.tail.next = this.head;
    }

    this.size++;
  }

  // 取出一个元素
  shift() {
    if (this.size === 0) {
      return "当前栈为空";
    }

    const val = this.head.value;

    if (this.size === 1) {
      this.tail = null;
      this.head = null;
    } else {
      this.head = this.head.next;
      this.tail.next = this.head;
    }

    this.size--;

    return val;
  }

  // 当前栈的程度
  size() {
    return this.size;
  }

  // 遍历链表
  print() {
    if (this.size === 0) {
      return "当前栈为空";
    }

    let node = this.head;

    while (node) {
      console.log(node.value);
      node = node.next;
      if (node === this.head) {
        return;
      }
    }
  }
}

const doubleLinkStack = new DoubleStack();

doubleLinkStack.insert(1);
doubleLinkStack.insert(2);
doubleLinkStack.insert(3);
doubleLinkStack.insert(4);

doubleLinkStack.print();

console.log(
  "🚀 ~ This is a result of console.log ~ ✨: ",
  doubleLinkStack.shift()
);

doubleLinkStack.print();

测试结果:
image.png

1.4 队列

队列是一种先进先出的线性数据结构, 可以使用数组, 链表等来实现

数组队列

class ArrayQueue {
  queue = [];

  // 插入一个数据
  insert(value) {
    this.queue.unshift(value);
  }

  // 取出一个数据
  pop() {
    if (this.queue.length === 0) {
      return "当前队列为空";
    }
    return this.queue.splice(this.queue.length - 1, 1)[0];
  }

  size() {
    return this.queue.length;
  }

  print() {
    this.queue.forEach((item) => {
      console.log(item);
    });
  }
}

const queue = new ArrayQueue();

queue.insert(1);
queue.insert(2);
queue.insert(3);
queue.insert(4);
queue.print();

console.log("🚀 ~ This is a result of console.log ~ ✨: ");

console.log(queue.pop());
console.log(queue.pop());
console.log(queue.pop());
console.log(queue.pop());
console.log(queue.pop());

测试结果: image.png

链表队列

class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class LinkQueue {
  constructor() {
    this.head = null;
    this.tail = null;
    this.size = 0;
  }

  // 入队
  push(data) {
    const node = new Node(data);

    if (this.size === 0) {
      this.head = node;
      this.tail = node;
      this.head.next = this.tail;
    } else {
      this.tail.next = node;
      this.tail = node;
    }

    return ++this.size;
  }

  // 出队
  pop() {
    if (this.size === 0) {
      return "当前队列为空";
    }

    const value = this.tail.data;

    if (this.size === 1) {
      this.head = null;
      this.tail = null;
    } else {
      let current = this.head;
      let next = current.next;

      while (next.next) {
        current = current.next;
        next = current.next;
      }

      this.tail = current;
      this.tail.next = null;
    }

    this.size--;
    return value;
  }

  print() {
    if (this.size === 0) {
      return "当前队列为空";
    }

    let current = this.head;

    while (current) {
      console.log(current.data);
      current = current.next;
    }
  }

  getSize() {
    return this.size;
  }
}

const linkqueue = new LinkQueue();

linkqueue.push(1);
linkqueue.push(2);
linkqueue.push(3);
linkqueue.push(4);

linkqueue.print();

console.log(linkqueue.getSize(), "====");

console.log("🚀 ~ This is a result of console.log ~ ✨: ");

console.log(linkqueue.pop());
console.log(linkqueue.getSize(), "====");

console.log(linkqueue.pop());

console.log(linkqueue.pop());
console.log(linkqueue.pop());

测试结果:

image.png

1.5 散列表(哈希表)

在js中没有哈希表,哈希表是字典一种实现。

  • 区别一:

    • 如果找key对应的value需要遍历key,那么想要省去遍历的过程,用哈希表来表示。
  • 区别二:排列顺序

    • 字典是根据添加的顺序进行排列的
    • 哈希表不是添加的顺序进行排列的
class Hash {
  table = [];

  getHashCode(key) {
    let hash = 0;

    for (let i = 0; i < key.length; i++) {
      hash += key.charCodeAt(i);
    }

    return hash;
  }

  // 存储值
  set(key, value) {
    const hashCode = this.getHashCode(key);

    this.table[hashCode] = value;
  }

  get(key) {
    const hashCode = this.getHashCode(key);
    return this.table[hashCode];
  }

  del(key) {
    const hashCode = this.getHashCode(key);
    const temp = this.table[hashCode];

    delete this.table[hashCode];

    return temp;
  }
}

const hashList = new Hash();

hashList.set("person", "张三");

console.log(hashList.get("person"));
hashList.del("person")
console.log(hashList.get("person"));

测试结果: image.png

1.6 二叉树

class Node {
  constructor(value) {
    this.value = value;
    this.right = null;
    this.left = null;
  }
}

class Tree {
  constructor() {
    this.root = null;
  }

  insert(value) {
    const newNode = new Node(value);
    if (this.root === null) {
      this.root = newNode;
    } else {
      this.insertNode(this.root, newNode);
    }
  }

  insertNode(node, newNode) {
    if (newNode.value > node.value) {
      if (node.left === null) {
        node.left = newNode;
      } else {
        this.insertNode(node.left, newNode);
      }
    } else {
      if (node.right === null) {
        node.right = newNode;
      } else {
        this.insertNode(node.right, newNode);
      }
    }
  }

  // 前序遍历
  preOrderTraversal() {
    const result = [];
    this.preOrderTranversalNode(this.root, result);
    return result;
  }

  preOrderTranversalNode(node, result) {
    if (node === null) {
      return;
    }
    result.push(node.value);
    this.preOrderTranversalNode(node.left, result);
    this.preOrderTranversalNode(node.right, result);
  }

  // 中序遍历
  middleOrder() {
    const result = [];
    this.middleOrderNode(this.root, result);
    return result;
  }

  middleOrderNode(node, result) {
    if (node === null) {
      return;
    }
    this.middleOrderNode(node.left, result);
    result.push(node.value);
    this.middleOrderNode(node.right, result);
  }

  // 后序遍历
  afterOrder() {
    const result = [];
    this.afterOrderNode(this.root, result);
    return result;
  }

  afterOrderNode(node, result) {
    if (node === null) return;
    this.afterOrderNode(node.left, result);
    this.afterOrderNode(node.right, result);
    result.push(node.value);
  }

  // 层序遍历
  leaval() {
    const result = [];
    this.leavalOrder(this.root, result);
    return result;
  }

  leavalOrder(node, result) {
    if (node === null) return;

    const temp = [node];

    while (temp.length > 0) {
      const o = temp.shift();
      result.push(o.value);

      if (o.left) {
        temp.push(o.left);
      }

      if (o.right) {
        temp.push(o.right);
      }
    }
  }

  // 树的最小深度
  minDeep() {
    if (!this.root) return 0;

    const stack = [[this.root, 1]];

    while (stack.length) {
      const [o, n] = stack.shift();

      // 找到第一个度为 0 的节点
      if (!o.left && !o.right) {
        return n;
      }

      o.left && stack.push([o.left, n + 1]);
      o.right && stack.push([o.right, n + 1]);
    }
  }

  // 树的最大深度
  maxDeep() {
    if (!this.root) return 0;

    let sum = 0;
    let len = 0;
    const stack = [this.root];

    while (stack.length) {
      len = stack.length;
      sum++;

      // 只循环当前层级的数量
      while (len--) {
        const o = stack.shift();

        o.left && stack.push(o.left);
        o.right && stack.push(o.right);
      }
    }
    return sum;
  }

  // 翻转二叉树
  invertTreeNode(node) {
    if (node === null) return null;

    const temp = node.left;
    node.left = node.right;
    node.right = temp;

    this.invertTreeNode(node.left);
    this.invertTreeNode(node.right);
    return node;
  }

  // 相同树
  simpleTree(b, p) {
    if (b === null && p === null) return true;

    if (b === null || p === null) return false;

    if (b.value !== p.value) return false;

    return this.simpleTree(b.left, p.left) && this.simpleTree(b.right, p.right);
  }
}

const tree = new Tree();
tree.insert(10);
tree.insert(5);
tree.insert(15);
tree.insert(30);
tree.insert(14);
tree.insert(40);

// const result = tree.preOrderTraversal();
// const result = tree.middleOrder();
// const result = tree.afterOrder();
const result = tree.leaval();

console.log(result);

console.log(tree.minDeep());
console.log(tree.maxDeep());

const p = {
  value: 1,
  right: {
    value: 2,
    right: null,
    left: {
      value: 3,
      right: null,
      left: null,
    },
  },
  left: {
    value: 3,
    right: null,
    left: {
      value: 7,
      right: null,
      left: null,
    },
  },
};

const b = {
  value: 1,
  right: {
    value: 2,
    right: null,
    left: {
      value: 3,
      right: null,
      left: null,
    },
  },
  left: {
    value: 3,
    right: null,
    left: {
      value: 7,
      right: null,
      left: null,
    },
  },
};

console.log(tree.simpleTree(b, p))

测试结果: image.png

1.7 堆

堆是一种二叉树结构, 但是他又使用数组来实现, 关键在于数据的上浮和下沉,以及数据数据节点的对应关系, 比如: 父节点: (i-1)/2 左子节点: 2 * i + 1 右子节点: 2 * i + 2

又有两种常见的表现形式 小顶堆: 每一个父节点都比他的子节点小 大顶堆: 每一个父节点都比他的子节点大

// 小顶堆
class MinHeap {
  constructor() {
    this.heap = [];
  }

  // 获取父结点
  getParentIndex(i) {
    return Math.floor((i - 1) / 2);
  }

  // 获取左孩子
  getLeftIndex(i) {
    return 2 * i + 1;
  }

  // 获取右孩子
  getRightIndex(i) {
    return 2 * i + 2;
  }

  // 交换
  swap(parentIndex, index) {
    const temp = this.heap[parentIndex];
    this.heap[parentIndex] = this.heap[index];
    this.heap[index] = temp;
  }

  // 上浮操作
  up(index) {
    while (index > 0) {
      const parentIndex = this.getParentIndex(index);
      if (this.heap[parentIndex] > this.heap[index]) {
        this.swap(parentIndex, index);
        index = parentIndex;
      } else {
        break;
      }
    }
  }

  // 插入值
  insert(val) {
    this.heap.push(val);
    // 插入后上浮
    this.up(this.heap.length - 1);
  }

  // 下沉操作, 一般在删除堆顶元素后使用
  down(index) {
    const rightIndex = this.getRightIndex(index);
    const leftIndex = this.getLeftIndex(index);

    if (this.heap[leftIndex] < this.heap[index]) {
      this.swap(index, leftIndex);
      // 当前节点的值继续下沉
      this.down(leftIndex);
    }

    if (this.heap[index] > this.heap[rightIndex]) {
      this.swap(index, rightIndex);
      this.down(rightIndex);
    }
  }

  // 删除堆顶元素
  pop() {
    this.heap[0] = this.heap.pop();
    this.down(0);
  }

  // 获取堆顶元素
  peek() {
    return this.heap[0];
  }

  size() {
    return this.heap.length;
  }
}

let arr = new MinHeap();
arr.insert(5);
arr.insert(4);
arr.insert(6);
arr.insert(1);
// arr.pop();
console.log(arr);
console.log(arr.size());
console.log(arr.peek());

测试结果: image.png

1.8 跳表

1.9 图

1.10 Trie树