JS数据结构与算法

笔记链接

数组结构

在这里插入图片描述

栈结构

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

栈的封装

在这里插入图片描述

栈常见操作

push(element): 添加一个新元素到栈顶位置.
pop():移除栈顶的元素,同时返回被移除的元素。
peek():返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)。
isEmpty():如果栈里没有任何元素就返回true,否则返回false。
clear():移除栈里的所有元素。
size():返回栈里的元素个数。这个方法和数组的length属性很类似。

注意: 这里我们为了将属性方法放在一起, 没有使用原型来封装方法.

// 栈类
function Stack() {
    // 栈中的属性
    var items = []

    // 栈相关的方法
    // 压栈操作
    this.push = function (element) {
        items.push(element)
    }

    // 出栈操作
    this.pop = function () {
        return items.pop()
    }

    // peek操作
    this.peek = function () {
        return items[items.length - 1]
    }

    // 判断栈中的元素是否为空
    this.isEmpty = function () {
        return items.length == 0
    }

    // 获取栈中元素的个数
    this.size = function () {
        return items.length
    }
}

var stack = new Stack()

// 情况下代码模拟
stack.push(6)
stack.push(5)
stack.pop()     // 5

队列

在这里插入图片描述

队列常见的操作

在这里插入图片描述

优先级队列

在这里插入图片描述
代码实现:

// 封装优先级队列
function PriorityQueue() {
    var items = []

    // 封装一个新的构造函数, 用于保存元素和元素的优先级
    function QueueElement(element, priority) {
        this.element = element
        this.priority = priority
    }

    // 添加元素的方法
    this.enqueue = function (element, priority) {
        // 1.根据传入的元素, 创建新的QueueElement
        var queueElement = new QueueElement(element, priority)

        // 2.获取传入元素应该在正确的位置
        if (this.isEmpty()) {
            items.push(queueElement)
        } else {
            var added = false
            for (var i = 0; i < items.length; i++) {
                // 注意: 我们这里是数字越小, 优先级越高
                if (queueElement.priority < items[i].priority) {
                    items.splice(i, 0, queueElement)
                    added = true
                    break
                }
            }

            // 遍历完所有的元素, 优先级都大于新插入的元素时, 就插入到最后
            if (!added) {
                items.push(queueElement)
            }
        }
    }

    // 删除元素的方法
    this.dequeue = function () {
        return items.shift()
    }

    // 获取前端的元素
    this.front = function () {
        return items[0]
    }

    // 查看元素是否为空
    this.isEmpty = function () {
        return items.length == 0
    }

    // 获取元素的个数
    this.size = function () {
        return items.length
    }
}

// 创建优先级队列对象
var pQueue = new PriorityQueue()

// 添加元素
pQueue.enqueue("abc", 10)
pQueue.enqueue("cba", 5)
pQueue.enqueue("nba", 12)
pQueue.enqueue("mba", 3)

// 遍历所有的元素
var size = pQueue.size()
for (var i = 0; i < size; i++) {
    var item = pQueue.dequeue()
    alert(item.element + "-" + item.priority)
}

链表

链表常见操作

append(element):向列表尾部添加一个新的项

insert(position, element):向列表的特定位置插入一个新的项。

remove(element):从列表中移除一项。

indexOf(element):返回元素在列表中的索引。如果列表中没有该元素则返回-1。

removeAt(position):从列表的特定位置移除一项。

isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false。

size():返回链表包含的元素个数。与数组的length属性类似。

toString():由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值。

代码

// 封装链表的构造函数
function LinkedList() {
    // 封装一个Node类, 用于保存每个节点信息
    function Node(element) {
        this.element = element
        this.next = null
    }

    // 链表中的属性
    this.length = 0
    this.head = null

    // 链表尾部追加元素方法
    LinkedList.prototype.append = function (element) {
        // 1.根据新元素创建节点
        var newNode = new Node(element)

        // 2.判断原来链表是否为空
        if (this.head === null) { // 链表尾空
            this.head = newNode
        } else { // 链表不为空
            // 2.1.定义变量, 保存当前找到的节点
            var current = this.head
            while (current.next) {
                current = current.next
            }

            // 2.2.找到最后一项, 将其next赋值为node
            current.next = newNode
        }

        // 3.链表长度增加1
        this.length++
    }

    // 链表的toString方法
    LinkedList.prototype.toString = function () {
        // 1.定义两个变量
        var current = this.head
        var listString = ""

        // 2.循环获取链表中所有的元素
        while (current) {
            listString += "," + current.element
            current = current.next
        }

        // 3.返回最终结果
        return listString.slice(1)
    }

    // 根据下标删除元素
    LinkedList.prototype.insert = function (position, element) {
        // 1.检测越界问题: 越界插入失败
        if (position < 0 || position > this.length) return false

        // 2.定义变量, 保存信息
        var newNode = new Node(element)
        var current = this.head
        var previous = null
        index = 0

        // 3.判断是否列表是否在第一个位置插入
        if (position == 0) {
            newNode.next = current
            this.head = newNode
        } else {
            while (index++ < position) {
                previous = current
                current = current.next
            }

            newNode.next = current
            previous.next = newNode
        }

        // 4.length+1
        this.length++

        return true
    }

    // 根据位置移除节点
    LinkedList.prototype.removeAt = function (position) {
        // 1.检测越界问题: 越界移除失败, 返回null
        if (position < 0 || position >= this.length) return null

        // 2.定义变量, 保存信息
        var current = this.head
        var previous = null
        var index = 0

        // 3.判断是否是移除第一项
        if (position === 0) {
            this.head = current.next
        } else {
            while (index++ < position) {
                previous = current
                current = current.next
            }

            previous.next = current.next
        }

        // 4.length-1
        this.length--

        // 5.返回移除的数据
        return current.element
    }

    // 根据元素获取链表中的位置
    LinkedList.prototype.indexOf = function (element) {
        // 1.定义变量, 保存信息
        var current = this.head
        index = 0

        // 2.找到元素所在的位置
        while (current) {
            if (current.element === element) {
                return index
            }
            index++
            current = current.next
        }

        // 3.来到这个位置, 说明没有找到, 则返回-1
        return -1
    }

    // 根据元素删除信息
    LinkedList.prototype.remove = function (element) {
        var index = this.indexOf(element)
        return this.removeAt(index)
    }

    // 判断链表是否为空
    LinkedList.prototype.isEmpty = function () {
        return this.length == 0
    }

    // 获取链表的长度
    LinkedList.prototype.size = function () {
        return this.length
    }

    // 获取第一个节点
    LinkedList.prototype.getFirst = function () {
        return this.head.element
    }
}

