学习笔记第二周

111 阅读9分钟

周一题目

  1. leetcode: 分隔链表

    解题思路: 根据自己的理解,翻译过来就是一个链表中把小于给定数值进行翻转到大于或等于给定数值的前面,但是要注意的是原来有链表里的相对位置不能改变,也就是原来对应的前后顺序不能乱掉。整理了一番,和头插法很像,只不过翻转的时候和传入的数值进行的比对。(看题解,双链进行拼接感觉很不错,有空试试)

      /**
      * @param {ListNode} head
      * @param {number} x
      * @return {ListNode}
      */
      var partition = function(head, x) {
         if(head === null){
           return head;
         }
         const dummy = new ListNode(-1);
         dummy.next = head;
         let curr = head;
         let pre = dummy;
         if(curr.val< x){
             pre = curr;
         }
         while(curr&&curr.next){
           const next = curr.next;
           if(next.val < x && curr.val>=x) {
             curr.next = next.next;
             next.next =pre.next;
             pre.next = next;
           }else {
             curr = curr.next;
           }
           if(next.val<x){
              pre = next;
           }
           
         }
        
         return dummy.next;
    
      };
    
  2. leetcode: 复制带随机指针的链表

    解题思路:题目都不懂的渣渣。看看题解,如有1->2->3 这样的数据链表,1的random指向3,2的random指向null,3的random指向2,

    /**
    * @param {Node} head
    * @return {Node}
    */
    var copyRandomList = function(head) {
        if(head===null){
          return head;
        }
        let curr = head;
        //第一步:在每个原节点后面创建一个新节点
        //1->1'->2->2'->3->3'
        while(curr){
          const newCurr = new Node(curr.val);
          newCurr.next =curr.next;
          curr.next = newCurr;
          curr = newCurr.next;
        }
        curr = head;
        //第二步:设置新的random指向
        // 从上述可以模拟出当前random如果指向j的值,那么链表的next的random就指向j.next
        while(curr){
          if(curr.random!==null){
            curr.next.random = curr.random.next; 
          }
          curr = curr.next.next;
        }
        // 第三步,分离出1->1'->2->2'->3->3'  变成1->2->3 和 1'->2'->3'
        const dummy = new Node(-1);
        let newCurr = dummy;
        curr = head;
        while(curr){
          newCurr.next = curr.next;
          newCurr = newCurr.next;
          curr.next = newCurr.next; 
          curr = curr.next;
        }
        return dummy.next;
    
    
    
    };
    
  3. leetcode:设计循环队列

    首先要弄懂队列的含义,像栈一样,队列(queue)也是一种线性表,它的特性是先进先出,插入在一端,删除在另一端。就像排队一样,刚来的人入队(push)要排在队尾(rear),每次出队(pop)的都是队首(front)的人。 5.png 6.png 循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。

    解题思路: 根据上述说明和题目要求,先定义出构造函数

       /**
       * 定义
       * @param {number} k
       */
       var MyCircularQueue = function(k) {
           this.size = k; // 队列长度
           this.head = -1; // 头部
           this.tail = -1; // 尾部
           this.queqe = []; // 队列内容
       };
    
       /** 
       * 插入数据
       * @param {number} value
       * @return {boolean}
       */
       MyCircularQueue.prototype.enQueue = function(value) {
           //先判断是否已经满了,满了就返回false
           if(this.isFull()){
             return false;
           }
           if(this.isEmpty()){
              this.head = 0;
           }
           this.tail = (this.tail+1)%this.size;
           this.queqe[this.tail] = value;
           return true;
       };
    
       /**
       * 删除
       * @return {boolean}
       */
       MyCircularQueue.prototype.deQueue = function() {
          if(this.isEmpty()){
            return false;
          }
           this.quequ[this.head] === undefined;
           //只有一个数组的情况下
          if(this.head === this.tail){
            this.head = -1;
            this.taile = -1;
          }else {
             //设置头部的位置
             this.head = (this.head+1)%this.size;
          }
         
          
               
           return true;
       };
    
       /**
       * 查询头部
       * @return {number}
       */
       MyCircularQueue.prototype.Front = function() {
         if(this.isEmpty()){
            return -1;
          }
          return this.queqe[this.head];
       };
    
       /**
       * 查询尾部
       * @return {number}
       */
       MyCircularQueue.prototype.Rear = function() {
         if(this.isEmpty()){
            return -1;
          }
          return this.queqe[this.tail];
       };
    
       /**
       * 判断是否为空
       * @return {boolean}
       */
       MyCircularQueue.prototype.isEmpty = function() {
          return this.head === -1;
       };
    
       /**
       * 判断是否满
       * @return {boolean}
       */
       MyCircularQueue.prototype.isFull = function() {
         // 向队尾插入元素,tail+1 如果tail+1>=size 那么tail=(tail+1)%size 
         // 简单的说最后一个元素被使用后,要再存下一个元素,按照循环队列,
         // 可以存到数组的开头,
         return (this.tail+1)%this.size === this.head;
    
       };
    
       /**
       * Your MyCircularQueue object will be instantiated and called as such:
       * var obj = new MyCircularQueue(k)
       * var param_1 = obj.enQueue(value)
       * var param_2 = obj.deQueue()
       * var param_3 = obj.Front()
       * var param_4 = obj.Rear()
       * var param_5 = obj.isEmpty()
       * var param_6 = obj.isFull()
       */
    
  4. leetcode: 设计循环双端队列

    双端队列:头和尾都可以插入或者删除

    解题思路:把这个想象成数组,数组的头和尾都可以进行插入或删除,有原生的unshift,pop,shift的api。

        /**
        * @param {number} k
        */
        var MyCircularDeque = function (k) {
            this.deque = [];
            this.count = k;
        };
    
        /** 
        * @param {number} value
        * @return {boolean}
        */
        MyCircularDeque.prototype.insertFront = function (value) {
            if (this.isFull()) {
                  return false;
            }
            this.deque.unshift(value)
            return true;
    
        };
    
        /** 
        * @param {number} value
        * @return {boolean}
        */
        MyCircularDeque.prototype.insertLast = function (value) {
            if (this.isFull()) {
               return false; 
            }
            this.deque.push(value)
            return true;
        };
    
        /**
        * 
        * @return {boolean}
        */
        MyCircularDeque.prototype.deleteFront = function () {
            if(this.isEmpty()){
              return false;
            }
            this.deque.shift();
            return true;
        };
    
        /**
        * @return {boolean}
        */
        MyCircularDeque.prototype.deleteLast = function () {
           if(this.isEmpty()){
              return false;
            }
            this.deque.pop();
            return true;
        };
    
        /**
        * @return {number}
        */
        MyCircularDeque.prototype.getFront = function () {
            if(this.isEmpty()){
              return -1;
            }
            return this.deque[0];
        };
    
        /**
        * @return {number}
        */
        MyCircularDeque.prototype.getRear = function () {
            if(this.isEmpty()){
              return -1;
            }
            return this.deque[this.deque.length - 1];
        };
    
        /**
        * @return {boolean}
        */
        MyCircularDeque.prototype.isEmpty = function () {
            return !this.deque.length;
        };
    
        /**
        * @return {boolean}
        */
        MyCircularDeque.prototype.isFull = function () {
            return this.deque.length == this.count;
        };
    
    
     
    
    
  5. leetcode: 设计前中后队列

    解题思路:和双端对列比较相同,但是要算下中队列,注意点:当有两个 中间位置的时候,选择靠前面的位置进行操作

         var FrontMiddleBackQueue = function() {
             this.middleQueue = [];
         };
    
         /** 
         * @param {number} val
         * @return {void}
         */
         FrontMiddleBackQueue.prototype.pushFront = function(val) {
             this.middleQueue.unshift(val);
    
         };
    
         /** 
         * @param {number} val
         * @return {void}
         */
         FrontMiddleBackQueue.prototype.pushMiddle = function(val) {
             //判断数组是否为空
             if(this.middleQueue.length === 0){
               this.middleQueue.unshift(val);
             } else {  
               const l = Math.floor(this.middleQueue.length/2);
               this.middleQueue.splice(l,0,val);
             }
         };
    
         /** 
         * @param {number} val
         * @return {void}
         */
         FrontMiddleBackQueue.prototype.pushBack = function(val) {
           this.middleQueue.push(val);
         };
    
         /**
         * @return {number}
         */
         FrontMiddleBackQueue.prototype.popFront = function() {
           if(this.middleQueue.length>0){
             const l = this.middleQueue[0];
             this.middleQueue.shift();
             return l;
           }
           return -1;
         };
    
         /**
         * @return {number}
         */
         FrontMiddleBackQueue.prototype.popMiddle = function() {
             if(this.middleQueue.length === 0) {
                 return -1
             }
             let l =0;
             // 数组长度的奇偶数来判断中间位置
             if(this.middleQueue.length%2===0){
               l = Math.floor(this.middleQueue.length/2-1);
             }
             else{
               l = Math.floor(this.middleQueue.length/2);
              
             }
             const v = this.middleQueue[l];
             this.middleQueue.splice(l, 1);
             return v;
             
           
         };
    
         /**
         * @return {number}
         */
         FrontMiddleBackQueue.prototype.popBack = function() {
           if(this.middleQueue.length>0){
             const l = this.middleQueue[this.middleQueue.length -1];
             this.middleQueue.pop();
             return l;
           }
           return -1;
         };
    

周三题目

  1. leetcode:  煎饼排序

    解题思路:默认的初始数组状态排序大小不定,而我们只能对一部分数据进行翻转,如果想让最大值到最下面,首先要翻转到最上面,然后再翻转到最下。其次再找未翻转的最大值,以此类推。题目比较开放,也就是可能翻转成功,但是和示例给出的答案不一致。

      /**
      * @param {number[]} arr
      * @return {number[]}
      */
      var pancakeSort = function(arr) {
          const res = [];
          sort(arr, arr.length);
          return res;
      }
      function sort(arr, n) {
          if (n === 1) {
            return;
          };
          // 寻找最大值索引
          let maxL = 0;
          let maxIndex = 0;
          for (let i = 0; i < n; i++) {
              if (arr[i] > maxL) {
                  maxL = arr[i];
                  maxIndex = i;
              }
          }
    
          // 第一次反转,将最大值翻转到最上面
          reverse(arr, 0, maxIndex);
          // 记录反转
          res.push(maxIndex + 1);
          // 第二次翻转,翻转到最下面
          reverse(arr, 0, n - 1);
          res.push(n);
          // 递归
          sort(arr, n - 1);
    
        };
      
        // 翻转元素
        function reverse (arr, i, j) {
          // 翻转元素,对称翻转
          while (i < j) {
              const temp = arr[i];
              arr[i] = arr[j];
              arr[j] = temp;
              i++; j--;
          }
        }
      
    
  2. leetcode: 任务调度器

    解题思路:enenen~~~ 题目没看明白,看下别人的操作

      /**
      * @param {character[]} tasks
      * @param {number} n
      * @return {number}
      */
      var leastInterval = function(tasks, n) {
        let arr = Array(26).fill(0);
        for (let c of tasks) {
            //统计各个字母出现的次数
            arr[c.charCodeAt() - "A".charCodeAt()]++;
        }
        let max = 0;
        for (let i = 0; i < 26; i++) {
            //找到最大次数
            max = Math.max(max, arr[i]);
        }
        let ret = (max - 1) * (n + 1); //计算前n-1行n的间隔的时间大小
        for (let i = 0; i < 26; i++) {
            //计算和最大次数相同的字母个数,然后累加进ret
            if (arr[i] == max) {
                ret++;
            }
        }
        return Math.max(ret, tasks.length); //在tasks的长度和ret中取较大的一个
      }
    
  3. leetcode: 化栈为队

    解题思路:利用数组中的方法就能解决

      /**
      * Initialize your data structure here.
      */
      var MyQueue = function() {
        this.queue = [];
      };
    
      /**
      * Push element x to the back of queue. 
      * @param {number} x
      * @return {void}
      */
      MyQueue.prototype.push = function(x) {
          this.queue.push(x);
      };
    
      /**
      * Removes the element from in front of queue and returns that element.
      * @return {number}
      */
      MyQueue.prototype.pop = function() {
        if(this.empty()){
          return -1;
        }
        const l = this.queue[0];
        this.queue.shift();
        return l;
      };
    
      /**
      * Get the front element.
      * @return {number}
      */
      MyQueue.prototype.peek = function() {
        if(this.empty()){
          return -1;
        }
        return this.queue[0];
      };
    
      /**
      * Returns whether the queue is empty.
      * @return {boolean}
      */
      MyQueue.prototype.empty = function() {
         if(this.queue.length===0) {
           return true;
         }
         return false;
      };
    
      /**
      * Your MyQueue object will be instantiated and called as such:
      * var obj = new MyQueue()
      * obj.push(x)
      * var param_2 = obj.pop()
      * var param_3 = obj.peek()
      * var param_4 = obj.empty()
      */
    
  4. leetcode: 棒球比赛

    解题思路:根据题目,分2步,1.解析数组规则,最后相加。2.判断解析时候,前面是否都有数据。

      /**
      * @param {string[]} ops
      * @return {number}
      */
      var calPoints = function(ops) {
          const l = ops.length;
          const n = [];
          if(l<=1){
          return ops;
          }
          for(let i=0; i<l;i++){
              const nn = ops[i];
              if(nn === "C") {
                  n.pop();
              }else {
                  n.push(nn); 
              }
          }
          const nl = n.length;
          const newn = [];
          for(let i=0; i<nl;i++){
              const nn = n[i];
              switch (nn){
                case "+": 
                  const ll = (+newn[i-1])+(+newn[i-2]);
                  newn.push(ll); 
                  break;
                case "D":
                  const ld = (+newn[i-1])*2;
                  newn.push(ld);
                  break;
                default: 
                  newn.push(nn);
                  break;
              }
            }
            console.log(newn)
            return newn.reduce((a,b)=>(+a)+(+b));
          };
    
  5. leetcode: 比较含退格的字符串

    解题思路:#代表退格字符,按照题目给出的示例 “ab#c” 其实就等于“ac”,"ad#c"==>"ac",也就是说在2者比较之前,会对自身先进行运算到无退格字符。

      /**
      * @param {string} s
      * @param {string} t
      * @return {boolean}
      */
      var backspaceCompare = function(s, t) {
        return trueChart(s) === trueChart(t);
    
      };
      function trueChart(str) {
        const nS = str.split("");
        let tS = nS;
        for(let i=0;i<nS;i++){
          if(nS[i] === "#"){
            tS.splice(tS.length-1,1);
          }else{
              tS.push(nS[i]);
          }
        }
        return tS.join();
      }
    

