【算法】解题总结:剑指 Offer 35 复杂链表的复制、剑指 Offer 76 删除链表中重复的结点

JZ35 复杂链表的复制

(较难)

题目

描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。在这里插入图片描述
示例:
输入:{1,2,3,4,5,3,5,#,2,#}
输出:{1,2,3,4,5,3,5,#,2,#}
解析:我们将链表分为两段,前半部分{1,2,3,4,5}为ListNode,后半部分{3,5,#,2,#}是随机指针域表示。
以上示例前半部分可以表示链表为的ListNode:1->2->3->4->5
后半部分,3,5,#,2,#分别的表示为
1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null
如下图:在这里插入图片描述

示例
输入:
{1,2,3,4,5,3,5,#,2,#}
返回值:
{1,2,3,4,5,3,5,#,2,#}

思路

本题感觉也没有什么难度,不知道为什么划分为 “较难” 题型,本题的输入虽然看起来是输入了两个链表长度数量的元素,但是在后台校验时其实就是传了一个链表而已(下文也进行了本题后台机制的测试),而其中每个结点除最后一个结点外都有 next 结点;可以有 random 结点,也可以没有。而本题要求深拷贝链表,也就是将所有引用数据类型都进行真正的复制,而对于本链表来说,要将所有结点都复制一遍,用一个新创建的头结点保存后续结果,同时既要兼顾在创建中维护 next 结点、random 结点的指向,而要实现这些,只需遍历一次链表即可,即时间复杂度为 O(n),下面为代码实现。

实现

class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

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

public class JZ35复杂链表的复制 {
    public RandomListNode Clone(RandomListNode pHead) {
        if (pHead == null) {
            return null;
        }

        RandomListNode tmp = pHead; //tmp 指向第一个结点
        RandomListNode head = new RandomListNode(pHead.label); //结果链表
        RandomListNode cur = head;
        while (tmp.next != null) {
            cur.next = new RandomListNode(tmp.next.label);

            if (tmp.random != null) {
                cur.random = new RandomListNode(tmp.random.label);
            }
            cur = cur.next;
            tmp = tmp.next;
        }

        return head;
    }
}

在这里插入图片描述
(下面这里是当时比较好奇这道题的机制,是输入一个链表,还是真就是连 random 链的指向也输入进去,发现,确实是输入一个链表,因此,也没什么可以优化的了)
在这里插入图片描述

JZ76 删除链表中重复的结点

(中等)

题目

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

示例1
输入:
{1,2,3,3,4,4,5}
返回值:
{1,2,5}

示例2
输入:
{1,1,1,8}
返回值:
{8}

思路

我在解决此题时用的是贪心思想,首先创建一个结果链表的头结点,用来存储最终结果,局部最优就是每次都向此结果链表中加入一个值不重复的结点,而全局最优就是最终结果链表中都不会有重复的结点,虽然听上去是废话,但这确实是一种贪心思想,而此思想的核心问题,就在于其局部最优在本题中如何实现,我的方法是,将对原链表的一次遍历过程分为三种情况(由于第一个结点与最后一个结点分别没有前驱与后继,因此这两种情况要特殊考虑,而中间结点的情况都可以统一判断):

  • 第 1 次遍历:如果第一个结点值与第二个结点值不相等,那么就应将第一个结点加入结果链表;
  • 第 2 至第 n-1 次遍历:如果一个结点的值与它的前一个结点的值与后一个结点的值都不相等,那么就应该将此结点的值加入结果链表;
  • 第 n 次遍历:如果最后一个结点与前一个结点值不相等,那么就应该将此结点的值加入结果链表。

当然,也要考虑传入的参数结点是否为空,以及链表是否只有一个结点的特殊情况。

实现

public class JZ76删除链表中重复的结点 {
    public ListNode deleteDuplication(ListNode pHead) {
        if (pHead == null || pHead.next == null) {
            return pHead;
        }

        ListNode cur = pHead;
        ListNode res = new ListNode(-1);
        ListNode tmp = res;
        ListNode pre = null; //上一次的结果

        while (cur.next != null) {
            if ((pre == null && cur.val != cur.next.val) //第 1 次遍历
                    || (pre != null && cur.val != pre.val && cur.val != cur.next.val) //2 ~ n-1 次遍历
            ) {
                res.next = new ListNode(cur.val);
                res = res.next;
            }

            pre = cur;
            cur = cur.next;
        }

        if (cur.val != pre.val) { //第 n 次遍历
            res.next = new ListNode(cur.val);
        }

        return tmp.next;
    }
}

在这里插入图片描述

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超周到的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值