前端数据结构与算法

前端数据结构与算法

文章宝典

链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以快速删除和插入节点,只用修改节点的引用
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

实例

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

队列

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实例

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

并且左节点的值和后续节点的值都要小于等于该节点的值
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

根据图的节点之间的边是否有方向,可以分为有向图和无向图。

在这里插入图片描述

在这里插入图片描述
在有向图中,访问节点只能按照指定的方向进行。
在这里插入图片描述
在无向图中,可以以任意方向访问节点,因此它也容易构成环。
在这里插入图片描述

根据图的节点是否都可以遍历到,还分为连通图和非连通图(或叫作隔离图):
在这里插入图片描述
连通图中的每个节点都有连接的边。
在这里插入图片描述
非连通图则是由多个独立的图构成,它们之间没有边连接。
在这里插入图片描述
另外,如果连接节点的边还包含有额外信息,例如长度,那么这种图称为加权图。
在这里插入图片描述
使用代码表示图有多种方式,常见的有:
使用邻接矩阵。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用邻接节点数组
在这里插入图片描述

二叉树的遍历

遍历二叉树是指从根节点开始,访间树中所有的节点。它是其它一些算法的基础,例如深度优先搜索。
遍历二叉树是指从根节点开始,访间树中所有的节点。它是其它一些算法的基础,例如深度优先搜索。

对于二叉树、每个节点有左右两个子节点,对于是先访问左子节点、还是当前节点,或是右子节点,可以把遍历分为前序中序和后序三种。接下来我们分别看一下这三种形式。

先看前序遍历,前序遍历会先访问当前节点的值,之后访问左子节点或右子节点,这里假定先访问左子节点,然后再访问右子节点,对于每个子节点都是做同样的操作。

中序遍历则是先访问左子节点,再访问当前节点,最后访问右子节点,对于每个子节点也是重复这个过程。

后序遍历则是先访问左子节点的值,再访问右子节点的值,最后访问当前节点的值。对于每个子节点也是同样的操作。

二叉树遍历的代码实现,最简单直观的方法就是使用递归。

对于前序遍历,我们先打印出当前节点的值,然后递归的调用自己,传递左节点,再调用自己传递右节点。当子节点为 null 时退出递归。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
二叉树的前序遍历

function Node(value) {
    this.value = value;
    this.leftChild = null;
    this.rightChild = null;
}

var a = new Node("a");
var b = new Node("b");
var c = new Node("c");
var d = new Node("d");
var e = new Node("e");
var f = new Node("f");
var g = new Node("g");
a.leftChild = c;
a.rightChild = b;
c.leftChild = f;
c.rightChild = g;
b.leftChild = d;
b.rightChild = e;

function qianxubianli(Node) {
    if (Node === null) return;
    console.log(Node.value)
    qianxubianli(Node.leftChild)
    qianxubianli(Node.rightChild)
}
qianxubianli(a)

中序遍历
前面相同的Node设置就不重复写了。

function zhongxubianli(Node) {
    if (Node === null) return;
    zhongxubianli(Node.leftChild);
    console.log(Node.value);
    zhongxubianli(Node.rightChild)
}

zhongxubianli(a)

后序遍历

function houxubianli(Node) {
    if (Node === null) return;
    houxubianli(Node.leftChild);
    houxubianli(Node.rightChild)
    console.log(Node.value);
}

houxubianli(a)

根据前序遍历、中序遍历获得二叉树

function Node(value) {
    this.value = value;
    this.leftChild = null;
    this.rightChild = null;
}
const qianxu = ["a", "c", "f", "g", "b", "d", "e"];
const zhongxu = ["f", "c", "g", "a", "d", "b", "e"];

function fn(qianxu, zhongxu) {
    if (qianxu == null || zhongxu == null || qianxu.length == 0 || zhongxu.length == 0 || qianxu.length !== zhongxu.length) return;
    var root = new Node(qianxu[0])
    var index = zhongxu.indexOf(root.value);
    var qianxuLeft = qianxu.slice(1, index + 1);
    var qianxuRight = qianxu.slice(index + 1, qianxu.length);
    var zhongxuLeft = zhongxu.slice(0, index);
    var zhongxuRight = zhongxu.slice(index + 1, zhongxu.length);
    root.leftChild = fn(qianxuLeft, zhongxuLeft);
    root.rightChild = fn(qianxuRight, zhongxuRight);
    return root;
}
var root = fn(qianxu, zhongxu);
console.log(root.leftChild)
console.log(root.rightChild)

