zoukankan      html  css  js  c++  java
  • 数据结构(搁置)

    • JS中的数组是多种数据结构的封装,它存在快慢两种模式,快时数组开辟默认的内存空间,保存的每一项都是指针,所以能够保存不同类型的数据,同时有能实现数组的任意位高速读取的优势。但是当插入或删除时导致的空间不足,会导致需要开辟新的内存空间并整体复制当前值。慢的模式没有了解。
    • 数据结构实际上是数据在内存中的储存方法和读取方法。比如:栈和队列的存储都是一样的,使用一个连续的内存空间。他们的区别在于队列多使用了3个内存空间来保存位置变量,他们能够实现的功能也不一样。
    • 数据结构只是一种理论上的内存使用方式,在不同的语言中其实现方式是不同。数据结构更在乎的是在内存使用的不同方式(应该是结合了长期使用总结的功能需求上总结出来的)
    • 数据结构是为了方便程序访问数据和提高程序性能而使用,就像算法一样,优秀的数据结构能够以指数级提高程序速度
    • 实际在具体的编程实践中,数据结构的优化变得相当弱,更应该把精力关注在代码结构的优化及代码语义化上。(暂时这么认为中?数据结构或许在TypeScript中得到了优化)

    栈的特点是只能在某一端添加或删除数据,遵循先进后出的原则

    • 可用于判断是否对称,例如判断括号是否对称
    • 用JS的数组实现栈不会有多余的开销,因为它们都一样,当空间不足时需要重新开辟空间并复制原有数据
    var isValid = function (s) {
      let map = {
        '(': -1,
        ')': 1,
        '[': -2,
        ']': 2,
        '{': -3,
        '}': 3
      }
      let stack = []
      for (let i = 0; i < s.length; i++) {
        if (map[s[i]] < 0) {
          stack.push(s[i])
        } else {
          let last = stack.pop()
          if (map[last] + map[s[i]] != 0) return false
        }
      }
      if (stack.length > 0) return false
      return true
    };
    

    队列

    队列一个线性结构,特点是在某一端添加数据,在另一端删除数据,遵循先进先出的原则。

    • 循环队列,更高效率的队列实现
    • 相对于JS数组,应该是一种更高效率的实现方式。JS中数组在快模式时,头部的出数据会导致数组的拷贝,所以以下队列的方式效率会更高。(可能)
    class SqQueue {
      constructor(length) {
        this.queue = new Array(length)
        this.first = 0
        this.last = 0
        this.size = 0
        this.base = length
      }
      enQueue(item) {
        this.queue[this.last] = item
        this.size++
        this.last = (this.last + 1) % this.queue.length
        if (this.first === this.last) {
          this.__resize(this.size + this.base)
        }
      }
      deQueue() {
        if (this.first === this.last) {
          return undefined
        }
        let r = this.queue[this.first]
        this.queue[this.first] = null
        this.first = (this.first + 1) % this.queue.length
        this.size--
        if (this.size <= this.queue.length - 2 * this.base) {
          this.__resize(this.queue.length - this.base)
        }
        return r
      }
      getHeader() {
        if (this.first === this.last) {
          return undefined
        }
        return this.queue[this.first]
      }
      getLength() {
        return this.size
      }
      __resize(length) {
        let q = new Array(length)
        for (let i = 0; i < this.size; i++) {
          q[i] = this.queue[(i + this.first) % this.queue.length]
        }
        this.queue = q
        this.first = 0
        this.last = this.size
      }
    }
    

    链表

    链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。(使用对象树实现)

    • 链表在删除和插入上的表现优于数组,因为数组需要移动改变位之后的所有数据的位置(其实还受查询插入位影响,数组查询快于链表,插入时数组需要整体移动,JS中数组做了优化,实际综合效率哪个更高不确定)
    • 查询效率低于数组,总要从第一个依次向下查询
    • 能够确定的是链表相对于栈和队列,它在头部添加和删除的表现上最为优秀。
    • 链表和树的内存本质是一样的,只是数据组织结构不同
    class Node {
      constructor(v, next) {
        this.value = v
        this.next = next
      }
    }
    class LinkList {
      constructor() {
        this.size = 0
        this.dummyNode = new Node(null, null)
      }
      __find(header, index, currentIndex) {
        if (index === currentIndex) return header
        return this.__find(header.next, index, currentIndex + 1)
      }
      __addNode(v, index) { // 在某个位置添加,实际是改变这个位置上一项next的指向,及这个新增项next指向这个位置上一项next原本的指向
        if (index < 0 || index > this.size) throw Error('Index error')
        let prev = this.__find(this.dummyNode, index, 0)
        prev.next = new Node(v, prev.next)
        this.size++
        return prev.next
      }
      insertNode(v, index) {
        return this.__addNode(v, index)
      }
      addToFirst(v) {
        return this.__addNode(v, 0)
      }
      addToLast(v) {
        return this.__addNode(v, this.size)
      }
      __checkIndex(index){
        if (index < 0 || index > this.size-1) throw Error('Index error')
      }
      __removeNode(index) { // 获取删除项的前一项,修改它的next为删除项的next,设置删除项的next为null(删除关联让内存释放空间)
        this.__checkIndex(index)
        let prev = this.__find(this.dummyNode, index, 0)
        let node = prev.next
        prev.next = node.next
        node.next = null
        this.size--
        return node
      }
      removeFirstNode() {
        return this.__removeNode(0)
      }
      removeLastNode() {
        return this.__removeNode(this.size-1)
      }
      getNode(index) {
        this.__checkIndex(index)
        return this.__find(this.dummyNode, index, 0).next.value
      }
      getSize() {
        return this.size
      }
    }
    

    二叉树

    二叉树拥有一个根节点,每个节点至多拥有两个子节点,分别为:左节点和右节点。树的最底部节点称之为叶节点,当一颗树的叶数量数量为满时,该树可以称之为满二叉树。

    • 树本身存在层级结构,能够方便的自上而下或自下而上获取数据

    二分搜索树

    二分搜索树也是二叉树,拥有二叉树的特性。但是区别在于二分搜索树每个节点的值都比他的左子树的值大,比右子树的值小。

    • 这种存储方式很适合于数据搜索。用于整理一堆数据,方便后面查询比较。
    • 容易获取最大值和最小值
    • 也方便获取排序好的数据,本身结构就带有顺序特点
    • 搜索效率其实和添加顺序还是有关的,如果倒序添加数据,这个二叉树就会变成链表
    • 二分搜索树只是数据结构,用于排序的话数组的sort会快得多
    // 创建二叉树
    
    class Node {
      constructor(value) {
        this.value = value
        this.left = null
        this.right = null
      }
    }
    class BST {
      constructor() {
        this.root = null
        this.size = 0
      }
      _addChild(node, v) {
        if (!node) {
          this.size++
          return new Node(v)
        }
        if (node.value > v) {
          node.left = this._addChild(node.left, v)
        } else if (node.value < v) {
          node.right = this._addChild(node.right, v)
        }
        return node
      }
      addNode(v) {
        this.root = this._addChild(this.root, v)
      }
      getSize() {
        return this.size
      }
    }
    
    // 三种深度遍历方法,分别是先序遍历、中序遍历、后序遍历 
    
      // 先序遍历可用于打印树的结构(似乎并不能很好体现)
      _pre(node) {
        if (node) {
          console.log(node.value)
          this._pre(node.left)
          this._pre(node.right)
        }
      }
      preTraversal() {
        this._pre(this.root)
      }
    
      // 中序遍历得到排好序的值
      _mid(node) {
        if (node) {
          this._mid(node.left)
          console.log(node.value)
          this._mid(node.right)
        }
      }
      midTraversal() {
        this._mid(this.root)
      }
      
      // 后序遍历可用于先操作子节点再操作父节点的场景(vue钩子mounted先执行子节点的后执行父节点的,保证所有子节点挂载完才挂载父节点)
      _back(node) {
        if (node) {
          this._back(node.left)
          this._back(node.right)
          console.log(node.value)
        }
      }
      backTraversal() {
        this._back(this.root)
      }
    
    // 广度遍历
    // 利用之前讲过的队列结构来完成
    
      breadthTraversal() {
        if (!this.root) return null
        let size = Math.pow(2,Math.ceil(Math.log(this.size + 1)/Math.log(2))-1) // 二叉树的总长度决定了插入队列时占的最大位置(依据等比数列公式获取),使用最大位置能加快插入读取速度,但也更消耗内存。还有待优化
        let q = new SqQueue(size)
        q.enQueue(this.root)
        while (q.getLength()) {
          let n = q.deQueue()
          console.log(n.value)
          if (n.left) q.enQueue(n.left)
          if (n.right) q.enQueue(n.right)
        }
      }
    
    // 获取最大值和最小值
      _getNode(node,key) {
        if (!node[key]) return node.value
        return this._getNode(node[key],key)
      }
      getMin() {
        return this._getNode(this.root,'left')
      }
      getMax() {
        return this._getNode(this.root,'right')
      }
    
    // 向下取整向上取整
    
      floor(v) {
        let node = this._floor(this.root, v)
        return node ? node.value : null
      }
      _floor(node, v) {
        if (!node) return null
        if (node.value > v) {
          return this._floor(node.left, v)
        }
        if(node.value < v) {
          let right = this._floor(node.right, v) 
          if (right) return right
        }
        return node
      }
      top(v){
        let node = this._top(this.root, v)
        return node ? node.value : null
      }
      _top(node,v){
        if (!node) return null
        if (node.value < v) {
          return this._top(node.right, v)
        }
        if(node.value > v) {
          let left = this._top(node.left, v) 
          if (left) return left
        }
        return node
      }
    
    // 返回第K项
    
      select(k) {
        let node = this._select(this.root, k)
        return node ? node.value : null
      }
      _select(node, k) {
        if (!node) return null
        let size = node.left ? node.left.size  : 0
        if (size > k) return this._select(node.left, k)
        if (size < k) return this._select(node.right, k - size - 1)
        return node
      }
    
  • 相关阅读:
    软件工程概论之web基础
    java动手动脑——异常处理
    Java动手动脑——多态和继承
    java字串
    数据结构——用栈来判断回文字符串
    java一个能记录生成多少个对象的类
    openwrt设置默认登陆密码
    在ubuntu中安装luci解决iwinfo.h No such file or directory问题
    添加mysamba
    更改默认打开wifi功能
  • 原文地址:https://www.cnblogs.com/qq3279338858/p/10213192.html
Copyright © 2011-2022 走看看