面试题目: 第一个非重复的字符
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 …
以上是关于面试题目: 第一个非重复的字符的主要内容,如果未能解决你的问题,请参考以下文章
剑指Offer-时间效率与空间效率的平衡面试题50.2:字符流中第一个不重复的字符