一、题目:第一个只出现一次的字符

题目:在字符串中找出第一个只出现一次的字符。如输入"abaccdeff",则输出'b'。要求时间复杂度为O(n)

  最直观的想法是从头开始扫描这个字符串中的每个字符。当访问到某字符时拿这个字符和后面的每个字符相比较,如果在后面没有发现重复的字符,则该字符就是只出现一次的字符。如果字符串有n个字符,每个字符可能与后面的O(n)个字符相比较,因此这种思路的时间复杂度是O(n2),但是不满足要求。

二、解题思路:以空间换时间

  为了解决这个问题,我们可以定义一个哈希表(外部空间),其键值(Key)是字符,而值(Value)是该字符出现的次数。

  同时我们还需要从头开始扫描字符串两次:

  (1)第一次扫描字符串时,每扫描到一个字符就在哈希表的对应项中把次数加1。(时间效率O(n))

  (2)第二次扫描时,每扫描到一个字符就能从哈希表中得到该字符出现的次数。这样第一个只出现一次的字符就是符合要求的输出。(时间效率O(n))

  这样算起来,总的时间复杂度仍然是O(n),满足了题目要求,擦一擦汗,感叹:这*装得真有点技术!

  装完了B,开始将这个想法实现为代码:

    public static char FirstNotRepeatingChar(string str)
{
if(string.IsNullOrEmpty(str))
{
return '\0';
} char[] array = str.ToCharArray();
const int size = ;
// 借助数组来模拟哈希表,只用1K的空间消耗
uint[] hastTable = new uint[size];
// 初始化数组
for (int i = ; i < size; i++)
{
hastTable[i] = ;
} for (int i = ; i < array.Length; i++)
{
hastTable[array[i]]++;
} for (int i = ; i < array.Length; i++)
{
if (hastTable[array[i]] == )
{
return array[i];
}
} return '\0';
}

