链表补充题目

复制代码
package SecondBrush.LinkedList;
/**
 * 83. 删除排序链表中的重复元素
 * 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
 * */
/**
 * 这个题目自己就直接做出来了,只是循环条件少写了一个 cur.next != null
 * 首先这道题目是 排序链表,所以相等的一定在一起,只需要对比 cur.val 和 cur.next.val
 * 如果相等直接跨过去,
 * 不相等就直接 移动 指向
 * */

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

}
复制代码
复制代码
package SecondBrush.LinkedList;
/**
 * 82. 删除排序链表中的重复元素 II
 * 给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
 * 输入:head = [1,2,3,3,4,4,5]
 * 输出:[1,2,5]
 * */
/**
 * 想到的方法是双指针,因为这个可能设计到删除头结点,所以需要使用虚拟头结点
 * 简单梳理思路:因为涉及到删除元素,所以需要虚拟头节点,双指针,一个指向虚拟节点(prev),一个指向 head(cur),
 * 因为要删除重复元素,且是有序的,所以重复元素都是在一起的,所以使用cur找到最后一个重复元素
 * 如果pre.next 是cur,说明两个指针之间没有重复数据,否则 pre.next = cur.next
 * */

public class RemoveDuplicatesSortedListII_82 {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode dummynode = new ListNode(-1,head);
        ListNode prev = dummynode;
        ListNode cur = head;
        while (cur!= null){
            //如果出现重复的情况如下
            //相等的结点则跳过,走到相同值元素的最后一步
            while (cur.next!=null && cur.val == cur.next.val){
                cur = cur.next;
            }
            //如果pre和cur之间没有重复节点,pre后移
            if (prev.next == cur){
                prev = prev.next;
            }else {
                prev.next = cur.next;
            }
            cur = cur.next;
        }
        return dummynode.next;
    }
}
复制代码
复制代码
package SecondBrush.LinkedList;
/**
 * 92. 反转链表 II
 * 给你单链表的头指针 head 和两个整数 left 和 right ,其中left <= right 。
 * 请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
 * */
/**
 * 自己尝试写一下,将中间链表单独拿出来反转,之后,再进行拼接
 *
 *
 * */
public class ReverseLinkedListII_92 {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        // 设置 dummyNode 是这一类问题的一般做法
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;
        ListNode pre = dummyNode;
        for (int i = 0; i < left - 1; i++) {
            pre = pre.next;
        }
        ListNode cur = pre.next;
        ListNode next;
        for (int i = 0; i < right - left; i++) {
            next = cur.next;
            cur.next = next.next;
            next.next = pre.next;
            pre.next = next;
        }
        return dummyNode.next;
    }
}
复制代码
复制代码
package SecondBrush.LinkedList;
/**
 * 876. 链表的中间结点
 * 给你单链表的头结点 head ,请你找出并返回链表的中间结点。
 * 如果有两个中间结点,则返回第二个中间结点。
 * */
/**
 * 1.朴素解法:先遍历整个链表,得到长度,再取中间值,然后再遍历一半取目标值
 *
 * 2.快慢指针:没想到这个,有点环形链表的意思
 * */
public class MiddleLinkedList_876 {
    public ListNode middleNode(ListNode head) {
        ListNode p = head, q = head;
        while (q != null && q.next != null) {
            q = q.next.next;
            p = p.next;
        }
        return p;
    }
}
复制代码
复制代码
package SecondBrush.LinkedList;
/**
 * 剑指 Offer 22. 链表中倒数第k个节点
 * 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
 * 例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
 * */
/**
 * 1.朴素解法:想到的就是先遍历长度,然后减去 倒数长度,从前往后走
 * 2.快慢指针:这个和之前的有一道题目很像,
 * */
public class GetKthFromEnd_22 {

    public ListNode getKthFromEnd(ListNode head, int k) {
        int len = 0;
        ListNode cur = head;
        while (cur != null){
            cur = cur.next;
            len++;
        }
        cur = head;
        int cha = len - k;
        for (int i = 0; i < cha; i++) {
            cur = cur.next;
        }
        return cur.next;
    }

    public ListNode getKthFromEnd2(ListNode head, int k) {

        ListNode frontNode = head, behindNode = head;
        while (frontNode != null && k > 0) {

            frontNode = frontNode.next;
            k--;
        }

        while (frontNode != null) {

            frontNode = frontNode.next;
            behindNode = behindNode.next;
        }

        return behindNode;
    }

}
复制代码
复制代码
package SecondBrush.LinkedList;
/**
 * 234. 回文链表
 * 给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
 * 回文链表指的是有对称性
 * */
/**
 * 找到最后的节点,一个朝前遍历,一个朝后遍历,判断相遇前是否全部一致,如果不一致,说明不是回文链表
 * -- 初始想法,但是写不出来,后指针不好往前遍历
 *
 * 现在想起来快慢指针
 *
 * */
