数据结构与算法-前端

1. 时间空间复杂度

  1. 时间复杂度
  • 用大O表示,如O(1)、O(n)…
  • 定性描述算法的运行时间 在这里插入图片描述
  1. 空间复杂度
    运行过程中临时占用存储空间大小的量度

2. 栈:后进先出的结构 stack

push、pop
在这里插入图片描述

  • 有效的括号: https://leetcode-cn.com/problems/valid-parentheses
var isValid = function (s) {
    if (s.length % 2 === 1) { return false } // 奇数个
    const stack = []
    const map = new Map()
    map.set('{', '}')
    map.set('(', ')')
    map.set('[', ']')
    for (let i = 0; i < s.length; i++) {
        const item = s[i]
        if (map.has(item)) {
            stack.push(item)
        } else {
            const p = stack[stack.length - 1]
            if (map.get(p) === item) {
                stack.pop() // 匹配上的就剔除了
            } else {
                return false
            }
        }
    }
	//    如果最后栈为空,那么它是有效的括号
    return stack.length === 0 // 都是单边的情况
};

3. 队列:先进先出的结构 queue

push、shift

  • 写一个 RecentCounter 类来计算特定时间范围内最近的请求: https://leetcode-cn.com/problems/number-of-recent-calls
var RecentCounter = function () {
    this.q = []
};

/** 
 * @param {number} t
 * @return {number}
 */
RecentCounter.prototype.ping = function (t) {
    this.q.push(t)
    while (this.q[0] < t - 3000) {
        this.q.shift()
    }
    return this.q.length
};

4. 链表 link

元素存储不连续,用next指针连接。
237. 删除链表中的节点
206. 反转链表
2. 两数相加
83. 删除排序链表中的重复元素
141. 环形链表

实现一个instanceof查找实例是否在原型链上:

// params:A: 实例; B:构造函数
const instanceOf = (AB) => {
  let p = A
  while (p) {
    // 原型链上的实例是否指向构造函数
    if (p === B.prototype) {
      return true
    }
    p = p.__proto__ // 沿着原型链查找
  }
  return false
}

5. 集合 Set

无序且唯一的数据结构。

  • 两个数组的交集:https://leetcode-cn.com/problems/intersection-of-two-arrays
/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 * 时间复杂度:O(m * n);空间复杂度:O(m)。m为nums1的长度
 */
var intersection = function (nums1, nums2) {
	// 交集:用集合对num1去重;筛选出num2也包含的值。
    return [...new Set(nums1)].filter(item => nums2.includes(item))
};

// 集合的一些操作方法
const s = new Set();
s.add(1); // 添加
s.has(1); // 值是否存在
s.delete(1); // 删除

6. 字典 Map

349. 两个数组的交集
20. 有效的括号
1. 两数之和
3. 无重复字符的最长子串
76. 最小覆盖子串
存储唯一值的数据结构。

const m = new Map()
m.set("a",1) // 增
m.get("a") // 查
s.has("a"); // 值是否存在
m.delete("a") // 删

两数之和:https://leetcode-cn.com/problems/two-sum/

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 * 时间复杂度:O(n),空间复杂度:O(n)
 */
var twoSum = function (nums, target) {
    const map = new Map()
    for (let i = 0; i < nums.length; i++) {
        const n = nums[i]
        const n1 = target - n
        if (map.has(n1)) {
            return [map.get(n1), i]
        } else {
            map.set(n, i)
        }
    }
};

无重复字符的最长子串: https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/

/**
 * @param {string} s
 * @return {number}
 * 时间复杂度 O(n) 空间复杂度O(m) m为最长无重复字符
 * 双指针移动窗口确定字符串
 */
var lengthOfLongestSubstring = function (s) {
    const map = new Map()
    let l = 0 // 左指针
    let res = 0 // 最大值
    for (let r = 0; r < s.length; r++) { // 右指针移动
        //  当前值 需要在滑动窗口中 
        if (map.has(s[r]) && map.get(s[r]) >= l) {
            // 有值,说明重复了需要移动左指针到新的字符
            l = map.get(s[r]) + 1
        }
        res = Math.max(res, r - l + 1) // 最大值为当前窗口值,或者之前的最大值
        map.set(s[r], r) // 存储当前字符的下标
    }
    return res
};

7. 树 tree

分层数据结构。


const tree = {
  val: "a",
  children: [
    {
      val: "b",
      children: [
        { val: "d", children: [] },
        { val: "e", children: [] },
      ]
    },
    {
      val: "c",
      children: [
        { val: "f", children: [] },
        { val: "g", children: [] },
      ]
    },
  ]
}
  • 深度优先遍历:尽可能深的搜索树的分支。
const dfs = (root) => {
  console.log(root.val); // 访问根节点
  root.children.forEach(item => dfs(item)); // 对children逐个递归
}

dfs(tree) // a b d e c f g
  • 广度优先遍历:先访问离根节点最近的节点。
const bfs = (root) => {
  const q = [root] // 队列
  while (q.length > 0) {
    const n = q.shift() // 出队
    console.log(n.val); // 访问
    n.children.forEach((item) => { // 对children推到队列中
      q.push(item)
    })
  }
}

bfs(tree) // a b c d e f g

二叉树:树中每个节点最多只能有两个子节点。

/**
 * @param {TreeNode} root
 * @return {number}
 * 时间复杂度 O(n),空间复杂度 O(log n) 到 O(n)
 */
