此題解純屬我的觀點,若有錯誤,歡迎指出。git
Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array.算法
Note:
You may assume that nums1 has enough space (size that is greater or equal to m + n) to hold additional elements from nums2. The number of elements initialized in nums1 and nums2 are m and n respectively.windows
大意就是給定兩個已經排好序的數組arr1,arr2,保證第一個數組的可用空間可以裝下兩個數組全部的現有有效元素。目標是讓你將兩個數組合併成一個排好序的數組,並把全部的元素放在第一個數組裏面。數組
一個最直接能想到的辦法就是,開闢一個和arr1元素數量同樣長的數組arr3,先把arr1中的全部元素拷貝到arr3,同時清空arr1。而後兩個指針p2, p3分別指向arr2,arr3的起始位置。每次比較p2,p3指向元素的大小,取小的那個壓入arr1的末尾。直到p2, p3中有一個指針到達末尾了,就讓另外一個還沒結束的數組中剩餘的元素所有進入arr1。這種方法空間複雜度是O(n), 時間複雜度是O(n + m);app
僞代碼:spa
algorithm merge(arr1, arr2) is for i <- 1 to arr1_length do arr3 <- arr1[i]; p1 <- p2 <- p3 <- 1; while ((p2 ≠ arr2_length) and (p3 ≠ arr3_length)) do if (arr2[p2] > arr3[p3]) then arr1[p1] <- arr3[p3]; p3 <- p3 + 1; else arr1[p1] <- arr2[p2]; p2 <- p2 + 1; p1 <- p1 + 1; if (p2 ≠ arr2_length) then rest_arr <- arr2; rest_p <- p2; rest_length <- arr2_length else rest_arr <- arr3; rest_p <- p3; rest_length <- arr3_length while (rest_p ≠ rest_length) do arr1[p1] <- rest_arr[rest_p]; rest_p <- rest_p + 1; p1 <- p1 + 1; return arr1;
上面這種作法,須要耗費O(n)的空間,有沒有更高效的辦法呢?有!指針
試想數組的優點是什麼?能夠隨機訪問。同時,目前給定的數據是已經排好序的。那咱們能不能,不從數組的開始進行合併, 而是從數組的末尾開始合併呢?rest
作法就是,p1指向 arr1的最後一個元素,p2指向arr2的最後一個元素。p指向arr1空間末尾。每次從p1,p2中取比較大的那個元素壓到arr1[p]的位置。這樣,咱們的空間複雜度就是O(1),時間複雜度仍是O(m+n)code
傳進來的arr1的空間已經保證了能夠容納兩個數組的全部元素,最壞的狀況下,即便arr2的數據所有進入arr的末尾,也不會污染arr1的數據,因此這種作法是可行的。事件
algorithm merge(arr1, arr2) is p1 <- arr1_length; p2 <- arr2_length; p <- arr1_length + arr2_length; while ((arr1[p1] > 0) and (arr2[p2] > 0)) do if (arr1[p1] > arr2[p2]) then arr1[p] <- arr1[p1]; p1 <- p1 - 1; else arr1[p] <- arr2[p2] p2 <- p2 - 1; p <- p - 1; if (p1 > 0) then rest_p <- p1; rest_arr <- arr1; else rest_p <- p2; rest_arr <- arr2; while (p > 0) do arr1[p] <- rest_arr[rest_p] p <- p - 1; rest_p <- rest_p - 1; return arr1;
A message containing letters from A-Z
is being encoded to numbers using the following mapping:
'A' -> 1 'B' -> 2 ... 'Z' -> 26
Given an encoded message containing digits, determine the total number of ways to decode it.
For example,
Given encoded message "12"
, it could be decoded as "AB"
(1 2) or "L"
(12).
The number of ways decoding "12"
is 2.
大意就是給定了字母A-Z和數字1-26的對應法則,給出一串數字,算出有多少種可能的轉換成字母串的方式。
定義狀態變量 decode_ways[i] 表示(那咱們實際要求的就是decode_ways[s的長度],decode_ways[i]的初始值都是0),s[1..i] 這個數字串轉換成字母串的方式的數量。觀察這個題目,數字會變轉換成字母有兩種可能:
一個數字轉換成一個字母,好比"2"轉換成"B"
兩個數字一塊兒轉換成字母,好比"26"轉換成"Z"
按照所求的s的長度來劃分階段,那麼很明顯,當前階段所求的decode_ways[i]只和上一階段的decode_ways[i - 2]和decode_ways[i - 1]有關。
再來定義一下合法數字(valid number)的概念:
合法數字村在的形式是一個字符串,叫它s_num。s_num可能有一位,也可能有兩位。
若只有一位:只要s_num[1]不爲'0'就是合法的
如有兩位:只要s_num[1]不爲'0',且把s_num轉化成數字num以後,知足 1 <= num <= 26
因此狀態轉移方程就很明顯了:
decode_ways[i] = decode_ways[i - 1] * (s[i] is valid number ? 1 : 0) + decode_ways[i - 2] * (s[i - 1 .. i] is valid number ? 1 : 0)
再來看邊界條件:
若是s[1]存在:
若是s[1]是合法數字,則decode_ways[1] = 1,不然decode_ways[1] = 0;
若是s[1]不存在:直接返回 0;
若是s[2]存在:
若是s[2]是個合法數字,則decode_ways[2] = decode_ways[1];
若是s[1..2]是個合法數字,則decode_ways[2]自增1;
若是s[2]不存在:直接返回decode_ways[1];
肯定好邊界,一直算到decode_ways[s的長度]返回就行。
僞代碼:
algorithm num_decodings(s) is if s[1] exists then if s[1] is valid number then decode_ways[1] <- 1; else decode_ways[1] <- 0; else return 0; if s[2] exists then if s[2] is valid number then decode_ways[2] <- decode_ways[1]; if s[1..2] is valid number then decode_ways[2] <- decode_ways[2] + 1; else return decode_ways[1]; for i <- 3 to s_length do decode_ways[i] <- decode_ways[i - 1] * (s[i] is valid number ? 1 : 0) + decode_ways[i - 2] * (s[i - 1 .. i] is valid number ? 1 : 0); return decode_ways[s_length];
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
For example,
S = "ADOBECODEBANC"
T = "ABC"
Minimum window is "BANC"
.
Note:
If there is no such window in S that covers all characters in T, return the empty string ""
.
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.
大意就是,給定兩個字符串S,T。在S中找到一個最短的字符串(窗口),使得這個窗口,包含T字符串中的全部出現的字符,包括數量。要求複雜度是O(n),數據保證最短的窗口在S中只出現一次。
用 window_start 和 window_end 來標記窗口的先後邊界。一開始,window_start 和 window_end 都爲 1,表示從字符串的最開始開始。而後 window_end 不斷的日後移,到達某個位置的時候,出現了第一個合法窗口。而後,window_start 開始往右移,直到縮小到一個最小的合法窗口。記錄下窗口的邊界值 min_window_start 和 min_window_end。而後 window_start 日後移一位,使窗口變得不合法。接着再將 min_window_end 日後滑,直到出現一個合法的窗口,而後將 window_start 往右收縮窗口達到一個最小合法窗口。若是當前窗口比以前獲得的最小窗口還要小,就更新最小窗口。如此循環,直到最終獲得的臨時最小合法窗口的的window_end到達S的末尾。
判斷窗口是否合法的辦法是:咱們建立兩個字典,expected_char 和 appeared_char。他們每一個元素的初始值都是0。
expected_char 的鍵是字母,值是該字母在T中出現的次數。
appeared_char 的鍵是字母,值是該字母在當前窗口中出現的次數。
同時使用一個變量 appeared_number 用來標記在字符串 T 中,出如今當前窗口的字符的個數(同一個字符出現兩次按兩個字符計算,可是若是這個字符出如今 T 中出現的次數小於在窗口中出現的次數,按在 T 中出現的次數計算),也就是說,若是appeared_number和 T 的長度相等,就認爲這個窗口是合法的。這樣,就能快速的判斷當前的窗口是不是合法的。
僞代碼:
algorithm min_window(s, t) is window_start <- 1; window_end <- 1; min_window_start <- 0; min_window_end <- s_length + 1; appeared_number <- 0; for i <- 1 to t_length do expected_char[t[i]] <- expected_char[t[i]] + 1; for window_end <- 1 to s_length do if expected_char[s[window_end]] ≠ 0 then if (appeared_char[s[window_end]] < expected_char[s[window_end]]) then appeared_number <- appeared_number + 1; appeared_char[s[window_end]] <- appeared_char[s[window_end]] + 1; if t_length = appeared_number then while expected_char[s[window_start]] = 0 or (expected_char[s[window_start]] > 0 and expected_char[s[window_start]] < appeared[s[window_start]]) do if (expected_char[s[window_start]] ≠ 0) then appeared_char[s[window_start]] <- appeared_char[s[window_start]] - 1; window_start <- window_start + 1; if window_end - window_start < min_window_end - min_window_start then min_window_start <- window_start; min_window_end <- windwo_end; appeared_number <- appeared_number - 1; appeared_char[s[window_start]] <- appeared_char[s[window_start]] - 1; window_start <- window_start + 1; return s[min_window_start .. min_window_end];
因爲 window_end 和 window_start 最多都只可能把 S 遍歷一邊,因此判斷該算法的事件複雜度是線性的。空間複雜度和S、T長度沒有關係,和S、T中字符的種類有關係,因此是O(1)