首页 > 其他分享 >搞定leetcode面试经典150题之链表

搞定leetcode面试经典150题之链表

时间:2024-12-12 22:00:06浏览次数:6  
标签:150 head ListNode next 链表 节点 null leetcode

系列博客目录


文章目录


理论知识

链表是数据结构中一种非常常见且基础的结构,在 Java 中,链表被广泛应用于解决动态数据存储问题。与数组不同,链表的元素(节点)并不需要在内存中是连续存储的,而是通过指针(或引用)将每个节点连接在一起。

基本概念

链表由一系列节点(Node)组成,每个节点通常包含两个部分:

  1. 数据域 (Data):存储节点的数据。
  2. 指针域 (Next):指向链表中的下一个节点。

链表的基本类型

  1. 单向链表 (Singly Linked List)

    • 每个节点包含一个指向下一个节点的引用。
    • 头节点是链表的开始,尾节点的 nextnull
  2. 双向链表 (Doubly Linked List)

    • 每个节点有两个指针,一个指向下一个节点,一个指向前一个节点。
    • 适用于需要频繁进行双向遍历的情况。
  3. 循环链表 (Circular Linked List)

    • 末尾节点指向头节点,形成一个环。
    • 可以是单向链表或双向链表。

单向链表的基本结构

// 链表节点类
class Node {
    int data;        // 数据域
    Node next;       // 指针域,指向下一个节点

    public Node(int data) {
        this.data = data;
        this.next = null;
    }
}

// 单向链表类
class LinkedList {
    Node head;      // 链表的头节点

    public LinkedList() {
        this.head = null;
    }

    // 添加节点到链表末尾
    public void append(int data) {
        Node newNode = new Node(data);
        if (head == null) {
            head = newNode; // 如果链表为空,直接让头节点指向新节点
        } else {
            Node current = head;
            while (current.next != null) {
                current = current.next; // 遍历到链表末尾
            }
            current.next = newNode; // 将最后一个节点的 next 指向新节点
        }
    }

    // 在链表开头添加节点
    public void prepend(int data) {
        Node newNode = new Node(data);
        newNode.next = head;  // 新节点的 next 指向当前的头节点
        head = newNode;       // 更新头节点为新节点
    }

    // 删除指定节点
    public void delete(int data) {
        if (head == null) return;

        if (head.data == data) {
            head = head.next; // 删除头节点
            return;
        }

        Node current = head;
        while (current.next != null && current.next.data != data) {
            current = current.next; // 遍历找到要删除的节点
        }

        if (current.next != null) {
            current.next = current.next.next; // 删除当前节点的下一个节点
        }
    }

    // 打印链表
    public void printList() {
        Node current = head;
        while (current != null) {
            System.out.print(current.data + " ");
            current = current.next;
        }
        System.out.println();
    }
}

public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList list = new LinkedList();
        list.append(10);
        list.append(20);
        list.append(30);
        list.printList();  // 输出: 10 20 30

        list.prepend(5);
        list.printList();  // 输出: 5 10 20 30

        list.delete(20);
        list.printList();  // 输出: 5 10 30
    }
}

解释

  • Node 类:代表链表的节点,包含数据和指向下一个节点的引用 next
  • LinkedList 类:表示链表本身,包含指向链表头部的引用 head
    • append:将新节点添加到链表末尾。
    • prepend:将新节点添加到链表开头。
    • delete:删除链表中指定值的节点。
    • printList:打印链表的所有元素。

链表的基本操作

  1. 遍历:遍历链表从头到尾,访问每一个节点。
  2. 插入节点:在链表的头部、尾部或指定位置插入节点。
  3. 删除节点:从链表中删除某个指定值的节点。
  4. 查找节点:根据值查找节点的位置。
  5. 反转链表:将链表的节点顺序反转。

双向链表

双向链表中的每个节点都有两个引用:

  • prev:指向前一个节点。
  • next:指向下一个节点。

双向链表比单向链表有更多的灵活性,可以在两个方向上进行遍历。

class DoublyNode {
    int data;
    DoublyNode next;
    DoublyNode prev;

    public DoublyNode(int data) {
        this.data = data;
        this.next = null;
        this.prev = null;
    }
}

class DoublyLinkedList {
    DoublyNode head;
    DoublyNode tail;

    public DoublyLinkedList() {
        this.head = null;
        this.tail = null;
    }

    // 在链表末尾添加节点
    public void append(int data) {
        DoublyNode newNode = new DoublyNode(data);
        if (tail == null) {
            head = newNode;
            tail = newNode;
        } else {
            tail.next = newNode;
            newNode.prev = tail;
            tail = newNode;
        }
    }