var maxDepth = function (root) {
    let res = 0; // 记录最大深度
    const dfs = (n, l) => {
        // l: 深度层级
        if (!n) return
        // console.log(n.val, l)
        if (!n.left && !n.right) { // 没有左节点跟右节点就是最深的节点
            res = Math.max(res, l) // 最大深度
        }
        dfs(n.left, l + 1)
        dfs(n.right, l + 1)
    }
    dfs(root, 1)
    return res
};

111. 二叉树的最小深度: 广度优先遍历

/**
 * @param {TreeNode} root
 * @return {number}
 * 时间复杂度 O(n) 空间复杂度 O(n)
 */
var minDepth = function (root) {
    if (!root) return 0
    const q = [[root, 1]]
    // 广度优先遍历,并记录节点层级
    while (q.length) {
        const [n, l] = q.shift()
        // console.log(n.val, l)
        if (!n.left && !n.right) {
            return l
        }
        if (n.left) q.push([n.left, l + 1])
        if (n.right) q.push([n.right, l + 1])
    }

    // 遇到叶子节点,返回节点层级,停止遍历

};

102. 二叉树的层序遍历:广度优先遍历

/**
 * @param {TreeNode} root
 * @return {number[][]}
 * 时间复杂度O(n) 空间复杂度O(n)
 */
var levelOrder = function (root) {
    if (!root) return []
    const q = [root]
    const res = []
    while (q.length) {
        let len = q.length
        res.push([])
        while (len--) {
            const n = q.shift()
            console.log(n.val)
            res[res.length - 1].push(n.val)  // 队列中都是同一个层级的
            if (n.left) q.push(n.left)
            if (n.right) q.push(n.right)
        }

    }
    return res
};

94. 二叉树中序遍历

/**
 * @param {TreeNode} root
 * @return {number[]}
 * 中序遍历:左 -> 根 -> 右
 */
//  递归
// var inorderTraversal = function(root) {
//     const res = []
//     const rec = (n)=>{
//         if(!n) return 
//         rec(n.left)
//         res.push(n.val)
//         rec(n.right)
//     }
//     rec(root)
//     return res
// };
// 非递归:栈实现; 时间复杂度、空间复杂度都是O(n)
var inorderTraversal = function (root) {
    const res = []
    const stack = []
    let p = root
    while (stack.length || p) {
        // 先把左节点都入栈
        while (p) {
            stack.push(p)
            p = p.left
        }

        const n = stack.pop()
        res.push(n.val)
        p = n.right
    }
    return res
};

遍历JSON的所有字典值

const json = {
  a: { b: { c: 1 } },
  d: [1, 2]
}

const bfs = (n, path) => {
  console.log(n, path); // 节点值,路径
  Object.keys(n).forEach((item) => {
    bfs(n[item], path.concat(item))
  })
}

bfs(json, [])

8. 图 graph

图:网络结构的抽象模型;可以表示任何二元关系,比如道路、航班…

数据:

const graph = {
    0: [1, 2],
    1: [2],
    2: [0, 3],
    3: [3]
}
  1. 深度优先遍历:
// 深度优先遍历
const visited = new Set()
const dfs = (n)=>{
    console.log(n)
    visited.add(n)
    graph[n].forEach(c => {
        if (!visited.has(c)) { // 需要遍历不是重复的
            dfs(c)
        }
    });
}

dfs(2) // 2 0 1 3
  1. 广度优先遍历:
// 广度优先遍历
const visited = new Set()
visited.add(2)
const q = [2]
while (q.length) {
    const n = q.shift()
    console.log(n);
    graph[n].forEach(c => {
        if (!visited.has(c)) { // 需要遍历不是重复的
            q.push(c)
            visited.add(c) // 防止队列中出现重复的元素
        }
    });
}
// 2 0 3 1

9. 堆 heap

堆是一种特殊的完全二叉树
特点: 所有节点都大于等于(最大堆)或小于等于(最小堆)它的子节点。
js中: 用数组表示。左侧子节点位置:2index + 1,右侧:2index+2,父节点位置(index-1)/ 2。
应用: 快速找出最大值、最小值。

10. 排序和搜索

排序算法动图演示

  1. 排序
  • 冒泡排序:比较所有相邻元素,如果第一个比第二个大,则交换他们。
    时间复杂度:O(n^2)
const arr = [5, 4, 3, 2, 1]
for (let i = 0; i < arr.length - 1; i++) {
    for (let j = 0; j < arr.length - 1 - i; j++) {
        // 从小到大排序
        if (arr[j] > arr[j + 1]) {
            const temp = arr[j];
            arr[j] = arr[j + 1]
            arr[j + 1] = temp
        }
    }
}
console.log(arr); // [1, 2, 3, 4, 5]
  • 选择排序
    找到数组中的最小值,选中它并将其放在第一位;第二小值,放第二位。
// 选择排序
const arr = [5, 4, 3, 2, 1]
for (let i = 0; i < arr.length - 1; i++) {
    let indexMin = i
    for (let j = i; j < arr.length; j++) {
        if (arr[j] < arr[indexMin]) {
            indexMin = i
        }
    }
    if (indexMin !== i) {
        const temp = arr[i]
        arr[i] = arr[indexMin]
        arr[indexMin] = temp
    }
}
console.log(arr); // [1, 2, 3, 4, 5]
  • 插入排序

  • 归并排序

  • 快速排序

  1. 搜索
  • 顺序搜索

  • 二分搜索

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值