leetcode刷题01--数据结构与算法前置知识必知必会

261 阅读10分钟

八大数据结构

前言
数据结构是一种具有一定的逻辑关系,在计算机中应用某种存储结构,并且封装了相应操作的数据元素集合。它包含三方面的内容,逻辑关系、存储关系以及操作。
常见的数据结构如下

栈的描述

栈是一种运算受限的线性表,限定仅在表尾插入和删除操作,是一种先入后出的数据结构

image.png

栈的实现

想要用js实现栈的数据结构,需要保证实现栈具备以下的基础功能

  • push 新增元素
  • pop 删除栈顶元素
  • peek 返回栈顶的元素
  • clear清空栈
  • size 栈的大小,也就是元素的个数
  • isEmpty栈是否为空

js实现栈通常有两种方式
1、用数组模拟实现

// 用数组来模拟创建栈
const stack = [];
// 入栈
stack.push(0);
// 出栈
stack.pop();
// 返回栈顶元素
const peekValue = stack.pop();
// 清空栈
stack.length = 0;
// 获取栈的大小,判断栈是否为空
stack.length

2、用class实现

class Stack {
    constructor () {
        this.items = [];
    }
    // 新增
    push(el) {
        this.items.push(el);
    }
    // 删除栈顶元素
    pop () {
        this.items.pop();
    }
    ......
}

队列

对列的描述

队列和栈类似,也是一种特殊的线性表,和栈不同的是,队列值允许在表的一端进行插入操作,另外一端进行删除操作。 如图:队列的存储结构

image.png

队列的实现

队列的常用方法

  1. enqueue(element):将元素添加到队列的末尾。
  2. dequeue():移除并返回队列的第一个元素。
  3. front():返回队列的第一个元素,但不对队列进行修改。
  4. isEmpty():检查队列是否为空,如果为空则返回true,否则返回false。
  5. size():返回队列中元素的个数。
  6. clear():清空队列中的所有元素。 同样可以用js 模拟或者
class Queue {
  constructor() {
    this.items = [];
  }

  // 入队
  enqueue(element) {
    this.items.push(element);
  }

  // 出队
  dequeue() {
    if (this.isEmpty()) {
      return "队列已空";
    }
    return this.items.shift();
  }

  // 查看队头元素
  front() {
    if (this.isEmpty()) {
      return "队列已空";
    }
    return this.items[0];
  }

  // 检查队列是否为空
  isEmpty() {
    return this.items.length === 0;
  }

  // 获取队列长度
  size() {
    return this.items.length;
  }

  // 清空队列
  clear() {
    this.items = [];
  }

  // 打印队列元素
  print() {
    console.log(this.items.toString());
  }
}

// 使用示例
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
console.log(queue.front()); // 输出 1
console.log(queue.size()); // 输出 3
console.log(queue.isEmpty()); // 输出 false
queue.dequeue();
queue.print(); // 输出 2, 3
queue.clear();
console.log(queue.isEmpty()); // 输出 true

数组

数组的描述

数组是一种聚合数据类型,它是将具有相同类型的若干变量有序地组织在一起的集合。

在js中,数组又有三种数组类型
1、普通数组,构造函数为Array
2、类数组(Array-like)是一种类似于数组的对象, 具有索引和length属性,例如 arguments对象,document.querySelectorAll查询到的dom集合,
3、类型化数组(Typed-Array),是Javascript中一种特殊的数组对象,它允许直接操作二进制数据,基于底层Array Buffer对象构建

链表

链表的描述

链表是一种数据元素按照链式存储结构进行存储的数据结构,这种存储结构具有在物理上存在非连续的特点。
链表分为,单链表、双链表、循环链表

image.png

链表的实现

// 定义节点类
class Node {
  constructor(value) {
    this.value = value;
    this.next = null;
  }
}

