代码随想录训练营|链表章节汇总

DAY1 第一章

1、链表理论基础

  •  链表是通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域,一个是执指针域。链表的入口节点称为链表的头节点,也就是head。
  • 链表又分为单链表,双链表和循环链表
  • 链表在内存中不是连续分布的。各个节点分布在内存的不同地址空间上,通过指针串联在一起

链表的定义

class ListNode {
  val;
  next = null;
  constructor(value) {
    this.val = value;
    this.next = null;
  }
}

 2、移除链表元素

题目:203. 移除链表元素 - 力扣(LeetCode)

题意:删除链表中等于给定值 val 的所有节点。

示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]

示例 2: 输入:head = [], val = 1 输出:[]

思路:设置一个虚拟头节点进行删除操作

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */
var removeElements = function(head, val) {
    const ret= new ListNode(0,head);
    let cur=ret;
    while(cur.next){
        if(cur.next.val===val){
            cur.next=cur.next.next;
            
        }
        else{
            cur=cur.next;
        }
        
    }
    return ret.next
  
   }

3、设计链表

707. 设计链表 - 力扣(LeetCode)

题意:

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
  • class ListNode {
        constructor(val, next) {
            this.val = val;
            this.next = next;
        }
    }
    var MyLinkedList = function () {
        this.head = null;
        this.val = null;
        this.size = 0;
    };
    
    
    /** 
     * @param {number} index
     * @return {number}
     */
    MyLinkedList.prototype.get = function (index) {
        console.log('get index: ',index)
        //判断index有效性
        if (index < 0 || index >= this.size)
            return -1;
        //定义一个虚拟头节点,他的下一个是原链表的头节点
        const dummyHead = new ListNode(-1, this.head);
        let current = dummyHead.next;
        while (index) {
            current = current.next;
            index--;//很细腻
        }
        console.log('get: ',current.val)
        return current.val;
    };
    
    /** 
     * @param {number} val
     * @return {void}
     */
    MyLinkedList.prototype.addAtHead = function (val) {
    
        //定义一个虚拟头节点,他的下一个是原链表的头节点
        const dummyHead = new ListNode(-1, this.head);
    
        //定义新节点
        const newNode = new ListNode(val, null);
        newNode.next = dummyHead.next;
        dummyHead.next = newNode;
        this.head = newNode;
        this.size++;
        console.log('addAtHead: ',this.head)
    };
    
    /** 
     * @param {number} val
     * @return {void}
     */
    MyLinkedList.prototype.addAtTail = function (val) {
    
        //定义一个虚拟头节点,他的下一个是原链表的头节点
        const dummyHead = new ListNode(-1, this.head);
        //定义新节点
        const newNode = new ListNode(val, null);
        if (this.size == 0) {
            this.head = newNode;
            this.size++;
            console.log('addAtTail: ', this.head)
            return;
        }
        let current = dummyHead;//很细腻,要插入尾节点,必须找到当前的尾节点,但可能存在当前链表为空的情况
        while (current.next != null) {
            current = current.next;
        }
        current.next = newNode;
        this.size++;
        console.log('addAtTail: ', this.head)
    
    
    };
    
    /** 
     * @param {number} index 
     * @param {number} val
     * @return {void}
     */
    MyLinkedList.prototype.addAtIndex = function (index, val) {
        if (index <= 0) {
            this.addAtHead(val);
            return;
        }
        if (index == this.size) {
            this.addAtTail(val);
            return;
        }
        if (index > this.size)
            return;
        //定义一个虚拟头节点,他的下一个是原链表的头节点
        const dummyHead = new ListNode(-1, this.head);
        //定义新节点
        const newNode = new ListNode(val, null);
        let current = dummyHead;//很细腻
        while (index) {
            current = current.next;
            index--;
        }
        newNode.next = current.next;
        current.next = newNode;
        this.size++;
        console.log('addAtIndex: ', this.head)
    
    };
    
    /** 
     * @param {number} index
     * @return {void}
     */
    MyLinkedList.prototype.deleteAtIndex = function (index) {
        // console.log('deleteAtIndex: ', this.head)
        if (index < 0 || index >= this.size) return;
        if (index == 0) {
            this.head = this.head.next;
            this.size--;
            return;
        }
        //定义一个虚拟头节点,他的下一个是原链表的头节点
        const dummyHead = new ListNode(-1, this.head);
        let current = dummyHead;//很细腻
        while (index) {
            current = current.next;
            index--;
        }
        current.next = current.next.next;
        this.size--;
        console.log('deleteAtIndex: ', this.head)
    };
    
    /**
     * Your MyLinkedList object will be instantiated and called as such:
     * var obj = new MyLinkedList()
     * var param_1 = obj.get(index)
     * obj.addAtHead(val)
     * obj.addAtTail(val)
     * obj.addAtIndex(index,val)
     * obj.deleteAtIndex(index)
     */
    

    这个题目综合考察链表的用法,包括获取第Index个节点数值,在前面或后面插入节点,或者在Index位置插入,以及删除节点。我们要对Index进行判断,每次变化对size进行改变。对边界不确定的时候,可以使用极端值。

