链表高频面试题

1. 两个链表第一个公共子节点

LeetCode160

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

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

listA = [4,1,8,4,5], listB = [5,6,1,8,4,5]

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

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

1.1. 集合

    /**
     * 方法1:通过集合来辅助查找
     *
     * @param headA
     * @param headB
     * @return
     */
    public static ListNode findFirstCommonNodeBySet(ListNode headA, ListNode headB) {
        HashSet<ListNode> set = new HashSet<>();
        // 将第一个链表的每个节点放入set中
        while (headA != null) {
            set.add(headA);
            headA = headA.next;
        }
        // 遍历第二个链表中的每个节点,判断是否存在于set中
        while (headB != null) {
            if (set.contains(headB))
                return headB;
            headB = headB.next;
        }
        return null;
    }

1.2. 哈希

    /**
     * 方法2:通过Hash辅助查找
     *
     * @param pHead1
     * @param pHead2
     * @return
     */
    public static ListNode findFirstCommonNodeByMap(ListNode pHead1, ListNode pHead2) {
        // 两个链表的头结点不能为空
        if (pHead1 == null || pHead2 == null) {
            return null;
        }
        ListNode current1 = pHead1;
        ListNode current2 = pHead2;
        HashMap<ListNode, Integer> hashMap = new HashMap<>();
        // 将第一个链表中的每个节点存入 hashmap,没有用到hashmap的value,只用到了key,所以本题使用hashset结构更合适
        while (current1 != null) {
            hashMap.put(current1,null);
            current1 = current1.next;
        }
        // 遍历第二个链表,判断key是否存在hashmap中
        while (current2 != null) {
            if (hashMap.containsKey(current2)) {
                return current2;
            }
            current2 = current2.next;
        }
        return null;
    }

1.3. 栈

    /**
     * 方法3:通过栈
     */
    public static ListNode findFirstCommonNodeByStack(ListNode headA, ListNode headB) {
        // 定义两个栈 将两个链表的节点存放到两个栈中
        Stack<ListNode> stackA = new Stack<>();
        Stack<ListNode> stackB = new Stack<>();
        // 将两个链表的节点存放到两个栈中
        while (headA != null) {
            stackA.push(headA);
            headA = headA.next;
        }
        while (headB != null) {
            stackB.push(headB);
            headB = headB.next;
        }
        // 比较两个栈 栈顶的值 若相等则出栈,直至找到最后一组相等的结点
        ListNode pNode = null;
        while (stackA.size() > 0 && stackB.size() > 0) {
            if (stackA.peek() == stackB.peek()) {
                pNode = stackA.pop();
                stackB.pop();
            }else {
                break;
            }
        }
        return pNode;
    }

2. 回文串

LeetCode234

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:

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

示例 2:

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

2.1. 全部压栈

    /**
     * 方法2:全部压栈
     *
     * @param head
     * @return
     */
    public static boolean isPalindromeByAllStack(ListNode head) {
        // 定义栈 将链表中结点的值依次压入栈
        Stack<Integer> stack = new Stack<>();
        ListNode temp = head;
        while (temp != null) {
            stack.push(temp.val);
            temp = temp.next;
        }
        // 栈中的值依次出栈,与链表中的值依次比较,若有一个不一致,则不是回文串
        // 栈中的值出栈顺序为原顺序逆序,head从头遍历,正好符合回文串定义
        while (head != null) {
            if (head.val != stack.pop()){
                return false;
            }
            head = head.next;
        }
        return true;
    }

2.2. 将所有数据压栈,仅弹出一半数据进行比较

    /**
     * 方法3:将所有数据压栈,仅弹出一半数据进行比较
     * 因为回文串,前半部分的逆序就是后半部分,所以只需比较一半数据即可
     * @param head
     * @return
     */
    public static boolean isPalindromeByHalfStack(ListNode head) {
        if (head == null)
            return true;
        ListNode temp = head;
        Stack<Integer> stack = new Stack<>();
        //链表的长度
        int len = 0;
        //把链表节点的值存放到栈中
        while (temp != null) {
            stack.push(temp.val);
            temp = temp.next;
            len++;
        }
        //len长度除以2,右移1位,左边加0,相当于除以2
        len >>= 1;
        //然后再出栈
        while (len-- >= 0) {
            if (head.val != stack.pop())
                return false;
            head = head.next;
        }
        return true;
    }

3. 合并有序链表

3.1. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

输入:L1 = [1,2,4], L2 = [1,3,4] 输出:[1,1,2,3,4,4]

示例 2:

输入:L1 = [], L2 = [] 输出:[]

示例 3:

输入:L1 = [], L2 = [0] 输出:[0]

3.1.1. 基本思路

新建一个链表,然后分别遍历两个链表,每次都选择最小的结点连接到新链表上。

  /**
     * 方法1:最基础的做法
     * 新建一个链表,然后分别遍历两个链表,每次都选择最小的结点连接到新链表上。
     * @param list1
     * @param list2
     * @return
     */
    public static ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        // 新建一个链表,这个新链表有一个虚拟头结点,便于处理
        ListNode newNode = new ListNode(-1);
        // 因为newNode需要移动,所以先赋值给res备份一下
        ListNode res = newNode;
        while (list1 != null || list2 != null) {
            if (list1 != null && list2 != null) {
                // 如果第一个链表的值大于第二个链表的值,则将第二个链表的值加到新链表newNode上
                if (list1.val > list2.val) {
                    newNode.next = list2;
                    list2 = list2.next;// 将list2的头指针往后移动一个位置
                } else if (list1.val < list2.val) {
                    newNode.next = list1;
                    list1 = list1.next;
                } else {
                    newNode.next = list2;
                    list2 = list2.next;
                    newNode = newNode.next;
                    newNode.next = list1;
                    list1 = list1.next;
                }
                newNode = newNode.next; // 将newNode的头指针往后移动一个位置
            }
            if (list1 != null && list2 == null) { // 此时list2已经为空,只需连接list1的值
                newNode.next = list1;
                list1 = list1.next;
                newNode = newNode.next;
            }
            if (list1 == null && list2 != null) {
                newNode.next = list2;
                list2 = list2.next;
                newNode = newNode.next;
            }
        }
        return res.next;
    }
    /**
     * 方法2:比方法1更加精简的实现方法
     *
     * @param l1
     * @param l2
     * @return
     */
    public static ListNode mergeTwoListsMoreSimple(ListNode l1, ListNode l2) {
        ListNode prehead = new ListNode(-1);
        ListNode prev = prehead;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }
        // 最多只有一个还未被合并完,直接接上去就行了,这是链表合并比数组合并方便的地方
        prev.next = l1 == null ? l2 : l1;
        return prehead.next;
    }

3.2. 合并K个链表

    /**
     * 合并K个链表
     *
     * @param lists
     * @return
     */
    public static ListNode mergeKLists(ListNode[] lists) {
        ListNode res = null;
        for (ListNode list : lists) {
            res = mergeTwoListsMoreSimple(res, list);
        }
        return res;
    }

3.3. 合并两个链表

LeetCode1669

给你两个链表 list1 和 list2 ,它们包含的元素分别为 n 个和 m 个。

请你将 list1 中下标从 a 到 b 的全部节点都删除,并将list2 接在被删除节点的位置。

下图中蓝色边和节点展示了操作后的结果:

请你返回结果链表的头指针。

示例 :

输入:list1 = [0,1,2,3,4,5], a = 3, b = 4, list2 = [1000000,1000001,1000002] 输出:[0,1,2,1000000,1000001,1000002,5] 解释:我们删除 list1 中下标为 3 和 4 的两个节点,并将 list2 接在该位置。上图中蓝色的边和节点为答案链表。

思路:找到链表list1保留部分的尾结点和链表list2的尾结点,将两链表连接起来就行了。

 public static ListNode mergeInBetween(ListNode list1, ListNode list2, int a, int b) {
        ListNode pre1 = list1,post1 = list1, post2 = list2;
        int i = 0, j = 0;
        while (pre1 != null && post1 != null && j < b + 1) {
            // 找到链表1,位置a的前一个位置
            if (i < a - 1) {
                pre1 = pre1.next;
                i++;
            }
            // 找到链表1,位置b的后一个位置
            if (j < b + 1) {
                post1 = post1.next;
                j++;
            }
        }
        // 寻找list2的尾结点
        while (post2.next != null){
            post2 = post2.next;
        }
        // 链表1尾巴连接链表2头部,链2尾部连接链1后半部分的头
        pre1.next = list2;
        post2.next = post1;
        return list1;
    }