周五题目

  1. leetcode: 验证栈序列

    解题思路:题目的意思是入栈和出栈的根据给定数组的顺序进行排列组合,如果都能匹配上为true,不能匹配false,按照这个思路,可以先定义一个临时栈。这个临时的栈的作用是模拟压入弹出,当popped中index指向的位置的元素和临时栈顶的元素一致时,出栈 。并且 index++,最后判断临时栈是否为空。

      /**
      * @param {number[]} pushed
      * @param {number[]} popped
      * @return {boolean}
      */
      var validateStackSequences = function(pushed, popped) {
          let temp = [];
          let index = 0;
          const l = pushed.length;
          for(let i=0;i<l;i++){
            temp.push(pushed[i]);
            while (popped[index] && popped[index] === temp[temp.length - 1]) {
              temp.pop();
              index++;
            }
            
          }
          console.log(temp)
          return temp.length === 0;
    
        };
    
  2. leetcode: 有效的括号

    解题思路:根据上一题的示例,可以把左边的括号当入栈,右边和左边对称的当出栈,如果正好栈为空=>true,否则为false

      /**
      * @param {string} s
      * @return {boolean}
      */
      var isValid = function(s) {
          const obj = {')':'(', ']':'[', '}':'{'};
          const temp = [];
          for(let p of s) {
              if(obj[p]){
                  if(temp.pop() !== obj[p]) {
                    return false;
                  }
              }else{
                  temp.push(p);
              }
          }
          return temp.length === 0;
    
      };
    
  3. leetcode: 删除最外层的括号

    解题思路:

    1. 利用计数法,出现"(" 加上1,当计数大于0的时候进行拼接"(",出现")"减去1,当计数大于1的时候,进行拼接")".
    2. 利用上一题的出入栈的思维,碰到 "(" 就入栈,碰到 ")" 就出栈,利用2个栈进行操作。
      /** 方法1: 计数法
      * @param {string} s
      * @return {string}
      */
      var removeOuterParentheses = function(s) {
        let nS =  "";
        let count = 0;
        if(s){
          for (let i = 0; i < s.length; i++) {
              if(s[i] === "(" && count++ > 0) {nS += "("}
              if(s[i] === "(" && count-- > 1) {nS += "("};
          }
    
        }
        return nS;
    
      };
    
    /** 方法2: 出入栈
    * @param {string} s
    * @return {string}
    */
    var removeOuterParentheses = function(s) {
      const stack = [];
      const res = [];
      for (let i of s) {
        if (stack.length === 0 && i === '(') {
          stack.push(i);
        } else if (stack.length && i === '(') {
          stack.push(i);
          res.push(i);
        } else if (stack.length > 1 && i === ')') {
          stack.pop();
          res.push(i);
        } else if (stack.length === 1 && i === ')') {
          stack.pop();
        }
      }
      return res.join('');
    };
    
  4. leetcode: 移除无效的括号

    解题思路:根据题意,有效的括号代表()即左右配对,如左右不配对,需要删除。也利用栈进行操作,循环遍历下,把括号放到栈里,如果不能配对,就进行删除。

      /**
      * @param {string} s
      * @return {string}
      */
      var minRemoveToMakeValid = function(s) {
          const stack = [];
          const res = [...s];
          const stackDel = [];
          for(let i=0; i< s.length;i++){
            if(s[i]=== "("){
              stack.push(i);
            }
            if(s[i]=== ")") {
              if(stack.length>0){
                stack.pop();
              }else {
                stackDel.push(i);
              }
            }
    
          }
          const del = stack.concat(stackDel);
          for(let i of del){
            res[i]= "";
          }
          return res.join("");
      };
    
  5. leetcode: 二叉树的后序遍历

    解题思路:先理解树的概念 树是由结点和边组成的,不存在环的一种数据结构
    7.png 与树有关的术语

    1. 树的结点(node):包含一个数据元素及若干指向子树的分支;
    2. 孩子结点(child node): 结点的子树的根称为该结点的孩子;
    3. 双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲;
    4. 兄弟结点:同一双亲的孩子结点;堂兄结点:同一层上结点;
    5. 叶子结点:也叫终端结点,是度为 0 的结点;
    6. 树的深度:树中最大的结点层数;

    二叉树的含义:二叉树是一种被高频使用的特殊树。在二叉树中,每个结点最多有两个分支,即每个结点最多有两个子结点,分别称作左子结点和右子结点。 二叉树的操作:前序遍历,中序遍历,后续遍历。 这里的序指的是父结点的遍历顺序,前序就是先遍历父结点,中序就是中间遍历父结点,后序就是最后遍历父结点。不管哪种遍历,都是通过递归调用完成的。

    前序遍历,对树中的任意结点来说,先打印这个结点,然后前序遍历它的左子树,最后前序遍历它的右子树。

    中序遍历,对树中的任意结点来说,先中序遍历它的左子树,然后打印这个结点,最后中序遍历它的右子树。

    后序遍历,对树中的任意结点来说,先后序遍历它的左子树,然后后序遍历它的右子树,最后打印它本身。

      /**
      * Definition for a binary tree node.
      * function TreeNode(val, left, right) {
      *     this.val = (val===undefined ? 0 : val)
      *     this.left = (left===undefined ? null : left)
      *     this.right = (right===undefined ? null : right)
      * }
      */
      /**
      * @param {TreeNode} root
      * @return {number[]}
      */
      var postorderTraversal = function(root) {
        return postorder(root, []);
      };
      function postorder (root, res){
        if(!root){
          return res;
        }
        postorder(root.left, res);
        postorder(root.right, res);
        res.push(root.val);
        return res;
      }