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;
}