惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News | PayPal Newsroom
IT之家
IT之家
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
大猫的无限游戏
大猫的无限游戏
GbyAI
GbyAI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LangChain Blog
S
SegmentFault 最新的问题
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Project Zero
Project Zero
P
Privacy & Cybersecurity Law Blog
V
Vulnerabilities – Threatpost
博客园 - 三生石上(FineUI控件)
Recorded Future
Recorded Future
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
宝玉的分享
宝玉的分享
aimingoo的专栏
aimingoo的专栏
T
Tor Project blog
T
The Exploit Database - CXSecurity.com
Schneier on Security
Schneier on Security
H
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
M
MIT News - Artificial intelligence
W
WeLiveSecurity
P
Proofpoint News Feed
A
About on SuperTechFans
S
Securelist
I
InfoQ
G
Google Developers Blog
博客园 - 司徒正美
博客园 - 叶小钗
Latest news
Latest news
F
Fortinet All Blogs
G
GRAHAM CLULEY
腾讯CDC
Jina AI
Jina AI
S
Schneier on Security
I
Intezer
V
Visual Studio Blog
美团技术团队
V2EX - 技术
V2EX - 技术
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Cloudflare Blog
Microsoft Security Blog
Microsoft Security Blog
Blog — PlanetScale
Blog — PlanetScale
P
Proofpoint News Feed
罗磊的独立博客
Y
Y Combinator Blog

时间的朋友

Windows 命令行密码重置 Anaconda安装 typescript 注解解读1 Konvajs Shape加载自定义图片 sshpass 使用 why-is-node-running webgl笔记 SharedArrayBuffer is not defined blender 常用快捷键 vue -- v3.4commit提交记录2 vue2 升级vue3报错问题整理 着色器 expressjs 源码 hyper-V arch linux 网络配置 element input数字格式化 three 拼接货架 WebAudio笔记 Windows nginx重启bat脚本 vue -- v3.4commit提交记录 URI malformed vue3 -- Class 对象在组件中使用范例 | 时间的朋友 ruby 安装和升级 element-plus 老版本cascader使用卡死问题 vue3 内置Transition组件 前端memo的实现 Vue -- vue-class-component源码 linux 优化脚本 typescript 装饰器 microbundle 源码 WSL2问题解决WslRegisterDistribution failed with error: 0x800701bc vue -- vue3利用createVNode函数,建立命令式调用组件 SSH connection failed: connect ECONNREFUSED 请求中获取浏览器推荐语言 flutter 生命周期 双向链表 堆和栈 EventSource 单向链表 简易的事件监听EventBus fork的仓库更新分支 forwardRef 定义的组件添加静态属性 js 获取滚动元素 canvas requestAnimationFrame画一个clock mousedown event中保持input的focus状态 vue -- @vue/compiler-core整体逻辑 IOS端h5 fixed滚动问题 vue -- compile结果代码解读 vue -- transformElement源码 Vue -- 内置指令源码 vue3 -- @vue/compiler-sfc compileScript源码 浏览器工作原理 对比2个版本号的方法 vite 向entry html中注入代码 小程序 -- 内部使用webview绑定微信公众号openId github coding同步action 小程序 -- 微信外部浏览器或者链接打开方式整理 MSE -- MediaSource 的前端使用 rollup 打包vue2组件 常用工具函数整理 Benchmark.js 使用 import-html-entry js沙箱实现源码 import-html-entry 笔记 axios core源码 国内开源镜像网站 nuxt -- docker-compose进行部署 vue -- provide和inject原理 vue -- hoistStatic原理 jenkins 配置模板代码 vue -- compileTemplate原理 小程序开发问题整理 webpack-dev-server proxy代理模块 vite 代理中更改请求头问题 Async.js flutter 问题整理 比较两个数组的不同项 ms源码解析 浏览器同源策略 前端缓存笔记 webpack5 schema-utils tsc 和 babel 编译typescript区别 typescript 学习笔记 esbuild api整理 Webpack Chain 源码 deepmerge库源码解读 Vue 自定义指令的执行机制 linux 问题整理 webpack5学习笔记 elementui form多表单验证 Webpack5 -- Assest Module vue -- 实现MenuTree组件 Vue -- VFor的编译处理 vue -- 使用中问题整理 算法基础知识 浏览器环境检测函数整理 yarn 问题搜集 c++笔记 webpack 模块加载原理 webpack 模块加载原理(二) Zero Width Characters slate.js API整理
算法基础知识 | 时间的朋友
2022-08-02 · via 时间的朋友

队列和双端队列 🔗

双端队列是一种将栈的原则和队列的原则混合在一起的数据结构。

队列 🔗

FIFO(First Input First Output), 先进先出,新添加的元素在队列的尾部,从顶部移除元素。

