数据结构基础

168 阅读3分钟
  • 链表

特点:

查找节点 O(n);插入、删除节点O(1);

应用场景:

  1. 操作系统内的动态内存分配
  2. LRU缓存淘汰算法
// 生成链表
var MyLinkedList = function() {
    this.head = null
    this.tail = null
    this.length = 0
};

var ListNode = function(val) {
    this.val = val
    this.next = null
}

MyLinkedList.prototype.get = function(index) {
    if (index >=0 && index < this.length) {
        let i = 0
        let cur = this.head
        while (i < index) {
            cur = cur.next
            i ++
        }
        return cur.val
    } else {
        return -1
    }
};

MyLinkedList.prototype.addAtHead = function(val) {
    const lastHead = this.head
    const node = new ListNode(val)
    this.head = node
    this.head.next = lastHead
    if (!this.tail) {
        this.tail = node
        this.tail.next = null
    }
    this.length ++
};

MyLinkedList.prototype.addAtTail = function(val) {
    const lastTail = this.tail
    const node = new ListNode(val)
    this.tail = node
    if (lastTail) {      
        lastTail.next = this.tail
    }
    if (!this.head) {
        this.head = node
        this.head.next = null
    }
    this.length ++
};

MyLinkedList.prototype.addAtIndex = function(index, val) {
    if (index === this.length) {
        this.addAtTail(val)
    } else if (index <= 0) {
        this.addAtHead(val)
    } else if (index > 0 && index < this.length) {
        let i = 0
        let prev = this.head
        while (i < index - 1) {
            prev = prev.next
            i ++
        }
        const node = new ListNode(val)
        node.next = prev.next
        prev.next = node
        this.length ++
    }
};

MyLinkedList.prototype.deleteAtIndex = function(index) {
    if (index > 0 && index < this.length) {
        let i = 0
        let prev = null
        let cur = this.head
        while (i < index) {
            prev = cur
            cur = cur.next
            i ++
        }
        prev.next = cur.next
        if (index === this.length - 1) {
            this.tail = prev
        }
        this.length --
    } else if (index === 0) {
        this.head = this.head.next
        this.length --
    }
};
// 打印
const output = function(head) {
  let cur = head
  while (cur) {
    console.log(cur.val)
    cur = cur.next || null
  }
};
  • 队列

应用场景:

  1. CPU超线程技术 (虚拟四核)
  2. 线程池 任务队列 (缓冲区)

单调队列

解决区间最值问题

入队

队尾入队,将之前破坏单调性的元素都从队尾移出(维护单调性)

出队

如果对首元素超出区间范围,则将元素从队首出队

特性

队首元素永远是(滑动窗口)区间最值

// m为滑动窗口大小
const monotoneQueue = (arr, m) => {
  let queue = []
  for (let i = 0; i < arr.length; i++) {
    while (queue.length && arr[queue[queue.length - 1]] > arr[i]) {
      queue.pop()
    }
    queue.push(i)
    if (i - queue[0] === m) queue.shift()
    if (i + 1 < m) continue
    console.log(arr[queue[0]])
  }
}

特点:事件之间的完全包含关系

应用场景:

  1. 操作系统线程栈
  2. 表达式求值

单调栈

维护最近(大/小)关系

const monotoneStack = (arr) => {
  // 单调递增
  let stack = []
  // 存放向前能找到最近的小于值
  let prev = new Array(arr.length).fill(-1)
  // 存放向后能找到最近的小于值
  let next = new Array(arr.length).fill(arr.length)
  for (let i=0; i<arr.length; i++) {
    while (stack.length && arr[stack[stack.length-1]] > arr[i]) {
      next[stack[stack.length-1]] = i
      stack.pop()
    }
    if (!stack.length) {
      prev[i] = -1
    } else {
      prev[i] = stack[stack.length-1]
    }
    stack.push(i)
  }
  console.log(prev)
  console.log(next)
}
// input  [6,7,9,0,8,3,4,5,1,2]
// prev   [-1,0,1,-1,3,3,5,6,3,8]
// next   [3,3,3,10,5,8,8,8,10,10]

分类

二叉树

  • 每个节点度最多为2
  • 度为0的节点比度为2的节点多1个
完全二叉树
  • 只有在最后一层的右侧有空节点的树
  • 编号为 i 的子节点=> 左孩子:2i;右孩子:2i+1
  • 可以用连续空间存储(数组)
满二叉树

只有度为0和度为2的节点

完美二叉树

每一层都是满的

应用

节点代表集合,边代表关系,多用于处理查找