双向链表

// 创建双向链表的构造函数
function DoublyLinkedList() {
    // 创建节点构造函数
    function Node(element) {
        this.element = element
        this.next = null
        this.prev = null // 新添加的
    }

    // 定义属性
    this.length = 0
    this.head = null
    this.tail = null // 新添加的

    // 定义相关操作方法
    // 在尾部追加数据
    DoublyLinkedList.prototype.append = function (element) {
        // 1.根据元素创建节点
        var newNode = new Node(element)

        // 2.判断列表是否为空列表
        if (this.head == null) {
            this.head = newNode
            this.tail = newNode
        } else {
            this.tail.next = newNode
            newNode.prev = this.tail
            this.tail = newNode
        }

        // 3.length+1
        this.length++
    }

    // 在任意位置插入数据
    DoublyLinkedList.prototype.insert = function (position, element) {
        // 1.判断越界的问题
        if (position < 0 || position > this.length) return false

        // 2.创建新的节点
        var newNode = new Node(element)

        // 3.判断插入的位置
        if (position === 0) { // 在第一个位置插入数据
            // 判断链表是否为空
            if (this.head == null) {
                this.head = newNode
                this.tail = newNode
            } else {
                this.head.prev = newNode
                newNode.next = this.head
                this.head = newNode
            }
        } else if (position === this.length) { // 插入到最后的情况
            // 思考: 这种情况是否需要判断链表为空的情况呢? 答案是不需要, 为什么?
            this.tail.next = newNode
            newNode.prev = this.tail
            this.tail = newNode
        } else { // 在中间位置插入数据
            // 定义属性
            var index = 0
            var current = this.head
            var previous = null

            // 查找正确的位置
            while (index++ < position) {
                previous = current
                current = current.next
            }

            // 交换节点的指向顺序
            newNode.next = current
            newNode.prev = previous
            current.prev = newNode
            previous.next = newNode
        }

        // 4.length+1
        this.length++

        return true
    }

    // 根据位置删除对应的元素
    DoublyLinkedList.prototype.removeAt = function (position) {
        // 1.判断越界的问题
        if (position < 0 || position >= this.length) return null

        // 2.判断移除的位置
        var current = this.head
        if (position === 0) {
            if (this.length == 1) {
                this.head = null
                this.tail = null
            } else {
                this.head = this.head.next
                this.head.prev = null
            }
        } else if (position === this.length -1) {
            current = this.tail
            this.tail = this.tail.prev
            this.tail.next = null
        } else {
            var index = 0
            var previous = null

            while (index++ < position) {
                previous = current
                current = current.next
            }

            previous.next = current.next
            current.next.prev = previous
        }

        // 3.length-1
        this.length--

        return current.element
    }

    // 根据元素获取在链表中的位置
    DoublyLinkedList.prototype.indexOf = function (element) {
        // 1.定义变量保存信息
        var current = this.head
        var index = 0

        // 2.查找正确的信息
        while (current) {
            if (current.element === element) {
                return index
            }
            index++
            current = current.next
        }

        // 3.来到这个位置, 说明没有找到, 则返回-1
        return -1
    }

    // 根据元素删除
    DoublyLinkedList.prototype.remove = function (element) {
        var index = this.indexOf(element)
        return this.removeAt(index)
    }

    // 判断是否为空
    DoublyLinkedList.prototype.isEmpty = function () {
        return this.length === 0
    }

    // 获取链表长度
    DoublyLinkedList.prototype.size = function () {
        return this.length
    }

    // 获取第一个元素
    DoublyLinkedList.prototype.getHead = function () {
        return this.head.element
    }

    // 获取最后一个元素
    DoublyLinkedList.prototype.getTail = function () {
        return this.tail.element
    }

    // 遍历方法的实现
    // 正向遍历的方法
    DoublyLinkedList.prototype.forwardString = function () {
        var current = this.head
        var forwardStr = ""

        while (current) {
            forwardStr += "," + current.element
            current = current.next
        }

        return forwardStr.slice(1)
    }

    // 反向遍历的方法
    DoublyLinkedList.prototype.reverseString = function () {
        var current = this.tail
        var reverseStr = ""

        while (current) {
            reverseStr += "," + current.element
            current = current.prev
        }

        return reverseStr.slice(1)
    }

    // 实现toString方法
    DoublyLinkedList.prototype.toString = function () {
        return this.forwardString()
    }
}

