如何在字符串中找到第一個不重復的字符


編寫一個Java程序來查找一個字符串中第一個非重復的字符,這是在編程測試中很常見的一個問題,因為字符串處理在程序員面試中是一個普遍的話題。面試前最好是准備好一些熟知的編程問題,例如使用遞歸反轉字符串,或者檢查一個字符串是否是回文(即正反讀順序一致)。查找第一個非重復字符的問題也是在同一個范疇。在給出解決方案之前,我們先來弄懂這個問題。我們需要編寫一個函數,這個函數接受一個字符串作為參數,並返回第一個不重復的字符。例如字符串“hello”,除了“l”之外所有字符都是不重復的,但是“h”是第一個不重復的字符。同樣,字符串“swiss”中“w”是第一個不重復的字符。一種解決該問題的方法就是創建一張表來記錄每個字符的出現次數,然后選出第一個不重復的元素。關鍵在於字符的次序,你的代碼必須返回第一個非重復的字符。另外,在這篇文章中,我們可以看到3個解決該問題的示例。我們的第一個方案是使用LinkedHashMap來記錄字符個數,因為LinkedHashMap維持的元素順序與插入順序一致,而我們正是按照字符串中字符出現的順序來將字符插入Map中的。當我們掃描字符串時,只需要迭代LinkedHashMap並找出值為1的元素。是的,這種方案只需要一個LinkedHashMap以及兩個循環。我們的第二種解決方案是時間和空間的折衷,在一次遍歷中找出不重復的字符。這次,我們使用一個Set和一個List去分開保存重復和不重復的字符。當我們完成一次時間復雜度為O(n)的字符串掃描后,我們可以通過訪問List來獲得這個神奇的不重復字符,該時間為復雜度為O(1),因為List是有序的,我們可以通過get(0)獲得第一個元素。我們的第三種解決方案也是類似的,不過這次我們使用HashMap而不是LinkedHashMap,我們會在第一掃描計算各個字符的出現次數之后,再次掃描字符串去找到第一個不重復的字符。接下來我們將為這個問題編寫示例代碼和單元測試程序。你也可以去我的字符串面試問題列表中看看更多類似的Java編程題。

 

這里有在給定字符串中找到第一個非重復字符的完整示例代碼,該程序給出了三個找到第一個非重復字符的方法,每個方法都有自己的解決問題的算法。

第一個算法實現在getFirstNonRepeatedChar(String str)方法中。它首先獲得字符串的字符數組,然后遍歷數組並建立一個哈希表,哈希表的鍵為字符,值為該字符出現的次數。下一步它會遍歷LinkedHashMap去找到第一個值為1的元素,那便是第一個非重復的字符,因為LinkedHashMap維護的元素順序與插入順序一致,而我們遍歷字符數組是從頭遍歷到尾。這種算法的缺點在於它需要兩個循環,第一個循環的次數與字符串的字符個數成正比,而第二個循環的次數與字符串中重復的字符個數成正比。最差的情況是非重復的字符出現在字符串的最尾部,那么這個算法需要2*N的時間去解決這個問題。

第二個算法實現在firstNonRepeatingChar(String word)方法中。這種解決方案可以在一次字符串掃描中找到第一個不重復的字符,它應用了典型的空間時間權衡技術。它使用了兩個存儲空間來減少一次循環,是標准的空間-時間折衷。由於我們將重復和不重復的字符分開存放,在循環結束后,存放不重復字符的列表中的第一個元素就是我們所要找的第一個非重復字符。這種解決方案稍微優於上一種。如果在字符串中沒有不重復的字符,你可以選擇返回null或者空字符串。

第三個算法實現在firstNonRepeatedCharacter(String word)方法中,它與第一種方案非常類似,唯一不同在於它使用了HashMap。由於HashMap並不保證一種特定的順序,我們必須依賴源字符串去找到第一個不重復的字符。第三種解決方案的算法如下:

  1. 掃描字符串並將每個字符的出現次數保存在HashMap中
  2. 遍歷字符串並從Map中獲取每個字符的個數

由於我們從左往右掃描字符,當找到某個字符的計數為1時,我們就可以跳出循環,它就是第一個非重復的字符。在這里,字符次序是靠再次遍歷源字符串來實現。

 

 1//采用的第二種方法,將重復的元素放入到set集合,不重復的元素放入List集合。
//感覺這兩部分都可以放入到list集合呀?試試就知道了。不能使用兩個List集合,因為當你將重復元素往set集合里存時,list中不再包含此重復元素,當下次再遇到此重復元素時,又會存入list集合,並存入到set集合,假使此時set集合變為list集合的話,
//在其中中會包含兩個相同元素。可以解決,將set提前判決就行。

import java.util.*; 2 3 public class Programming { 4 public static char firstNonRepeatingChar(String word) { 5 Set<Character> repeating = new HashSet<Character>(); 6 List<Character> nonRepeating = new ArrayList<Character>(); 7 for (int i = 0; i < word.length(); i++) { 8 char letter = word.charAt(i);//依次獲得字符串中的每個字符 9 //System.out.println(letter); 10 //System.out.println(repeating); 11 if (repeating.contains(letter)) { 12 continue; 13 } 14 15 if (nonRepeating.contains(letter)) { 16 nonRepeating.remove((Character) letter);//這里強制轉換和不強制轉換區別這么大么?,如果這里輸入的是char的話,默認轉變為Int了 17 repeating.add(letter); 18 } else { 19 nonRepeating.add(letter); 20 } 21 System.out.println(nonRepeating); 22 } 23 return nonRepeating.get(0); 24 } 25 26 public static void main(String[] args) 27 { 28 Programming pro=new Programming (); 29 String str="hello"; 30 char c=pro.firstNonRepeatingChar(str); 31 System.out.println("輸入的字符串為: "+str); 32 System.out.println("字符串中第一個非重復的字符為: "+c); 33 } 34 }

關於上述16行的強制轉換可查看下面鏈接,這里重載是具有優先級的,假設不強制的話char類型默認轉換為int型,並優先調用remove(int index)這個方法,而不是remove(Object o)此方法。

轉:http://shmilyaw-hotmail-com.iteye.com/blog/1447631


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM