剑指OFFER-复杂链表的复制(Java)

1. 反转链表

1.1 题目描述

输入一个链表,反转链表后,输出新链表的表头。

1.2 示例1

输入

{1,2,3}

返回值

{3,2,1}

1.3 核心代码实现

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    
    
    public ListNode ReverseList(ListNode head) {
    
    
        if(head == null || head.next == null)
            return head;
        ListNode pre = null;    //pre记录当前节点的前一个节点
        ListNode next = null;    //next记录当前节点的下一个节点
        while(head != null){
    
    
            next = head.next;    //先用next指针记录当前节点的下一个节点地址
            head.next = pre;    //让当前节点与链表断开并指向前一个节点pre,反转
            pre = head;    //继续下一个节点
            head = next;
        }
        return pre;
    }
}

2. 合并两个排序的链表

2.1 题目描述

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

2.2 示例1

输入

{1,3,5},{2,4,6}

返回值

{1,2,3,4,5,6}

2.3 核心代码实现

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    
    
    public ListNode Merge(ListNode list1,ListNode list2) {
    
    
        /*
        //递归,若list1<list2,则list1为新序列的头节点,继续比较list1.next与list2
        if(list1 == null) return list2;
        if(list2 == null) return list1;
        if(list1.val < list2.val){
            list1.next = Merge(list1.next, list2);
            return list1;
        }else{
            list2.next = Merge(list2.next, list1);
            return list2;
        }
        */
        
        ListNode head = new ListNode(0);    //初始化一个节点值为0的空节点
        ListNode result = head;
        while(list1 != null && list2 != null){
    
    
            if(list1.val < list2.val){
    
    
                result.next = list1;
                list1 = list1.next;
            }else{
    
    
                result.next = list2;
                list2 = list2.next;
            }
            result = result.next;
        }
        if(list1 != null)    //list1的节点多于list2的
            result.next = list1;
        if(list2 != null)    //list2的节点多于list1的
            result.next = list2;
        return head.next;
    }
}

3. 复杂链表的复制

3.1 题目描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

3.2 核心代码实现

import java.util.HashMap;
/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/

public class Solution {
    
    
    public RandomListNode Clone(RandomListNode pHead){
    
    
        if(pHead == null) return pHead;
        
        RandomListNode result = new RandomListNode(pHead.label);    //result作为新链表的头结点
        RandomListNode p1 = pHead;    //p1获取链表的头结点
        RandomListNode p2 = result;    //p2获取新链表的头结点
        
        HashMap<RandomListNode, RandomListNode> map = new HashMap<>();
        //第一次遍历,利用pHead将所有值存入map,避免被替换,需要new
        while(pHead != null){
    
    
            map.put(pHead, new RandomListNode(pHead.label));
            pHead = pHead.next;
        }

        //第二次遍历
        while(p1 != null){
    
    
            p2.next = map.get(p1.next);    //拷贝映射关系
            p2.random = map.get(p1.random);
            
            p1 = p1.next;    //同时移动p1和p2
            p2 = p2.next;
        }
        
        return result;    //返回拷贝后的头结点
    }
}

4. 孩子们的游戏(圆圈中最后剩下的数)

4.1 题目描述

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
如果没有小朋友,请返回-1

4.2 示例1

输入

5,3

返回值

3

4.3 核心代码实现

import java.util.ArrayList;

public class Solution {
    
    
    public int LastRemaining_Solution(int n, int m) {
    
    
        if(n == 0) return -1;    //没有小盆友时
        /*
        //数学归纳法:设f[n]表示有n个小盆友时最后获奖小盆友的编号,则f[1]=0;f[n]=(f[n-1]+m)%n;
        int index = 0;    //只有一个小盆友时,编号为0的小盆友获奖
        //有两个或两个以上小盆友时
        for(int i = 2; i <= n; i++){
            index = (index + m) % i;
        }
        return index;
        */
        
        //方法二:借助list的add和remove方法
        ArrayList<Integer> list = new ArrayList<>();
        for(int i = 0; i < n; i++){
    
    
            list.add(i);    //模拟初始小盆友序列
        }
        int index = 0;    //index记录要删除的元素位置,初始化为0
        while(list.size() > 1){
    
        //当不止一个小盆友时
            index = (index + m - 1) % list.size();    //开始新一轮报数
            list.remove(index);    //将唱歌小盆友对应的编号从list中移除
        }
        return list.get(0);    //返回list中仅剩的一个元素
    }
}

5. 删除链表中重复的结点

5.1 题目描述

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

5.2 示例1

输入

{1,2,3,3,4,4,5}

返回值

{1,2,5}

5.3 核心代码实现

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    
    
    public ListNode deleteDuplication(ListNode pHead){
    
    
        if(pHead == null || pHead.next == null) return pHead;
        
        ListNode head = new ListNode(0);    //定义一个哨兵结点head
        head.next = pHead;
        ListNode pre = head;
        ListNode cur = head.next;
        
        while(cur != null){
    
    
            if(cur.next != null && cur.val == cur.next.val){
    
    
                //遇到重复结点时,cur继续往前走,直到不同后跳出循环
                while(cur.next != null && cur.val == cur.next.val){
    
    
                    cur = cur.next;
                }
                //退出循环后,当前的cur仍指向重复结点
                cur = cur.next;    //让cur指向下一个不重复的结点
                pre.next = cur;    //让pre连接不重复的结点
            }else{
    
    
                pre = cur;
                cur = cur.next;
            }
        }
        return head.next;
    }
}

6. 链表中环的入口结点

6.1 题目描述

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

6.2 核心代码实现

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/

//提示:画图理解

public class Solution {
    
    
    public ListNode EntryNodeOfLoop(ListNode pHead){
    
    
        if(pHead == null || pHead.next == null) return null;
        
        ListNode fastP = pHead;    //定义一个快指针并初始化
        ListNode slowP = pHead;    //定义一个慢指针并初始化
        
        while(fastP != null && fastP.next != null){
    
    
            fastP = fastP.next.next;    //快指针每次跑两个element
            slowP = slowP.next;    //慢指针每次跑一个element
            if(fastP == slowP){
    
        //快指针与慢指针相遇
                ListNode slowP2 = pHead;    //重新定义一个慢指针从头开始跑
                while(slowP2 != slowP){
    
        //两个慢指针同步跑,直到两者相遇
                    slowP2 = slowP2.next;
                    slowP = slowP.next;
                }
                return slowP;    //返回任意一个慢指针,此时两者指向同一结点
            }
        }
        return null;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_48440312/article/details/110850115
今日推荐