【面试必看】链表的常见笔试题

1. 合并链表

链接:https://leetcode.cn/problems/merge-two-sorted-lists

  • 题目描述

给定两个有序链表,将两个升序链表合并为一个新的升序链表并返回。

在这里插入图片描述

  • 题解

解法一:递归解法

class Solution {
   public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
  		//特殊情况的考虑
        if(list1 ==null){ return list2; }
        if(list2 ==null){ return list1; }
        //此时,两个链表都不为空,开始合并
        if(list1.val <= list2.val){
            list1.next=mergeTwoLists(list1.next,list2);
            return list1;
        }else{
            list2.next =mergeTwoLists(list1,list2.next);
            return list2;
        }
    }
}

解法二:非递归解法

class Solution {
     public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
    if (list1 ==null){
        return list2;
    }
    if (list2 ==null){
        return list1;
    }
         //此时,两个链表都不为空,合并两个链表
        ListNode dummyHead =new ListNode(1001);     //创建一个虚拟头节点
        //利用尾插进行链表的合并
        ListNode tail= dummyHead;
        while (list1 != null && list2 != null){
            if (list1.val <= list2.val){
                tail.next=list1;
                tail =list1;
                list1=list1.next;
            }else {
                tail.next =list2;
                tail =list2;
                list2 =list2.next;
            }
        }
        //循环走完,表示此时已经有链表为空
        if(list1 ==null){
            tail.next =list2;
        }
        if (list2 == null){
            tail.next =list1;
        }
        return dummyHead.next;
    }
}

2. 反转链表

在这里插入图片描述

  • 题解
/**
     * 反转链表
     * @param head
     * @return
     */
    public ListNode reverseList(ListNode head) {
        //边界条件
        if (head ==null || head.next ==null){
            return head;
        }
        //定义头节点后的节点
        ListNode second =head.next;
        //反转第二个节点之后的所有节点
        ListNode newHead =reverseList(head.next);
        //改变指针引用,将sec指向head,head指向空
        second.next =head;
        head.next =null;
        return newHead;
    }

    //解法二  新建一个链表,头插法
    public ListNode reverseList1(ListNode head) {
        if (head ==null || head.next ==null){
            return head;
        }
        ListNode dummyHead =new ListNode(5001);
        //遍历原链表,边头插法链表
        while (head !=null){
            ListNode node =new ListNode(head.val);
            node.next =dummyHead.next;
            dummyHead.next =node;
            head=head.next;
        }
        return dummyHead.next;
    }

    //递归解法
    public ListNode reverseList2(ListNode head) {
        if (head ==null || head.next ==null){
            return head;
        }
        ListNode sec =head.next;
        //反转第二个节点之后的节点
        ListNode newHead =reverseList(head.next);
        //将sec指向head,head指向空
        sec.next =head;
        head.next =null;
        return newHead;
    }

3. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个结点。
在这里插入图片描述

  • 题解
 /**
     * 快慢指针的解题思路
     * @param head
     * @param k
     * @return
     */
    public ListNode FindKthToTail(ListNode head,int k) {
        ListNode fast =head;
        ListNode slow=head;
        //先让快指针走k步
        for (int i = 0; i < k; i++) {
            //TODO:极端情况考虑,链表的长度小于k,走k步会发生空指针异常
           if (fast ==null) {
               return null;
           }
            fast =fast.next;
        }
        //接下来,让快慢指针同时走
        while (fast != null){
            fast =fast.next;
            slow =slow.next;
        }
        //此时,fast为空,low走到倒数第k个节点
        return slow;
    }

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

在这里插入图片描述

  • 题解
class Solution {
      /**
         * 给定一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
         * @param head
         * @param n
         * @return
         */
        public ListNode removeNthFromEnd(ListNode head, int n) {
            //虚拟头节点的解法
            ListNode dummyHead =new ListNode(-1);
            dummyHead.next =head;

            //快慢指针都从虚拟头节点开始向后走
            ListNode fast =dummyHead;
            ListNode slow =dummyHead;

            //先让快指针走k+1步
            for (int i = 0; i <=n ; i++) {
                fast =fast.next;
            }
            while (fast !=null ){
                fast=fast.next;
                slow =slow.next;
            }
            //此时,slow就是待删除节点的前驱节点
            //node就是待删除的节点
           ListNode node =slow.next;
            slow.next =node.next;
            node.next =null;
            return dummyHead.next;
        }
}

