剑指Offer(java版):第一个只出现一次的字符
2019独角兽企业重金招聘Python工程师标准>>>
题目:在字符串中找出第一个只出现一次的字符。如输入"abaccdeff",则输出'b'.
看到这样的题目,我们最直观的想法就是从头开始扫描这个字符串中的字 符。当访问某个字符时拿这个字符和后面的每个字符相比较,如果在后面没有发现重复的字符,则该字符就是只出现一次的字符。如果字符串有n个字符,每个字符 可能与后面的O(n)个字符想比较,因此这种思路的时间复杂度为O(n2),面试官不会满意这种思路,它会提示我们继续想更快的思路。
由于题目与字符出现的次数有关,我们是不是可疑统计每个字符在该字符 串中出现的次数,要达到这个目的,我们需要一个数据容器来存放每个字符出现的次数。在这个容器中可以根据字符来查找它出现的次数,也就是说这个容器的作用 就是把一个字符映射称一个数字。在常用的数据容器中,哈希表正是这个用途。
为了解决这个问题,我们可以定义哈希表的键值(key)是字符,而值 (Value)是该字符出现的次数。同时我们还需要从头开始扫描字符串两次。第一次扫描字符串时,每扫描到一个字符就在哈希表中的对应项中把次数加1.接 下来第二次扫描时,每扫描到一个字符就能从哈希表中得到该字符出现的次数。这样第一个只出现一次的字符就是符合要求的输出。
用Java代码实现我们的思路:
package cglib;
import java.util.LinkedHashMap;
public class jiekou {
public Character firstNotRepeating(String str){
if(str == null)
return null;
char[] strChar = str.toCharArray(); //将字符串转换成数组
LinkedHashMap<Character,Integer> hash = new LinkedHashMap<Character,Integer>();
for(char item:strChar){
if(hash.containsKey(item))
hash.put(item, hash.get(item)+1);
else
hash.put(item, 1);
}
for(char key:hash.keySet())
{
if(hash.get(key)== 1)
return key;
}
return null;
}
public static void main(String[] args){
String str = "abaccdebff";
jiekou test = new jiekou();
System.out.println(test.firstNotRepeating(str));
}
}
输出d
拓展1:
在前面的例子中,我们之所以可以把哈希表的大小设为256,是因为字符(char)是8个bit的类型,总共只有256个字符。但实际上字符不只是256个,比如中文就有几千个汉字。如果题目要求考虑汉字,前面的算法是不是有问题?如果有,可以怎么解决。
public class jiekou {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自动生成的方法存根
String testString="ccaaddddb北京bb11大学??//";
getFirstMaxOccurrenceChar(testString);
}
/*查找第一次出现单独字符的主函数*/
private static void getFirstMaxOccurrenceChar(String temString) {
char[] temp=temString.toCharArray();
MyHashTable myHashTable=new MyHashTable();
for (char c : temp) {
MyData myData=new MyData();
myData.setCharData(c);
myHashTable.insert(myData);
}
MyData[] result=MyHashTable.getHashMap();
boolean flag=false;
for (int i = 0; i < result.length; i++) {
MyData myData = result[i];
/*只要hash表中该数据不为null且计数为1则输出并跳出循环*/
if (myData!=null&&myData.getCount()==1) {
System.out.println("第一次出现单字符为:"+myData.getCharData());
flag=true;
break;
}
}
if (flag==false) {
System.out.println("不存在单字符!");
}
}
}
/*设计hash表,包含一个长度为Oxffff的数组和insert函数*/
class MyHashTable{
private static MyData[] hashMap=new MyData[0xffff];
/*如果第一次插入,则将计数设置为1,否则计数+1*/
public void insert(MyData myData){
if (hashMap[myData.getCharData()]==null) {
myData.setCount(1);
}else {
myData.setCount(hashMap[myData.getCharData()].getCount()+1);
}
hashMap[myData.getCharData()]=myData;
}
public static MyData[] getHashMap() {
return hashMap;
}
}
/*设计hash表中的类型,即一个字符和它的计数*/
class MyData{
private char charData;
private int count;
public char getCharData() {
return charData;
}
public void setCharData(char charData) {
this.charData = charData;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
输出
第一次出现单字符为:京
拓展2:
定义一个函数,输入两个字符串,从第一个字符串中删除在第二个字符串中出现过的所有字符。例如第一个字符串"we are students",第二个字符串是"aeiou",结果应该是"w r stdnts"。
package cglib;
public class jiekou {
public static String fun1 ( String s, String b )
{
if (s.isEmpty ())
{
return "";
}
char first = s.charAt (0);
if (b.indexOf (first) != -1)//返回 String 对象b内第一次出现子字符串的字符位置
{
return fun1 (s.substring (1), b);//截取s的下标为1的字符串,跟b继续比较
}
return first + fun1 (s.substring (1), b);//b中没有这个,则没有的这个字符返回
}
public static void print ( String s )
{
for ( int i = 0; i < s.length (); i++ )
{
System.out.print (s.charAt (i));
}
}
public static void main ( String args[] )
{
String str = "we are students";
String str1 = "aeiou";
String str2 = fun1 (str, str1);
print (str2);
}
}
输出:
w r stdnts
拓展3: 定义一个函数,删除字符串中所有重复出现的字符。例如输入"google",则输出结果应该是"gole"。
package cglib;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class jiekou {
static StringBuffer sb = new StringBuffer();
// 普通的方法,不使用集合
static void removeDuplicateByOriginalMethod(String str) {
System.out.println("方法一:普通方法");
char[] cy = str.toCharArray();
String temp = "";
for (int i = 0; i < cy.length; i++) {
if (temp.indexOf(cy[i]) == -1) {
temp += cy[i];
}
}
System.out.println("去除重复字符后:" + temp);
sb.setLength(0);
}
// 方法二,使用LinkedHashSet可以在去掉重复字符后按照原字符顺序排列字符
static void removeDuplicateByLinkedHashSet(String str, String[] ss, int len) {
System.out.println("方法二:LinkedHashSet");
Set<String> set = new LinkedHashSet<String>();
iterate(set, ss, len);
System.out.println("去除重复字符后:" + sb.toString());
// 清空StringBuffer对象sb
sb.setLength(0);
}
// 方法三,使用ArrayList可以在去掉重复字符后按照原字符顺序排列字符
static void removeDuplicateByArrayList(String str, String[] ss, int len) {
System.out.println("方法三:ArrayList");
List<String> list = new ArrayList<>();
iterate(list, ss, len);
System.out.println("去除重复字符后:" + sb.toString());
// 记住要输出后才清空sb
sb.setLength(0);
}
// 集合迭代器,用于去除重复字符并重新拼接字符
static void iterate(Object obj, String[] ss, int len) {
if (obj instanceof Set) {
System.out.println("迭代器正在迭代Set");
@SuppressWarnings("unchecked")
Set<String> set = (Set<String>) obj;
for (int i = 0; i < len; i++) {
if (!set.contains(ss[i])) {
set.add(ss[i]);
}
}
for (String s : set) {
sb.append(s);
}
}
if (obj instanceof List) {
System.out.println("迭代器正在迭代List");
@SuppressWarnings("unchecked")
List<String> list = (List<String>) obj;
for (int i = 0; i < len; i++) {
if (!list.contains(ss[i])) {
list.add(ss[i]);
}
}
for (String s : list) {
sb.append(s);
}
}
}
public static void main(String[] args) {
String str = "google";
String[] ss = str.split(""); // 在此处先拆分字符串,处理后再传给各个需要用到的方法,提高程序性能。
int len = ss.length;
System.out.println("等待去除重复字符的字符串:" + str);
//方法一
removeDuplicateByOriginalMethod(str);
// 方法二
removeDuplicateByLinkedHashSet(str, ss, len);
// 方法三
removeDuplicateByArrayList(str, ss, len);
}
}
输出:
方法一:普通方法
去除重复字符后:gole
方法二:LinkedHashSet
迭代器正在迭代Set
去除重复字符后:gole
方法三:ArrayList
迭代器正在迭代List
去除重复字符后:gole
拓展4:
请完成一个函数,判断输入的两个字符串是否是Anagram,即互为变位词
变位词(anagrams)指的是组成两个单词的字符相同,但位置不同的单词。比如说, abbcd和abcdb就是一对变位词。该题目有两种做法:
O(nlogn)的解法
由于组成变位词的字符是一模一样的,所以按照字典序排序后,两个字符串也就相等了。 因此我们可以用O(nlogn)的时间去排序,然后用O(n)的时间比较它们是否相等即可。
package cglib;
import java.util.Arrays;
public class jiekou {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(func("silent", "listen"));
System.out.println(func("", ""));
System.out.println(func("silent", "liste"));
}
public static boolean func(String str1, String str2) {
if(str1.length() != str2.length()){
return false;
}
char[] arr1 = str1.toCharArray();
char[] arr2 = str2.toCharArray();
Arrays.sort(arr1);
Arrays.sort(arr2);
for(int i = 0; i < arr1.length; i++) {
if(arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
}
输出
true
true
false
O(n)的解法
由于组成变位词的字符是一模一样的, 因此我们可以先统计每个字符串中各个字符出现的次数, 然后看这两个字符串中各字符出现次数是否一样。如果是,则它们是一对变位词。 这需要开一个辅助数组来保存各字符的出现次数。我们可以开一个大小是256的整数数组, 遍历第一个字符串时,将相应字符出现的次数加1;遍历第二个字符串时, 将相应字符出现的次数减1。最后如果数组中256个数都为0,说明两个字符串是一对变位词。 (第1个字符串中出现的字符都被第2个字符串出现的字符抵消了), 如果数组中有一个不为0,说明它们不是一对变位词。
package cglib;
public class jiekou {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(anagram("silent", "listen"));
//System.out.println(anagram("", ""));
//System.out.println(anagram("silent", "liste"));
}
private static boolean anagram(String s1,String s2){
int[] nums = new int[26];
char[] s1_char = s1.toCharArray();
char[] s2_char = s2.toCharArray();
int s1_length = s1_char.length;
int s2_length = s2_char.length;
if(s1_length != s2_length){
return false;
}
for(int i=0; i<s1_length; i++){
System.out.println("s1的s1_char[i]="+s1_char[i]);
int index = s1_char[i] - 'a';
System.out.println("s1的index="+index);
nums[index]++;
System.out.println("s1的nums[index]="+nums[index]);
}
for(int i=0; i<s1_length; i++){
System.out.println("s2的s2_char[i]="+s2_char[i]);
int index = s2_char[i] - 'a';
System.out.println("s2的index="+index);
nums[index]--;
System.out.println("s2的nums[index]="+nums[index]);
}
for(int i=0; i<nums.length; i++){
System.out.println("nums的i="+i);
System.out.println("nums[i]="+nums[i]);
if(nums[i]>0) return false;
}
return true;
}
}
输出:
s1的s1_char[i]=s
s1的index=18
s1的nums[index]=1
s1的s1_char[i]=i
s1的index=8
s1的nums[index]=1
s1的s1_char[i]=l
s1的index=11
s1的nums[index]=1
s1的s1_char[i]=e
s1的index=4
s1的nums[index]=1
s1的s1_char[i]=n
s1的index=13
s1的nums[index]=1
s1的s1_char[i]=t
s1的index=19
s1的nums[index]=1
s2的s2_char[i]=l
s2的index=11
s2的nums[index]=0
s2的s2_char[i]=i
s2的index=8
s2的nums[index]=0
s2的s2_char[i]=s
s2的index=18
s2的nums[index]=0
s2的s2_char[i]=t
s2的index=19
s2的nums[index]=0
s2的s2_char[i]=e
s2的index=4
s2的nums[index]=0
s2的s2_char[i]=n
s2的index=13
s2的nums[index]=0
nums的i=0
nums[i]=0
nums的i=1
nums[i]=0
nums的i=2
nums[i]=0
nums的i=3
nums[i]=0
nums的i=4
nums[i]=0
nums的i=5
nums[i]=0
nums的i=6
nums[i]=0
nums的i=7
nums[i]=0
nums的i=8
nums[i]=0
nums的i=9
nums[i]=0
nums的i=10
nums[i]=0
nums的i=11
nums[i]=0
nums的i=12
nums[i]=0
nums的i=13
nums[i]=0
nums的i=14
nums[i]=0
nums的i=15
nums[i]=0
nums的i=16
nums[i]=0
nums的i=17
nums[i]=0
nums的i=18
nums[i]=0
nums的i=19
nums[i]=0
nums的i=20
nums[i]=0
nums的i=21
nums[i]=0
nums的i=22
nums[i]=0
nums的i=23
nums[i]=0
nums的i=24
nums[i]=0
nums的i=25
nums[i]=0
true
转载于:https://my.oschina.net/u/2822116/blog/725453
剑指Offer(java版):第一个只出现一次的字符相关推荐
- 剑指offer java版 test3—从尾到头打印链表
标题:剑指offer java版 test3-从尾到头打印链表 题目:输入一个链表,按链表从尾到头的顺序返回一个ArrayList. 解答:知识不够全面,用ArrayList做的 但是看到大佬们还可以 ...
- Java详解剑指offer面试题50--第一个只出现一次的字符
Java详解剑指offer面试题50–第一个只出现一次的字符 找出字符串中找出第一个只出现一次的字符,比如输入"abacceff",则输出'b' 要想知道某个字符是不是只出现了一次 ...
- 剑指offer java版 test42—找出和为S的两个数,左右夹逼法(双指针法)
前言:受上一题的启发,这题自己也编出来了.在碰到这种有序数列的时候,经常会用到双指针法,一个指左边,一个指右边,然后依照规则移动,增加或缩小范围.很实用. 题目: 输入一个递增排序的数组和一个数字S, ...
- 剑指offer java版(三)
二叉搜索树的后序遍历 问题描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 解题思路 对于后序遍历来说, ...
- 剑指offer java版(一)
二维数组中的查找 问题描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数 ...
- 剑指offer python版 数组中只出现一次的两个数字
def aa(nums):hashes={}for s in nums:hashes[s]=hashes[s]+1 if hashes.get(s) else 1for s in nums:if ha ...
- 剑指offer没有java版吗_剑指Offer(Java版) 持续更新中
面试题2 单例(之前有整理,略) 面试题3 二维数组中的查找 public boolean find(int target, int [][] array) { boolean found = fal ...
- 剑指Offer第二版Java代码实现
剑指Offer第二版Java代码实现 A.单例模式 面试题 2:实现Singleton模式 B.面试需要的基础知识 面试题 3:数组中重复的数字 面试题 4:二维数组的查找 面试题 5:替换空格 面试 ...
- 剑指offer最新版_剑指Offer——Java版本(持续更新)
0 前言 邻近校招,算法要命!!! 本文为研究剑指Offer过程中的笔记,整理出主要思路以及Java版本题解,以便记忆和复习. 参考整理来自<剑指Offer 第二版>. 特别注意,对每道题 ...
- 剑指offer第二版(leetcode)Java题解(不断更新)
1 数组中的重复数字 题目 在一个长度为 n 的数组 nums 里的所有数字都在 0-n-1 的范围内.数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次.请找出数组中任意一 ...
最新文章
- python学习笔记(3) -- 常用数据类型
- Go基础之--数组和切片
- 【控制】《多智能体机器人系统信息融合与协调》范波老师-第8章-Agent 技术在机器人智能控制系统的应用
- C# vb.net 分别引用C++编译DLL
- java构造块_java中的静态代码块、构造代码块、构造方法详解
- VMware安装Centos7后有线线缆被拔出
- oracle 查询简单,Oracle简单查询
- [JS进阶] JS 之Blob 对象类型
- jquery uploadify 相关文档 按钮样式显示不全 Google浏览器显示问题
- Layui 数据表格动态cols(字段)动态变化
- 童年修复系列-SNES芯片组介绍及FPGA实现
- 智能生活 App SDK 开发入门教程
- 工作窃取算法 work-stealing
- 查看计算机当前用户的sid,查看SID
- php压缩中文文件,phpzip压缩中文文件时候出现压缩无大小或无法压缩
- linux常用命令_Linux常用命令总结
- matlab获取图像某点RGB值、灰度值的方法
- 联想计算机网络唤醒是怎么回事,笔记本开启网络唤醒功能设置的操作方法
- From blockchain consensus back to Byzantine consensus
- GitHub上史上最全的Android开源项目分类汇总 (转)