JavaScript (JS)常见数据结构与算法的封装

栈结构的封装

// 封装一个栈
    function Stack (){
        this.items = []
    // 实现栈的基本操作
    // 1.将元素压入栈
    Stack.prototype.push = function(ele){
        this.items.push(ele)
    }
    // 2.将栈顶移除并返回
    Stack.prototype.pop = function(){
        return this.items.pop()
    }
    // 3.查看一下栈顶元素
    Stack.prototype.peek = function(){
        return this.items[this.items.length-1]
    }
    // 4.判断栈是否为空
    Stack.prototype.isEmpty = function(){
        return this.items.length == 0
    }
    // 5.获取栈中元素的个数
    Stack.prototype.size = function(){
        return this.items.length
    }
    // 6.toString方法   将栈中元素获取为字符串
    Stack.prototype.toString = function(){
        var total = ""
        for(var i=0 ;i<this.items.length;i++){
            total += this.items[i] +" "
        }
        return total
    }
    
    }
    // 构建一个栈
    var p = new Stack()
    
    // p.push(12)
    // p.push(13)
    // p.push(14)
    // p.push(12)
    // p.pop()
    // b = p.pop()
    // a = p.peek()
    // c = p.isEmpty()
    // d = p.size()
    // e = p.toString()
    // console.log(p)
    // console.log(a)
    // console.log(b)
    // console.log(c)
    // console.log(d)
    // console.log(e)
    
    // 函数十进制转二进制
    function dec2bin(ele){
        // 构建一个栈
        var stack = new Stack
        // 循环操作
        while (ele>0){
            // 往栈里加ele的余数
            stack.push(ele%2)
            ele = Math.floor(ele / 2)
        }
        // 将我们的栈余数取出来
        var eleResult = ""
        while (!stack.isEmpty()){
            eleResult += stack.pop()
        }
        return eleResult
    }

    console.log(dec2bin(100))

队列结构

// 封装队列
        function Queue(){
            // 用数组封装
            this.items = []
            // 1.将元素添加到队列中
            Queue.prototype.enqueue = function(ele){
            this.items.push(ele)
            }
            // 2.将队列中第一个元素删除
            Queue.prototype.dequeue = function(){
                return this.items.shift()
            }
            // 3.检查最前端元素
            Queue.prototype.front = function(){
                return this.items[0]
            }
            // 4.查看队列是否为空
            Queue.prototype.isEmpty = function(){
                return this.items.length == 0
            }
            // 5.查看队列中元素个数
            Queue.prototype.size = function(){
                return this.items.length
            }
            // 6.toString 方法
            Queue.prototype.toString = function(){
                var result = ""
                for(var i = 0;i<this.items.length;i++){
                    result += this.items[i] + " "
                    // result += this.items[i] 
                }
                return result
            }
        }

        var queue = new Queue()

        // Queue.enqueue("sada")
        queue.enqueue("sada")
        // queue.enqueue("sada")
        // queue.enqueue("sada")
        // queue.enqueue("45646")
        // queue.enqueue("45646")
        // queue.enqueue("45646")
        // queue.enqueue("45646")
        // 用alert 的话可以执行一遍整个函数
        // 故不用返回tostring方法来获取
        alert(queue)
        // console.log(queue)


        // 面试题 
        // 击鼓传花
        function passGame (namelist,num){
            // 创建一个队列
            var queue = new Queue()
            // 1.将名字顺序传进去
            for(var i=0;i<namelist.length;i++){
                queue.enqueue(namelist[i])
            }
            // 当队列大小为1时停止游戏
            while(queue.size() > 1){
                // 2.将num之前的元素全部放到最后
            for(var i = 0;i<num - 1;i++){
                queue.enqueue(queue.dequeue())
            }
            // 3.将为nun的去掉
                queue.dequeue()
            }
            // 将剩下的那个获胜者及角标取出
            var result = queue.front()
            // 获取一开始的位置
            var biao = namelist.indexOf(result)
            alert("剩下的人时"+result+"他的位置为"+biao)
        }
        // passGame(["lige","dasda","dwhi","beijing","guangzhou"],5)

优先级队列封装

// 封装一个优先级队列
        function priorityQueue() {
            // 内建一个类封装数据
            function eleQueue(ele, priority) {
                this.ele = ele
                this.priority = priority
            }
            // 构建一个队列
            this.items = []
            // 1.添加一个元素插入方法
            priorityQueue.prototype.addQueue = function (ele, priority) {
                // 创建一个eleQueue对象获取数据
                var queue = new eleQueue(ele, priority)
                // 判断数组是否为空
                if (this.items.length == 0) {
                    this.items.push(queue)
                } 
                // 否就执行添加方法
                else {
                    // 创建一个标志类似于质数检查
                    var istrue = true
                    for (var i = 0; i < this.items.length; i++) {
                        if (queue.priority < this.items[i].priority) {
                            // 数组的插入在前插入
                            this.items.splice(i, 0, queue)
                            istrue = false
                            break
                        }
                    }
                    if (istrue) {
                        this.items.push(queue)
                    }
                }

            }

            // 2.将队列中第一个元素删除
            priorityQueue.prototype.dequeue = function () {
                return this.items.shift()
            }
            // 3.检查最前端元素
            priorityQueue.prototype.front = function () {
                return this.items[0]
            }
            // 4.查看队列是否为空
            priorityQueue.prototype.isEmpty = function () {
                return this.items.length == 0
            }
            // 5.查看队列中元素个数
            priorityQueue.prototype.size = function () {
                return this.items.length
            }
            // 6.toString 方法
            priorityQueue.prototype.toString = function () {
                var result = ""
                for (var i = 0; i < this.items.length; i++) {
                    result += this.items[i].ele + "-" + this.items[i].priority + "  "
                    // result += this.items[i] 
                }
                return result
            }
        }
        var pq = new priorityQueue()
        pq.addQueue(2, 100)
        pq.addQueue(3, 400)
        pq.addQueue(5, 500)
        pq.addQueue(3, 20)
        pq.addQueue(4, 200)
        alert(pq)

