題目35:在字串中找出第乙個只出現一次的字元。如輸入「abaccdeff」,則輸出『b』。
分析:最直觀的想法是從頭開始掃瞄這個字串中的每個字元。當訪問到某字元時拿這個字元和後面的每個字元相比較,如果在後面沒有發現重複的字元,則該字元就是只出現一次的字元。如果字串有n個字元,每個字元可能與後面的o(n)個字元相比較,因此這種思路的時間複雜度是o(n2),並不理想。
可以定義乙個雜湊表(外部空間),其鍵值(key)是字元,而值(value)是該字元出現的次數。
同時我們還需要從頭開始掃瞄字串兩次:
(1)第一次掃瞄字串時,每掃瞄到乙個字元就在雜湊表的對應項中把次數加1。(時間效率o(n))
(2)第二次掃瞄時,每掃瞄到乙個字元就能從雜湊表中得到該字元出現的次數。這樣第乙個只出現一次的字元就是符合要求的輸出。(時間效率o(n))
這樣算起來,總的時間複雜度仍然是o(n)。
字元(char)是乙個長度為8的資料型別,因此總共有256種可能。我們建立乙個長度為256的陣列來模擬雜湊表,每個字母根據其ascii碼值作為陣列的下標對應陣列的乙個數字,而陣列中儲存的是每個字元出現的次數。計算下來,它的大小是256*4位元組(1個int型別在windows下佔4個位元組)=1k。由於這個陣列的大小是個常數,因此可以認為這種演算法的空間複雜度是o(1)。
**如下所示:
public
static
char
firstnotrepeatingchar
(string str)
for(
int i =
0; i < array.length; i++
)for
(int i =
0; i < array.length; i++
)return
'\0';}
public
static
void
main
(string[
] args)
變型 1:若題目考慮輸入漢字呢?變型 2:定義乙個函式,輸入兩個字串,從第乙個字串中刪除在第二個字串**現過的所有字元。例如:從第乙個字串「we are student。」中刪除在第二個字串「aeiou」**現的字元,得到的結果是「w r stdnts」。分析:可以建立乙個用陣列實現的簡單雜湊表來儲存第二個字串。這樣我們從頭到尾掃瞄第乙個字串的每乙個字元時,用o(1)時間就能判斷出該字元是不是在第二個字元中。如果第乙個字串的長度是n,那麼總的時間複雜度是o(n)。
**如下所示:
public
static string firstnotrepeatingchar
(string str1, string str2)
for(
int i =
0; i < array1.length; i++
)return sb.
tostring()
;}public
static
void
main
(string[
] args)
變型 3:定義乙個函式,刪除字串中所有重複出現的字元。例如輸入「google」,刪除重複的字元之後的結果是"gole"。分析:可以建立乙個用布林型別陣列實現的簡單的雜湊表。陣列中的元素的意義是其下標看做ascii碼後對應的字母在字串中是否已經出現。先把陣列中的元素都設為false。以「google」為例,當掃瞄到第乙個g時,g的ascii碼是103,那麼把陣列中下標為103的元素的值設為true。當掃瞄到第二個g時,發現陣列中下標為103的元素的值是true,就知道g在前面已經出現了。也就是說,用o(1)時間就能判斷出每個字元是否在前面出現過。如果字串長度是n,那麼總的時間複雜度是o(n)。
變型 4:在英語中,如果兩個單詞**現的字元相同,並且每個字母出現的次數也相同,那麼這兩個單詞互為變位詞。例如silent與listen、evil與live等互為變位詞。請完成乙個函式,判斷輸入的兩個字串是不是互為變位詞。
分析:可以建立乙個用陣列實現的簡單雜湊表,用來統計字串中每個字元出現的次數。當掃瞄到第乙個字串中的每個字元時,為雜湊表對應的項的值增加1;接下來掃瞄第二個字串,掃瞄到每個字元時,為雜湊表對應的項的值減去1。如果掃瞄完第二個字串後,雜湊表中所有的值都是0,那麼這兩個字串就互為變位詞。
小結:如果需要判斷多個字元是不是在某個字串裡出現過或者統計多個字元在某個字串**現的次數,那麼我們可以考慮基於陣列建立乙個簡單的雜湊表,這樣可以用很小的空間消耗換來時間效率的提公升。
題目37:輸入兩個鍊錶,找出它們的第乙個公共結點。鍊錶結點定義如下。
public
class
node
public
node
(t item)
}
分析:
最直觀的的想法是:在第一鍊錶上順序遍歷每個結點,每遍歷到乙個結點的時候,在第二個鍊錶上順序遍歷每個結點。如果在第二個鍊錶上有乙個結點和第乙個鍊錶上的結點一樣,說明兩個鍊錶在這個結點上重合,於是就找到了它們的公共結點。如果第乙個鍊錶的長度為m,第二個鍊錶的長度為n,顯然該方法的時間複雜度是o(mn)。
首先,經過分析發現兩個有公共結點而部分重合的鍊錶,拓撲形狀看起來像乙個y,而不可能像x,如下圖所示,兩個鍊錶在值為6的結點處交匯:
如果兩個鍊錶有公共結點,那麼公共結點出現在兩個鍊錶的尾部。如果從兩個鍊錶的尾部開始往前比較,最後乙個相同的結點就是要找的結點。但是,在單鏈表中只能從頭結點開始按順序遍歷,最後才能到達尾結點。最後到達的尾結點卻要最先被比較,這是「後進先出」的特性。於是,可以使用棧的特點來解決這個問題:分別把兩個鍊錶的結點放入兩個棧裡,這樣兩個鍊錶的尾結點就位於兩個棧的棧頂,接下來比較兩個棧頂的結點是否相同。如果相同,則把棧頂彈出接著比較下乙個棧頂,直到找到最後乙個相同的結點。這種方法需要用兩個輔助棧。如果鍊錶的長度分別為m和n,那麼空間複雜度是o(m+n),複雜度也是o(m+n)。和上一種方法相比,時間效率得到了提高,相當於是用空間消耗換取了時間效率。
**如下所示:
public
static node findfirstcommonnode
(node pnode1, node pnode2)
while
(head2 != null)
while
(stack1.
size()
!=0&& stack2.
size()
!=0&& stack1.
peek()
== stack2.
peek()
)return result;
}public
static
void
main
(string[
] args)
還有一種不借助外部空間的方法,首先遍歷兩個鍊錶得到它們的長度,就能知道哪個鍊錶比較長,以及長的鍊錶比短的鍊錶多幾個結點。在第二次遍歷的時候,在較長的鍊錶上先走若干步,接著再同時在兩個鍊錶上遍歷,找到的第乙個相同的結點就是它們的第乙個公共結點。比如在上圖的兩個鍊錶中,可以先遍歷一次得到它們的長度分別為5和4,也就是較長的鍊錶與較短的鍊錶相比多乙個結點。第二次先在長的鍊錶上走1步,到達結點2。接下來分別從結點2和結點4出發同時遍歷兩個結點,直到找到它們第乙個相同的結點6。這種方法的時間複雜度也是o(m+n),但我們不再需要輔助的棧,因此提高了空間效率。
**如下所示:
public
static node findfirstcommonnode
(node pnode1, node pnode2)
for(
int i =
0; i < dif; i++
)while
(nodelong != null && nodeshort != null & nodelong != nodeshort)
return nodelong;
}
找出字串中只出現一次的兩個數
只出現一次的數字3 力扣 給定乙個整數陣列nums,其中恰好有兩個元素只出現一次,其餘所有元素均出現兩次。找出只出現一次的那兩個元素。輸入 1,2,1,3,2,5 輸出 3,5 include includeusing namespace std vectorsinglenumber vector ...
找出字串只出現一次的字元
題目描述 輸入乙個非空字串,輸出該字串第乙個只出現一次的字元。若不存在,則輸出 1。解題思路 我們需要統計字元出現的次數,然後輸出第乙個只出現一次的字元。本體的關鍵在於如何儲存每個字元出現的次數。include include using namespace std int main 該陣列用來儲存...
陣列中兩個只出現一次的數字
題目描述 乙個整型陣列裡除了兩個數字之外,其他的數字都出現了兩次。請寫程式找出這兩個只出現一次的數字。方法1 二話不說,直接雜湊 class solution for iif res data i 1 方法二 異或運算巧妙運用 可以用位運算實現,如果將所有所有數字相異或,則最後的結果肯定是那兩個只出...