5. 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
![在这里插入图片描述](https://img-blog.csdnimg.cn/f4551216f3d84251981d5340a1e3657f.png

  • 题解
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode pA =headA;
        ListNode pB =headB;
        //1.相交时刚好返回交点
        //2.不相交时两个同时走到null
        while (pA !=pB){
            pA = pA==null ?headB :pA.next;
            pB = pB==null ?headA :pB.next;
        }
        return pA;
    }
}

6. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

在这里插入图片描述

  • 题解
 /**
 * 解题思路:快慢指针
 * 快指针一次走两步,慢指针一次走一步,链表若存在环,慢指针一定能追上快指针
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        //快指针
        ListNode fast = head;
        //满指针
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            //一次走两步
            fast = fast.next.next;
            //一次走一步
            slow = slow.next;
            //此时,fast和low指向相同的节点,成环
            if (fast == slow) {
                return true;
            }
        }
        //此时,fast==null 或fast.next ==null,直线结构
        return false;
    }
}

7. 回文链表

这里是引用

  • 题解
    /**
     * 判断单链表是否尾回文链表
     * @param head
     * @return
     */
 public boolean isPalindrome(ListNode head) {
    //找到中间节点
        ListNode middleNode =middleNode(head);
    //反转中间链表及其之后的链表
        ListNode r2 =reverseList(middleNode);
        while (r2 != null){
            if (head.val != r2.val){
                return false;
            }
            //继续向后遍历
            r2 =r2.next;
            head =head.next;
        }
        return  true;
    }

    /**
     * 查找链表的中间节点
     * @param head
     * @return
     */
    public ListNode middleNode(ListNode head) {
        //快指针,从头开始,一次走两步
        ListNode fast =head;
        //慢指针,从头开始,一次走一步
        ListNode low =head;
        //循环终止两件 :fast为空(偶数个节点),fast.next为空(奇数个节点)
        while (fast != null && fast.next !=null){
            low =low.next;          //走一步
            fast =fast.next.next;       //走两步
        }
        return low;
    }

    /**
     * 反转链表
     * @param head
     * @return
     */
    public ListNode reverseList(ListNode head) {
        //边界条件
        if (head ==null || head.next ==null){
            return head;
        }
        //定义头节点后的节点
        ListNode second =head.next;
        //反转第二个节点之后的所有节点
        ListNode newHead =reverseList(head.next);
        //改变指针引用,将sec指向head,head指向空
        second.next =head;
        head.next =null;
        return newHead;
    }

8. 反转指定区间上范围的链表

在这里插入图片描述

  • 题解
import java.util.*;
public class Main {
    //链表的节点
    static class  Node{
        int val;
        Node next;
        public Node(int val) {
            this.val = val;
        }
    }
    public static void main(String[] args) {
        Scanner in =new Scanner(System.in);
        //链表元素的个数
        int n= in.nextInt();
        in.nextLine();
        //链表的节点值
        String nodeValue = in.nextLine();
        String[] nodes =nodeValue.split(" ");
        //尾插创建链表 虚拟头节点
        Node dummyHead =new Node(-1);
        Node tail =dummyHead;
        for (int i = 0; i <n ; i++) {
        Node node =new Node(Integer.parseInt(nodes[i]));
            tail.next =node;
            tail =node;
        }
        //获取区间范围
        String range= in.nextLine();

        //左区间
        int left =Integer.parseInt(range.split(" ")[0]);
        //右区间
        int right =Integer.parseInt(range.split(" ")[1]);
        Node newHead =reversePartList(dummyHead.next,left,right);
        //进行输出处理
        while (newHead !=null){
            System.out.print(newHead.val+" ");
            newHead =newHead.next;
        }
    }

    //反转区间范围【left,right】的链表
    private static Node reversePartList(Node head, int left, int right) {
        Node dummyHead =new Node(-1);
        //挂载虚拟头节点
        dummyHead.next =head;
        //prev 指向待反转链表的前驱节点
        Node prev =dummyHead;
        for (int i = 1; i <left ; i++) {
            prev =prev.next;
        }
        //cur就是待反转链表的第一个节点
        Node cur =prev.next;
        for (int i = left; i <right ; i++) {
            Node third =cur.next;
            //将third节点先从原链表中删除再头插到cur
            cur.next =third.next;
            //头插
           third.next =prev.next;
           prev.next=third;
        }
        return dummyHead.next;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值