链表刷题整理

48 阅读6分钟

链表算法整理

真的非常气愤,花了很多时间去差错,到最后发现居然是因为判断条件只写了一个等于号和construtor 写成了contructer,硬是找了半天错误,感觉时间都被浪费掉了,诶

1.移除链表元素

  • leetcode.cn/problems/re…

  • 简单的移除元素 ,只需要找到他的pre节点,值得思考的是最好加上一个头节点!如果不使用头结点,就直接把head指针向后移动一个,所以在单链表中移除头结点 和 移除其他节点的操作方式是不一样,其实在写代码的时候也会发现,需要单独写一段逻辑来处理移除头结点的情况

  • var removeElements = function(head, val) {
        let node = new ListNode(0,head)
        let pre = node
        let cur = pre.next
        while(cur!==null) {
            if(cur.val === val){
                console.log("pre",pre.val)
                pre.next = cur.next
                cur = cur.next
            }else{
                cur = cur.next
                pre = pre.next
            }
        }
        return node.next
    };
    

2.设计链表 (坑惨了)

image-20230303115912195

  • 下次一定要细心一点 诶

  • lass LinkNode {
        constructer(val ,next){
            this.val = val
            this.next = next
        }
    }
    var MyLinkedList = function() {
        this._size = 0
        this._head = null
        this._tail = null
    };
    class LinkNode {
        constructor(val, next) {
            this.val = val;
            this.next = next;
        }
    }
    
    /**
     * Initialize your data structure here.
     * 单链表 储存头尾节点 和 节点数量
     */
    var MyLinkedList = function() {
        this._size = 0;
        this._tail = null;
        this._head = null;
    };
    /** 
     * @param {number} index
     * @return {number}
     */
    MyLinkedList.prototype.get = function(index) {
        if(index < 0 || index >= this._size) return -1;
        // 获取当前节点
        return this.getNode(index).val;
    };
    
    /** 
     * @param {number} val
     * @return {void}
     */
    MyLinkedList.prototype.addAtHead = function(val) {
        const node = new LinkNode(val, this._head);
        this._head = node;
        this._size++;
        if(!this._tail) {
            this._tail = node;
        }
    };
    
    /**
     * Append a node of value val to the last element of the linked list. 
     * @param {number} val
     * @return {void}
     */
    MyLinkedList.prototype.addAtTail = function(val) {
        const node = new LinkNode(val, null);
        this._size++;
        if(this._tail) {
            this._tail.next = node;
            this._tail = node;
            return;
        }
        this._tail = node;
        this._head = node;
    };
    
    /** 
     * @param {number} index 
     * @param {number} val
     * @return {void}
     */
     MyLinkedList.prototype.getNode = function(index) {
        if(index < 0 || index >= this._size) return null;
        // 创建虚拟头节点
        let cur = new LinkNode(0, this._head);
        // 0 -> head
        while(index-- >= 0) {
            cur = cur.next;
        }
        return cur;
    };
    MyLinkedList.prototype.addAtIndex = function(index, val) {
        if(index > this._size) return;
        if(index <= 0) {
            this.addAtHead(val);
            return;
        }
        if(index === this._size) {
            this.addAtTail(val);
            return;
        }
        // 获取目标节点的上一个的节点
        const node = this.getNode(index - 1);
        node.next = new LinkNode(val, node.next);
        this._size++;
    };
    
    /** 
     * @param {number} index
     * @return {void}
     */
    MyLinkedList.prototype.deleteAtIndex = function(index) {
        if(index < 0 || index >= this._size) return;
        if(index === 0) {
            this._head = this._head.next;
            // 如果删除的这个节点同时是尾节点,要处理尾节点
            if(index === this._size - 1){
                this._tail = this._head
            }
            this._size--;
            return;
        }
        // 获取目标节点的上一个的节点
        const node = this.getNode(index - 1);    
        node.next = node.next.next;
        // 处理尾节点
        if(index === this._size - 1) {
            this._tail = node;
        }
        this._size--;
    };
    

3.反转链表

  • leetcode.cn/problems/re…

  • 依然是找到他的前一个节点就好了,双指针,但是不习惯使用递归方法,尝试使用

  • var reverseList = function(head) {
        
        let pre = null
        let cur = head
        while(cur!==null) {
            let temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        }
        return pre
        
    };
    //记得return喔!!!!!!!!!
    var reverseList = function(head) {
        function reverse(pre,cur) {
            if(!cur) return pre
            let temp = cur.next
            cur.next = pre  
            return reverse(cur, temp)
        }
        return reverse(null,head)
        
    };
    