根据中序遍历、后序遍历获得二叉树

function Node(value) {
    this.value = value;
    this.leftChild = null;
    this.rightChild = null;
}
const zhongxu = ["f", "c", "g", "a", "d", "b", "e"];
const houxu = ["f", "g", "c", "d", "e", "b", "a"];

function fn(houxu, zhongxu) {
    if (houxu == null || zhongxu == null || houxu.length == 0 || zhongxu.length == 0 || houxu.length !== zhongxu.length) return;
    var root = new Node(houxu[houxu.length - 1]);
    var index = zhongxu.indexOf(root.value);
    var houLeft = houxu.slice(0, index);
    var houRight = houxu.slice(index, houxu.length - 1);
    var zhongLeft = zhongxu.slice(0, index);
    var zhongRight = zhongxu.slice(index + 1, zhongxu.length);
    root.leftChild = fn(houLeft, zhongLeft);
    root.rightChild = fn(houRight, zhongRight);
    return root;
}
var root = fn(houxu, zhongxu);
console.log(root.leftChild)
console.log(root.rightChild)

前序遍历

在这里插入图片描述
压入调用栈—执行完代码开始出栈
在这里插入图片描述


代码:

调用 traversePreOrder() 遍历 root | [traversePreOrder(13)]
第一行先打印出根节点的值 13| [traversePreOrder(13)]
接下来,执行到遍历左节点的方法 traversePreOrder(6),放入调用栈并执行,打印出 6,此时因为遍历左节点的方法还未 return,所以遍历右子节点的函数调用还不能执行 | 调用栈:[traversePreOrder(6), traversePreOrder(13)]。
接着,递归调用 traversePreOrder() 遍历 6 的左子节点,函数压入调用栈并执行,打印出 3 | 调用栈:[traversePreOrder(3), traversePreOrder(6), traversePreOrder(13) ]。
接着递归调用 traversePreOrder(treeNode.left) 遍历 3 的左子节点|调用栈:[traversePreOrder(null),traversePreOrder(3), traversePreOrder(6) , traversePreOrder(13)]
此时,执行 traversePreOrder(null),treeNode 为 null,函数执行到 if 后,直接 return|调用栈:[traversePreOrder(3), traversePreOrder(6) , traversePreOrder(13)]
那么接下来就从调用栈拿出最顶部的函数 traversePreOrder(3),继续执行,调用 traversePreOrder(treeNode.right) |调用栈:[traversePreOrder(null), traversePreOrder(3), traversePreOrder(6) , traversePreOrder(13)]
treeNode.right 也是 null,所以函数直接返回 |调用栈:[ traversePreOrder(3), traversePreOrder(6) , traversePreOrder(13)]。
接着继续执行 traversePreOrder(3),后面没有代码了,所以 traversePreOrder(3) 函数返回。| [ traversePreOrder(6) , traversePreOrder(13)]
接着执行 traversePreOrder(6),调用它里边的 traversePreOrder(treeNode.right) | [traversePreOrder(9), traversePreOrder(6) , traversePreOrder(13)]。
执行打印出 9 | 调用栈 [traversePreOrder(9), traversePreOrder(6) , traversePreOrder(13)]。
接着调用 9 里的 traversePreOrder(treeNode.left) | [traversePreOrder(7), traversePreOrder(9), traversePreOrder(6) , traversePreOrder(13)]。
执行打印出 7。后面它没有子节点,所以它里边的 traversePreOrder(treeNode.left)traversePreOrder(treeNode.right) 直接返回,这里就不演示,它本身到这里也执行结束了。| [traversePreOrder(9), traversePreOrder(6) , traversePreOrder(13)]。
又回到节点 9,它没有右节点,所以也执行结束并返回。| [traversePreOrder(6) , traversePreOrder(13)]。
到了 traversePreOrder(6),它的代码也执行完毕了,返回。| [ traversePreOrder(13)]
现在开始继续执行 13 根节点的 traversePreOrder(treeNode.right) 方法了,这里边的执行顺序和之前一样,我们简单过一下。
调用 traversePreOrder(20) 打印出 20 | [traversePreOrder(20), traversePreOrder(13)]。
遍历左节点并打印出 15| [traversePreOrder(15), traversePreOrder(20), traversePreOrder(13)]15 没有子节点直接返回,遍历 20 的右节点,打印出 28| [traversePreOrder(28), traversePreOrder(20), traversePreOrder(13)]28 没有左子节点,遍历右子节点,打印出 32 并返回。| [traversePreOrder(32), traversePreOrder(20), traversePreOrder(13)]20 节点遍历完毕并返回。| [traversePreOrder(13)]13 根节点遍历完毕并返回 | []。

