算法之链表(leetCode)

124 阅读3分钟

链表

基本概念

  • 链表是一种通过指针串联在一起的线性结构每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点指针域指向null空指针的意思)。
  • 链接的入口节点称为链表的头结点也就是head

image.png

链表类型

  • 单链表(上述就是单链表):每个节点只有一个指针域,节点只能指向节点的下一个节点
  • 双链表(每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点),既可以向前查询也可以向后查询

image.png

  • 循环链表(链表首尾相连):用来解决约瑟夫环问题

image.png

约瑟夫环问题: image.png

主要方法

链表的定义:

function ListNode(val, next) {
     this.val = (val===undefined ? 0 : val)
     this.next = (next===undefined ? null : next)
}

链表操作的两种方式:

  • 直接使用原来的链表来进行删除操作。
  • 设置一个虚拟头结点再进行删除操作。
    js中可以使用:const ret = new ListNode(0, head);

示例

移除链表元素

题目链接:[203.移除链表元素](203. 移除链表元素 - 力扣(LeetCode) (leetcode-cn.com))

image.png

解答:

// 设置一个虚拟头结点来解决
var removeElements = function(head, val) {
    const dummyNode = new ListNode(0, head);
    let cur = dummyNode;
    while(cur.next){
        // if(cur.next.val === val){
        //     cur.next = cur.next.next;
        // } else {
        //     cur = cur.next; 
        // }
        if(cur.next.val === val){
            cur.next = cur.next.next;
            continue;// 以防cur.next.next.val的值仍然为val的情况,所以还需要检查一遍
        }
        cur = cur.next; 
    }
    return dummyNode.next;
};
// 直接在原来链表上进行修改
var removeElements = function(head, val) {
    // 删除头部节点
    while(head !== null && head.val === val) {
        head = head.next;
    }
    if(head === null) return head;
    // 删除非头部节点
    let pre = head, cur = head.next;
    while(cur){
        if(cur.val === val){
            pre.next = cur.next;
        } else {
            pre = pre.next;
        }
        cur = cur.next;
    }   
    return head;
}

设计链表

题目链接:[707.设计链表](707. 设计链表 - 力扣(LeetCode) (leetcode-cn.com))

image.png

image.png

class LinkNode {
    constructor(val, next){
        this.val = (val === undefined ? 0 : val);
        this.next = (next === undefined ? null : next);
    }
}

var MyLinkedList = function() {
    this._size = 0;
    this._tail = null;
    this._head = null;
};

MyLinkedList.prototype.getNode = function(index) {
    if(index < 0 || index >= this._size) return null;
    let cur = new LinkNode(0, this._head);
    while(index-- >= 0){
        cur = cur.next;
    }
    return cur;
};

MyLinkedList.prototype.get = function(index) {
    if(index < 0 || index >= this._size) return -1;
    return this.getNode(index).val;
};

MyLinkedList.prototype.addAtHead = function(val) {
    const node = new LinkNode(val, this._head);
    this._head = node;
    this._size++;
    if(!this._tail){
        this._tail = node;
    }
};

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;
};

MyLinkedList.prototype.addAtIndex = function(index, val) {
    if(index === this._size) {
        this.addAtTail(val);
        return;
    }
    if(index > this._size) {
        return;
    }
    if(index <= 0) {
        this.addAtHead(val);
        return;
    }
    const node = this.getNode(index - 1);
    node.next = new LinkNode(val, node.next);
    this._size++;
};

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--;
};

反转链表

题目链接:[206.反转链表](206. 反转链表 - 力扣(LeetCode) (leetcode-cn.com))

image.png

双指针解法

var reverseList = function(head) {
    if(!head || !head.next) return head;
    let pre = null, cur = head;
    while(cur){
        let temp = cur.next;
        cur.next = pre;
        pre = cur;
        cur = temp;
    }
    return pre;
};

递归解法

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

两两交换链表中的节点

题目链接:[24.两两交换链表中的节点](Loading Question... - 力扣(LeetCode) (leetcode-cn.com))

image.png

image.png

解法:虚拟头结点进行模拟

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

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

题目链接:[19.删除链表的倒数第N个节点](19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode) (leetcode-cn.com))

解法:快慢指针

var removeNthFromEnd = function(head, n) {
    let ret = new ListNode(0, head);
    let slow = fast = ret;
    while(n--){
        fast = fast.next;
    }
    while(fast.next){
        slow = slow.next;
        fast = fast.next;
    }
    slow.next = slow.next.next;
    return ret.next;
};

链表相交

题目链接:[面试题 02.07. 链表相交](面试题 02.07. 链表相交 - 力扣(LeetCode) (leetcode-cn.com))

image.png

image.png

image.png

解法

var getIntersectionNode = function(headA, headB) {
    let ptrA = headA, ptrB = headB;
    const getLength = (head) => {
        let len = 0, node = head;
        while(node){
            len++;
            node = node.next;
        }
        return len;
    }
    let lenA = getLength(headA), lenB = getLength(headB);
    if(lenA < lenB){
        [ptrA, ptrB] = [ptrB, ptrA];
        [lenA, lenB] = [lenB, lenA];
    }
    let i = lenA - lenB;
    while(i--){
        ptrA = ptrA.next;
    }
    while(ptrA && ptrA !== ptrB){
        ptrA = ptrA.next;
        ptrB = ptrB.next;
    }
    return ptrA;
};

环形链表II

题目链接:[142.环形链表II](142. 环形链表 II - 力扣(LeetCode) (leetcode-cn.com))

image.png

解法 从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

var detectCycle = function(head) {
    if(!head || !head.next) return null;
    let slow = head.next, fast = head.next.next;
    while(fast && fast.next && fast !== slow){
        slow = slow.next;
        fast = fast.next.next;
    }
    if(!fast || !fast.next) return null;
    slow = head;
    while(fast !== slow){
        slow = slow.next;
        fast = fast.next;
    }
    return slow;
};