列表结构

单向链表的封装

// 封装单向链表类
    function LinkList(){
      // 封装一个内部类:节点类
      function Node(data){
        this.data = data;
        this.next = null;
      }
      
      // 属性
      // 属性head指向链表的第一个节点
      this.head = null;
      this.length = 0;
       
       // 一.实现append方法
       LinkList.prototype.append = data => {
        //1.创建新节点
        let newNode = new Node(data)

        //2.添加新节点
        //情况1:只有一个节点时候
        if(this.length == 0){
          this.head = newNode
        //情况2:节点数大于1,在链表的最后添加新节点  
        }else {              
          //让变量current指向第一个节点
          let current = this.head
          //当current.next(下一个节点不为空)不为空时,一直循环,直到current指向最后一个节点
          while (current.next){
            current = current.next
          }
          // 最后节点的next指向新的节点
          current.next = newNode
        }
        //3.添加完新结点之后length+1
        this.length += 1
      }
      // 二、实现toString方法
      LinkList.prototype.toString = () => {
        // 1.定义变量
        let current = this.head
        let listString = ""

    // 2.循环获取一个个的节点
        while(current){ 
          listString += current.data + " "
          current = current.next//千万不要忘了拼接完一个节点数据之后,让current指向下一个节点
        }
        return  listString
      }

    // 3. insert方法
    LinkList.prototype.insert = function(positon,data){
        // 对position进行一个合法判断
        if(positon < 0 || positon >this.length ) {return false}
        // 创建新的节点
        var newNode = new Node(data)
        // 将节点插入
        if(positon == 0){
            // 当position为0时将元素插入最前面
            newNode.next = this.head
            this.head = newNode
        }else{
            // 创建变量index来定位current到positon处位置
            var index = 0
            var current = this.head
            var previous = null
            while (index++ < positon){
                // current定位到position处
                previous = current
                current = current.next
            }
            // 将newCode插入进来
            newNode.next = current
            previous.next = newNode
        }
        // 最后整体长度  length +1
        this.length +=1
        return true
    }
    // 4. get 方法 获取对应位置元素的数据
    LinkList.prototype.get = function(position){
      // 判断position 是否合法
      if(position <0 || position >= this.length) return null

      // 创建一个标识
      var index = 0
      var current = this.head
      while(index++ < position){
        current = current.next
      }
      return current.data
    }
    // 5. indexOf 方法  查看对应数据的下标
    LinkList.prototype.indexOf = function(data){
      var index = 0
      var current = this.head
      while(current){
        if(current.data == data){
          return index
        }
        current = current.next
        index +=1
      }
      return -1
    }
    // 6. updata  方法 可以更新对应位置的数据
    LinkList.prototype.updata = function(position,data){
      if(position <0 || position >=this.length)   return false
      var index = 0
      var current = this.head
      while( index < position){
          current = current.next
          index += 1
        }
        current.data = data
        return true
    }
    // 7. removeAt 方法  将对应位置的元素删除
    LinkList.prototype.removeAt = function(position){
      if(position <0 || position >=this.length)   return null
        var index = 0 
        var current = this.head
        var previous = null
        if(position == 0){
          this.head = this.head.next
        }else{
          while(index++ < position){
            previous = current
            current = current.next
          }
          previous.next = current.next
        }
        // 设置长度减一
        this.length -= 1
        return current.data
    }
    // 8.remove 方法 根据元素来定位删除对应的节点
    LinkList.prototype.remove  = function(data){
        var positon = this.indexOf(data)
        return this.removeAt(positon)
    }

    // 9.isEmpty  方法判断链表是否为空
    LinkList.prototype.isEmpty = function(){
      return this.length == 0
    }
    // 10.size  判断链表大小
    LinkList.prototype.size = function(){
      return this.length
    }
  }
    
 //测试代码
    //1.创建LinkList
    let list = new LinkList()
    
    //2.插入数据
    list.append('aaa')
    list.append('aaa')
    list.append('bbb')
    list.append('bbb')
    list.append('ccc')

    console.log(list);

//     list.insert(0,"dsa")
//     var a= list.get(0)
//     console.log(list.toString() + a )
//     list.updata(2,"hwc")
//     console.log(list.toString())    

//     // 测试removeAt方法
//     list.removeAt(0)
//     console.log(list.toString())    
// //      测试remove 方法
// list.remove("hwc")
// console.log(list.toString())    
// alert(list.isEmpty())
// alert(list.size())

双向链表的封装

