链表算法刷题指南

一、链表基础知识

1、链表的存储方式:

链表是通过指针域的指针链接在内存中各个节点。

所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。

2、链表增删改查时间复杂度:

(1)增删:O(1)

(2)查:O(n)

链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。

3、链表定义

(1)Java

java 复制代码
public class ListNode {
    // 结点值
    int val;

    // 下一个结点
    ListNode next;

    // 节点的构造函数(无参)
    public ListNode() {
    }

    // 节点的构造函数(有一个参数)
    public ListNode(int val) {
        this.val = val;
    }

    // 节点的构造函数(有两个参数)
    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

(2)JavaScript

javascript 复制代码
class ListNode {
  val;
  next = null;
  constructor(value) {
    this.val = value;
    this.next = null;
  }
}

(3)C++

cpp 复制代码
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

//1、通过自己定义构造函数初始化节点:
ListNode* head = new ListNode(5);

//2、也可以不自己定义构造函数,C++默认生成一个构造函数。
//但是这个构造函数不会初始化任何成员变量
//使用默认构造函数初始化节点:
ListNode* head = new ListNode();
head->val = 5;

//总结:如果不定义构造函数使用默认构造函数,在初始化的时候不能直接给变量赋值

二、移除链表元素

问题:

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

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

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

示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]

(1)C++(使用原来的链表来进行移除节点操作

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //删除头结点
        while(head!=NULL && head->val==val){
            ListNode* tmp = head;
            head=head->next;
            delete tmp;
        }
        //删除非头结点
        ListNode* a=head;
        while(a!=NULL && a->next!=NULL){
            if(a->next->val==val){
                ListNode* tmp =a->next;
                a->next=a->next->next;
                delete tmp;
            }else{
                a=a->next;
            }
        }
        return head;
    }
};

//时间复杂度: O(n)空间复杂度: O(1)

(2)C++(设置一个虚拟头结点在进行移除节点操作

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummy = new ListNode(0);
        dummy->next=head;
        ListNode* a = dummy;
        while(a->next!=NULL){
            if(a->next->val==val){
                ListNode* tmp=a->next;
                a->next=a->next->next;
                delete tmp;
            }else{
                a=a->next;
            }
        }
        //return 头结点的时候,dummyNode->next;才是新的头结点
        head=dummy->next;
        delete dummy;
        return head;
    }
};

//时间复杂度: O(n)空间复杂度: O(1)

(3)Java(使用原来的链表来进行移除节点操作

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while(head!=null&&head.val==val){
            head=head.next;
        }

        ListNode a=head;
        while(a!=null && a.next!=null){
            if(a.next.val==val){
                a.next=a.next.next;
            }else{
                a=a.next;
            }
        }
        return head;
    }
}

(4)Java(设置一个虚拟头结点在进行移除节点操作

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
       ListNode dummy=new ListNode();
       dummy.next=head;
       ListNode a=dummy;
       while(a.next!=null){
        if(a.next.val==val){
            a.next=a.next.next;
        }else{
            a=a.next;
        }
       }
        return dummy.next;
    }
}

