劍指Offer(java版):第一個只出現一次的字符

題目:在字符串中找出第一個只出現一次的字符。如輸入"abaccdeff",則輸出'b'.java

看到這樣的題目,咱們最直觀的想法就是從頭開始掃描這個字符串中的字 符。當訪問某個字符時拿這個字符和後面的每一個字符相比較,若是在後面沒有發現重複的字符,則該字符就是隻出現一次的字符。若是字符串有n個字符,每一個字符 可能與後面的O(n)個字符想比較,所以這種思路的時間複雜度爲O(n2),面試官不會滿意這種思路,它會提示咱們繼續想更快的思路。面試

因爲題目與字符出現的次數有關,咱們是否是可疑統計每一個字符在該字符 串中出現的次數,要達到這個目的,咱們須要一個數據容器來存放每一個字符出現的次數。在這個容器中能夠根據字符來查找它出現的次數,也就是說這個容器的做用 就是把一個字符映射稱一個數字。在經常使用的數據容器中,哈希表正是這個用途。算法

爲了解決這個問題,咱們能夠定義哈希表的鍵值(key)是字符,而值 (Value)是該字符出現的次數。同時咱們還須要從頭開始掃描字符串兩次。第一次掃描字符串時,每掃描到一個字符就在哈希表中的對應項中把次數加1.接 下來第二次掃描時,每掃描到一個字符就能從哈希表中獲得該字符出現的次數。這樣第一個只出現一次的字符就是符合要求的輸出。數組

用Java代碼實現咱們的思路:app

package cglib;函數

import java.util.LinkedHashMap;性能

public class jiekou {ui

    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));  
    }  
     }this

 

輸出dgoogle

 

拓展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

相關文章
相關標籤/搜索