力扣刷题 --- 链表篇

121 阅读4分钟

image.png

203. 移除链表元素 简单题 image.png

方法一:在原链表上直接删除

var removeElements = function(head, val) {
    // 处理头结点
   while(head && head.val == val)  head = head.next;
   let pre = head;
   let cur;
   if(head) cur = head.next;
   while(cur) {
       if(cur.val === val) {
           pre.next = cur.next;
       }else pre = cur;
       cur = cur.next;
   }
   return head;
};

方法二: 设置一个虚拟头结点

var removeElements = function(head, val) {
    //构建虚拟头结点,便于处理删除节点为头结点的情况
    let node = new ListNode(0,head);
    let pre = node; // pre记录前一结点 
    while(head) { // head为当前结点
        if(head.val === val) {
            pre.next = head.next;
        } else pre = head;
        head = head.next;
    }
    return node.next; // node.next为链表的头结点
};

707. 设计链表 中等题

image.png

class LinkNode {
    constructor(val,next) {
        this.val = val;
        this.next = next;
    }
}
var MyLinkedList = function() {
    this.head = null;
    this.tail = null;
    this.size = 0;
};
MyLinkedList.prototype.getNode = function(index) {
    if(index < 0 || index >= this.size) return null;
    // 0 --> this.head
    let cur = this.head;
    while(index--) {
        cur = cur.next;
    }
    return cur;
}
/** 
 * @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) {
    let node = new ListNode(val,this.head);
    this.head = node;
    this.size++;
    if(!this.tail) {
        this.tail = this.head;
    }
};
/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtTail = function(val) {
    let node = new ListNode(val);
    this.size++;
    if(this.tail) {
        this.tail.next = node;
        this.tail = node;
    }else {
        this.head = node;
        this.tail = node;
    }
};

/** 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
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 ;
    }
    let pre = this.getNode(index-1);
    let node = new ListNode(val);
    node.next = pre.next;
    pre.next = node;
    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;
    } else {
        // 获取目标节点的上一个节点
        let pre = this.getNode(index-1);
        pre.next = pre.next.next;
        // 处理尾节点
        if(index == this.size-1) {
            this.tail = pre;
        }
    }
    this.size--;
};

206. 反转链表 简单题

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表

方法一: 递归法

function reverse(pre,head) {
    if(head == null) return pre;
    let next = head.next;
    head.next = pre;
    return reverse(head,next);
}
var reverseList = function(head) {
    return reverse(null,head);
};

方法二: 非递归法

var reverseList = function(head) {
    let node = null;
    while(head) {
        let next = head.next;
        head.next = node;
        node = head;
        head = next;
    }
    return node;
};

24. 两两交换链表中的节点 中等题

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)

image.png

image.png

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

19. 删除链表的倒数第 N 个结点 image.png

方法一

先遍历链表计算链表长度,链表长度 - n - 1 为要删除节点的上一节点位置 (不推荐)

var removeNthFromEnd = function(head, n) {
    // 统计整个链表长度
    let len = 0;
    let cur = head;
    while(cur) {
        cur = cur.next;
        len++;
    }
    // 删除的是头节点
    if(n == len) return head.next;
    // 删除的是非头节点
    len = len - n -1;
    cur = head;
    while(len--) {
        cur = cur.next;
    }
    cur.next = cur.next.next;
    return head;
};

方法二

如果要删除倒数第n个节点,让fast移动n+1步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的下一个节点就可以了

image.png

var removeNthFromEnd = function(head, n) {
    // 设置虚拟头节点,避免头结点的处理
    let node = new ListNode(0,head);
    // fast,slow都从虚拟头结点开始
    let slow = node,fast = node;
    // 快指针先走 n 步
    while(n--) {
        fast = fast.next;
    }
    // 然后快慢指针同时走,快指针到达最后一个时,slow来到待删除节点的前一个
    while(fast.next) {
        slow = slow.next;
        fast = fast.next;
    }
    slow.next = slow.next.next;
    return node.next;
};

面试题 02.07. 链表相交 简单题

image.png

image.png

//求链表的长度
var getListLen = function(head){
    let len =0;
    while(head){
        len++;
        head = head.next;
    }
    return len;
}
var getIntersectionNode = function(headA, headB) {
    let lenA =getListLen(headA);
    let lenB =getListLen(headB);
    //固定: A作为较长的链表,B为较短的链表
    if(lenA < lenB){
        [lenA,lenB]=[lenB,lenA];
        [headA,headB] = [headB,headA];
    }
    //两个链表长度之差
    let n = lenA -lenB;
    //让长链表走(两个链表长度之差)个节点
    //长,短链表此时步伐同步,则会相遇或相遇于null
    while(n--)headA=headA.next;
    while(headA && headA !==headB){
        headA=headA.next;
        headB=headB.next;
    }
    //A,B相交 则headA为第一个相交节点,不相交则headA为null
    return headA;
};

142. 环形链表 II 中等题

image.png

image.png

方法一: 哈希表

var detectCycle = function(head) {
    let map = new Map();
    while(head) {
        if(map.has(head)) {
            return head;
        }
        map.set(head);
        head = head.next;
    }
    return null;
};

方法二: 快慢指针法(双指针)

主要考虑以下两个问题:

  • 判断链表是否有环
  • 如果有环,如何找到这个环的入口 !

判断链表是否有环

使用快慢指针法: slow走一步,fast走两步

如果链表没有环,那么fast一定会先到链表尾节点

如果链表有环,那么fast一定先入环,slow和fast一定会在环中相遇

为什么有环的时候,slow和fast一定会在环中相遇呢???

image.png

如果有环,如何找到这个环的入口 !

slow与fast相遇在环中,此时slow回到链表的头节点

slow,fast 一次都走一步,它们第一次相遇的节点就是环的入口节点!!!

那么解释一下为什么这样它们第一次相遇的节点就是环的入口节点???

image.png

var detectCycle = function(head) {
    // 链表无环
    if(!head || !head.nextreturn null;
    // 判断链表是否有环
    let slow = head.next, fast = head.next.next// slow 初始化值一定不是null,fast可能为null
    // 如果无环, fast或fast.next 一定先到达null
    while(fast && fast.next && slow != fast) {
        slow = slow.next;
        fast = fast.next.next;
    }
    // 判断循环结束的情况 
    if(!fast || !fast.nextreturn null// 无环
    // 有环,如何找到入环的第一个节点呢
    // slow回到链表头节点,fast,slow一起走,都是走一步,那么它们一定会在入环的第  一个节点相遇
    slow = head;
    while(slow != fast) {
        slow = slow.next;
        fast = fast.next;
    }
    return slow;
};