面试题目: 第一个非重复的字符

Posted xiaoranone

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试题目: 第一个非重复的字符相关的知识,希望对你有一定的参考价值。

面试题目: 第一个非重复的字符

面试官: 看你的简历还不错,本科也是学的计算机专业,要不咱们先做道题.
我: (瑟瑟发抖)好.

面试官: 给你一个字符串,请找出第一个只出现一次的字符.
我: 我思考一下,如果不存在只出现一次的字符,返回什么?
面试官: 返回空’ '.顺便说一下,字符串中只包含acii码字符.

我: (内心独白:这个题目简单啊,可以直接暴力,管他呢,先给出一个暴力解) ok. 我可以用一个两层循环,第一层遍历每一个字符,第二层判断是否出现一次,如果是,因为我是从从遍历,可以直接返回这个结果.
面试官: 恩,不错,可以解决这个问题,请给我代码吧.
我: 您看下这个代码.

char first_char_1(string s) 
	if(s.size() == 0) return ' ';
	if(s.size() == 1) return 1;

	char ans = ' ';
	for(int i = 0; i < s.size(); i++) 
		int k = 0;
		for(int j = 0; j < s.size(); j++) 
			if(s[i] == s[j]) k ++;
		
		if(k == 1) 
			ans = s[i];
			break;
		
	
	return ans;


面试官: 考虑到边界条件,不错. 如果出现一个的字符都在前面,你这个代码效率应该不错,但是一般不会这样,你这个代码还能再运行快点吗?
我: 我这个思路的时间复杂度是O(n*n),主要是因为我在判断重复字符的时候用到了两层循环,如果先排序判断是否重复,时间复杂度就是O(nlogn).我再从排序好的字符中找到出现一次的字符.
面试官: 你这个思路不错,时间也会更快,但是排序之后,你找到的出现一次的字符,不一定就是第一个出现的吧?
我: 哦,对,那我应该在排序的时候记录每个字符的原始下标,这样我就需要用到一个结构体数据结.我能使用额外的空间吗?
面试官: 可以,听着你的想法还行,你写下代码吧.
我: …, 您看下代码

struct Node 
	char c;
	int index;
	Node() 
	Node(char _c, char _index):c(_c),index(_index) 
;

int cmp(Node a, Node b) 
	return a.c < b.c;


char first_char_2(string s) 
	if(s.size() == 0) return ' ';
	if(s.size() == 1) return s[0];

	Node a[s.size() + 1];
	for(int i = 0; i < s.size(); i++) 
		a[i] = Node(s[i], i);
	
	sort(a, a + s.size(), cmp);
	
	Node ans(' ', s.size()+1);
	for(int i = 0; i < s.size(); i++) 
		// cout<<a[i].c<<" "<<a[i].index<<endl;
		if(i == 0) 
			if(a[i].c != a[i+1].c && ans.index > a[i].index) 
				ans = a[i];
			
		
		else if(i == s.size()-1) 
			if(a[i].c != a[i-1].c && ans.index > a[i].index) 
				ans = a[i];
			
		
		else 
			if(a[i].c != a[i-1].c && a[i].c != a[i+1].c && ans.index > a[i].index) 
				ans = a[i];
			
		
	
	return ans.c;


面试官: 定义一个结构体保存字符和下标,之后排序,在排序好的数组中找到出现一次且下标最小的一个字符,时间复杂度是: O(n) + O(nlogn) + O(n). 运行时间复杂度总的可以算是O(nlogn),是比之前快了,你还可以给出更优的方法吗?比如使用一个什么数据结果?
我: 数据结构,空间换时间,记录每个字符出现的字符. 方法二的优化方式是排序,可以直接判断某个字符是否出现多次。对于方法一计算每一个字符的个数,我可以从个数出发,遍历字符串,使用hash表记录每个字符出现的字数. 再从头遍历一下字符串,遇到出现一个的字符就返回结果.

面试官: 点点头.示意我写下代码.
我: …, 写好了.

char first_char_3(string s) 
	if(s.size() == 0) return ' ';
	if(s.size() == 1) return 1;
	
	char ans = ' ';
	int cnt[256] = 0; // hash 表
	for(int i = 0; i < s.size(); i++) 
		cnt[s[i]] ++;
	
	for(int i = 0; i < s.size(); i++) 
		if(cnt[s[i]] == 1) 
			ans = s[i];
			break;
		
	
	return ans;


面试官: 不错,现在的时间复杂度是O(n)+O(n),总的时间复杂度是O(n). 你反应还挺快,这个算法已经很快了,我想再考考你,你还能继续加快这个算法吗?
我: 还能优化?这个已经是最优的吧,因为要返回的是第一个出现一次的字符,所以只能第一次记录次数,第二次得到结果。必须需要遍历两次吧.
面试官: 你说的没错,但是在实际中,字符串都是很长的,我前面说过字符串都是由256个acii码组成的,你可以从这个角度考虑下.
我: 你的意思是,同样使用hash表,第一个循环负责记录字符的次数,这个必须要有. 那我只能从第二个循环思考,如果我在第一层循环中,不仅记录下来每个字符的次数,还记录下来这个字符第一次出现的下标,那我就可以在256个字符中直接找到结果. 时间复杂度就是:O(n) + O(256).
面试官: 你的想法是对的,你写代码试试吧.
我: 写好了.

struct TNode 
	int k;
	int index;
	TNode() 
	TNode(int _k, int _index):k(_k), index(_index) 
;

char first_char_4(string s) 
	if(s.size() == 0) return ' ';
	if(s.size() == 1) return s[0];
	
	TNode cnt[256];
	for(int i = 0; i < 256; i++) 
		cnt[i].k = 0;
		cnt[i].index = -1;
	

	for(int i = 0; i < s.size(); i++) 
		if(cnt[s[i]].index == -1) 
			cnt[s[i]].index = i;
		
		cnt[s[i]].k ++;
	
	
	int t_index = s.size() + 1;
	int ans = (char) ' ';
	for(int i = 0; i < 256; i++) 
		if(cnt[i].k == 1 && cnt[i].index < t_index) 
			t_index = cnt[i].index;
			ans = i;
		
	
	return (char) ans;


面试官: 看你的思路和代码能力都还不错. 你先定义一个数据结构保存字符出现的次数和第一次的下标,在256个字符中找到出现一次,而且只有下标最小的字符. 在n很大的时候,时间复杂度是很小. 不错,算法题就这样吧,咱们谈谈你简历上的项目吧.
我: bala bala …

以上是关于面试题目: 第一个非重复的字符的主要内容,如果未能解决你的问题,请参考以下文章

2020/6/24 面试题总结

剑指Offer-时间效率与空间效率的平衡面试题50.2:字符流中第一个不重复的字符

web前端面试宝典——带你直击面试重难点(40个经典题目,涵盖近90%的考点,码字2w,干货满满!)

剑指Offer面试题67. 把字符串转换成整数

剑指Offer面试题67. 把字符串转换成整数

[LeetCode]面试题67. 把字符串转换成整数(字符串)