4、翻转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。206. 反转链表 - 力扣(LeetCode)

思路:双指针法,定义指针cur和pre(反过来的指针指向),两个指针依次往后移动。

注意我们循环遍历终止条件,以及考虑临时指针存放下一节点的。考虑pre和cur移动的先后顺序

先移动pre,后移动cur

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

};

DAY2 第二章

1、24. 两两交换链表中的节点 

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

思路:如图所示,若实现两两交换,需要执行以下三个步骤。

首先创建虚拟头节点,使cur节点指向dummyHead,每次移动两个节点,判断循环结束条件为cur.next(链表长度为偶数)和cur.next.next(链表长度为奇数)是否为空。

然后注意临时指针储存,这里我们存储1和3的指针

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var swapPairs = function(head) {
    const dummpyHead= new ListNode(0,head);
    let cur=dummpyHead;
    while(cur.next!=null&&cur.next.next!=null){
        let temp=cur.next;
        let temp1=cur.next.next.next;
        cur.next=cur.next.next;
        cur.next.next=temp;
        temp.next=temp1;
        cur=cur.next.next;
    }
    return dummpyHead.next;
};

 2、19.删除链表的倒数第N个节点 

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

题目:

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

进阶:你能尝试使用一趟扫描实现吗?

输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5] 示例 2:

输入:head = [1], n = 1 输出:[] 示例 3:

思路:利用快慢指针的思想,快指针第一次走n步,第二次快指针和慢指针一同出发,直到快执政指向链表末尾,这时候删掉慢指针所指向节点。这里需要用到虚拟头节点

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
   const dummyHead=new ListNode(0,head);
   
   let fast=dummyHead;
   let slow=dummyHead;
    for(let i=1; i<=n+1; i++) {  
        fast = fast.next;  
    }  
    while(fast != null) {  
        fast = fast.next;  
        slow = slow.next;  
    }  
    // Skip the desired node  
    slow.next = slow.next.next;  
    return dummyHead.next;  
};

3、面试题 02.07. 链表相交 

面试题 02.07. 链表相交 - 力扣(LeetCode)

题目描述:给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

思路:首先让他们尾部对齐排列,然后比较curA和curB是否相同,若不相同,则同时向后移动,如果遇到curA===curB,则找到焦点。

那么如何使得他们尾部对其,首先先算出两个单链表的长度,然后求出两个单链表长度的差值,固定A链表比较长,让curA移动到和curB末尾对其的位置。

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */
var getLens=function(head){
    let lens=0;
    cur=head;
    while(cur!=null){
        cur=cur.next;
        lens=lens+1;
    }
    return lens;
}
var getIntersectionNode = function(headA, headB) {
    let curA=headA,
    curB=headB,
    lensA=getLens(headA),
    lensB=getLens(headB);
    if(lensA<lensB){
        [curA,curB]=[curB,curA];
        [lensA,lensB]=[lensB,lensA];
    }
    let i=lensA-lensB;
    while(i-->0){
        curA=curA.next
    }
    while(curA && curA!=curB){
        curA=curA.next;
        curB=curB.next;
    }
    return curA;
};

4、 142.环形链表II 

142. 环形链表 II - 力扣(LeetCode)

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

思路

这道题目,不仅考察对链表的操作,而且还需要一些数学运算。

主要考察两知识点:

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

首先判断链表是否环,使用快慢指针法,从头节点出发,fast指针每次移动两个,slow指针每次移动1个。相对于slow来说,fast是一个一个节点靠近slow,所以fast一定可以和slow重合。

如果有环,如何找到环的入口,这个时候需要用到数学方程推导。

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var detectCycle = function(head) {
    let fast=head;
    let slow=head;
    while(fast!=null&&fast.next!=null){
        fast=fast.next.next;
        slow=slow.next;
        if(slow==fast){
            index1=fast;
            index2=head;
            while(index1!=index2){
                index1=index1.next;
                index2=index2.next;
            }
            return index2;
        }
    }
    return null;
};

总结

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值