    // 打印链表(从头到尾)
    public void printList() {
        DoublyNode current = head;
        while (current != null) {
            System.out.print(current.data + " ");
            current = current.next;
        }
        System.out.println();
    }

    // 打印链表(从尾到头)
    public void printReverse() {
        DoublyNode current = tail;
        while (current != null) {
            System.out.print(current.data + " ");
            current = current.prev;
        }
        System.out.println();
    }
}

public class DoublyLinkedListDemo {
    public static void main(String[] args) {
        DoublyLinkedList list = new DoublyLinkedList();
        list.append(10);
        list.append(20);
        list.append(30);
        list.printList();  // 输出: 10 20 30
        list.printReverse();  // 输出: 30 20 10
    }
}

双向链表的特点:

  • 每个节点有两个引用:nextprev,可以从头到尾或从尾到头进行遍历。
  • 插入和删除操作比单向链表更高效,因为你不需要遍历到目标位置。

总结

  • 单向链表:每个节点指向下一个节点,适用于只需要从头到尾遍历的情况。
  • 双向链表:每个节点有两个指针,可以双向遍历,适用于需要双向遍历的场景。
  • 循环链表:尾节点指向头节点,形成循环,适用于需要无限循环的情况。

链表的优势在于其动态大小和灵活的插入、删除操作,适用于需要频繁修改数据的情况,如实现队列、栈等数据结构。

例题

206.反转链表

链接
反转链表可以作为一种处理手段,在很多题目中进行应用。

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 reverseList(ListNode head) {
        ListNode lastNode = null;//最新的链表的头结点	
        ListNode nextNode;//原链表的下一个结点
        while(head!=null){
            nextNode = head.next;
            head.next = lastNode;
            lastNode = head;
            head = nextNode;
        }
        return lastNode;
    }
}

27.回文链表

链接
使用了反转链表进行处理。

class Solution {
    public boolean isPalindrome(ListNode head) {
        int count = 0;
        ListNode curr = head;
        while(curr != null){
            count++;
            curr = curr.next;
        }
        count = count / 2;
        curr = head;
        while(count > 0){
            curr = curr.next;
            count--;
        }
        ListNode reverse = reverseList(curr);
        while(reverse != null){
            if(reverse.val!=head.val){
                return false;
            }
            reverse = reverse.next;
            head = head.next;
        }
        return true;
    }
    public ListNode reverseList(ListNode head) {
        ListNode lastNode = null;
        ListNode nextNode;
        while(head!=null){
            nextNode = head.next;
            head.next = lastNode;
            lastNode = head;
            head = nextNode;
        }
        return lastNode;
    }
}

141.环形链表

链接
快慢指针

public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head.next;
        while (slow != fast) {
            if (fast == null || fast.next == null) { //注意先判断fast == null
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        return true;
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/linked-list-cycle/solutions/440042/huan-xing-lian-biao-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

21.合并有序链表

链接
非常巧妙的递归方法。

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        } else if (l2 == null) {
            return l1;
        } else if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/merge-two-sorted-lists/solutions/226408/he-bing-liang-ge-you-xu-lian-biao-by-leetcode-solu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.两数相加

链接

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int carry = 0;
        ListNode head = null;
        ListNode tail = null;
        while(l1 != null || l2 != null){
            int n1 = l1 == null ? 0 : l1.val;
            int n2 = l2 == null ? 0 : l2.val;
            int add =n1 + n2 + carry;
            if(head == null){
                head = tail = new ListNode(add%10);
            }else {
                tail.next = new ListNode(add%10);
                tail = tail.next;
            }
            carry = add / 10;
            if(l1 != null) l1 = l1.next;
            if(l2 != null) l2 = l2.next;
            
        }
        if(carry>0){
            tail.next = new ListNode(carry);
        }
        return head;
    }
}

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

链接
tips:没有头结点可以自己创建一个头结点来简化问题。

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode root = new ListNode();
        root.next = head;
        int count = n+1;
        head = root;
        ListNode res = head;
        while(head!=null){
            if(count>0){
                count--;
            }else {
                res = res.next;
            }
            head = head.next;
        }
        res.next = res.next.next;
        return root.next;
    }
}

138.随机链表的复制

链接