此时,前序遍历就完成了,结果是:
13  6  3  9  7  20  15 28  32

在这里插入图片描述

中序遍历

在这里插入图片描述
在这里插入图片描述

后续遍历

在这里插入图片描述
在这里插入图片描述

O 表示法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
大 O 表示法是粗略的估算算法的效率,会直接忽略常数,例如我们使用平行的两个 for 循环,遍历两次数组,可能直接得出时间复杂度为 O(2N),但是最终我们还是表示为 O(N):
在这里插入图片描述
对于无关输入大小,只执行固定行数的代码,无论是 1 行、5 行还是 10 行,都可以认为是 O(1):
在这里插入图片描述
对于算法的评价,会分为最佳情况、平均情况和最坏情况,因为有的算法会根据输入数据的不同,会有不同的时间复杂度,大 O 表示法通常表示的是最坏情况。

在这里插入图片描述

对于空间复杂度,大 O 表示法表示的是算法在执行时,需要额外占用的内存空间,例如对于冒泡排序,它不需要额外创建存储空间,而是就地对原数组进行排序,所以它的空间复杂度是 O(1):
在这里插入图片描述

常见算法的时间复杂度

下面列出一些常见算法的时间复杂度:

数组的访问:O(1)
链表的插入和删除:O(1)
数组的查找:O(N)
折半查找:O(logN),因为每次查找都会少 N / 2 数据。
冒泡排序、选择排序、插入排序、快速排序:O(N2)
归并排序:O(NlogN)
有些算法的时间复杂度并不稳定,例如插入排序、快速排序等。它们会根据原始输入的数组是否已经整体有序,所表现出来的时间复杂度也不相同。例如插入排序在最好情况下的时间复杂度是 O(n)。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

树的广度优先遍历

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

归并排序

##

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

动态规划 Dynamic Programming DP

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作为前端开发者,掌握数据结构和算法是非常重要的。这可以帮助您更好地设计和实现前端应用程序,提高代码的效率和性能。以下是一些建议,可以帮助前端开发者掌握数据结构和算法: 1. 学习基础知识:首先需要了解基本的数据结构和算法,例如数组、链表、栈、队列、哈希表、排序算法、查找算法等等。可以通过阅读相关的书籍或在线教程来学习这些知识。 2. 刷题:练习是掌握数据结构和算法的最好方法。可以刷LeetCode、Hackerrank等在线刷题网站上的算法题目。逐渐地,您可以尝试解决更复杂的问题,并更好地理解算法的实现和应用。 3. 实践项目:在实际项目中,可以运用已掌握的数据结构和算法。例如,可以使用二叉树来构建导航栏,使用哈希表来处理用户输入,使用动态规划来解决复杂的问题等等。 4. 学习前端框架:学习流行的前端框架(如React,Vue等),它们提供了大量的优化技术和工具,可以帮助您更好地处理数据结构和算法方面的问题。 5. 加入社区:参与在线社区、论坛或技术博客,了解前端开发者如何使用数据结构和算法来解决实际问题。这也可以帮助您了解当前前端开发领域的最新趋势和技术。 总之,掌握数据结构和算法需要坚持学习和不断练习。只有通过不断学习和实践,才能不断提高自己的技能和水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值