【算法】解题总结:剑指Offer 54 字符流中第一个不重复的字符(2 种方法)、剑指Offer 55 链表中环的入口结点

JZ54 字符流中第一个不重复的字符

(中等)

题目

描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
后台会用以下方式调用Insert 和 FirstAppearingOnce 函数
在这里插入图片描述
返回值描述:
如果当前字符流没有存在出现一次的字符,返回#字符。

示例
输入:
“google”
返回值:
“ggg#ll”

思路

此题思路不难,只需用

  • 一个哈希 map :来记录当前字符串中每个字符出现的次数
  • 一个队列(或栈或字符串或数组):用来使得当前的结果具有顺序性,(因为题目要求是返回第一个不重复的字符,而哈希 map 中键的集合是无序的,因此需要一个有序的 “容器” 来辅助记录当前只出现一次的字符,没有则为 ‘#’)

当然,队列的使用只是为了解决哈希 map 中键的集合是无序的这个问题,我们自然也能够想到可以直接使用 LinkedHashMap 这个内部实现为有序的键值对,这样就不需再用辅助 “容器” 了。

可见思路十分简单,有两种主要的实现方法,但是当我想清楚了准备开始写时,发现这个题是需要严格卡后台测试执行的代码模板,这就导致这题写起来非常不灵活。

后台模板如下:
在这里插入图片描述

方法一:HashMap + 队列

这种方法需要考虑非常多的特殊情况,我当时是试着写了一下:
在这里插入图片描述

在这里插入图片描述
之后又改了一下,也还是有问题,因此这种方法很容易出错,或者说是我写的有问题

(下面这段代码提交不对,还是有问题,已经花了太多时间了,不打算再改了,但也是一种思路)

class JZ54字符流中第一个不重复的字符2 {
    LinkedList<Character> list = new LinkedList();
    HashMap<Character, Integer> map = new HashMap<>();

    //Insert one char from stringstream
    public void Insert(char ch) {
        if (!map.containsKey(ch)) {
            map.put(ch, 1);
            //当前元素不存在,但之前可能已存在一个更提前的元素,我们需要判断
            if (list.size() > 0) {
                if (list.getLast() == '#') { //之前不存在符合的元素
                    list.add(ch);
                } else { //之前存在符合的
                    list.add(list.getLast());
                }
            } else {
                list.add(ch);
            }
        } else {
            map.put(ch, -1);
            //如果当前字符元素已存在,并且等于list中最后一个
            if (list.getLast() == ch) {
                list.add('#');
            } else { //如果当前字符元素已存在,并且不等于list中最后一个,那如果之前存在另一个符合的元素则让其充当此位置
                boolean flag = false;
                for (int i = 0; i < list.size(); i++) {
                    if (list.get(i) == 1) {
                        list.add(list.get(i));
                        flag = true; //找到
                        break;
                    }
                }
                if (!flag) { //没找到
                    list.add('#');
                }

            }
        }
    }

    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce() {
        return list.getLast();
    }
}

方法二:LinkedHashMap

【实现】

public class JZ54字符流中第一个不重复的字符 {

    Map<Character, Integer> map = new LinkedHashMap(); //哈希

    //Insert one char from stringstream
    public void Insert(char ch) {
        if(map.containsKey(ch)) {
            map.put(ch,-1);
        } else {
            map.put(ch, 1);
        }
    }

    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce() {
        Iterator<Character> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            char cur = iterator.next();
            if(map.get(cur) == 1) {
                return cur;
            }
        }
        return '#';
    }
}

在这里插入图片描述

JZ55 链表中环的入口结点

(中等)

题目

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


输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台将这2个会组装成一个有环或者无环单链表
返回值描述:
返回链表的环的入口结点即可。而我们后台程序会打印这个节点

示例1
输入:
{1,2},{3,4,5}
返回值:
3
说明:
返回环形链表入口节点,我们后台会打印该环形链表入口节点,即3

示例2
输入:
{1},{}
返回值:
“null”
说明:
没有环,返回null,后台打印"null"

示例3
输入:
{},{2}
返回值:
2
说明:
只有环形链表节点2,返回节点2,后台打印2

思路

刚开始看这题时差点没理解什么意思,还好前几天刚玩了悠悠球,灵光一现,便突然明白了,意思大概如下图,以输入 {1,2}, {3,4,5} 为例:
在这里插入图片描述
理解题意后就不难了,找出环的入口结点,就是在遍历链表时,返回第一个重复的结点(肯定不能是通过值的重复来判断,我们要通过结点对象的引用是否重复来判断)。

实现

HashSet<ListNode> set = new HashSet();

    public ListNode EntryNodeOfLoop(ListNode pHead) {
        if (pHead == null) {
            return null;
        }

        //不存在时才可以继续遍历
        while (pHead != null && !set.contains(pHead)) {
            set.add(pHead);
            pHead = pHead.next;
        }

        return pHead;
    }

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

超周到的程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值