最常见的例子就是排队。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Queue {
    _count: number;
    _first: number;
    _items: {
        [key: number]: any
    };
    constructor() {
        this._count = 0;
        this._first = 0;
        this._items = {};
    }

    pop() {
        const item = this._items[this._first];
        delete this._items[this._first];
        this._first++;
        return item;
    }

    push(element: any) {
        this._count++;
        this._items[this._count] = element;
    }

    peek() {
        if (this.isEmpty()) return undefined;

        return this._items[this._first];
    }

    size() {
        return this._count - this._first;
    }

    isEmpty() {
        return this.size() === 0;
    }
}

双端队列 🔗

是一种允许我们同时从前端和后端添加和移除元素的特殊队列。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class Deque {
    _count: number;
    _first: number;
    _items: {
        [key: number]: any
    };
    constructor() {
        this._count = 0;
        this._first = 0;
        this._items = {};
    }

    addFront(element: any) {
        if (this.isEmpty()) {
            this.addBack(element)
        } else if(this._first > 0) {
            this._first--;
            this._items[this._first] = element;
        } else {
            for (let i = this._count; i > 0;i--) {
                this._items[i] = this._items[i-1];
            }
            this._first = 0;
            this._items[this._first] = element;
        }
    }

    addBack(element: any) {
        this._count++;
        this._items[this._count] = element;
    }

    removeFront() {

    }

    removeBack() {
        const item = this._items[this._first];
        delete this._items[this._first];
        this._first++;
        return item;
    }

    peekFront() {
        if (this.isEmpty()) return undefined;

        return this._items[this._first];
    }

    peekBack() {
        return this._items[this._count];
    }

    size() {
        return this._count - this._first;
    }

    isEmpty() {
        return this.size() === 0;
    }
}

链表 🔗

普通链表 🔗

链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。

image.png

在数组中,我们可以直接访问任何位置的任何元素,而要想访问链表中间的一个元素,则需要从起点(表头 )开始迭代链表直到找到所需的元素。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
class LinkedList {
    head: LNode | null;
    equalsFn: Function;
    count: number;
    constructor(fn: Function) {
        this.count = 0;
        this.equalsFn = fn;
        this.head = null;
    }

    push(element: any) {
        const node = new LNode(element);
        this.count++;

        if (this.head) {
            let current = this.head.next;
            while (current?.next) {
                current = current.next;
            }
            current!.next = element;
        } else {
            this.head = node;
        }
    }

    insert(element: any, position: number) {
        if (position >= 0 || position <= this.count) {
            if (position == 0) {
                const current = this.head;
                const insertNode = new LNode(element);
                insertNode.next = current;
                this.head = current;
                return true
            } else {
                const prev = this.getElementAt(position - 1);
                const insertNode = new LNode(element);
                const current = prev?.next;
                insertNode.next = current;
                prev?.next = insertNode;
                return true
            }
            this.count++;
        }

        return false;

    }

    getElementAt(index: number) {
        if (index >= 0 && index <= this.count) {
            let node = this.head;
            for (let i = 0; i < index && node; i++) {
                node = node.next!;
            }
            return node;
        } else {
            return undefined;
        }
    }

    remove(element: any) {
        const index = this.indexOf(element);
        if (index > -1) {
            this.removeAt(index)
            this.count--;

        }
    }

    indexOf(element: any) {
        if (!element) return -1;
        let node = this.head;
        for (let i = 0; i < this.count; i++) {
            if (node?.node === element) {
                return i
            } else {
                node = node?.next!
            }
        }

        return -1
    }

    removeAt(position: number) {
        if (position < 0 || position > this.count) return

        let node = this.head;
        for (let i = 0; i <= position; i++) {
            node = node?.next!
        }

        const next = node?.next;
        node!.next = next?.next;
    }

    isEmpty() {
        return this.count === 0;
    }

    size() {
        return this.count;
    }
}

class LNode {
    node: any;
    next: LNode | undefined;

    constructor(element: any) {
        this.node = element;
        this.next = undefined;
    }
}

双向链表 🔗

双向链表和普通链表的区别在于,在链表中,一个节点只有链向下一个节点的链接;而在双向链表中,链接是双向的:一个链向下一个元素,另一个链向前一个元素

image.png

循环链表 🔗

可以像链表一样只有单向引用,也可以像双向链表一样有双向引用。循环链表和链表之间唯一的区别在于,最后一个元素指向下一个元素的指针(tail.next)不是引用undefined,而是指向第一个元素(head)

image.png

有序链表 🔗

是指保持元素有序的链表结构。除了使用排序算法之外,我们还可以将元素插入到正确的位置来保证链表的有序性

集合 🔗

集合是由一组无序且唯一(即不能重复)的项组成的。该数据结构使用了与有限集合相同的数学概念,但应用在计算机科学的数据结构中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class CSet {
    items: any;

    constructor() {
        this.items = {};
    }

    add(element) {
        if(!this.has(element)) {
            this.items[element] = element;
            return true;
        }
        return false;
    }

    delete(element) {
        if(this.has(element)) {
            delete this.items[element];
            return true
        }
        return false
    }

    clear() {
        this.items = {};
    }

    has(element) {
        return Object.prototype.hasOwnProperty.call(this.items, element);
    }

    size() {
        return Object.keys(this.items).length
    }
}

