Arts 第十一週(5/27 ~ 6/2)

ARTS是什麼?
Algorithm:每週至少作一個leetcode的算法題;
Review:閱讀並點評至少一篇英文技術文章;
Tip:學習至少一個技術技巧;
Share:分享一篇有觀點和思考的技術文章。java


Algorithm

LeetCode 132. Palindrome Partitioning II程序員

思路分析
web

給定一個字符串,把它拆分紅迴文字符串,假設一分爲二(切一刀)爲一次,問最少要拆分多少次。看到最小這個關鍵詞,而後再看這個問題,發現原問題是能夠分解成更小的子問題的,好比一刀切成兩個字符串,這兩個字符串就是原問題的子問題,且 原問題的解 = 子問題1的解 + 子問題2的解 + 1,所以首先想到的就是動態規劃,可是這裏困擾我蠻久的是狀態的定義和遞推方程的推導。一開始我定義的動態規劃數組只有一維,dp[i] 表示問題 [0, i] 的解,我感受這樣的狀態定義簡單,並且遞推方程也特別的好寫,即 dp[i] = dp[j] + 1,這裏的 j 是在 [0,i) 之間的一個數,咱們只須要保證 [j, i] 是迴文串,在遍歷的過程當中,已經記錄了 [0, j] 的最優解,結果記錄在 dp[j] 中,因而我寫出了下面的代碼算法


參考代碼(優化前)數據庫

public int minCut(String s) {
    if (s == null || s.length() <= 1) {
        return 0;
    }
    
    int n = s.length();
    char[] sArr = s.toCharArray();
    
    int[] dp = new int[n];
    Arrays.fill(dp, Integer.MAX_VALUE);
    
    dp[0] = 0;
    for (int i = 1; i < n; ++i) {
        if (validation(sArr, 0, i)) {
            dp[i] = 0;
            continue;
        }
        
        for (int j = 0; j < i; ++j) {
            if (dp[j] != Integer.MAX_VALUE && validation(sArr, j + 1, i)) {
                dp[i] = Math.min(dp[i], dp[j] + 1);
            }
        }
    }
    
    
    return dp[n - 1];
}

private boolean validation(char[] sArr, int i, int j) {
    while (true) {
        if (i >= j) {
            break;
        }
        
        if (sArr[i++] != sArr[j--]) {
            return false;
        }
    }
    
    return true;
}
複製代碼

優化思路
編程

上面給出的解是能夠 work 的,可是你能夠看到的是這裏重複不斷地去判斷一個子串是否是迴文串花費了大量的時間,直接一點的優化思路就是在這個上面作文章,用一個二維的 boolean 數組來儲存字符串的每一個區間是否是迴文串,可是這樣作仍是免不了去進行大量的判斷迴文串的工做。若是須要進一步優化,仔細看看回文串,其有個特色就是 str1 = c1 + str2 + c2,若是要保證這裏的 str1 是迴文串,需有兩個條件,str2 是迴文串,並且頭尾兩個字符必須相等,即 c1 = c2,這裏的子問題是 str2,遍歷的方法仍是和上面不變,可是如今咱們須要一個二維的數組用來記錄迴文串的狀況,更新與遍歷同步進行,不須要反覆地判斷一個字符串是否是迴文串,這樣就利用空間換時間的方式,大大提高了程序運行時的效率,代碼以下數組


參考代碼(優化後)網絡

public int minCut(String s) {
    if (s == null || s.length() <= 1) {
        return 0;
    }
    
    int n = s.length();
    char[] sArr = s.toCharArray();
    
    boolean[][] isPalindrom = new boolean[n][n];
    int[] dp = new int[n];
    
    for (int i = 0; i < n; ++i) {
        int min = Integer.MAX_VALUE;
        for (int j = 0; j <= i; ++j) {
            if ((sArr[j] == sArr[i]) && (j + 1 >= i - 1 || isPalindrom[j + 1][i - 1])) {
                isPalindrom[j][i] = true;
                
                if (j == 0) {
                    min = 0;
                } else {
                    min = Math.min(min, dp[j - 1] + 1);
                }
            }
        }
        
        dp[i] = min;
    }
    
    return dp[n - 1];
}
複製代碼


Review

兩篇關於高效學習編程文章,其中第二篇文章是第一篇的一個引用:
數據結構

The Key To Accelerating Your Coding Skills框架

Smart Google Search