function doubleLink (){
        // 内部类 
        function link(data){
            this.data = data
            this.prev = null
            this.next = null
        }
        // 属性 
        this.length = 0
        this.tail = null
        this.head = null

        //1. append  方法
        doubleLink.prototype.append = function(data){
            // 引入数据
            var newNode = new link(data)
            if(this.length == 0){
                this.head = newNode
                this.tail = newNode
            }else{
                this.tail.next = newNode
                newNode.prev = this.tail
                this.tail = newNode
            }
            // 然后总体长度加1
            this.length += 1
        }
        
        // 2.insert 方法 在指定位置插入节点
        doubleLink.prototype.insert = function(position,data){
            // 判断定位的位置是否合法
            if(position <0 && position >this.length )  return false
                // 创建变量
            var index = 0
            var current = this.head
            var newNode = new link(data)
            if(position == 0){   // 1. 第一种为0插入到头的节点   
                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{   //  第三种插入到中间的节点
                while(index ++ < position){   // 让current 定位到要插入的节点位置
                    current = current.next
                }    //  定位完成后开始插入
                newNode.prev = current.prev
                newNode.next = current
                current.prev.next = newNode
                current.prev = newNode
            }
            // 总体长度加1
            this.length += 1
            return true
        }
        // 将链表转为字符串的方法
        // 1.  backwards  向后遍历  toString方法
        doubleLink.prototype.toString = function(){
            var result = ""
            var current = this.head
            while(current){
                result += current.data + " "
                current = current.next
            }
            return result
        }
        // 2.forwards 方法从后向前遍历
        doubleLink.prototype.forwards = function(){
            var result = ""
            var current = this.tail
            while(current){
                result += current.data + " "
                current = current.prev
            }
            return result
        }
        // 3. get 方法获取对应位置节点的数据
        doubleLink.prototype.get = function(position){
            if(position <0 || position >=this.length){return null}
                // 2分遍历 提升效率
            if(this.length / 2 > position){
            var current = this.head
            var index = 0
            while(index++ <position){
                current = current.next
            }
            }else{
                var current = this.tail
                var index = this.length-1
                while(index-- >position){
                    current = current.prev
                }
            }
            return current.data
        }
        // 4.indexOf 方法 返回元素所在节点位置,没找到就返回-1
        doubleLink.prototype.indexOf = function(data){
            var current = this.head
            var index = 0
            while(current){
                if(current.data == data){
                    return index
                }
                index ++
                current = current.next
            }
            return -1
        }
        // 5.updata 方法 修改某个位置的元素
        doubleLink.prototype.updata = function(position , data){
            if(position <0||position>=this.length) return false
            var current = this.head
            var index = 0
            while(index++<position){
                current = current.next
            }
            current.data = data
        }
        // 6. removeAt 方法 移除特定位置的节点
        doubleLink.prototype.removeAt = function(position){
            if(position <0||position>=this.length) return false
            var current = this.head
            var index = 0
            if(position == 0){
                this.head = this.head.next
                this.head.prev = null
            }else if(position == this.length-1){
                this.tail = this.tail.prev
                this.tail.next = null
            }else{
            while(index++ < position){
                current = current.next
            }
            current.prev.next = current.next
            current.next.prev = current.prev
            }
            this.length --
            return current.data
        }
        // 7. remove 方法根据元素来删除指定节点
        doubleLink.prototype.remove = function(data){
            return this.removeAt(this.indexOf(data))
        }
        // 8. isEmpty 方法
        doubleLink.prototype.inEmpyt = function(){
            return this.length ==0
        }
        // 9. size 方法
        doubleLink.prototype.size = function(){
            return this.length
        }
        // 10.getHead获取链表第一个元素
        doubleLink.prototype.getHead = function(){
            return this.head.data
        }
        // 11.getTail获取链表最后一个元素
        doubleLink.prototype.getTail = function(){
            return this.tail.data
        }
    }

    // 代码测试
    var lk = new doubleLink()
    lk.append("aaa")
    lk.append("bbb")
    lk.append("ccc")
    lk.append("ccc")
    // alert(lk)
    // alert(lk.forwards())
    // 测试 insert 方法
    lk.insert(0,"abc")
    lk.insert(5,"hwc")
    lk.insert(2,"ljc")
    console.log(lk.toString());
    // get方法的测试
    // console.log(lk.get(0));
    // console.log(lk.get(6));
    // indexOf 方法的测试
    // console.log(lk.indexOf("hwc"));
    // console.log(lk.indexOf("ljc"));
    // updata 方法的测试
    // lk.updata(0,"woshinidie")
    // lk.updata(6,"inidie")
    // removeAt 方法测试
    // lk.removeAt(0) 
    // console.log(lk.remove(6));
    // lk.removeAt(1)

    // remove 方法的测试
    // lk.remove("hwc")
    // lk.remove("ljc")
    // console.log(lk.toString()); 
    // 其他方法的测试
    // console.log(lk.inEmpyt());
    // console.log(lk.size());
    // console.log(lk.getHead());
    // console.log(lk.getTail());

反转链表

var reverseList = function(head) {
    if(!head) return null
    var p = head.next
    head.next = null //  让头节点变为尾节点
    var temp
    while(p){
        temp = p.next  // 让迭代的节点next指向前一个节点
        p.next = head
        head = p
        p = temp
    }
    return head
};
// 递归的基线条件:遍历到末节点(node.next === null)
// 递归的递归条件:node.next !== null
// 当遇到末节点时,返回末节点,前一节点接收末节点,并把末节点的next设置为自身,返回前一节的,继续下去
// 考虑特殊情况:undefined和null
var reverseList = function (head) {
    // 闭包
    if (head === undefined || head === null) return null
    var originalHead = head
    var reverseHead
    var reverse = function (head) {
        if (head.next === null) {
            reverseHead = head
            return head
        } else {
            var node = reverse(head.next)
            node.next = head
            if (originalHead === head) {
                head.next = null
                return reverseHead
            } else {
                return head
            }
        }
    }
    return reverse(head)
};

复杂链表的复制

//     使用Map数据结构来进行赋值
// 键存放指向节点的指针
// 值存放new Node
// 第一次循环构建Map数据结构
// 第二次循环给值的next域和random域进行赋值
// 记住一点:这里的值的next域和random域不能指向node的节点,只能指向map数据结构中的值的节点
// 实现代码
var copyRandomList = function(head) {

    // 首先创建一个Map,键用来存储head指针域,值用来存储复制的节点
    let node = head;
    const m = new Map();

    // 遍历现有链表
    while (node) {
        m.set(node,new Node(node.val));
        node = node.next;
    }
    // 到这里,链表的键已经存放的是指针,值存放的是next域和random域为空,值为指针所指的val
    // 让node重新指向head
    node = head;
    // 第二次遍历,给Map数据结构中的值,添加上next域和random域
    while (node) {
        m.get(node).next = node.next ? m.get(node.next):null;
        m.get(node).random = node.random ? m.get(node.random):null;
        node = node.next;
    }
    node = head;
    return m.get(node);

};

lru 缓存

  //  一个Map对象在迭代时会根据对象中元素的插入顺序来进行
// 新添加的元素会被插入到map的末尾,整个栈倒序查看
class LRUCache {
  constructor(capacity) {
    this.secretKey = new Map();
    this.capacity = capacity;
  }
  get(key) {
    if (this.secretKey.has(key)) {
      let tempValue = this.secretKey.get(key);
      this.secretKey.delete(key);
      this.secretKey.set(key, tempValue);
      return tempValue;
    }
    else return -1;
  }
  put(key, value) {
    // key存在,仅修改值
    if (this.secretKey.has(key)) {
      this.secretKey.delete(key);
      this.secretKey.set(key, value);
    }
    // key不存在,cache未满
    else if(this.secretKey.size<this.capacity){
      this.secretKey.set(key, value);
    }
    // 添加新key,删除旧key
    else{
      this.secretKey.set(key,value);
      // 删除map的第一个元素,即为最长未使用的
      //这个 keys 方法返回一个新的 Iterator 对象, 它按插入顺序包含了 Map 对象中每个元素的键 next()调用了接口获取第一个键的值
      //这个 values 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值。 *
      //  这个 entries 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组
      this.secretKey.delete(this.secretKey.keys().next().value);
    }
  }
}
let cache = new LRUCache(2);
cache.put(1, 1);
cache.put(2, 2);
console.log("cache.get(1)", cache.get(1))// 返回  1
cache.put(3, 3);// 该操作会使得密钥 2 作废
console.log("cache.get(2)", cache.get(2))// 返回 -1 (未找到)
cache.put(4, 4);// 该操作会使得密钥 1 作废
console.log("cache.get(1)", cache.get(1))// 返回 -1 (未找到)
console.log("cache.get(3)", cache.get(3))// 返回  3
console.log("cache.get(4)", cache.get(4))// 返回  4

集合结构的封装

function Set(){
        // 属性
        this.item = {}
        // 方法
        // 1.add方法
        Set.prototype.add = function(data){
            if(this.has(data))  return false
            this.item[data] = data
        }
        // 2.has 方法
        Set.prototype.has = function(data){
            return this.item.hasOwnProperty(data)  // 判断是否有该属性
        }
        // 3.remove 方法
        Set.prototype.remove = function(data){
            if(!this.has(data)) return false
            delete this.item[data]
            return true
        }
        // 4.clear 方法
        Set.prototype.clear = function(){
            this.item = {}
        }
        // 5.size 方法
        Set.prototype.size = function(){
            return Object.keys(this.item).length
        }
        // 6.getValue 方法获取集合所有的值
        Set.prototype.getValue = function(){
            return Object.keys(this.item)
        }
        // 集合之间的操作
        // 并集
        Set.prototype.union = function(otherSet){
            // 创建个新集合
            let Newset = new Set()
            // 将A集合放进去
            for (let i of this.getValue()){
                Newset.add(i)
            }
            // 将B集合放进去
            for(let i of otherSet.getValue()){
                Newset.add(i)
            }
            return Newset.getValue()
        }
        // 交集 
        Set.prototype.intersection = function(otherSet){
            // 创建个新集合
            let Newset = new Set()
            let result = this.getValue()
            for(let i of result){
                if(otherSet.has(i)){
                    Newset.add(i)
                }
            }
        // }
            return Newset.getValue()
        }
        // 差集的实现
        Set.prototype.different = function(otherSet){
            let Newset = new Set()
            // 将A的元素拿出来
            let result = this.getValue()
            // 循环比较
            for(let i of result){
                if(!otherSet.has(i)){
                    Newset.add(i)
                }
            }
            return Newset.getValue()
        }
        // 子集的判断
        Set.prototype.subSet = function(otherSet){
            // 将A的集合提出来
            let result = this.getValue()
            // 判断是否是B的子集
            for(let i of result){
                if (!otherSet.has(i)) {
                    return false
                }else{
                    return true
                }
            }
        }
    }

    // 代码测试
    let set = new Set()
    set.add('ddd')
    set.add('aaa')
    set.add('bbb')
    set.add('ccc')
    // console.log(set.getValue());
    set.remove("aaa")
    set.remove("bbb")
    console.log(set.getValue());
    // console.log(set.size());
    // console.log(set.clear());
    // console.log(set.getValue());
    // let b = new Set()
    // b.add("aaa")
    // b.add("bbb")
    // b.add("ccc")
    // for( let i of b.getValue()){
    //     console.log(i);
    // }

    // 集合的测试
    let setB = new Set()
    setB.add("www")
    setB.add("qqq")
    setB.add("ddd")
    setB.add("rrr")
    setB.add("ccc")
    // setB.add("ddd")
    console.log(setB.getValue());
    // console.log(set.union(setB));
    // 交集
    // console.log(set.intersection(setB));
    // 差集
    // console.log(setB.different(set));
    // 子集的判断
    console.log(setB.subSet(set));

哈希表

哈希函数的实现

// 哈希函数
  // str 要插入的字符串 , size  数组的大小
  function hashFuc(str,size){
    // 1.创建一个hasCode 将str 转为较大的数
    let hasCode = 0
    //  2. 用霍纳算法,计算hasCode的值
    for(let i =0 ;i<str.length;i++){
      hasCode = hasCode * 37 + str.charCodeAt(i)
    }
    // 3. 将大的值取余操作
    let result = hasCode % size
    return result
  }


  // 代码测试
  alert(hashFuc("abc",7))
  alert(hashFuc("nba",7))
  alert(hashFuc("cba",7))
  alert(hashFuc("mba",7))

哈希表添加更改

// 哈希表
    function hashTable() {
      // 属性
      this.storage = []  // 存储
      this.count = 0  // 计数
      this.limit = 7  // 表长

      // 哈希函数
      // str 要插入的字符串 , size  数组的大小
      hashTable.prototype.hashFuc = function (str, size) {
        // 1.创建一个hasCode 将str 转为较大的数
        let hasCode = 0
        //  2. 用霍纳算法,计算hasCode的值
        for (let i = 0; i < str.length; i++) {
          hasCode = hasCode * 37 + str.charCodeAt(i)
        }
        // 3. 将大的值取余操作
        let result = hasCode % size
        return result
      }

      // 1. 添加更改 put
      hashTable.prototype.put = function(key,value){
        // 获取哈希表索引 根据key来获取
        let index = this.hashFuc(key,this.limit)
        // 判断对应索引有没有对应的bucket数组
        let bucket = this.storage[index]
        if(bucket == null){
          bucket = []
          this.storage[index] = bucket
        }
        // 判断buckt 里有无重复key
        for(let i of this.storage[index]){
          if(i[0] == key){
            i[1] = value
            return true
          }
        }
        // 添加
        
        this.storage[index].push([key,value])
        this.count += 1

        // 判断数组是否需要扩容
        if(this.count > this.limit * 0.75){
          this.resize(this.getPrime(this.limit *2))
        }

        return true

        
      }

      // 2.获取对应key的值 get 方法
      hashTable.prototype.get = function(key){
        // 获取index
        let index = this.hashFuc(key,this.limit)
        // 判断有无bucket
        if(this.storage[index] == null) return null
        let bucket = this.storage[index]
        for (let i of bucket){
          if(i[0] == key){
            return i[1]
          }
        }
        // 若无找到返回null
        return null
      } 

      // 3. 删除对应key的 delete方法
      hashTable.prototype.delete = function(key){
        // 获取下标索引
        let index = this.hashFuc(key,this.limit)
        let bucket = this.storage[index]
        // 判断对应链表有无空
        if(bucket == null) return null
        // 循环找相等的key
        for(let i in bucket){
          let tuple = bucket[i]
          // 找到就删除
          if(tuple[0] == key){
            bucket.splice(i,1)
            this.count -= 1

            // 判断是否需要减容
            if(this.limit >7 && this.count < this.limit * 0.25){
              this.resize(this.getPrime(Math.floor(this.limit / 2)))
            }
            return 
          }
        }
        // 删除失败
        return null
      }

      // 4.判断哈希表是否为空
      hashTable.prototype.isEmpty = function(){
        return this.count == 0
      }
      // 5.返回哈希表的个数
      hashTable.prototype.size = function(){
        return this.count
      }

      // 6.数组扩容/减容 resize
      hashTable.prototype.resize = function(newsize){
        // 将旧数组保留
        let oldsize = this.storage
        // 属性重置
        this.limit = newsize
        this.count = 0
        this.storage = []

        // 将旧数组元素加到新数组
        for(let i in oldsize){
          let bucket = oldsize[i]
          if(bucket[0] == null){
              continue
            }
          for(let j in bucket){
            // 将桶里的数据全部加进去
            this.put(j[0],j[1])
          }
        }
      }

      // 7.判断是否为质数 isPrime
      hashTable.prototype.isPrime = function(num){
        for(i=2;i<Math.sqrt(num);i++){
          if(num % i == 0){
            return false
          }
        }
        return true
      }

      // 8.将一个数转为质数  getPrime
      hashTable.prototype.getPrime = function(num){
        while(!this.isPrime(num)){
          num++
        }
        return num
      }
    }

    // 测试方法
    let ht = new hashTable()
    // 添加
    ht.put("aaa",123)
    ht.put("bbb",333)
    ht.put("ccc",223)
    ht.put("ddd",523)
    ht.put("eee",523)
    ht.put("fff",523)
    // alert(ht.get("aaa"))
    // 更改
    ht.put("aaa",222)
    ht.put("aww",222)
    // alert(ht.get("aaa"))
    // 删除
    // ht.delete("aaa")
    // alert(ht.get("aaa"))
    // 测试扩容
    alert(ht.limit)

二叉搜索树

对称的二叉树

//     请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

// 例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

//     1
//    / \
//   2   2
//  / \ / \
// 3  4 4  3
// 但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

//     1
//    / \
//   2   2
//    \   \
//    3    3

var isSymmetric = function(root) {
    if(root == null) return null
    return dfs(root.left,root.right)
    function dfs(left,right){
      // 当左右都为空则为true
      if(left== null && right ==null) return true
      // 对称的条件
      if(left != null && right != null && left.val == right.val){
        // 继续递归判断
          return dfs(left.left,right.right) && dfs(left.right,right.left)
      }
      return false
    }
};

二叉搜索树的封装

function BinarySearchTree(){
    // 属性的封装
    this.root = null

    // 传进的节点封装
    function Node(key){
      this.key = key
      this.left = null
      this.right = null
    }

    // 1.insert 插入方法
    BinarySearchTree.prototype.insert = function(key){
      const newNode = new Node(key)
      if(!this.root){
        this.root = newNode
      }else{
        this.insertNode(this.root,newNode)
      }
    }

    BinarySearchTree.prototype.insertNode = function(node,newNode){
      if(newNode.key < node.key){
        // 向左查找
        if(node.left == null){
          node.left = newNode
        }else{
          this.insertNode(node.left,newNode)
        }
      }else{
        //向右查找
        if(node.right == null){
          node.right = newNode
        }else{
          this.insertNode(node.right,newNode)
        }
      }
    }

    // 数的遍历 (先序遍历)
    BinarySearchTree.prototype.preOrderTranversal = function(handle){
      this.preOrderTranversalNode(this.root,handle)
    }
    BinarySearchTree.prototype.preOrderTranversalNode = function(node,handle){
      if(node!==null){
        // 打印遍历过的节点
        handle(node.key)
        // 遍历所有左节点
        this.preOrderTranversalNode(node.left,handle)
        // 遍历所有右节点
        this.preOrderTranversalNode(node.right,handle)
      }
    }

    // (中序遍历)
    BinarySearchTree.prototype.midOrderTranversal = function(handle){
      this.midOrderTranversalNode(this.root,handle)
    }
    BinarySearchTree.prototype.midOrderTranversalNode = function(node,handle){
      if(node!==null){
        // 遍历所有左节点
        this.midOrderTranversalNode(node.left,handle)
        // 打印遍历过的节点
        handle(node.key)
        // 遍历所有右节点
        this.midOrderTranversalNode(node.right,handle)
      }
    }
    // (后序遍历)
    BinarySearchTree.prototype.postOrderTranversal = function(handle){
      this.postOrderTranversalNode(this.root,handle)
    }
    BinarySearchTree.prototype.postOrderTranversalNode = function(node,handle){
      if(node!==null){
        // 遍历所有左节点
        this.postOrderTranversalNode(node.left,handle)
        // 遍历所有右节点
        this.postOrderTranversalNode(node.right,handle)
        // 打印遍历过的节点
        handle(node.key)
      }
    }

    // min 找到树中最小值
    BinarySearchTree.prototype.min = function(){
      let node = this.root
      var key = null
      while(node){
        key = node.key
        node = node.left 
      }
      return key
    }

    // max 找到树中最大值
    BinarySearchTree.prototype.max = function(){
      let node = this.root
      var key = null
      while(node){
        key = node.key
        node = node.right
      }
      return key
    }
    // search 查找特地值
    BinarySearchTree.prototype.search = function(key){
      return this.searchNode(this.root,key)
    }
    BinarySearchTree.prototype.searchNode = function(node,key){
      if(node == null ) return false
      if(node.key == key) return true 
      if(key < node.key){
        return this.searchNode(node.left,key)
      }else{
        return this.searchNode(node.right,key)
      }
    }

    // 删除指定节点
    BinarySearchTree.prototype.delete = function(key){
      // 定义变量
      var current = this.root
      var parent = null
      var isLeftChild 

      // 1.查找对应节点位置
      while(current.key!=key){
        if(key < current.key){
          isLeftChild = true
          parent = current
          current = current.left
        }else{
          isLeftChild = false
          parent = current
          current = current.right
        }
        // 两种都没找到
        if(current==null) return false
      }
      // 2.1 叶子节点
      if(current.left==null && current.right==null){
        if(this.root == current) {this.root = null}
        else if(isLeftChild){
          parent.left = null
        }else{
          parent.right = null
        }
      }
      // 2.2 只有一个子节点
      else if(current.right==null){
        if(current == this.root){
          this.root = current.left
        }else if(isLeftChild){
          parent.left = current.left
        }else{
          parent.right = current.left
        }
      }else if(current.left == null){
        if(current == this.root){
          this.root = current.right
        }else if(isLeftChild){
          parent.left = current.right
        }else{
          parent.right = current.right
        }
      }
      // 2.3 有两个子节点
      else{
        let success =  this.getSuccess(current)
        if(this.root == current){
          success.right = current.right
          success.left = current.left
        }else if(isLeftChild){
          parent.left = success
        }else{
          parent.right = success
        }
        success.left = current.left
      }
      return true
    }
    BinarySearchTree.prototype.getSuccess = function(delNode){
      let success = delNode
      let successParent = delNode
      let current = delNode.right
      while(current!=null){
        successParent = success
        success = current
        current = current.left
      }
      // 判断找到的最左节点有无右节点
      if(success  != delNode.right){
        successParent.left = success.right
        success.right = delNode.right
      }
      return success
    }
  }
  
  // 代码测试
  const app = new BinarySearchTree()
  app.insert(11)
  app.insert(7)
  app.insert(15)
  app.insert(5)
  app.insert(3)
  app.insert(9)
  app.insert(8)
  app.insert(10)
  app.insert(13)
  app.insert(12)
  app.insert(14)
  app.insert(20)
  app.insert(18)
  app.insert(25)
  app.insert(6)
// 先序遍历
  // var result = ""
  // app.preOrderTranversal(function(key){
  //   result += key + ' '
  // })
  // console.log(result);
  // // 中序遍历
  // result = ""
  // app.midOrderTranversal(function(key){
  //   result += key + ' '
  // })
  // console.log(result);
  // 后序遍历
  result = ""
  app.postOrderTranversal(function(key){
    result += key + ' '
  })
  console.log(result);

//   // 测试最值
//   console.log(app.min());
//   console.log(app.max());

// 测试查找
// console.log(app.search(20));
// console.log(app.search(3));
// console.log(app.search(6));
// console.log(app.search(30));
// console.log(app.delete(6));
// console.log(app.delete(15));
// console.log(app.delete(18));
// console.log(app.delete(30));
// result = ""
//   app.midOrderTranversal(function(key){
//     result += key + ' '
//   })
//   console.log(result);

树的子结构

//     输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

// B是A的子结构, 即 A中有出现和B相同的结构和节点值。

// 例如:
// 给定的树 A:

//      3
//     / \
//    4   5
//   / \
//  1   2
// 给定的树 B:

//    4 
//   /
//  1
// 返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值
var isSubStructure = function(A, B) {
  // 判断是否是树的子结构有两种情况
    // 情况1:当前节点是子结构
    // 情况2:当前节点的左右子树是子结构
    // 如果A节点为空,或者B节点为空,都说明不是子树
    if (!A || !B) {
        return false;
    }
    return dfs(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B)

    function dfs(A,B) {
        // 如果B的节点为空,说明B已经遍历完了,说明此时B是A的子结构
        if (!B) {
            return true;
        }
        // 如果A都遍历完了,说明B不是子结构
        if (!A) {
            return false;
        }
        // 如果当前节点不同,则返回false
        if (A.val !== B.val) {
            return false;
        }
        // 当前节点相同,还要判断当前节点的左右子树是否都相同
        return dfs(A.left,B.left) && dfs(A.right,B.right)
    
    }
};

BFS 广度优先搜索

// BFS
   // 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
   var levelOrder = function(root) {
    if(root == null) return []
    var queue = []
    queue.push(root)
    var res = []
    // 当队列还有元素时就继续遍历
    // 先将根元素放入遍历
    while(queue.length){
      // 将头元素拿出来遍历
        var key = queue.shift()
        // 遍历到的元素加入数组中
        res.push(key.val)
        // 如果有左节点就放进去遍历
        if(key.left){
            queue.push(key.left)
        }
        // 如果有右节点就放右节点遍历
        if(key.right){
            queue.push(key.right)
        }
    }
    return res
};

排序算法

堆排序

var len
  // 建立最大堆
  function buildMaxHeap(arr){
    len = arr.length
    // 从非叶子节点开始调整
    for(let i = Math.floor(len/2) ; i>=0;i--){
      heapify(arr,i)
    }
  }
  // 堆调整
  function heapify(arr,i){
      var left = 2*i+1
      var right = 2*i+2
      var largest = i
      if(left < len && arr[left]>arr[largest]){
        largest = left
      }
      if(right < len && arr[right]>arr[largest]){
        largest = right
      }
      if(largest != i){
        swap(arr,i,largest)
        heapify(arr,largest)
      }
    }
  function swap(arr,m,n){
    var temp = arr[m]
    arr[m] = arr[n]
    arr[n] = temp
  }
  function heapSort(arr){
    // 建立最大堆
    buildMaxHeap(arr)
    // 将堆顶元素放到末尾
    for(let i = arr.length-1;i>0;i--){
      swap(arr,0,i)
      len--
      // 每次交换完后调整堆
      heapify(arr,0)
    }
    return arr
  }

let arr = [12,3,2,4,5,2,1,3]
console.log(heapSort(arr)); 

归并排序

function mergeSort(arr){
  if(arr.length<2){
    return arr
  }
  // 分
  var middle = Math.floor(arr.length/2)
  var left = arr.slice(0,middle)
  var right = arr.slice(middle)
  return merge(mergeSort(left),mergeSort(right))

  // 治
  function merge(left,right){
    var result = []
    while(left.length && right.length){
      if(left[0] < right[0]){
        result.push(left.shift())
      }else{
        result.push(right.shift())
      }
    }
    while(left.length){
      result.push(left.shift())
    }
    while(right.length){
      result.push(right.shift())
    }
    return result
  }
}

console.log(mergeSort([1,3,5,7,2,4,6,8]));

快速排序

// 快速排序的思想
    // (1)在数据集之中,选择一个元素作为"基准"(pivot)。
// (2)所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。

// (3)对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

     var quickSortFind = function(arr){
      // 递归结束条件
      if(arr.length<=1){
        return arr
      }
      // 从中间开时选择基准元素
      var center = Math.floor(arr.length/2 )
      var pivot = arr.splice(center,1)[0]
      var left = []
      var right = []
      // 循环比较出比基准小的加在左数组,大的加到右数组
      for(var i = 0;i<arr.length;i++){
        if(arr[i] < pivot){
          left.push(arr[i])
        }else{
          right.push(arr[i])
        }
      }
      // 最后循环递归出所有子集剩下一个元素时再并成一个数组
      return [...quickSortFind(left),pivot,...quickSortFind(right)]
      //  quickSortFind(left).concat(pivot,quickSortFind(right))
    }
  
    console.log(quickSortFind([1,3,5,7,2,4,6,8]));

冒泡排序,希尔,多重排序

function arrayList(){
    // 属性
    this.array = []
    // 方法
    // 添加元素的方法
    arrayList.prototype.insert = function(key){
      this.array.push(key)
    }
    // 重写toString方法
    arrayList.prototype.toString = function(){
      return this.array.join("-")
    }
    // 元素交换方法
    arrayList.prototype.swap = function(m,n){
      var c = this.array[m]
      this.array[m] = this.array[n]
      this.array[n] = c 
    }


    // 冒泡排序
    arrayList.prototype.bubbleSort = function(){
      length = this.array.length
      for(let j = 0;j<length;j++){
        for(let i = 0;i<length-1-j;i++){
          if(this.array[i] > this.array[i+1]){
            this.swap(i,i+1)
          }
        }
      }
    }
    // 选择排序
    arrayList.prototype.selectionSort = function(){
      length = this.array.length
      // 外层循环从0开始一个一个进行选择
      for(let i = 0;i<length-1;i++){
        var min = i
        // 内层循环  将min 跟列表内min+1的元素一个一个进行对比
        for(let j = min+1;j<length;j++){
          if(this.array[min] > this.array[j]){
            min = j
          }
        }
        // 将选择完的元素进行对调
        this.swap(min,i)
      }
    }
    // 插入排序
    arrayList.prototype.insertSort = function(){
      var length = this.array.length
      // 从i位置开始取出
      for(let i = 1;i<length;i++){
        var temp = this.array[i]
        var j = i
        // 将i位置的元素一次和前面进行比较
        while(this.array[j-1] > temp && j>0){
          this.array[j] = this.array[j-1]
          j--
        }
        // 比较完后j位置的数据放置temp
        this.array[j] = temp
      }
    }
    // 希尔排序
    arrayList.prototype.shell = function(){
      var gap = Math.floor(this.array.length / 2)
      while(gap>=1){
        for(let i =gap;i<this.array.length;i++){
          var temp = this.array[i]
          var j = i
          // 逐步向前比较排序
          while(this.array[j-gap] > temp &&j>=gap-1){
            this.array[j] = this.array[j-gap]
            j -= gap
          }
          this.array[j] = temp
        }
        gap = Math.floor(gap / 2 )
      }
    }
  }

    // 测试
    const list = new arrayList()
    list.insert(213)
    list.insert(2)
    list.insert(3)
    list.insert(1)
    list.insert(5)
    list.insert(3)
    list.insert(333)
    list.insert(22)
    list.insert(15)
    list.insert(33)
    list.insert(22)
    // 冒泡排序
    list.bubbleSort()
    alert(list)
    // 选择排序
    // list.selectionSort()
    // 插入排序
    // list.insertSort()
    // 希尔排序
    // list.shell()
    
    alert(list)

算法题

背包问题

// 目标和
//     输入:nums = [1,1,1,1,1], target = 3
// 输出:5
// 解释:一共有 5 种方法让最终目标和为 3 。
// -1 + 1 + 1 + 1 + 1 = 3
// +1 - 1 + 1 + 1 + 1 = 3
// +1 + 1 - 1 + 1 + 1 = 3
// +1 + 1 + 1 - 1 + 1 = 3
// +1 + 1 + 1 + 1 - 1 = 3

// 递归遍历每种方程,最后满足条件的count++
var findTargetSumWays = function(nums, target) {
    let count = 0;
    const backtrack = (nums, target, index, sum) => {
        if (index === nums.length) {
            if (sum === target) {
                count++;
            }
        } else {
            backtrack(nums, target, index + 1, sum + nums[index]);
            backtrack(nums, target, index + 1, sum - nums[index]);
        }
    }
    
    backtrack(nums, target, 0, 0);
    return count;
};

动态规划

//     给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
// 示例 1:
// 输入: [2,3,-2,4]
// 输出: 6
// 解释: 子数组 [2,3] 有最大乘积 6。
// 示例 2:
// 输入: [-2,0,-1]
// 输出: 0
// 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
    // 2 思路
// 这道题用动态规划的思路做,由于有负数的存在,两个负数相乘为正,则还必须在迭代的过程中同时维护乘积的最小值和最大值
    var maxProduct = function(nums) {
  let len = nums.length;
  if (len === 0) return 0;
  if (len === 1) return nums[0];
  let max = nums[0];
  let min = nums[0];
  let ans = nums[0];
  for (let i=1; i<len; i++) {
    let maxF = Math.max(max * nums[i], nums[i], min * nums[i]);
    let minF = Math.min(max * nums[i], nums[i], min * nums[i]);
    max = maxF;
    min = minF;
    ans = Math.max(ans, max);
  }
  return ans;
};

//  打家劫舍 , 【1,3,2】 取 3
// 这次的房屋围成了一个环状,如果偷了第一家,就一定不能偷最后一家,
// 因此我们考虑将环状拆分为两段,分别为1...n-1和2...n,分别用动态规划计算其能偷到的最大金额,然后两者取最大即可
var rob = function(nums) {
    if(nums.length == 1 || nums.length == 2) return Math.max(...nums)
    var d = [nums[0]]
    var d1 = [0,nums[1]]
    var len = nums.length
    for(let i = 1 ;i<len-1;i++){
        if(i == 1){
            d[i] = Math.max(d[0],nums[i])
        }else{
            d[i] = Math.max(d[i-1],d[i-2]+nums[i])
        }
    }
    for(let j = 2;j<len;j++){
        if(j==2){
            d1[j] = Math.max(d1[1],nums[j])
        }else{
            d1[j] = Math.max(d1[j-1],d1[j-2]+nums[j])
        }
    }
    return Math.max(d[d.length-1],d1[d1.length-1])
};

// 股票最大利润
// 动态规划,主要是找出f(n)的表达式
// 最大利润,就是当前价格,减去以往那么多天的出现过的最小价格
// f(n) = prices[n] - min(n - 1)
// 核心是记录截止到当天n之前出现的最小的价格
var maxProfit = function(prices) {
    if(prices <= 1) return 0
    var profit = 0
    var minArr = [prices[0]]
    var nowPrice
    var min
    for (let i = 1;i<prices.length;i++){
        nowPrice = prices[i]
        min = minArr[minArr.length-1]
        if(nowPrice - min >profit){
            profit = nowPrice -min
        }
        if(nowPrice < min){
            minArr.push(nowPrice)
        }
    }
    return profit
};

// 连续子数组最大和
// 输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
// 输出: 6
// 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
var maxSubArray = function(nums) {
    let res = nums[0];
    for (let i = 1; i < nums.length; ++i) {
        if (nums[i - 1] > 0) {
            nums[i] += nums[i - 1];
        }
        res = Math.max(res, nums[i]);
    }
    return res;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值