public class PalindromeLinkedList_234 {
    public boolean isPalindrome(ListNode head) {
        // 要实现 O(n) 的时间复杂度和 O(1) 的空间复杂度,需要翻转后半部分
        if (head == null || head.next == null) {
            return true;
        }
        ListNode fast = head;
        ListNode slow = head;
        // 根据快慢指针,找到中间节点或者前半部分链表的尾节点(注意判断条件)
        while(fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        // 反转后半部分链表
        slow = reverse(slow.next);
        while(slow != null) {
            if (head.val != slow.val) {
                return false;
            }
            head = head.next;
            slow = slow.next;
        }
        return true;
    }

    /**
     * 反转(迭代)
     */
    private ListNode reverse(ListNode head){
        ListNode pre = null;
        ListNode next = null;
        while(head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

}
复制代码
复制代码
package SecondBrush.LinkedList;
/**
 * 21. 合并两个有序链表
 * 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
 * 输入:l1 = [1,2,4], l2 = [1,3,4]
 * 输出:[1,1,2,3,4,4]
 * */
/**
 * 思路:初始想法,定义两个指向,分别指向两个链表的头结点,然后谁小,就排在前面
 *
 * */
public class MergeTwoSortedLists_21 {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        // 类似归并排序中的合并过程
        ListNode dummyHead = new ListNode(0);
        ListNode cur = dummyHead;
        while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
                cur.next = list1;
                cur = cur.next;
                list1 = list1.next;
            } else {
                cur.next = list2;
                cur = cur.next;
                list2 = list2.next;
            }
        }
        // 任一为空,直接连接另一条链表
        if (list1 == null) {
            cur.next = list2;
        } else {
            cur.next = list1;
        }
        return dummyHead.next;
    }
}
复制代码
复制代码
package SecondBrush.LinkedList;
/**
 * 面试题 02.04. 分割链表
 * 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
 * 你不需要保留每个分区中各节点的初始相对位置。
 * 输入:head = [1,4,3,2,5,2], x = 3
 * 输出:[1,2,2,4,3,5]
 * */
/**
 * 初始想法,首先要找到,第一个大于或等于x的节点,这个直接遍历即可--
 *
 * 想的不太对
 * */
public class PartitionListLCCI_0204 {

    public ListNode partition(ListNode head, int x) {
            ListNode small = new ListNode(0);
            ListNode smallHead = small;
            ListNode large = new ListNode(0);
            ListNode largeHead = large;
            while (head != null) {
                if (head.val < x) {
                    small.next = head;
                    small = small.next;
                } else {
                    large.next = head;
                    large = large.next;
                }
                head = head.next;
            }
            large.next = null;
            small.next = largeHead.next;
            return smallHead.next;
        }

}
复制代码
复制代码
package SecondBrush.LinkedList;
/**
 * 141. 环形链表
 *给你一个链表的头节点 head ,判断链表中是否有环。
 * 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
 * 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。
 * 注意:pos 不作为参数进行传递。仅仅是为了标识链表的实际情况。
 * 如果链表中存在环,则返回 true 。 否则,返回 false 。
 * */
public class LinkedListCycle_141 {
    public boolean hasCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow){
                return true;
            }
        }
        return false;
    }
}
复制代码
复制代码
package SecondBrush.LinkedList;

/**
 * 剑指 Offer 06. 从尾到头打印链表
 * 输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
 */


public class PrintLinkedList_offer06 {
    public int[] reversePrint(ListNode head) {
        ListNode cur = head;
        int count = 0;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        int[] nums = new int[count];
        cur = head;
        for (int i = count - 1; i >= 0; i--) {
            nums[i] = cur.val;
            cur = cur.next;
        }
        return nums;
    }
}
复制代码
复制代码
package SecondBrush.LinkedList;
/**
 * 剑指 Offer 35. 复杂链表的复制
 * 请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,
 * 还有一个 random 指针指向链表中的任意节点或者 null。
 * */
public class CopyRandomList_offer35 {
    public Node copyRandomList(Node head) {
        if (head == null) {
            return head;
        }
        //将拷贝节点放到原节点后面,例如1->2->3这样的链表就变成了这样1->1'->2->2'->3->3'
        for (Node node = head, copy = null; node != null; node = node.next.next) {
            copy = new Node(node.val);
            copy.next = node.next;
            node.next = copy;
        }
        //把拷贝节点的random指针安排上
        for (Node node = head; node != null; node = node.next.next) {
            if (node.random != null) {
                node.next.random = node.random.next;
            }
        }
        //分离拷贝节点和原节点,变成1->2->3和1'->2'->3'两个链表,后者就是答案
        Node newHead = head.next;
        for (Node node = head, temp = null; node != null && node.next != null;) {
            temp = node.next;
            node.next = temp.next;
            node = temp;
        }

        return newHead;
    }
}
复制代码

 

posted @   坤坤无敌  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
点击右上角即可分享
微信分享提示