集合结构

集合的特点

  • 集合通常是由一组无序的, 不能重复的元素构成.
    • 和数学中的集合名词比较相似, 但是数学中的集合范围更大一些, 也允许集合中的元素重复.
    • 在计算机中, 集合通常表示的结构中元素是不允许重复的.
  • 其实集合你可以将它看成一种特殊的数组.
    • 特殊之处在于里面的元素没有顺序, 也不能重复.
    • 没有顺序意味着不能通过下标值进行访问,
    • 不能重复意味着相同的对象在集合中只会存在一份.

操作的方法

add(value):向集合添加一个新的项。

remove(value):从集合移除一个值。

has(value):如果值在集合中,返回true,否则返回false。

clear():移除集合中的所有项。

size():返回集合所包含元素的数量。与数组的length属性类似。

values():返回一个包含集合中所有值的数组。

还有一些集合其他相关的操作, 暂时用不太多, 这里暂不封装.

字典

常见的操作

set(key,value):向字典中添加新元素。

remove(key):通过使用键值来从字典中移除键值对应的数据值。
has(key):如果某个键值存在于这个字典中,则返回true,反之则返回false。

get(key):通过键值查找特定的数值并返回。

clear():将这个字典中的所有元素全部删除。

size():返回字典所包含元素的数量。与数组的length属性类似。

keys():将字典所包含的所有键名以数组形式返回。

values():将字典所包含的所有数值以数组形式返回。

操作实现

// 创建字典的构造函数
function Dictionay() {
    // 字典属性
    this.items = {}

    // 字典操作方法
    // 在字典中添加键值对
    Dictionay.prototype.set = function (key, value) {
        this.items[key] = value
    }

    // 判断字典中是否有某个key
    Dictionay.prototype.has = function (key) {
        return this.items.hasOwnProperty(key)
    }

    // 从字典中移除元素
    Dictionay.prototype.remove = function (key) {
        // 1.判断字典中是否有这个key
        if (!this.has(key)) return false

        // 2.从字典中删除key
        delete this.items[key]
        return true
    }

    // 根据key去获取value
    Dictionay.prototype.get = function (key) {
        return this.has(key) ? this.items[key] : undefined
    }

    // 获取所有的keys
    Dictionay.prototype.keys = function () {
        return Object.keys(this.items)
    }

    // 获取所有的value
    Dictionay.prototype.values = function () {
        return Object.values(this.items)
    }

    // size方法
    Dictionay.prototype.size = function () {
        return this.keys().length
    }

    // clear方法
    Dictionay.prototype.clear = function () {
        this.items = {}
    }
}

哈希表

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

插入&修改操作

在这里插入图片描述

哈希表扩容

在这里插入图片描述

容量质数

在这里插入图片描述

function isPrime(num) {
    for (var i = 2; i < num; i++) {
        if (num % i == 0) {
            return false
        }
    }
    return true
}

// 测试
alert(isPrime(3)) // true
alert(isPrime(32)) // false
alert(isPrime(37)) // true

但是, 这种做法的效率并不高. 为什么呢?

对于每个数n,其实并不需要从2判断到n-1
一个数若可以进行因数分解,那么分解时得到的两个数一定是一个小于等于sqrt(n),一个大于等于sqrt(n).
比如16可以被分别. 那么是28, 2小于sqrt(16), 也就是4, 8大于4. 而44都是等于sqrt(n)
所以其实我们遍历到等于sqrt(n)即可

function isPrime(num) {
    // 1.获取平方根
    var temp = parseInt(Math.sqrt(num))

    // 2.循环判断
    for (var i = 2; i <= temp; i++) {
        if (num % i == 0) {
            return false
        }
    }
    return true
}

树结构

树的相关术语

(度、节点、层次、路径、深度…)

二叉树

(特性、满二叉树、完全二叉树、搜索二叉树(添加、查找(前中后序遍历)、删除(三种情况)))

AVL树

红黑树

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

变色

在这里插入图片描述

旋转

在这里插入图片描述

插入

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

删除

在这里插入图片描述

图结构

图的术语

图的表示

邻接矩阵、邻接表

图的遍历

广度优先搜索(BFS)、深度优先搜寻(DFS)

排序算法

冒泡排序

选择排序

插入排序

希尔排序

快速排序

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值