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版):第一个只出现一次的字符相关推荐

  1. 剑指offer java版 test3—从尾到头打印链表

    标题:剑指offer java版 test3-从尾到头打印链表 题目:输入一个链表,按链表从尾到头的顺序返回一个ArrayList. 解答:知识不够全面,用ArrayList做的 但是看到大佬们还可以 ...

  2. Java详解剑指offer面试题50--第一个只出现一次的字符

    Java详解剑指offer面试题50–第一个只出现一次的字符 找出字符串中找出第一个只出现一次的字符,比如输入"abacceff",则输出'b' 要想知道某个字符是不是只出现了一次 ...

  3. 剑指offer java版 test42—找出和为S的两个数,左右夹逼法(双指针法)

    前言:受上一题的启发,这题自己也编出来了.在碰到这种有序数列的时候,经常会用到双指针法,一个指左边,一个指右边,然后依照规则移动,增加或缩小范围.很实用. 题目: 输入一个递增排序的数组和一个数字S, ...

  4. 剑指offer java版(三)

    二叉搜索树的后序遍历 问题描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 解题思路 对于后序遍历来说, ...

  5. 剑指offer java版(一)

    二维数组中的查找 问题描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数 ...

  6. 剑指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 ...

  7. 剑指offer没有java版吗_剑指Offer(Java版) 持续更新中

    面试题2 单例(之前有整理,略) 面试题3 二维数组中的查找 public boolean find(int target, int [][] array) { boolean found = fal ...

  8. 剑指Offer第二版Java代码实现

    剑指Offer第二版Java代码实现 A.单例模式 面试题 2:实现Singleton模式 B.面试需要的基础知识 面试题 3:数组中重复的数字 面试题 4:二维数组的查找 面试题 5:替换空格 面试 ...

  9. 剑指offer最新版_剑指Offer——Java版本(持续更新)

    0 前言 邻近校招,算法要命!!! 本文为研究剑指Offer过程中的笔记,整理出主要思路以及Java版本题解,以便记忆和复习. 参考整理来自<剑指Offer 第二版>. 特别注意,对每道题 ...

  10. 剑指offer第二版(leetcode)Java题解(不断更新)

    1 数组中的重复数字 题目 在一个长度为 n 的数组 nums 里的所有数字都在 0-n-1 的范围内.数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次.请找出数组中任意一 ...

最新文章

  1. python学习笔记(3) -- 常用数据类型
  2. Go基础之--数组和切片
  3. 【控制】《多智能体机器人系统信息融合与协调》范波老师-第8章-Agent 技术在机器人智能控制系统的应用
  4. C# vb.net 分别引用C++编译DLL
  5. java构造块_java中的静态代码块、构造代码块、构造方法详解
  6. VMware安装Centos7后有线线缆被拔出
  7. oracle 查询简单,Oracle简单查询
  8. [JS进阶] JS 之Blob 对象类型
  9. jquery uploadify 相关文档 按钮样式显示不全 Google浏览器显示问题
  10. Layui 数据表格动态cols(字段)动态变化
  11. 童年修复系列-SNES芯片组介绍及FPGA实现
  12. 智能生活 App SDK 开发入门教程
  13. 工作窃取算法 work-stealing
  14. 查看计算机当前用户的sid,查看SID
  15. php压缩中文文件,phpzip压缩中文文件时候出现压缩无大小或无法压缩
  16. linux常用命令_Linux常用命令总结
  17. matlab获取图像某点RGB值、灰度值的方法
  18. 联想计算机网络唤醒是怎么回事,笔记本开启网络唤醒功能设置的操作方法
  19. From blockchain consensus back to Byzantine consensus
  20. GitHub上史上最全的Android开源项目分类汇总 (转)

热门文章

  1. UBoot常用命令手册
  2. Zabbix server is not running 报错的排查
  3. 学习OpenCV,看这些!
  4. 台式电脑计算机硬盘清理,台式电脑重装系统win7清理磁盘碎片的方法
  5. Nacos源码NacosServiceRegistryAutoConfiguration
  6. 使用Docker-容器命令案例1
  7. 多路平衡查找树(B Tree)(分裂、合并)
  8. MyBatis 核心对象
  9. TransactionAwareDataSourceProxy
  10. 织入业务代码-LogAspect