常见的链表面试题(上)

文章目录

    • 1.移除链表元素
    • 2.删除链表中的重复元素
    • 3.反转链表
    • 4.链表的中间结点
    • 5.链表中倒数第k个结点
    • 6.合并两个有序链表
    • 7.分割链表

1.移除链表元素

leetcode203 移除链表元素
常见的链表面试题(上)_第1张图片
法一:普通迭代
常见的链表面试题(上)_第2张图片

    public ListNode removeElements(ListNode head, int val) {
        //该题使用双指针,pre指向待删除结点的前驱,cur指向待删除结点
        //引入虚拟头结点,解决头结点无前驱问题
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;
        ListNode pre = dummyHead;
        ListNode cur = head;
        //cur == null时已经遍历完整个链表,结束循环
        while (cur != null){
            //找到待删结点时
            if (cur.val == val){
                //pre的下一个结点指向cur的下一个结点,以此删除当前结点结点
                pre.next = cur.next;
                //cur后移,继续判断
                cur = pre.next;
            }else {
                //cur所在的结点不是待删结点时,两个指针同时后移
                pre = pre.next;
                cur = cur.next;
            }
        }
        return dummyHead.next;
    }

法二:递归
常见的链表面试题(上)_第3张图片

    public ListNode removeElements(ListNode head, int val) {
        //边界条件
        //当链表中没有结点时,不用删除
        if (head == null){
            return null;
        }
        //把链表拆分为头结点和剩下的结点
        //剩下的结点交给removeElements()处理
        head.next = removeElements(head.next,val);
        //判断头结点是否为待删除结点
        if (head.val == val){
            return head.next;
        }
        else {
            return head;
        }
    }

2.删除链表中的重复元素

leetcode82 删除排序链表中的重复元素
常见的链表面试题(上)_第4张图片
法一:普通迭代
此时需要用到三指针,pre指向待删结点的结点,cur和next用来判断是否出现重复元素,next指向第一个不重复的结点。
常见的链表面试题(上)_第5张图片

    public ListNode deleteDuplicates(ListNode head) {
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;
        ListNode pre = dummyHead;
        ListNode cur = head;
        //循环开始的条件是链表中至少有两个结点,并且需要保证cur.next和next.val不会出现空指针异常
        while (cur != null && cur.next != null){
            //next总是指向cur的后一个结点,我们将其定义在循环内
            ListNode next = cur.next;
            //当没有遇到重复元素时,三个指针同时后移
            if (cur.val != next.val){
                pre = pre.next;
                cur = cur.next;
            }else {
                //当找到重复元素时,我们让next一直后移判断,知道next指向第一个不与cur相等的结点
                //此时我们也要确保不会出现空指针异常
                while (next != null && cur.val == next.val){
                    next = next.next;
                }
                //删除所有重复的结点
                pre.next = next;
                cur = next;
            }
        }
        return dummyHead.next;
    }

法二:递归
常见的链表面试题(上)_第6张图片

    public ListNode deleteDuplicates(ListNode head) {
        //边界
        if (head == null || head.next == null){
            return head;
        }
        //首先判断头结点是否为重复结点
        //头结点不是重复结点时,head.next交给deleteDuplicates(ListNode head.next)处理
        //返回头结点
        if (head.val != head.next.val) {
            head.next = deleteDuplicates(head.next);
            return head;
        } else {
            //头结点是重复节点时
            //头结点后移,直到头结点的下一个结点不是重复节点
            //head.next交给deleteDuplicates(ListNode head.next)处理
            //返回head.next
            while (head.next != null && head.val == head.next.val){
                head = head.next;
            }
            head.next = deleteDuplicates(head.next);
            return head.next;
        }
    }

3.反转链表

leetcode206 反转链表
法一:创建新链表,头插原来链表中的元素

    public ListNode reverseList(ListNode head) {
        //法一:创建新链表,头插原来链表中的元素
        ListNode dummyHead = new ListNode();
        while (head != null){
            ListNode node = new ListNode(head.val,dummyHead.next);
            dummyHead.next = node;
            head = head.next;
        }
        return dummyHead.next;
    }