// 定义链表类
class LinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // 在链表尾部添加新节点
  append(value) {
    const newNode = new Node(value);
    if (this.head === null) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      this.tail.next = newNode;
      this.tail = newNode;
    }
    this.length++;
  }

  // 在链表指定位置插入新节点
  insert(position, value) {
    if (position < 0 || position > this.length) {
      return false;
    }
    const newNode = new Node(value);
    if (position === 0) {
      newNode.next = this.head;
      this.head = newNode;
      if (this.length === 0) {
        this.tail = newNode;
      }
    } else if (position === this.length) {
      this.tail.next = newNode;
      this.tail = newNode;
    } else {
      let current = this.head;
      let previous = null;
      let index = 0;
      while (index < position) {
        previous = current;
        current = current.next;
        index++;
      }
      previous.next = newNode;
      newNode.next = current;
    }
    this.length++;
    return true;
  }

  // 从链表中移除指定位置的节点
  removeAt(position) {
    if (position < 0 || position >= this.length) {
      return null;
    }
    let current = this.head;
    if (position === 0) {
      this.head = current.next;
      if (this.length === 1) {
        this.tail = null;
      }
    } else {
      let previous = null;
      let index = 0;
      while (index < position) {
        previous = current;
        current = current.next;
        index++;
      }
      previous.next = current.next;
      if (position === this.length - 1) {
        this.tail = previous;
      }
    }
    this.length--;
    return current.value;
  }

  // 返回链表中指定位置的节点值
  get(position) {
    if (position < 0 || position >= this.length) {
      return null;
    }
    let current = this.head;
    let index = 0;
    while (index < position) {
      current = current.next;
      index++;
    }
    return current.value;
  }

  // 返回链表中指定值的第一个节点位置
  indexOf(value) {
    let current = this.head;
    let index = 0;
    while (current) {
      if (current.value === value) {
        return index;
      }
      current = current.next;
      index++;
    }
    return -1;
  }

  // 返回链表是否为空
  isEmpty() {
    return this.length === 0;
  }

  // 返回链表的长度
  size() {
    return this.length;
  }

  // 清空链表
  clear() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // 将链表转换为数组
  toArray() {
    const result = [];
    let current = this.head;
    while (current) {
      result.push(current.value);
      current = current.next;
    }
    return result;
  }

  // 将链表转换为字符串
  toString() {
    return this.toArray().toString();
  }
}

树的描述

树结构是一种非线性存储结构,存储的是具有“一对多”关系的数据元素的集合 树的种类

  • 二叉树,树的任意节点的子树 <= 2
  • 满二叉树,叶子节点都在同一层,并且除了叶子节点之外所有的节点,都有两个子节点
  • 完全二叉树,对于一颗完全二叉树,深度为d(d > 1),除了第d层,所有的节点构成满二叉树,且第d层所有的节点连续的紧密排列
  • 哈夫曼树,也称为最优二叉树,是一种特殊的二叉树,带权路径最短的二叉树。
  • 二叉查找树,(二叉搜索树、二叉排序树、BST),若任意节点的子树不空,则左子树小与根节点,若任意节点的右子树不为空,则右子树大于根节点的值
  • AVL高度平衡树,任意节点子树的最大差别为1的二叉搜索树。增加或者删除可能需要通过一次或者多次树旋转来重新平衡这个树。
  • 红黑树,红黑树是每个节点,都带有颜色的二叉查找树,特征1,节点是红色或者黑色,特征2,根节点是黑色,特征3,每个红色节点的两个字节点都是黑色,每个叶子到跟的所有路径不能有两个连续的红色节点。特征4,从任意节点到每个叶子路径都包含相同数目的黑色节点
  • B树,一颗m阶B树是一颗平衡的m路搜索树,查找、插入、删除的操作性能都能保持在logn
  • 字典树,tire树,是一种树形结构,根节点不包含字符,除根节点之外,每个节点值包含一个字符,从跟节点到某一节点,路径链接起来,为改节点对应的字符串,每个节点所有的子节点包含的字符都不相同

图是一种非线性的数据结构,由节点(顶点)和连接节点的边组成。图可以用来表示各种实际问题中的关系和连接。

图的基本组成部分包括:

  1. 节点(顶点):图中的每个元素称为节点或顶点。节点可以表示实体、对象或抽象概念。
  2. 边:节点之间的连接称为边。边可以是有向的(箭头指向一个方向)或无向的(没有箭头)。
  3. 权重:边可以带有权重,表示节点之间的关联程度或距离。

图可以分为有向图和无向图:

  1. 有向图:边有方向,表示从一个节点到另一个节点的单向连接。
  2. 无向图:边没有方向,表示节点之间的双向连接。

图的常见应用包括:

  1. 社交网络:用图来表示人与人之间的关系,例如朋友关系、关注关系等。
  2. 地图导航:用图来表示地点之间的道路或路径,以便进行路线规划和导航。
  3. 网络拓扑:用图来表示计算机网络中的设备和连接,以便进行网络管理和故障排查。
  4. 排课表:用图来表示学校的课程和学生之间的关系,以便进行排课和课程安排。
  5. 知识图谱:用图来表示知识之间的关联和层次结构,以便进行知识管理和推荐。