4. 双指针

4.1. 寻找中间结点

LeetCode876

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:head = [1,2,3,4,5] 输出:[3,4,5] 解释:链表只有一个中间结点,值为 3 。

示例 2:

输入:head = [1,2,3,4,5,6] 输出:[4,5,6] 解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。

思路:
慢指针一次移动一个结点,快指针一次移动两个结点

public ListNode middleNode(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        if (head == null) {
            return head;
        }
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

4.2. 寻找链表的倒数第K个元素

输入一个链表,输出该链表中倒数第k个节点。本题从1开始计数,即链表的尾节点是倒数第1个节点。

示例 给定一个链表: 1->2->3->4->5, 和 k = 2。

输出: 4。

    /**
     *  给出k,找出该链表倒数第K个结点
     *  思路:fast指针先走到第K个结点,然后fast和slow 同时走,当fast指向null的时候,slow指向倒数第K个结点
     * @param head
     * @param k
     * @return
     */
    public static ListNode getKthFromEnd(ListNode head, int k) {
        ListNode fast = head;
        ListNode slow = head;
        // 让fast指向第K个结点
        while (fast != null && k > 0){
            fast = fast.next;
            k--;
        }
        // 让fast 和 slow同步移动
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }

4.3. 旋转链表

LeetCode61

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:

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

思路:

先用双指针策略找到倒数K的位置,也就是{1,2,3}和{4,5}两个序列,之后再将两个链表拼接成{4,5,1,2,3}就行了。具体思路是: 因为k有可能大于链表长度,所以首先获取一下链表长度len,如果然后k=k % len,如果k == 0,则不用旋转,直接返回头结点。否则:

  1. 快指针先走k步。
  2. 慢指针和快指针一起走。
  3. 快指针走到链表尾部时,慢指针所在位置刚好是要断开的地方。把快指针指向的节点连到原链表头部,慢指针指向的节点断开和下一节点的联系。
  4. 返回结束时慢指针指向节点的下一节点。
public ListNode rotateRight(ListNode head, int k) {
        // 当K为0或者头结点为空的时候,直接返回头节点
        if (k == 0 || head == null) {
            return head;
        }
        ListNode temp = head;// 备份头结点 
        ListNode fast = head;
        ListNode slow = head;
        int len = 0;
        // 拿到链表长度len
        while(head != null) {
            head = head.next;
            len++;
        }
        if (k % len == 0) {
            return temp;
        }
        // 用快指针找到倒数第K+1个节点
        while((k % len) >0) {
            k--;
            fast = fast.next;
        }
        // 快慢指针同时移动,快指针移动到最后一个元素时,慢指针指向倒数第K+1个节点
        while(fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        ListNode res = slow.next;
        slow.next = null;
        fast.next = temp;
        return res;
    }

5. 删除结点

5.1. 删除特定结点

LeetCode203

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

示例 1:

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

思路:

  1. 我们创建一个虚拟链表头dummyHead,使其next指向head。
  2. 开始循环链表寻找目标元素,注意这里是通过cur.next.val来判断的。
  3. 如果找到目标元素,就使用cur.next = cur.next.next;来删除。
  4. 注意最后返回的时候要用dummyHead.next,而不是dummyHead。
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode cur = dummyHead;
        while (cur.next != null) {
            if (cur.next.val == val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return dummyHead.next;
    }

5.2. 删除倒数第n个结点

LeetCode19

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

示例 1:

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

    public static ListNode removeNthFromEndByTwoPoints(ListNode head, int n) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode fast = head;
        ListNode slow = dummyHead;
        for (int i = 0; i < n; ++i) {
            fast = fast.next;
        }
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        assert slow.next != null;
        slow.next = slow.next.next;
        return dummyHead.next;
    }

5.3. 删除重复元素

5.3.1. 保留一个重复元素

LeetCode83

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

示例 1:

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

思路:

由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。具体地,我们从指针 cur 指向链表的头节点,随后开始对链表进行遍历。如果当前 cur 与cur.next 对应的元素相同,那么我们就将cur.next 从链表中移除;否则说明链表中已经不存在其它与cur 对应的元素相同的节点,因此可以将 cur 指向 cur.next。当遍历完整个链表之后,我们返回链表的头节点即可。 另外要注意的是 当我们遍历到链表的最后一个节点时,cur.next 为空节点,此时要加以判断。

    public ListNode deleteDuplicates(ListNode head) {
        if (head == null) {
            return head;
        }
        ListNode cur = head;
        while (cur.next != null) {
            if (cur.val == cur.next.val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return head;
    }

5.3.2. 删除所有重复元素

LeetCode82

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

示例 1:

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

为了防止开头就有重复的元素,这个题搞一个虚拟头结点

 public ListNode deleteDuplicates(ListNode head) {
        if (head == null) {
            return head;
        }
        // 使用第三个构造函数,直接将虚拟节点连接到头结点head
        ListNode dummyHead = new ListNode(0, head);
        ListNode cur = dummyHead;
        while (cur.next != null && cur.next.next != null) {
            if (cur.next.val == cur.next.next.val) {
                int x = cur.next.val;
                // 实在是巧妙
                while (cur.next != null && cur.next.val == x) {
                    cur.next = cur.next.next;
                }
            } else {
                cur = cur.next;
            }
        }
        return dummyHead.next;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/206833.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

11-22 SSM3

书城分页查询 使用mybatis分页插件&#xff1a; 请完成登陆注册 -> 跳转到首页 解决前端上架时间点击切换 以及侧边栏点击由背景颜色的改变 完成超链接的绑定点击时间 -> jquery $(document).ready(function() { // 初始化上架时间状态为 true&#xff08;上架&…

记录一次爱快路由ACL策略引起的大坑

环境&#xff1a; A公司和B公司采用爱快的ipsec互联 B公司同时有加密软件限制网络 问题&#xff1a;对方ERP无法连接我们的数据库服务器 先简单测试了下1433端口是不是通的 下面的测试结果&#xff0c;直接ping是通的&#xff0c;但是加上1433端口后就不通 排查过程&#xff1…

centos7下执行yum命令报错

前言 在Linux系统中&#xff0c;安装nginx时候&#xff0c;需要先安装环境。 Nginx是使用C语言开发&#xff0c;安装nginx需要先从官网上将源码下载&#xff0c;然后编译&#xff0c;编译需要gcc环境,但是在安装gcc环境的时候&#xff0c;执行命令报错。 yum install –y gcc-…

【开源】基于JAVA的大学计算机课程管理平台

项目编号&#xff1a; S 028 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S028&#xff0c;文末获取源码。} 项目编号&#xff1a;S028&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 实验课程档案模块2.2 实验资源模块2…

有什么值得推荐的node. js练手项目吗?

前言 可以参考一下下面的nodejs相关的项目&#xff0c;希望对你的学习有所帮助&#xff0c;废话少说&#xff0c;让我们直接进入正题 1、 NodeBB Star: 13.3k 一个基于Node.js的现代化社区论坛软件&#xff0c;具有快速、可扩展、易于使用和灵活的特点。它支持多种数据库&…

使用JAVA语言写一个排队叫号的小程序

以下是一个简单的排队叫号的小程序&#xff0c;使用JAVA语言实现。 import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class NumberingSystem {public static void main(String[] args) {Queue<String> queue new LinkedList<…

WPF绘制进度条(弧形,圆形,异形)

前言 WPF里面圆形进度条实现还比较麻烦,主要涉及到的就是动态绘制进度条的进度需要用到简单的数学算法。其实原理比较简单,我们需要的是话两条重叠的弧线,里面的弧线要比里面的弧线要宽,这样简单的雏形就出来了。 基础写法 我们可以用Path来绘制弧线,代码如下: <Gr…

推荐几款python在线学习和电子书网站

学习python的过程中&#xff0c;虽然下载了很多的电子书&#xff0c;但是在学习过程中基本上都是通过一些在线网站或者在线电子书进行的。 下面给大家推荐几个在线学习教程网站和电子书网站。 《菜鸟教程》 一句话介绍&#xff1a;很多初学者的选择 网址&#xff1a;https:…

C++11——右值引用和移动语义

左值和右值 在C11之前&#xff0c;我们很少去关注左值和右值这一概念&#xff0c;但是在C11中&#xff0c;加入了一个非常重要的语法&#xff1a;右值引用。 左值和右值&#xff0c;一般来说可以当作字面意思&#xff0c;左值是经常出现在表达式左边的值&#xff0c;右值是经…

【开源视频联动物联网平台】写一个物联网项目捐献给Dromara组织

一、平台简介 MzMedia开源视频联动物联网平台&#xff0c;简单易用&#xff0c;更适合中小企业和个人学习使用。适用于智能家居、农业监测、水利监测、工业控制&#xff0c;车联网&#xff0c;监控直播&#xff0c;慢直播等场景。 支持抖音&#xff0c;视频号等主流短视频平台…

Linux--系统结构与操作系统

文章目录 冯诺依曼体系结构为什么要有内存&#xff1f;场景一 操作系统何为管理&#xff1f; 冯诺依曼体系结构 冯诺依曼体系结构是计算机体系结构的基本原理之一。它将程序和数据都以二进制形式存储&#xff0c;以相同的方式处理和存取。 上图是冯诺依曼体系结构的五大组成部…

处理机调度与作业调度

处理机调度 一个批处理型作业&#xff0c;从进入系统并驻留在外存的后备队列上开始&#xff0c;直至作业运行完毕&#xff0c;可能要经历如下的三级调度 高级调度 也称为作业调度、长程调度、接纳调度。调度对象是作业 主要功能&#xff1a; 挑选若干作业进入内存 为作业创建…

git push 报错 error: src refspec master does not match any 解决

git报错 ➜ *** git:(main) git push -u origin "master" error: src refspec master does not match any error: failed to push some refs to https://gitee.com/***/***.git最新版的仓库初始化后 git 主分支变成了 main 方法 1.把 git 默认分支名改回 master …

Sentinel的一些知识二

11231041 下一个是 熔断规则是异常数 和异常比例一样 只是换成了异常个数 1秒内的异常数有3个&#xff0c;就熔断2秒 下一步 进行压力测试 11231343 热点规则 没懂这个热点规则存在的意义 某个用户访问过于频繁&#xff0c;对其进行限制&#xff0c;给其他用户访问的机…

mysql中删除数据后,新增数据时id会跳跃,主键自增id不连续

引言&#xff1a; 在使用MySQL数据库时&#xff0c;有时候我们需要删除某些记录&#xff0c;但是删除记录后可能会导致表中的id不再连续排序。 如何实现删除记录后让id重新排序的功能。 如图&#xff1a; 删除数据后&#xff0c;中间的id不会自动连续。 下面有两种方法进行重…

万界星空科技仓库管理wms系统

企业在管理库存时&#xff0c;尤其是生产制造企业&#xff0c;使用传统方式比如纸笔、Excel 管理库存&#xff0c;由于工具和信息化存在局限&#xff0c;导致在管理库存时出现如下问题&#xff1a; 1、通过纸笔记录出入库申请&#xff0c;人为手动计算易出错&#xff0c;数据易…

金石工程项目管理系统 SQL注入漏洞复现

0x01 产品简介 金石工程项目管理软件是一款工程项目管理软件,专门针对建筑工程项目开发,可以用于各种工地的项目管理。 0x02 漏洞概述 金石工程项目管理系统TianBaoJiLu.aspx接口处存在SQL注入漏洞&#xff0c;攻击者可通过该漏洞获取数据库中的信息&#xff08;例如&#xff…

外贸B2B自建站怎么建?做海洋建站的方法?

如何搭建外贸B2B自建站&#xff1f;外贸独立站建站方法有哪些&#xff1f; 对于许多初次涉足者来说&#xff0c;搭建一个成功的外贸B2B自建站并不是一件轻松的任务。海洋建站将为您详细介绍如何有效地建设外贸B2B自建站&#xff0c;让您的国际贸易之路更加畅通无阻。 外贸B2B…

TR转发路由器测评—云企业网实现跨地域跨VPC的网络互通测评实战【阿里云产品测评】

文章目录 一.转发路由器 Transit Router 测评1.1 准备阶段1.2 本文测评收获1.3 什么是云企业网实例、转发路由器实例和云数据传输服务 二.使用云企业网实现跨地域跨VPC的网络互通2.2 **测试连通性**2.3 网络拓扑如下&#xff1a; 心得&#xff1a;总结&#xff1a; 声明&#x…

LeetCode Hot100 438.找到字符串中所有字母异位词

题目&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 代码&#xff1a; class Solution …