class Solution {
    public Node copyRandomList(Node head) {
        if(head == null) return null;
        Node curr = head;
        while(curr != null){
            Node copy = new Node(curr.val);
            copy.next = curr.next;
            curr.next = copy;
            curr = copy.next;
        }
        curr = head;
        while(curr != null){
            if(curr.random != null){
                curr.next.random = curr.random.next;
            }
            curr = curr.next.next;
        }
        curr = head;
        Node res = curr.next;
        Node nextCopy;
        Node copyNode;
        Node next;
        while(true){
            next = curr.next.next;
            if(next == null){
                curr.next = null;
                break;
            }
            copyNode = curr.next;
            nextCopy = next.next;
            curr.next = next;
            copyNode.next = nextCopy;
            curr = curr.next;
        }
        return res;
    }
}

标签:150,head,ListNode,next,链表,节点,null,leetcode
From: https://blog.csdn.net/buyaotutou/article/details/144397507

相关文章

  • leetcode 1750. 删除字符串两端相同字符后的最短长度
    1750.删除字符串两端相同字符后的最短长度注意审题,是相同的字符,而不是相同的字符串。所以对于abcccab来说就是输出7classSolution{public:intminimumLength(strings){intleft=0,right=s.size()-1;while(left<right){if(......
  • leetcode 125. 验证回文串
    125.验证回文串二刷,用时3ms,内存9.81MB一定要注意,是移除所有除了数字、字母以外的字符classSolution{public://'a'-'A'=32boolisPalindrome(strings){intleft=0,right=s.size()-1;while(left<right){while(left<......
  • EtherNet/IP转profinet网模块应用在AB罗克韦尔PLC与西门子1500PLC通讯案例
        在工业自动化领域,不同品牌的PLC(可编程逻辑控制器)之间进行通讯往往是项目实施中面临的一个重要问题。本文将详细介绍如何利用EtherNet/IP转profinet网关模块(远创智控的YC-PN-EIP)实现罗克韦尔PLC与西门子1500PLC之间稳定、高效的通讯,帮助大家在类似的项目......
  • 2024/12/7【哈希表】 LeetCode453 四数相加II ,知识点:defaultdict,lambda函数,dict的get
    454.四数相加II-力扣(LeetCode)代码随想录(programmercarl.com)本题解题步骤:首先定义一个unordered_map(在python中为字典),key放a和b两数之和,value放a和b两数之和出现的次数。遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中。定义int变量count,用来统计a+b+......
  • leetcode 2516. 每种字符至少取 K 个
    2516.每种字符至少取K个逆向思维:滑动窗口内的字符a最多个数为(原字符串a的个数-k),b和c同理。求出这个滑动窗口最长长度res,结果返回size-resclassSolution{public:inttakeCharacters(strings,intk){intsize=s.size(),res=0;intlette......
  • 链表的一步步实现(需有一部分c语言基础)【缓慢更新中
    链表的一步步实现(需有一部分c语言基础)(由于本人上课实在没学懂链表的具体实现步骤,于是写下这篇博客记录学习过程,有兴趣的新手也可以跟着学习1.认识链表的结构&创建简单静态链表并输出数据Q:什么是链表?A:链表是由一系列节点组成,每个节点包含两个域,一个是数据域,用来保存数据,另外一......
  • 数据结构与算法之美:再谈单链表(进阶)
            Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!我的博客:<但凡.我的专栏:《数据结构与算法之美》、《编程之路》、《题海拾贝》欢迎点赞,关注! 目录 1、使用C++实现单链表1.1节点的声明1.2节点的初始化1.3头插和尾插1.3.1头插......
  • [LeetCode] 1368. Minimum Cost to Make at Least One Valid Path in a Grid 使网格图
    Givenan mxn grid.Eachcellofthegridhasasignpointingtothenextcellyoushouldvisitifyouarecurrentlyinthiscell.Thesignof grid[i][j] canbe:1 whichmeansgotothecelltotheright.(i.egofrom grid[i][j] to grid[i][j+1])......
  • 1321. 餐馆营业额变化增长 - 力扣(LeetCode)
    1321.餐馆营业额变化增长-力扣(LeetCode)目标输入输入:营业额customer_idnamevisited_onamount1Jhon2019/1/11002Daniel2019/1/21103Jade2019/1/31204Khaled2019/1/41305Winston2019/1/51106Elvis2019/1/61407Anna2019/1/71508Maria2019/1/8809Jaze2019/1/91101Jhon2019......
  • 1341. 电影评分 - 力扣(LeetCode)
    1341.电影评分-力扣(LeetCode)目标输入输入:评分表movie_iduser_idratingcreated_at1132020/1/121242020/2/111322020/2/121412020/1/12152020/2/172222020/2/12322020/3/13132020/2/223242020/2/25输入:用户表user_idname1Daniel2Monica3Maria4James输入:电影表movie......