维护集合最值得神兵利器
完全二叉树优先队列维护集合最值得神兵利器
字典树字符串及相关转换问题得神兵利器
多叉树/森林AC自动机字符串及相关转换问题得神兵利器
并查集连通性问题的神兵利器
二叉排序树AVL树语言标准库中重要的数据检索容器的底层实现
2-3树语言标准库中重要的数据检索容器的底层实现
红黑树语言标准库中重要的数据检索容器的底层实现
B-树/B+树文件系统,数据库 底层重要数据结构

递归

  1. 数据归纳法 -> 结构归纳法
  2. 赋予递归函数一个明确的意义
  3. 思考边界条件
  4. 实现递归过程
// 生成树
function TreeNode(val) {
  this.val = val
  this.left = this.right = null
}

const creatTree = function(src) {
  let root = new TreeNode()
  let result = new TreeNode()
  result = null
  let queue = []
  for (let i = 0; i < src.length; i++) {
    if (i == 0) {
      root = new TreeNode(src[i])
      result = root
      queue.push(root)
    }
    if (queue.length) {
      root = queue.shift()
    } else {
      break
    }
    if (i + 1 < src.length && src[i + 1] !== null) {
      root.left = new TreeNode(src[i + 1])
      queue.push(root.left)
    }
    if (i + 2 < src.length && src[i + 2] !== null) {
      root.right = new TreeNode(src[i + 2])
      queue.push(root.right)
    }
    i = i + 1
  }
  return result
}

是一种完全二叉树,处理集合最值

小顶堆

任意三节点中父节点值都小于两个子节点的值

大顶堆

任意三节点中父节点值都大于两个子节点的值

// 实现大顶堆
class MaxHeap {
  constructor(data = []) {
    this.data = data
    // this.comparator = (a,b) => a-b
    this.heapify()
  }
  heapify() {
    if (this.size() < 2) return
    for(let i=1;i < this.size(); i++) {
      this.bubbleUp(i)
    }
  }

  peek() {
    if (this.size === 0) return null
    return this.data[0]
  }

  push(value) {
    this.data.push(value)
    this.bubbleUp(this.size() - 1)
  }

  pop() {
    if (this.size === 0) {
      return null
    }
    const result = this.data[0]
    const last = this.data.pop()
    if (this.size() !== 0) {
      this.data[0] = last
      this.bubbelDown(0)
    }
    return result
  }
  
  swap(index1, index2) {
    [this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]]
  }

  size() {
    return this.data.length
  }

  bubbleUp(index) {
    while(index > 0) {
      const parentInd = Math.floor((index-1) / 2)
      if (this.data[parentInd] < this.data[index]) {
        this.swap(index, parentInd)
        index = parentInd
      } else {
        break;
      }
    }
  }

  bubbelDown(index) {
    const lastIndex = this.size() - 1
    while(true) {
      const leftIndex = index * 2 + 1;
      const rightIndex = index * 2 + 2;
      let findIndex = index
      if (leftIndex<= lastIndex && this.data[leftIndex] > this.data[findIndex]) {
        findIndex = leftIndex
      }
      if (rightIndex<= lastIndex && this.data[rightIndex] > this.data[findIndex]) {
        findIndex = rightIndex
      }
      if (index !== findIndex) {
        this.swap(index, findIndex)
        index = findIndex
      } else {
        break
      }
    }
  }
}

并查集

Quick-Find算法

染色法:记录每个节点的颜色,把连接到一起的点染成同一个颜色

Quick-Union算法

树形结构:记录每个节点的父节点编号,将连接到一起的两个点,其中一个的父节点指向另一个

优化1(平衡性优化)

合并两个集合时,数量多的集合根节点作为新集合的根节点

优化2(路径压缩)✔

当当前节点的父节点不为根节点时,当前节点的父节点指向父节点的父节点(节点上移)

AlgorithmConstructorUnionFind
Quick-FindNN1
Quick-UnionNTree heightTree height
Weighted Quick-UnionN1gN1gN
Weighted Quick-Union With Path CompressionNNear to 1Near to 1
class UnionSet {
  constructor(size) {
    this.set = new Array(size)
    this.cnt = new Array(size)
    for(let i=0; i<size; i++) {
      this.set[i] = i
      this.cnt[i] = 1
    }
  }

  get(a) {
    return this.set[a] = this.set[a] === a ? a : this.get(this.set[a])
  }

  merge(a, b) {
    this.cnt[this.get(b)] += this.cnt[this.get(a)]
    this.set[this.get(a)] = this.get(b)
  }
}