日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法--第一个只出现一次的字符

發布時間:2023/12/4 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法--第一个只出现一次的字符 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第一個只出現一次的字符

  • 題目:在字符串中找出第一個只出現一次的字符,比如輸入“wersdfxvsdfwer”,則輸出x。

  • 方法一:

    • 還是老規矩,初始想法是從頭遍歷每一個字符,每遍歷一個字符都與后面n-1個字符比較
    • 如果發現后面字符中包含相同字符則查詢下一個字符
    • 如果后面字符都沒有相同的字符,則放回當前字符
    • 方法的時間復雜度是O(n2)
  • 方法一是最簡單的方式,時間復雜度最大。接下來開始優化改這個算法

  • 方法二:

    • 與次數相關,我們可以將每個字符與出現的次數存儲起來,需求開辟額外的存儲空間
    • 顯然key,value形式的存儲第一個想到的是java中的Map結構
    • 通過Map,我們可以通過字符串key,來找到他對應的次數value
    • 有如下實現:
/*** 找出數組中第一個只出現一次的字符 HashMap* @author liaojiamin* @Date:Created in 10:22 2021/6/10*/ public class FindNotRepeatChar {public static void main(String[] args) {System.out.println(findNotRepeatCharHashMap("wersdfxvsdfwer")); }/*** HashMap情況* */public static char findNotRepeatCharHashMap(String str){if(str == null){return '\u0000';}if(str.length() == 1){return str.charAt(0);}Map<Character, Integer> map = new HashMap<>();char[] target = str.toCharArray();for (int i = 0; i < target.length; i++) {Character key = target[i];if(map.containsKey(key)){map.put(key, map.get(key) + 1);}else {map.put(key, 1);}}for (int i = 0; i < target.length; i++) {if(map.get(target[i]) == 1){return target[i];}}return '\u0000';}}
  • 如上算法的實現是沒問題的,得到的第一個是x,但是此時我們是借助了java的HashMap的api來實現我們的算法

  • 如果只能用基礎數據結構以及自己實現的方法呢(如此變態的要求),我們接著來優化。

  • 方法三:

    • 在方法二的基礎上我們其實只需要解決HashMap存在的順序問題,能否自定義一個哈希表
    • 因為題目的特殊性,我們需要將先出現的字符放在某個數據結構的前面
    • 那么自定義一個哈希表,直接通過hashCode來指定對應的位置
    • 因為字符char,在C++中是1個字節,8位 28 = 256,在java中是2個字節,16位,65535
    • 那么我們創建一個長度為65535的數組,每個字母根據其ASCII碼作為數組下標對應的一個數字,二數組中存儲的是每個字符出現的次數
    • 這樣我們創建了一個大小為65535,以字符ASCII碼作為鍵值的哈希表
    • 還有一個小的關鍵點在于,字符的ASCII編碼可以直接通過Integer.valueOf得到,恰好,Character的HashCode方法也是直接轉int,他是是相等的
    • 如上代碼有如下實現:
/*** 找出數組中第一個只出現一次的字符* @author liaojiamin* @Date:Created in 10:22 2021/6/10*/ public class FindNotRepeatChar {public static void main(String[] args) {System.out.println(findNotRepeatChar("wersdfxvsdfwer"));}/*** 自定義Hash表方法* */public static char findNotRepeatChar(String str){if(str == null){return '\u0000';}if(str.length() == 1){return str.charAt(0);}int[] charValue = new int[256];char[] target = str.toCharArray();for (int i = 0; i < target.length; i++) {Integer position = new Character(target[i]).hashCode();charValue[position] += 1;}for (int i = 0; i < target.length; i++) {Integer position = new Character(target[i]).hashCode();if(charValue[position] == 1){return target[i];}}return '\u0000';} }
  • 以上代碼能正確得到我們需要的結果,并且第一次遍歷,在哈希表中更新一個字符出現的次數時間是O(1)。如果字符串長度為n,那么一次掃描時間復雜度是O(n)

  • 第二次遍歷得到的Hash表同樣可以O(1)時間復雜度得到一個字符的次數,所以時間復雜度依然是O(1)

  • 這樣總的來說時間復雜度還是O(1)

  • 同時我們需要一個 65535 的整數數組,由于數組的大小是個常數,也就是數組大小不會和目標字符串的長度相關,那么空間復雜度是O(1)

  • 問題:

    • 在方法三種,的確可以在純英文字符的情況下得到確定值,算法也是正確的,但是實際上工作中字符遠比65535個多,而且hashCode也會有沖突的時候,比如***存在中英文混合情況,方法二就無法求解***
  • 方法四:

    • 基于方法二的基礎上我們解決中英文混用造成的沖突以及順序問題
    • 我想到了HashMap中用到的哈希沖突解決方法,分離鏈表發
    • 我們定義一個鏈表數組,每次沖突后,將沖突元素添加到鏈表尾部
    • 然后依次遍歷找出為 1 的節點即可
    • 如下實現:
/*** 找出數組中第一個只出現一次的字符* @author liaojiamin* @Date:Created in 10:22 2021/6/10*/ public class FindNotRepeatChar {public static void main(String[] args) {System.out.println(findNotRepeatCharCompatibleChina("wersdfxxv我sdfwer"));} /*** 異常情況:無法保證中文,英文數據在數組中順序* 包含中文情況* */public static char findNotRepeatCharCompatibleChina(String str){if(str == null){return '\u0000';}if(str.length() == 1){return str.charAt(0);}ListNode listArray[] = new ListNode[str.length()];char[] target = str.toCharArray();for (int i = 0; i < target.length; i++) {Integer position = (new Character(target[i]).hashCode())%str.length();if(listArray[position] == null){listArray[position] = new ListNode(String.valueOf(target[i]), 1);}else {ListNode listNode = MyLinkedList.search(listArray[position], String.valueOf(target[i]));if(listNode != null){listNode.setValue(listNode.getValue()+1);}else {MyLinkedList.addToTail(listArray[position], String.valueOf(target[i]), 1);}}}for (int i = 0; i < listArray.length; i++) {if(listArray[i] != null){ListNode header = listArray[i];while (header != null){if(header.getValue() == 1){return header.getKey().charAt(0);}header = header.getNext();}}}return '\u0000';}}
  • 如上,算法參照HashMap的思想對hash表進行處理,算法能得到正確的值。

  • 我們試圖通過一個 字符串大小的鏈表數組來存儲對應存量數據,其中涉及到HashCode%str.length取模得到對應位置

  • 雖然獲取數組位置的時候回有沖突,并且不能保證順序,但是我們每次都通過原始數組去查找遍歷,依然可以得到第一個出現一次的字符

  • 方法中用的鏈表ListNode,以及鏈表對應的方法 MyLinkedList 都是自定義的方法,可以在之前的文章:數據結構與算法–鏈表實現以及應用 找到詳細的實現以及說明。

  • 方法的時間時間復雜度兩次遍歷都是O(n)

  • 在每次遍歷有hash沖突的節點時候,我們需要調用 MyLinkedList.search 找到當前key值對應的節點,此處復雜度取決于hash沖突的多少

  • 因此時間復雜度應該大于O(n)

  • 空間復雜度額外存儲于字符串長度正相關也是O(n)

  • 方法五

    • 在方法四中算法是正確,但是有一定的復雜度,涉及到hash沖突解決,取模定位,鏈表節點查詢這種復雜的操作
    • 因為我們收到方法三的定式思維影響用的hash表的結構存儲,其實完全不用
    • 我們可以用鏈表存儲,不用hash作為key,直接用對應的字符作為key,這樣可以用少于O(n)的空間來存儲
    • 如上分析有如下實現:
/*** 找出數組中第一個只出現一次的字符* @author liaojiamin* @Date:Created in 10:22 2021/6/10*/ public class FindNotRepeatChar {public static void main(String[] args) {System.out.println(findNotRepeatCharCompatibleChinaLinkList("哈哈wersvdfxx我v我sdfwer去"));}/*** 用鏈表解決* */public static char findNotRepeatCharCompatibleChinaLinkList(String str) {if (str == null) {return '\u0000';}if (str.length() == 1) {return str.charAt(0);}//初始化鏈表ListNode listNode = new ListNode(String.valueOf(str.charAt(0)), 1);char[] target = str.toCharArray();for (int i = 1; i < target.length; i++) {ListNode header = MyLinkedList.search(listNode, String.valueOf(target[i]));if(header != null){header.setValue(header.getValue() + 1);}else {MyLinkedList.addToTail(listNode, String.valueOf(target[i]), 1);}}for (int i = 0; i < target.length; i++) {ListNode targetNode = MyLinkedList.search(listNode, String.valueOf(target[i]));if(targetNode != null && targetNode.getValue() == 1){return target[i];}}return '\u0000';} }
  • 如上用鏈表存儲的實現方式時間復雜度與之前一樣,也是大于O(n),但是在代碼復雜度上減少很多
  • 此方法不涉及到hash沖突解決等問題,都是直接存儲,空間復雜度是小于O(n)的

上一篇:數據結構與算法–丑數
下一篇:數據結構與算法–數組中的逆序對

總結

以上是生活随笔為你收集整理的数据结构与算法--第一个只出现一次的字符的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。