法二:改变原来链表的指向方向
常见的链表面试题(上)_第7张图片

    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while (cur != null){
            //暂存cur的下一个结点
            ListNode next = cur.next;
            //改变cur的指向方向
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }

法三:递归
常见的链表面试题(上)_第8张图片

    public ListNode reverseList(ListNode head) {
        //边界
        if (head == null || head.next == null){
            return head;
        }
        //node指向反转后的尾结点
        ListNode node = head.next;
        ListNode newHead = reverseList(head.next);
        head.next = null;
        node.next = head;
        return newHead;
    }

4.链表的中间结点

leetcode876 链表的中间结点
常见的链表面试题(上)_第9张图片
法一:遍历整个链表,找到中间结点

    public ListNode middleNode(ListNode head) {
        //遍历链表,count记录链表的结点个数
        int count = 0;
        for (ListNode x = head;x != null;x = x.next){
            count++;
        }
        //middle是中间结点的索引
        int middle = count / 2;
        ListNode node = head;
        //node走middle步到达中间结点
        for (int i = 0; i < middle; i++) {
            node = node.next;
        }
        return node;
    }

法二:快慢指针,走相同的路程,fast的速度是slow的两倍,即fast走两步,slow走一步,当fast走到终点时,slow正好走到中间位置。

    //快慢指针
    public ListNode middleNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

5.链表中倒数第k个结点

Offer22 链表中倒数第k个结点
常见的链表面试题(上)_第10张图片
常见的链表面试题(上)_第11张图片

    public ListNode getKthFromEnd(ListNode head, int k) {
        //判断链表是否为空和k的合法性
        if (head == null || k <= 0){
            return null;
        }
        ListNode fir = head;
        ListNode sec = head;
        int count = 0;
        //fir比sec多走k步
        for (int i = 0; i < k; i++) {
            fir = fir.next;
            count++;
        }
        //k超出链表范围
        if (count < k){
            return null;
        }
        while (fir != null){
            fir = fir.next;
            sec = sec.next;
        }
        return sec;
    }

6.合并两个有序链表

leetcode21 合并两个有序链表
常见的链表面试题(上)_第12张图片
常见的链表面试题(上)_第13张图片
常见的链表面试题(上)_第14张图片

    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        //判空
        if (list1 == null){
            return list2;
        }
        if (list2 == null){
            return list1;
        }
        ListNode dummyHead = new ListNode();
        ListNode node = dummyHead;
        while (list1 != null && list2 != null){
            //不断去寻找两个链表中较小的结点,将其连接到尾部
            if (list1.val <= list2.val){
                node.next = list1;
                node = node.next;
                list1 = list1.next;
            }else {
                node.next = list2;
                node = node.next;
                list2 = list2.next;
            }
        }
        //当list1和list2有一个为空时,跳出循环
        //若list1为空,则链表尾部连接上list2剩余的结点
        if (list1 == null){
            node.next = list2;
            //反之,链表尾部连接上list1剩余的结点
        }else {
            node.next = list1;
        }
        return dummyHead.next;
    }

7.分割链表

分割链表
常见的链表面试题(上)_第15张图片
创建两个新链表,一个存放原链表中小于x的结点,一个存放原链表中大于等于x的结点。
常见的链表面试题(上)_第16张图片

    public ListNode partition(ListNode head, int x) {
        if (head == null || head.next == null){
            return head;
        }
        //创建两个链表,一个存储小于x的节点,一个存储大于等于x的节点
        ListNode dummyHead1 = new ListNode();
        ListNode node1 = dummyHead1;
        ListNode dummyHead2 = new ListNode();
        ListNode node2 = dummyHead2;
        while (head != null){
            //node1存储小于x的节点
            if (head.val < x){
                node1.next = head;
                node1 = node1.next;
                head = head.next;
            }else {
                //node2存储大于等于x的节点
                node2.next = head;
                node2 = node2.next;
                head = head.next;
            }
        }
        //断开node2后面小于x的节点
        node2.next = null;
        //连接node1和node2
        node1.next = dummyHead2.next;
        return dummyHead1.next;
    }

你可能感兴趣的:(链表,leetcode,list)