第一篇主文章中給出了一個觀點就是,高效學習編程主要在於渡過 「轉折點」,什麼是 「轉折點」 ?做者解釋學習編程實際上是有兩個階段,第一個階段是模仿以及積累知識,在這個階段咱們主要看重的是區域性的知識的積累和學習,好比如何在一門不熟悉的語言中寫 for 循環,新建函數、對象,訪問數據庫,調用恰當的庫函數等等,這個階段咱們主要是跟着一些教程來作項目,在這個過程當中咱們會熟悉並掌握一些基本的技能。到了第二個階段,咱們則須要用本身的能力去解決一個問題,完成一個項目,這時咱們沒有能夠參照的教程以及攻略文檔,這時咱們的心態跟第一階段的時候發生了很大的變化,第一階段中,咱們明確知道手頭的項目是確定能夠完成的,由於咱們有教程,有答案,咱們內心沒有任何的顧慮;以前說的 「轉折點」 就是指第一階段過渡到第二個階段的那個時期,在此之中,咱們再也不像以前那樣,有一個明確的方向,咱們經常須要摸着石頭過河,試着解決本身歷來沒有遇到過的問題,這時你會感受很痛苦,緣由文章也說了,由於在 「轉折點」 中,你的解決問題的速度會相比第一個階段更慢,在第二階段咱們學的再也不是積累技能和知識點,而是過程性的學習,說的更直白些就是如何將以前學到的知識點串起來解決實際的問題,由點到線,這實際上是一個思惟維度的跳躍,難,是正常的。

做者同時也給出瞭如何快速渡過 「轉折點」 的建議,首先就是不要放棄,要知道學習編程的人都有這麼一個過程,如今的困難只是暫時的,不是永久的;另外就是學習看技術文檔,而不是反覆看手把手的教程。他同時指出,學習網絡開發實際上是有兩個轉折點,這兩個轉折點會同時到來,一個是對 web 框架的初步掌握,到理解,再到熟練運用;另一個是算法和數據結構的學習,咱們必須學習一些常見的算法和數據結構,這樣才能使咱們的思考更加的高效和優化。

在第二篇文章中,做者給了一個關於如何高效解決程序中出現的問題,以及如何提高這方面的能力。程序中出問題,對於編程新手或者編程老手都是再正常不過的,可是新手和老手的心態徹底是不同的,新手會以爲本身又遇到難題了,很心煩,而老手則會思考問題的方方面面,如何快速解決這個問題,解決問題其實就是簡單的兩個步驟

  1. 定位並找到問題的所在
  2. 尋找恰當的解決方案

不少人,包括我不少時候第一個步驟沒有作或者是沒有作完就開始執行第二個步驟,這其實並非高效的方法,解決問題的難點永遠在於準確的定位到問題,這就好像你的錢包掉了,你首先得清楚地想一想是在哪掉的,若是你定位的範圍比較小,那麼找起來就會很是輕鬆。固然,做者也說到這實際上是一個過程,一開始咱們遇到問題,去 Google 上面搜索解決問題的解的時候可能須要打開 10 個頁面,但隨着咱們對語言、框架等一些東西的瞭解,咱們遇到問題就會很快定位,這時咱們再去 Google 上面搜索可能只須要打開 2 個頁面。

第一篇文章的最後,做者也說了,編程就是一個持續學習的過程,咱們學習並掌握了一個技術的方方面面,這時咱們又會去學習下一個技術,接觸本身從未接觸過的領域,接受下一個更難的挑戰,咱們每到一個溫馨區就要想着如何跳出去,尋找下一個挑戰,持續學習纔是如何學好編程的最好答案。



Tip

此次分享一些字符編碼的知識,以前看到衆多的字符編碼,不知道它們之間的關係,以及爲何會有這種編碼?它們是怎麼演變而來的?它們解決了什麼問題?但願之後遇到字符編碼問題,至少心中再也不不知所措,首先,咱們來了解幾個關鍵詞:

  • 字符集(Character Set):已編號的字符的有序集合,好比 ASCII
  • 字符碼(Code Point):字符集中字符的數字編號,例如字母 'A' 在 ASCII 碼中的字符碼就是 65
  • 編碼:將字符轉化成字節流
  • 解碼:將字節流轉化成字符
  • 字符編碼(Character Encoding):將字符集中的編碼映射爲字節流的實現方案,例如最簡單的對於最簡單的 ASCII 編碼,例如字符 'A' 用這種編碼方案,表示就是 0x41,寫入設備就是 b'01000001'
  • 大小端:計算機傳輸多字節的時候,先傳高位字節仍是低位字節有着不同的見解,可是寫是由字節流從低向高寫,若是先寫入高字節就是大端模式,反之則是小端模式,大端模式字節序和流設備的地址順序是相反的,而小端模式則是相同,通常網絡協議都是採用大端模式進行傳輸,操做系統都是小端