图的算法包括:

  1. 深度优先搜索(DFS):从一个节点开始,尽可能深地访问图的节点,直到无法继续深入为止。
  2. 广度优先搜索(BFS):从一个节点开始,逐层地访问图的节点,先访问离起始节点最近的节点。
  3. 最短路径算法:计算图中两个节点之间的最短路径,例如 Dijkstra 算法和 Floyd-Warshall 算法。
  4. 最小生成树算法:找到连接图中所有节点的最小成本的子图,例如 Prim 算法和 Kruskal 算法。
  5. 拓扑排序:对有向无环图进行排序,使得每个节点的前驱节点都排在它的后面。
  6. 强连通分量:将有向图分解为强连通分量,其中每个分量内的节点可以互相到达。

堆(Heap)是一种特殊的树形数据结构,一般讨论的都是二叉堆,它满足以下两个条件:

  1. 堆是一个完全二叉树:除了最后一层,其他层的节点都是满的,最后一层的节点从左到右排列。
  2. 堆中每个节点的值都大于等于(或小于等于)其子节点的值:这个特性称为堆序性(Heap Property)。

根据堆序性的不同,堆可以分为两种类型:

  1. 最大堆(Max Heap):堆中每个节点的值都大于等于其子节点的值。也就是说,堆中的最大值位于根节点。
  2. 最小堆(Min Heap):堆中每个节点的值都小于等于其子节点的值。也就是说,堆中的最小值位于根节点。

堆的主要操作包括:

  1. 插入(Insertion):将一个新元素插入到堆中,保持堆序性。
  2. 删除根节点(Deletion):删除堆中的根节点,并重新调整堆,保持堆序性。
  3. 查找根节点(Find-Min/Max):返回堆中的根节点,即最小值或最大值。
  4. 修改节点(Heapify):修改堆中的某个节点的值,并重新调整堆,保持堆序性。

堆的应用包括:

  1. 堆排序(Heap Sort):利用堆的特性进行排序,时间复杂度为O(nlogn)。
  2. 优先队列(Priority Queue):利用堆的特性实现高效的插入和删除操作,用于处理具有优先级的任务。
  3. 哈夫曼编码(Huffman Coding):利用堆构建哈夫曼树,用于数据压缩。
  4. 最短路径算法(Dijkstra Algorithm):利用堆实现Dijkstra算法,用于求解最短路径问题。

散列表

散列表(Hash Table),也称为哈希表,是一种根据关键字直接访问数据的数据结构。它通过将关键字映射到一个固定大小的数组中来实现快速的查找、插入和删除操作。

散列表的核心思想是使用哈希函数(Hash Function)将关键字映射到数组的索引位置。哈希函数将关键字转换为一个整数,然后通过取余运算将其映射到数组中的某个位置。这个位置称为哈希值(Hash Value)或散列值(Hash Code)。

散列表的主要操作包括:

  1. 插入(Insertion):将一个键值对(Key-Value)插入到散列表中。首先通过哈希函数计算关键字的哈希值,然后将键值对存储在对应的数组位置中。如果发生哈希冲突(两个关键字映射到同一个位置),则需要解决冲突。
  2. 查找(Search):根据给定的关键字在散列表中查找对应的值。通过哈希函数计算关键字的哈希值,然后在对应的数组位置中查找值。如果发生哈希冲突,需要继续查找直到找到对应的值或者确定值不存在。
  3. 删除(Deletion):根据给定的关键字从散列表中删除对应的键值对。通过哈希函数计算关键字的哈希值,然后在对应的数组位置中删除键值对。如果发生哈希冲突,需要继续查找并删除对应的键值对。

解决哈希冲突的方法包括:

  1. 链地址法(Chaining):将哈希冲突的键值对存储在同一个位置的链表中。
  2. 开放地址法(Open Addressing):在发生哈希冲突时,通过一定的探测序列(如线性探测、二次探测等)在散列表中找到下一个可用的位置。

散列表的优点是能够实现快速的查找、插入和删除操作,平均时间复杂度为O(1)。然而,散列表的性能取决于哈希函数的设计和冲突解决方法的选择。较差的哈希函数或冲突解决方法可能导致较高的冲突率,从而降低散列表的性能。

散列表在实际应用中有广泛的应用,例如字典、数据库索引、缓存等。了解散列表的原理和操作对于高效地处理大量数据非常重要。

算法的度量衡-时间复杂度和空间复杂度

常见的时间空间复杂度有

  1. 常数时间复杂度(O(1)):无论输入规模大小,算法的执行时间都是固定的。
  2. 线性时间复杂度(O(n)):算法的执行时间与输入规模成线性关系。
  3. 对数时间复杂度(O(log n)):算法的执行时间与输入规模的对数成关系。
  4. 平方时间复杂度(O(n^2)):算法的执行时间与输入规模的平方成关系。
  5. 指数时间复杂度(O(2^n)):算法的执行时间与输入规模的指数成关系。