LeetCode 88,91,76題解

LeetCode 88,91,76題解

此題解純屬我的觀點,若有錯誤,歡迎指出。git

(LeetCode 88.Merge Sorted Array)

題目

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;

(LeetCode 91.Decode Ways)

題目

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] 這個數字串轉換成字母串的方式的數量。觀察這個題目,數字會變轉換成字母有兩種可能:

  1. 一個數字轉換成一個字母,好比"2"轉換成"B"

  2. 兩個數字一塊兒轉換成字母,好比"26"轉換成"Z"

按照所求的s的長度來劃分階段,那麼很明顯,當前階段所求的decode_ways[i]只和上一階段的decode_ways[i - 2]和decode_ways[i - 1]有關。

再來定義一下合法數字(valid number)的概念:

合法數字村在的形式是一個字符串,叫它s_num。s_num可能有一位,也可能有兩位。

  1. 若只有一位:只要s_num[1]不爲'0'就是合法的

  2. 如有兩位:只要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)

再來看邊界條件:

  1. 若是s[1]存在:

    1. 若是s[1]是合法數字,則decode_ways[1] = 1,不然decode_ways[1] = 0;

若是s[1]不存在:直接返回 0;

  1. 若是s[2]存在:

    1. 若是s[2]是個合法數字,則decode_ways[2] = decode_ways[1];

    2. 若是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];

(LeetCode 76.Minimum Window Substring)

題目

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)

相關文章
相關標籤/搜索