下面是我畫的一張圖,算是一個小型知識地圖吧,能夠大概描述字符編碼的演變,以及相關編碼方法的由來,能夠先看看,圖中的文字描述是指遇到的問題

計算機是隻能識別數字的,要讓計算機識別字符和文字,咱們須要把字符和文字轉換爲數字,因而有了字符編碼的存在。最開始,科學家門發明了最簡單的字符編碼,ASCII 碼,這種編碼方式只涵蓋了英文大小寫字母以及一些經常使用的符號,還有一些控制符號。這裏咱們使用一一對應的方式來查表進行字符的編碼和解碼,這裏必須強調的是字符和數字必須一一對應,不然會出現一樣的字符在計算機上顯示出來的東西不同。另外有一點就是,ASCII 碼是單字節的編碼方式,也就是隻有 8 個 bits 用來表示單個字符,可是這裏的 ASCII 碼僅使用了非符號位,也就是 7 個 bits 來表示,所以最多隻能表示 127 個字符,剩下的一個 bit 用做通訊的奇偶校驗。

後來人們發現不少很經常使用的字符 ASCII 碼沒有涵蓋,因而打起了最高位的那個 bit 的注意,OEM 字符集是對 ASCII 的擴展,把最高位也考慮進來,因而 ASCII 擴展成了能夠表示 256 個字符,可是 OEM 有不少個版本,對於這些版本有區別的是在 128~256 表示的字符,對於前面的 127 個字符是徹底兼容 ASCII 碼的。

隨着計算機的普及,不少非拉丁語系的國家也開始使用計算機,好比中國,韓國,日本,這麼看來使用 ASCII 的單字節編碼的方式是確定行不通了,因而出現了雙字節和多字節編碼方式,圖中的 GBKGB2312 就是雙字節編碼,可是編碼的方式仍是查表,表的大小仍是 256,可是如今不止一張表了,有多張表,兩個字節,第一個字節用來表示字符在第幾張表中,第二個字節表示字符在該表中具體所在的位置(相似於行和列的關係)。你能夠發如今這個時候出現了不少不一樣的字符集,若是一個計算機想要解碼一個文檔就得安裝與這個文檔匹配的字符集。

多字節編碼的方式雖然是解決了不少問題,可是如今的問題是不少內容不能出如今同一個文檔中,一份日文的文檔就得用日文的字符集,一份中文的文檔就要用相似 GBK,GB2312 這樣的字符集,若是說一份文檔既有日文,又有中文,那麼該如何解決?也就是說咱們仍是沒有一個能夠表示全部字符的字符集。因而這個時候有了 Unicode 字符集,它將全部的字符按頻繁程度劃分紅了 17 個層面,每一個層面有 2 個字節的空間,咱們最經常使用的層面是 BMP 層面,基本涵蓋了世界上全部的字符,注意這裏說的 Unicode 只是字符集,它不是編碼方法,由於使用以前的查表的方式來編碼解碼會使字符和字節流的耦合度太高,不利於之後的擴展,雖然每一個字符在 Unicode 表中均可以找到惟一對應的編碼(Unicode 碼),可是決定最終的字節流的仍是具體的字符編碼。對於這裏基於 Unicode 的字符編碼,一開始的話是 UCS-2,它也是雙字節的編碼方式,可是其只考慮了 BMP 層面的字符,對於 Unicode 的其餘層面的字符無法表示,因而有了 UTF-16,這裏會使用 2~4 個字節來表示具體字符,也就是最少都要使用 2 個字節,可是這裏會有一些效率問題,咱們常用的 ASCII 碼中的東西若是用 2 個字節表示,最高位都是 0x00,這是很是沒有必要的,那麼因而就有了 UTF-8,其採用 1~4 個字節來編碼,這種編碼方式對於 ASCII 碼中的內容用單字節傳輸,大大節省了編碼效率,另外的話也解決了一些不兼容問題,好比 C 語言中的函數都將 0x00 視爲字符串的末尾。這裏還有一個編碼方式是我國的 GB18030,由於它也能夠將 Unicode 裏面的全部字符都轉化成字節流,全部也是 Unicode 編碼方式,可是這裏不一樣的是,它仍是採用查表的方式來進行編碼,區別於 UTF-8 和 UTF-16 的規則型編碼。



Share

這周看完了一本書,《簡單的邏輯學》,讀以前以爲邏輯學就是那些哲學家搞的東西,可是這本書從基本的邏輯學論證開始講起,很基礎,小白都能讀的懂,讀完以後發現邏輯在咱們工做以及生活中無處不在,學會用邏輯去思考有助於咱們得知事情的真相,減小沒必要要的焦慮,此次就來寫個讀書感悟吧

程序員須要瞭解的邏輯學思想

相關文章
相關標籤/搜索