PS:字符(char)是一个长度为8的数据类型,因此总共有256种可能。(在C#中char则是长度为16位也就是2个字节)这里我们只列举char是1个字节的情况,我们创建一个长度为256的数组来模拟哈希表,每个字母根据其ASCII码值作为数组的下标对应数组的一个数字,而数组中存储的是每个字符出现的次数。计算下来,它的大小是256*4字节(1个int类型在Windows下占4个字节)=1K。由于这个数组的大小是个常数,因此可以认为这种算法的空间复杂度是O(1)。 

三、单元测试

3.1 测试用例

    // 常规输入测试,存在只出现一次的字符
[TestMethod]
public void FirstCharTest1()
{
char actual = CharHelper.FirstNotRepeatingChar("google");
Assert.AreEqual(actual, 'l');
} // 常规输入测试,不存在只出现一次的字符
[TestMethod]
public void FirstCharTest2()
{
char actual = CharHelper.FirstNotRepeatingChar("aabccdbd");
Assert.AreEqual(actual, '\0');
} // 常规输入测试,所有字符都只出现一次
[TestMethod]
public void FirstCharTest3()
{
char actual = CharHelper.FirstNotRepeatingChar("abcdefg");
Assert.AreEqual(actual, 'a');
} // 鲁棒性测试,输入NULL
[TestMethod]
public void FirstCharTest4()
{
char actual = CharHelper.FirstNotRepeatingChar(null);
Assert.AreEqual(actual, '\0');
}

3.2 测试结果

  (1)测试通过情况

  (2)代码覆盖率

四、总结扩展

  如果需要判断多个字符是不是在某个字符串里出现过或者统计多个字符在某个字符串中出现的次数,我们都可以考虑基于数组创建一个简单的哈希表(或者使用基类库中提供的现成的哈希表结构类型)。这样可以用很小的空间消耗换来换取时间效率的提升。

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

剑指Offer面试题:30.第一个只出现一次的字符的更多相关文章

  1. 剑指Offer - 九度1283 - 第一个只出现一次的字符

    剑指Offer - 九度1283 - 第一个只出现一次的字符2013-11-21 21:13 题目描述: 在一个字符串(1<=字符串长度<=10000,全部由大写字母组成)中找到第一个只出 ...

  2. 【Java】 剑指offer(50-1) 字符串中第一个只出现一次的字符

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 在字符串中找出第一个只出现一次的字符.如输入"abacc ...

  3. 剑指offer(34)第一个只出现一次的字符

    题目描述 在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置 题目分析 只需要用map记录字符出现的次数就行,比较简单的题 代码 f ...

  4. 【剑指Offer】34、第一个只出现一次的字符

      题目描述:   在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).   解题思路: ...

  5. 剑指offer——面试题30:包含min函数的栈

    #include"iostream" #include"stdio.h" using namespace std; ; ; template<typena ...

  6. 剑指Offer:面试题30——最小的k个数(java实现)

    问题描述: 输入n个整数,找出其中最小的k个数 思路1: 先排序,再取前k个 时间复杂度O(nlogn) 下面给出快排序的代码(基于下面Partition函数的方法) public void Quic ...

  7. 剑指offer面试题30:最小的k个数

    一.题目描述 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 二.解题思路 1.思路1 首先对数组进行排序,然后取出前k个数 ...

  8. 剑指offer面试题30.包含min函数的栈

    一开始写的垃圾代码,push和pop都是O(N) class Solution { public: vector<int> vec; int min_val=INT_MAX,min_cnt ...

  9. 剑指offer 面试题56. 数组中只出现一次的两个数字

    题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 方法1:用set记录出现过的数字 class Solution { public: void F ...

  10. C++版 - 剑指offer之面试题37:两个链表的第一个公共结点[LeetCode 160] 解题报告

    剑指offer之面试题37 两个链表的第一个公共结点 提交网址: http://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46?t ...

随机推荐

  1. Flume(2)组件概述与列表

    上一节搭建了flume的简单运行环境,并提供了一个基于netcat的演示.这一节继续对flume的整个流程进行进一步的说明. 一.flume的基本架构图: 下面这个图基本说明了flume的作用,以及f ...

  2. ppmoney 总结一

    1.JQ $.get() <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...

  3. Python-面向对象(类)二

    一.成员修饰符 • 共有成员 • 私有成员 __+字段 __:成员修饰符 无法直接访问,只能通过该成员所属类的方法简介访问 class Foo: def __init__(self, name, ag ...

  4. mysql salve从库设置read only 属性

    在MySQL数据库中,在进行数据迁移和从库只读状态设置时,都会涉及到只读状态和Master-slave的设置和关系.     经过实际测试,对于MySQL单实例数据库和master库,如果需要设置为只 ...

  5. Android 6.0 - 动态权限管理的解决方案

    Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用户体验, 同时也为程序员带来新的负担. 动态权限管理就是这样, 一方面让用户更加容易的控制自己的隐私, 一方面需要重新适配应 ...

  6. 进击的Python【第十七章】:jQuery的基本应用

    进击的Python[第十七章]:jQuery的基本应用

  7. mysql的查询、子查询及连接查询

    >>>>>>>>>> 一.mysql查询的五种子句         where(条件查询).having(筛选).group by(分组). ...

  8. 尽量用goto代替尾递归

    void PrintList(List L) { if(L!=Null) { PrintElement(L->Element); PrintLisr(L->Next); } } 所谓尾递归 ...

  9. [转]MSI安装程序中的文件替换

    原文链接:http://teach.hanzify.org/article/652-1233562028.html 前言 最近有汉化朋友问起如何不重新制作MSI文件,而直接用汉化好的文件替换MSI安装 ...

  10. solr update接口常用方法

    solr索引数据更新接口:http://localhost:8080/solr/update 有以下一些操作可以通过update接口完成,只能使用post的形式提交数据. 1.添加索引 确保field ...