4.两两交换链表的节点

  • leetcode.cn/problems/sw…

  • var swapPairs = function(head) {
        let node = new ListNode(0,head)
        let pre = node
        while(pre.next&& pre.next.next) {
            let cur1 = pre.next
            let cur2 = pre.next.next
            pre.next = cur2
            cur1.next = cur2.next
            cur2.next = cur1
            pre = cur1
        }
    
        return node.next
    };
    

5.删除链表的倒数第N个节点

  • leetcode.cn/problems/re…

  • 使用双指针方法,倒数第几个元素就让 fast节点先走几步,然后同时移动fast,slow指针slow指针指向的就是要删除的节点的前一个节点,此题印象比较深刻之前做过。

  • var removeNthFromEnd = function(head, n) {
        let node = new ListNode(0,head)
        let pre = node
        let rear = node
        while(n>0){
            n--
            rear = rear.next
        }
        while(rear.next!== null) {
            rear = rear.next
            pre = pre.next
        }
        pre.next = pre.next.next
        return node.next
    };
    ```# 链表算法整理
    

真的非常气愤,花了很多时间去差错,到最后发现居然是因为判断条件只写了一个等于号和construtor 写成了contructer,硬是找了半天错误,感觉时间都被浪费掉了,诶

1.移除链表元素

  • leetcode.cn/problems/re…

  • 简单的移除元素 ,只需要找到他的pre节点,值得思考的是最好加上一个头节点!如果不使用头结点,就直接把head指针向后移动一个,所以在单链表中移除头结点 和 移除其他节点的操作方式是不一样,其实在写代码的时候也会发现,需要单独写一段逻辑来处理移除头结点的情况

  • var removeElements = function(head, val) {
        let node = new ListNode(0,head)
        let pre = node
        let cur = pre.next
        while(cur!==null) {
            if(cur.val === val){
                console.log("pre",pre.val)
                pre.next = cur.next
                cur = cur.next
            }else{
                cur = cur.next
                pre = pre.next
            }
        }
        return node.next
    };
    

2.设计链表 (坑惨了)

image-20230303115912195

  • 下次一定要细心一点 诶

  • lass LinkNode {
        constructer(val ,next){
            this.val = val
            this.next = next
        }
    }
    var MyLinkedList = function() {
        this._size = 0
        this._head = null
        this._tail = null
    };
    class LinkNode {
        constructor(val, next) {
            this.val = val;
            this.next = next;
        }
    }
    
    /**
     * Initialize your data structure here.
     * 单链表 储存头尾节点 和 节点数量
     */
    var MyLinkedList = function() {
        this._size = 0;
        this._tail = null;
        this._head = null;
    };
    /** 
     * @param {number} index
     * @return {number}
     */
    MyLinkedList.prototype.get = function(index) {
        if(index < 0 || index >= this._size) return -1;
        // 获取当前节点
        return this.getNode(index).val;
    };
    
    /** 
     * @param {number} val
     * @return {void}
     */
    MyLinkedList.prototype.addAtHead = function(val) {
        const node = new LinkNode(val, this._head);
        this._head = node;
        this._size++;
        if(!this._tail) {
            this._tail = node;
        }
    };
    
    /**
     * Append a node of value val to the last element of the linked list. 
     * @param {number} val
     * @return {void}
     */
    MyLinkedList.prototype.addAtTail = function(val) {
        const node = new LinkNode(val, null);
        this._size++;
        if(this._tail) {
            this._tail.next = node;
            this._tail = node;
            return;
        }
        this._tail = node;
        this._head = node;
    };
    
    /** 
     * @param {number} index 
     * @param {number} val
     * @return {void}
     */
     MyLinkedList.prototype.getNode = function(index) {
        if(index < 0 || index >= this._size) return null;
        // 创建虚拟头节点
        let cur = new LinkNode(0, this._head);
        // 0 -> head
        while(index-- >= 0) {
            cur = cur.next;
        }
        return cur;
    };
    MyLinkedList.prototype.addAtIndex = function(index, val) {
        if(index > this._size) return;
        if(index <= 0) {
            this.addAtHead(val);
            return;
        }
        if(index === this._size) {
            this.addAtTail(val);
            return;
        }
        // 获取目标节点的上一个的节点
        const node = this.getNode(index - 1);
        node.next = new LinkNode(val, node.next);
        this._size++;
    };
    
    /** 
     * @param {number} index
     * @return {void}
     */
    MyLinkedList.prototype.deleteAtIndex = function(index) {
        if(index < 0 || index >= this._size) return;
        if(index === 0) {
            this._head = this._head.next;
            // 如果删除的这个节点同时是尾节点,要处理尾节点
            if(index === this._size - 1){
                this._tail = this._head
            }
            this._size--;
            return;
        }
        // 获取目标节点的上一个的节点
        const node = this.getNode(index - 1);    
        node.next = node.next.next;
        // 处理尾节点
        if(index === this._size - 1) {
            this._tail = node;
        }
        this._size--;
    };
    

3.反转链表

  • leetcode.cn/problems/re…

  • 依然是找到他的前一个节点就好了,双指针,但是不习惯使用递归方法,尝试使用

  • var reverseList = function(head) {
        
        let pre = null
        let cur = head
        while(cur!==null) {
            let temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        }
        return pre
        
    };
    //记得return喔!!!!!!!!!
    var reverseList = function(head) {
        function reverse(pre,cur) {
            if(!cur) return pre
            let temp = cur.next
            cur.next = pre  
            return reverse(cur, temp)
        }
        return reverse(null,head)
        
    };
    

4.两两交换链表的节点

  • leetcode.cn/problems/sw…

  • var swapPairs = function(head) {
        let node = new ListNode(0,head)
        let pre = node
        while(pre.next&& pre.next.next) {
            let cur1 = pre.next
            let cur2 = pre.next.next
            pre.next = cur2
            cur1.next = cur2.next
            cur2.next = cur1
            pre = cur1
        }
    
        return node.next
    };
    

5.删除链表的倒数第N个节点

  • leetcode.cn/problems/re…

  • 使用双指针方法,倒数第几个元素就让 fast节点先走几步,然后同时移动fast,slow指针slow指针指向的就是要删除的节点的前一个节点,此题印象比较深刻之前做过。

  • var removeNthFromEnd = function(head, n) {
        let node = new ListNode(0,head)
        let pre = node
        let rear = node
        while(n>0){
            n--
            rear = rear.next
        }
        while(rear.next!== null) {
            rear = rear.next
            pre = pre.next
        }
        pre.next = pre.next.next
        return node.next
    };
    

6.链表相交

  • leetcode.cn/problems/in…

  • 俩种思路:

    • 使用set,把一条链表的节点全部加入到set中,然后看便利第二条链表

    • 两条相交的链表尾部从相交开始就是一样的 也就是说两条链表的长度差一定小于交点之前的长度差。

      平衡两条链表的长度,一起next即可找到交点

      var getIntersectionNode = function(headA, headB) {
          // let visited = new Set()
          // while(headA){
          //     visited.add(headA)
          //     headA = headA.next
          // }
          // while(headB){
          //     if(visited.has(headB)) return headB
          //     headB = headB.next
          // }
          // return null
          
          function getLength(head) {
              let len = 0
              while(head){
                  len++
                  head = head.next
              }
              return len
          }
          let len1 = getLength(headA)
          console.log(len1)
          let len2 = getLength(headB)
          console.log(len2)
          if(len1 < len2){
              [len1,len2] = [len2,len1];
              
              [headA,headB] = [headB,headA]
          }
          let sub = len1 - len2
          console.log(sub)
          while(sub > 0) {
              sub--
              headA = headA.next
          }
          while(headA&&headB) {
              if(headA == headB) return headA
              headA = headA.next
              headB = headB.next
          }
          return null
      };
      

7. 环形链表

  • leetcode.cn/problems/li…

  • 首先需要判断是否有环,此题印象比较深,一快一慢指针同时出发,如果有环一定可以相遇

  • 其次找到环的起点:

    • image-20230303175105835
    • 借助一下随想录的图,假设fast的速度是slow的两倍,而且最终他们相遇了,且肯定还是在圆中,不然不可能相遇,基于这个情况下可以列出等式 2(x+y) = n(y+z) + x+y,那么x+y = n(y+z)
      • 1.可能这个圈很大,那么n最小也就是1:x = y
      • 2.可能这个圈很小,x = n(y+z) -z: 这个等式的意思就是再说一样的速度走的话,一个人a从x起点开始走,另一个b从他们的交点开始走,a走直线,b就是在圆中走,a走到环的入口处,b走了n个圆距离-y,b的起点是交点,交点-y还是入口处
      • 所以可知找到交点 即可找到入口~!上代码
  • var detectCycle = function(head) {
        if(!head || !head.next) return null;
        let slow =head.next, fast = head.next.next;
        while(fast!==slow&&fast&&fast.next){
            fast = fast.next.next
            slow = slow.next
        }
        if(!fast||!fast.next) return null
        let cur = head
        while(cur!==slow){
            cur = cur.next
            slow = slow.next
        }
        return slow
    };