(5)JavaScript(设置一个虚拟头结点在进行移除节点操作

javascript 复制代码
/**
 * 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 res = new ListNode(0,head);
    let cur=res;
    while(cur.next){
        if(cur.next.val === val) {
            cur.next =  cur.next.next;
        }else{
            cur = cur.next;
        }
    }
    return res.next;
};

三、设计链表

问题:

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

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

(1)Java(单链表)

java 复制代码
class MyLinkedList {

    class ListNode{
        int val;
        ListNode next;
        ListNode(int val){
            this.val=val;
        }
    }

    //存储链表元素个数
    private int size;
    //虚拟头结点
    private ListNode head;

    //初始化链表
    public MyLinkedList() {
        this.size = 0;
        this.head = new ListNode(0);
    }
    
    public int get(int index) {
        if(index<0 || index>=size){
            return -1;
        }
        ListNode a=head;
        for(int i=0;i<=index;i++){
            a=a.next;
        }
        return a.val;
    }
    
    public void addAtHead(int val) {
        ListNode newNode=new ListNode(val);
        newNode.next=head.next;
        head.next=newNode;
        size++;
    }
    
    public void addAtTail(int val) {
        ListNode newNode=new ListNode(val);
        ListNode b=head;
        while(b.next!=null){
            b=b.next;
        }
        b.next=newNode;
        size++;
    }
    
    public void addAtIndex(int index, int val) {
        if(index<0||index>size){
            return;
        }
        ListNode c=head;
        for(int i=0;i<index;i++){
            c=c.next;
        }
        ListNode newNode=new ListNode(val);
        newNode.next=c.next;
        c.next=newNode;
        size++;
    }
    
    public void deleteAtIndex(int index) {
         if(index<0||index>=size){
            return;
        }
        ListNode c=head;
        for(int i=0;i<index;i++){
            c=c.next;
        }
        c.next=c.next.next;
        size--;
    }
}

(2)JavaScript(双链表)

java 复制代码
class MyLinkedList {

    class ListNode{
        int val;
        ListNode next,prev;
        ListNode(int val){
            this.val=val;
        }
    }

    //存储链表元素个数
    private int size;
    //虚拟头结点和尾结点
    private ListNode head,tail;

    //初始化链表
    public MyLinkedList() {
        this.size = 0;
        this.head = new ListNode(0);
        this.tail = new ListNode(0);
        this.head.next=tail;
        this.tail.prev=head;
    }
    
    public int get(int index) {
        if(index<0 || index>=size){
            return -1;
        }
        ListNode cur=head;
        if(index>=size/2){
            cur=tail;
            for(int i=0;i<size-index;i++){
                cur=cur.prev;
            }
        }else{
            cur=head;
            for(int i=0;i<=index;i++){
                cur=cur.next;
            }
        }
        return cur.val;
    }
    
    public void addAtHead(int val) {
        addAtIndex(0,val);
    }
    
    public void addAtTail(int val) {
        addAtIndex(size,val);
    }
    
    public void addAtIndex(int index, int val) {
        if(index<0||index>size){
            return;
        }
        ListNode pre=head;
        for(int i=0;i<index;i++){
            pre=pre.next;
        }
        ListNode newNode=new ListNode(val);
        newNode.next=pre.next;
        pre.next.prev=newNode;
        newNode.prev=pre;
        pre.next=newNode;
        size++;
    }
    
    public void deleteAtIndex(int index) {
         if(index<0||index>=size){
            return;
        }
        ListNode pre=head;
        for(int i=0;i<index;i++){
            pre=pre.next;
        }
        pre.next.next.prev=pre;
        pre.next=pre.next.next;
        size--;
        
    }
}

四、反转数组

问题:

题意:反转一个单链表。

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

双指针法:

(1)Java

java 复制代码
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode temp = null;
        ListNode pre=null;
        ListNode cur=head;
        while(cur!=null){
            temp=cur.next;
            cur.next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
    }
}

(2)JavaScript

javascript 复制代码
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    let cur=head,pre=null,temp=null;
    while(cur){
        temp=cur.next;
        cur.next=pre;
        pre=cur;
        cur=temp;
    }
    return pre;
};

(3)C++

cpp 复制代码
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* temp;
        ListNode* pre=NULL;
        ListNode* cur=head;
        while(cur!=NULL){
            temp=cur->next;
            cur->next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
    }
};

五、两两交换链表中的节点

问题:

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

(1)Java

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummyHead=new ListNode(-1);
        dummyHead.next=head;
        ListNode cur=dummyHead;
        ListNode temp;
        ListNode firstNode;
        ListNode secondNode;
        while(cur.next!=null&&cur.next.next!=null){
            temp = cur.next.next.next;
            firstNode=cur.next;
            secondNode=cur.next.next;
            cur.next=secondNode;
            secondNode.next=firstNode;
            firstNode.next=temp;
            cur=firstNode;
        }
        return dummyHead.next;
    }
}

(2)Javascript

javascript 复制代码
/**
 * 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) {
    let cur= new ListNode(0,head),temp=cur;
    while(temp.next&&temp.next.next){
        let pre=temp.next,right=temp.next.next;
        pre.next=right.next;
        right.next=pre;
        temp.next=right;
        temp=pre;
    }
    return cur.next;
};

(3)C++(时间复杂度:O(n);空间复杂度:O(1))

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead=new ListNode(0);
        dummyHead->next=head;
        ListNode* cur=dummyHead;
        while(cur->next!=nullptr&&cur->next->next!=nullptr){
            ListNode* temp=cur->next;
            ListNode* temp1=cur->next->next->next;

            cur->next=cur->next->next;
            cur->next->next=temp;
            cur->next->next->next=temp1;
            cur=cur->next->next;
        }
        ListNode* result=dummyHead->next;
        delete dummyHead;
        return result;
    }
};

六、链表相交

问题:

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

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:

示例 2:

示例 3:

注意:交点不是数值相等,而是指针相等

(1)Java

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
//让指针走完自己的链表后,去走另一条链表,最后输出p1或p2都可以。
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1=headA,p2=headB;
        while(p1!=p2){
            if(p1==null) p1=headB;
            else p1=p1.next;
            if(p2==null) p2=headA;
            else p2=p2.next;  
        }
        return p2;
    }
}

(2)JavaScript

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

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */
var getLength = function(head){
    let len=0,cur=head;
    while(cur!=null){
        len++;
        cur=cur.next;
    }
    return len;
}
var getIntersectionNode = function(headA, headB) {
    let curA=headA,curB=headB;
    let lenA=getLength(curA);
    let lenB=getLength(curB);
    // 让 curA 永远指向更长的链表
    if(lenA<lenB){
        [curA,curB]=[curB,curA];
        [lenA,lenB]=[lenB,lenA];
    }
    // curA 先走 长度差 步
    let gap = lenA - lenB;
    while(gap-- > 0){
        curA = curA.next;
    }
    // 然后一起走,直到相遇
    while(curA&&curA!=curB){
        curA=curA.next;
        curB=curB.next;
    }
    return curA;
};

(3)C++(时间复杂度:O(n + m);空间复杂度:O(1))

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA=headA;
        ListNode* curB=headB;
        int lenA=0,lenB=0;
        while(curA!=NULL){
            lenA++;
            curA=curA->next;
        }
        while(curB!=NULL){
            lenB++;
            curB=curB->next;
        }
        curA=headA;
        curB=headB;
        if(lenA<lenB){
            swap(lenA,lenB);
            swap(curA,curB);
        }
        int gap=lenA-lenB;
        while(gap--){
            curA=curA->next;
        }
        while(curA!=NULL){
            if(curA==curB){
                return curA;
            }
            curA=curA->next;
            curB=curB->next;
        }
        return NULL;
    }
};

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