并集 🔗

对于给定的两个集合,返回一个包含两个集合中所有元素的新集合。

image.png

交集 🔗

对于给定的两个集合,返回一个包含两个集合中共有元素的新集合。

image.png

差集 🔗

对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合。

image.png

子集 🔗

验证一个给定集合是否是另一集合的子集。

img

递归 🔗

每个递归函数都必须有基线条件,即一个不再递归调用的条件(停止点),以防止无限递归。

调用栈 🔗

每当一个函数被一个算法调用时,该函数会进入调用栈的顶部。当使用递归的时候,每个函数调用都会堆叠在调用栈的顶部,这是因为每个调用都可能依赖前一个调用的结果。

尾调用优化 🔗

ECMAScript 2015中, 如果函数内的最后一个操作是调用函数(就像示例中加粗的那行),会通过“跳转指令”(jump) 而不是**“子程序调用”(subroutinecall)**来控制。

迭代和递归优缺点 🔗

迭代的版本比递归的版本快很多,所以这表示递归更慢。但是,再看看三个不同版本的代码。递归版本更容易理解,需要的代码通常也更少。

🔗

树是一种分层数据的抽象模型。

image.png

一个节点可以有祖先和后代。

二叉树和二叉搜索树 🔗

二叉树中的节点最多只能有两个子节点:一个是左侧子节点,另一个是右侧子节点

二叉搜索树(Binary Search Tree)(BST)是二叉树的一种,但是只允许你在左侧节点存储(比父节点)小的值,在右侧节点存储(比父节点)大的值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class BST {
  root: any;
  compareFn: Function;
  constructor(compareFn) {
    this.root = null;
    this.compareFn = compareFn;
  }

  insert(key) {
    if (this.root == null) {
      this.root = new BSTNode(key);
    } else {
      this.insertNode(this.root, key);
    }
  }

  insertNode(node, key) {
    if (this.compareFn(key, node.key)) {
      if (node.left == null) {
        node.left = new BSTNode(key);
      } else {
        this.insertNode(node.left, key);
      }
    } else {
      if (node.right == null) {
        node.right = new BSTNode(key);
      } else {
        this.insertNode(node.right, key);
      }
    }
  }
}

class BSTNode {
  key: any;
  left: BSTNode | null;
  right: BSTNode | null;
  constructor(key) {
    this.key = key;
    this.left = null;
    this.right = null;
  }
}

树的遍历 🔗

中序遍历 🔗

中序遍历是一种以上行顺序访问BST所有节点的遍历方式,也就是以从最小到最大的顺序访问所有节点

image.png

先序遍历 🔗

先序遍历是以优先于后代节点的顺序访问每个节点的

image.png

后序遍历 🔗

后序遍历则是先访问节点的后代节点,再访问节点本身。后序遍历的一种应用是计算一个目录及其子目录中所有文件所占空间的大小。

image.png

最小值 🔗

树最后一层最左侧的节点,这是这棵树中最小的键

1
2
3
4
5
6
7
minNode(node) {
  let current = node
  while(current != null && current.left != null) {
    current = current.left
  }
  return current
}

最大值 🔗

树最右端的节点(同样是树的最后一层),这是这棵树中最大的键

1
2
3
4
5
6
7
maxNode (node) {
  let current = node;  
  while (current != null && current.right != null) {   
    current = current.right;  
  }  
  return current;
}

红黑树 🔗

红黑树也是一个自平衡二叉搜索树。

  • 顾名思义,每个节点不是红的就是黑的;
  • 树的根节点是黑的;
  • 所有叶节点都是黑的(用NULL引用表示的节点);
  • 如果一个节点是红的,那么它的两个子节点都是黑的;
  • 不能有两个相邻的红节点,一个红节点不能有红的父节点或子节点;
  • 从给定的节点到它的后代节点(NULL叶节点)的所有路径包含相同数量的黑色节点。

二叉堆和堆排序 🔗

它是一棵完全二叉树,表示树的每一层都有左侧和右侧子节点(除了最后一层的叶节点),并且最后一层的叶节点尽可能都是左侧子节点,这叫作结构特性。

  • 二叉堆不是最小堆就是最大堆。最小堆允许你快速导出树的最小值,最大堆允许你快速导出树的最大值。

  • 尽管二叉堆是二叉树,但并不一定是二叉搜索树(BST)

  • 在二叉堆中,每个子节点都要大于等于父节点(最小堆)或小于等于父节点(最大堆)

image.png
  • 它的左侧子节点的位置是2 * index + 1(如果位置可用
  • 它的右侧子节点的位置是2 * index + 2(如果位置可用)
  • 它的父节点位置是index / 2(如果位置可用)

堆排序算法 🔗

  • 用数组创建一个最大堆用作源数据。
  • 在创建最大堆后,最大的值会被存储在堆的第一个位置。我们要将它替换为堆的最后一个值,将堆的大小减1。
  • 最后,我们将堆的根节点下移并重复步骤2直到堆的大小为1。