问题:

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

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

示例 1:

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

示例 2:

输入:head = [1], n = 1 输出:[]

示例 3:

输入:head = [1,2], n = 1 输出:[1]

(1)Java

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyHead=new ListNode(0);
        dummyHead.next=head;
        ListNode left=dummyHead;
        ListNode right=dummyHead;
        for(int i=0;i<=n;i++){
            right=right.next;
        }
        while(right!=null){
            left=left.next;
            right=right.next;
        }
        left.next=left.next.next;
        return dummyHead.next;
    }
}

(2)JavaScript

javascript 复制代码
/**
 * 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) {
    let dummyHead=new ListNode(0,head);
    dummyHead.next=head;
    let left=dummyHead;
    let right=dummyHead;
    while(n--){
        right=right.next;
    }
    while(right.next!=null){
        left=left.next;
        right=right.next;
    }
    left.next=left.next.next;
    return dummyHead.next;
};

(3)C++

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead=new ListNode(0);
        dummyHead->next=head;
        ListNode* left=dummyHead;
        ListNode* right=dummyHead;
        while(n--&&right!=NULL){
            right=right->next;
        }
        while(right->next!=NULL){
            left=left->next;
            right=right->next;
        }
        left->next=left->next->next;
        return dummyHead->next;
    }
};

八、环形链表II

问题:

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

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

示例 1:

(1)Java

java 复制代码
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {// 有环
                ListNode index1 = fast;
                ListNode index2 = head;
                // 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}

(2)JavaScript

javascript 复制代码
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;
};

(3)C++

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast=head;
        ListNode* slow=head;
        while(fast!=NULL&&fast->next!=NULL){
            slow=slow->next;
            fast=fast->next->next;
            if(slow==fast){
                ListNode* a=fast;
                ListNode* b=head;
                while(a!=b){
                    a=a->next;
                    b=b->next;
                }
                return b;
            }
        }
        return NULL;

    }
};
相关推荐
Yungoal2 小时前
常见 时间复杂度计算
c++·算法
不爱吃炸鸡柳3 小时前
单链表专题(完整代码版)
数据结构·算法·链表
CylMK3 小时前
题解:AT_abc382_d [ABC382D] Keep Distance
算法
Dfreedom.3 小时前
计算机视觉全景图
人工智能·算法·计算机视觉·图像算法
Morwit4 小时前
【力扣hot100】 1. 两数之和
数据结构·c++·算法·leetcode·职场和发展
无小道4 小时前
算法——暴力+优化
算法·优化·暴力
Free Tester4 小时前
如何判断 LeakCanary 报告的严重程度
java·jvm·算法
zyq99101_15 小时前
DFS算法实战:经典例题代码解析
python·算法·蓝桥杯·深度优先
智者知已应修善业5 小时前
【51单片机单按键切换广告屏】2023-5-17
c++·经验分享·笔记·算法·51单片机