本部分主要是 CavsZhouyou 在練習《劍指 Offer》時所作的筆記,主要涉及算法相關知識和一些相關面試題時所作的筆記,分享這份總結給你們,幫助你們對算法的能夠來一次全方位的檢漏和排查,感謝原做者 CavsZhouyou 的付出,原文連接放在文章最下方,若是出現錯誤,但願你們共同指出!html
題目: 在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的 一個二維數組和一個整數,判斷數組中是否含有該整數。
思路:前端
(1)第一種方式是使用兩層循環依次遍歷,判斷是否含有該整數。這一種方式最壞狀況下的時間複雜度爲 O(n^2)。git
(2)第二種方式是利用遞增序列的特色,咱們能夠從二維數組的右上角開始遍歷。若是當前數值比所求的數要小,則將位置向下移動
,再進行判斷。若是當前數值比所求的數要大,則將位置向左移動,再進行判斷。這一種方式最壞狀況下的時間複雜度爲 O(n)。github
題目: 請實現一個函數,將一個字符串中的空格替換成「%20」。例如,當字符串爲 We Are Happy.則通過替換以後的字符串爲 We%20 Are%20Happy 思路: 使用正則表達式,結合字符串的 replace 方法將空格替換爲 「%20」 str.replace(/\s/g,"%20")
題目: 輸入一個鏈表,從尾到頭打印鏈表每一個節點的值。 思路: 利用棧來實現,首先根據頭結點以此遍歷鏈表節點,將節點加入到棧中。當遍歷完成後,再將棧中元素彈出並打印,以此來實現。棧的 實現能夠利用 Array 的 push 和 pop 方法來模擬。
題目: 輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸 入前序遍歷序列 {1,2,4,7,3,5,6,8} 和中序遍歷序列 {4,7,2,1,5,3,8,6},則重建二叉樹並返回。 思路: 利用遞歸的思想來求解,首先先序序列中的第一個元素必定是根元素。而後咱們去中序遍歷中尋找到該元素的位置,找到後該元素的左 邊部分就是根節點的左子樹,右邊部分就是根節點的右子樹。所以咱們能夠分別截取對應的部分進行子樹的遞歸構建。使用這種方式的 時間複雜度爲 O(n),空間複雜度爲 O(logn)。
題目: 用兩個棧來實現一個隊列,完成隊列的 Push 和 Pop 操做。 思路: 隊列的一個基本特色是,元素先進先出。經過兩個棧來模擬時,首先咱們將兩個棧分爲棧 1 和棧 2。當執行隊列的 push 操做時,直接 將元素 push 進棧 1 中。當隊列執行 pop 操做時,首先判斷棧 2 是否爲空,若是不爲空則直接 pop 元素。若是棧 2 爲空,則將棧 1 中 的全部元素 pop 而後 push 到棧 2 中,而後再執行棧 2 的 pop 操做。 擴展: 當使用兩個長度不一樣的棧來模擬隊列時,隊列的最大長度爲較短棧的長度的兩倍。
題目: 把一個數組最開始的若干個元素搬到數組的末尾,咱們稱之爲數組的旋轉。 輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的 最小元素。 例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲 1。 NOTE:給出的全部元素都大於 0,若數組大 小爲 0,請返回 0。 思路: (1)咱們輸入的是一個非遞減排序的數組的一個旋轉,所以原始數組的值遞增或者有重複。旋轉以後原始數組的值必定和一個值相 鄰,而且不知足遞增關係。所以咱們就能夠進行遍歷,找到不知足遞增關係的一對值,後一個值就是旋轉數組的最小數字。 (2)二分法
相關資料能夠參考:
《旋轉數組的最小數字》面試
題目: 你們都知道斐波那契數列,如今要求輸入一個整數 n,請你輸出斐波那契數列的第 n 項。 n<=39 思路: 斐波那契數列的規律是,第一項爲 0,第二項爲 1,第三項之後的值都等於前面兩項的和,所以咱們能夠經過循環的方式,不斷經過疊 加來實現第 n 項值的構建。經過循環而不是遞歸的方式來實現,時間複雜度降爲了 O(n),空間複雜度爲 O(1)。
題目: 一隻青蛙一次能夠跳上 1 級臺階,也能夠跳上 2 級。求該青蛙跳上一個 n 級的臺階總共有多少種跳法。 思路: 跳臺階的問題是一個動態規劃的問題,因爲一次只可以跳 1 級或者 2 級,所以跳上 n 級臺階一共有兩種方案,一種是從 n-1 跳上,一 種是從 n-2 級跳上,所以 f(n) = f(n-1) + f(n-2)。 和斐波那契數列相似,不過初始兩項的值變爲了 1 和 2,後面每項的值等於前面兩項的和。
題目:正則表達式
一隻青蛙一次能夠跳上 1 級臺階,也能夠跳上 2 級……它也能夠跳上 n 級。求該青蛙跳上一個 n 級的臺階總共有多少種跳法。算法
思路:數組
變態跳臺階的問題同上一個問題的思考方案是同樣的,咱們能夠獲得一個結論是,每一項的值都等於前面全部項的值的和。數據結構
f(1) = 1
f(2) = f(2-1) + f(2-2) //f(2-2) 表示 2 階一次跳 2 階的次數。
f(3) = f(3-1) + f(3-2) + f(3-3)
...
f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-(n-1)) + f(n-n)app
再次總結可得
| 1 ,(n=0 ) f(n) = | 1 ,(n=1 ) | 2\*f(n-1),(n>=2)
題目: 咱們能夠用 2*1 的小矩形橫着或者豎着去覆蓋更大的矩形。請問用 n 個 2*1 的小矩形無重疊地覆蓋一個 2\*n 的大矩形,總共 有多少種方法? 思路: 依舊是斐波那契數列的應用
題目: 輸入一個整數,輸出該數二進制表示中 1 的個數。其中負數用補碼錶示。 思路: 一個不爲 0 的整數的二進制表示,必定會有一位爲 1。咱們找到最右邊的一位 1,當咱們將整數減去 1 時,最右邊的一位 1 變爲 0,它後 面的全部位都取反,所以將減一後的值與原值相與,咱們就會可以消除最右邊的一位 1。所以判斷一個二進制中 1 的個數,咱們能夠判 斷這個數能夠經歷多少次這樣的過程。 如:1100&1011=1000
題目: 給定一個 double 類型的浮點數 base 和 int 類型的整數 exponent。求 base 的 exponent 次方。 思路: 首先咱們須要判斷 exponent 正負和零取值三種狀況,根據不一樣的狀況經過遞歸來實現。
題目: 輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得全部的奇數位於數組的前半部分,全部的偶數位於位於數組的後半 部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。 思路: 因爲須要考慮到調整以後的穩定性,所以咱們可使用輔助數組的方式。首先對數組中的元素進行遍歷,每遇到一個奇數就將它加入到 奇數輔助數組中,每遇到一個偶數,就將它將入到偶數輔助數組中。最後再將兩個數組合並。這一種方法的時間複雜度爲 O(n),空間 複雜度爲 O(n)。
題目: 輸入一個鏈表,輸出該鏈表中倒數第 k 個結點。 思路: 使用兩個指針,先讓第一個和第二個指針都指向頭結點,而後再讓第二個指針走 k-1 步,到達第 k 個節點。而後兩個指針同時向後 移動,當第二個指針到達末尾時,第一個指針指向的就是倒數第 k 個節點了。
題目: 輸入一個鏈表,反轉鏈表後,輸出鏈表的全部元素。 思路: 經過設置三個變量 pre、current 和 next,分別用來保存前繼節點、當前節點和後繼結點。從第一個節點開始向後遍歷,首先將當 前節點的後繼節點保存到 next 中,而後將當前節點的後繼節點設置爲 pre,而後再將 pre 設置爲當前節點,current 設置爲 ne xt 節點,實現下一次循環。
題目: 輸入兩個單調遞增的鏈表,輸出兩個鏈表合成後的鏈表,固然咱們須要合成後的鏈表知足單調不減規則。 思路: 經過遞歸的方式,依次將兩個鏈表的元素遞歸進行對比。
題目: 輸入兩棵二叉樹 A、B,判斷 B 是否是 A 的子結構。(ps:咱們約定空樹不是任意一個樹的子結構) 思路: 經過遞歸的思想來解決 第一步首先從樹 A 的根節點開始遍歷,在左右子樹中找到和樹 B 根結點的值同樣的結點 R 。 第二步兩棵樹同時從 R 節點和根節點以相同的遍歷方式進行遍歷,依次比較對應的值是否相同,當樹 B 遍歷結束時,結束比較。
題目: 操做給定的二叉樹,將其變換爲源二叉樹的鏡像。 思路: 從根節點開始遍歷,首先經過臨時變量保存左子樹的引用,而後將根節點的左右子樹的引用交換。而後再遞歸左右節點的子樹交換。
題目: 輸入一個矩陣,按照從外向裏以順時針的順序依次打印出每個數字, 例如,若是輸入以下矩陣: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 則依次打印出數字 1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10 思路: (1)根據左上角和右下角能夠定位出一次要旋轉打印的數據。一次旋轉打印結束後,往對角分別前進和後退一個單位,能夠肯定下一 次須要打印的數據範圍。 (2)使用模擬魔方逆時針解法,每打印一行,則將矩陣逆時針旋轉 90 度,打印下一行,依次重複。
題目: 定義棧的數據結構,請在該類型中實現一個可以獲得棧最小元素的 min 函數。 思路: 使用一個輔助棧,每次將數據壓入數據棧時,就把當前棧裏面最小的值壓入輔助棧當中。這樣輔助棧的棧頂數據一直是數據棧中最小 的值。
題目: 輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否爲該棧的彈出順序。假設壓入棧的全部數字均不相等。例如 序列 1,2,3,4,5 是某棧的壓入順序,序列 4,5,3,2,1 是該壓棧序列對應的一個彈出序列,但 4,3,5,1,2 就不多是該壓棧序 列的彈出序列。(注意:這兩個序列的長度是相等的) 思路: 咱們可使用一個輔助棧的方式來實現,首先遍歷壓棧順序,依次將元素壓入輔助棧中,每次壓入元素後咱們首先判斷該元素是否與出 棧順序中的此刻位置的元素相等,若是不相等,則將元素繼續壓棧,若是相等,則將輔助棧中的棧頂元素出棧,出棧後,將出棧順序中 的位置後移一位繼續比較。當壓棧順序遍歷完成後,若是輔助棧不爲空,則說明該出棧順序不正確。
題目: 從上往下打印出二叉樹的每一個節點,同層節點從左至右打印。 思路: 本質上是二叉樹的層序遍歷,能夠經過隊列來實現。首先將根節點入隊。而後對隊列進行出隊操做,每次出隊時,將出隊元素的左右子 節點依次加入到隊列中,直到隊列長度變爲 0 時,結束遍歷。
題目: 輸入一個整數數組,判斷該數組是否是某二叉搜索樹的後序遍歷的結果。若是是則輸出 Yes,不然輸出 No。假設輸入的數組的任意兩 個數字都互不相同。 思路: 對於一個合法而二叉樹的後序遍從來說,最末尾的元素爲根元素。該元素前面的元素能夠劃分爲兩個部分,一部分爲該元素的左子樹, 全部元素的值比根元素小,一部分爲該元素的右子樹,全部的元素的值比該根元素大。而且每一部分都是一個合法的後序序列,所以我 們能夠利用這些特色來遞歸判斷。
題目: 輸入一顆二叉樹和一個整數,打印出二叉樹中結點值的和爲輸入整數的全部路徑。路徑定義爲從樹的根結點開始往下一直到葉結點所經 過的結點造成一條路徑。 思路: 經過對樹進行深度優先遍歷,遍歷時保存當前節點的值並判斷是否和指望值相等,若是遍歷到葉節點不符合要求則回退處理。
題目: 輸入一個複雜鏈表(每一個節點中有節點值,以及兩個指針,一個指向下一個節點,另外一個特殊指針指向任意一個節點),返回結果爲 複製後複雜鏈表的 head。(注意,輸出結果中請不要返回參數中的節點引用,不然判題程序會直接返回空) 思路: (1)第一種方式,首先對原有鏈表每一個節點進行復制,經過 next 鏈接起來。而後當鏈表複製完成以後,再來設置每一個節點的 ra ndom 指針,這個時候每一個節點的 random 的設置都須要從頭結點開始遍歷,所以時間的複雜度爲 O(n^2)。 (2)第二種方式,首先對原有鏈表每一個節點進行復制,而且使用 Map 以鍵值對的方式將原有節點和複製節點保存下來。當鏈表復 制完成以後,再來設置每一個節點的 random 指針,這個時候咱們經過 Map 中的鍵值關係就能夠獲取到對應的複製節點,所以 沒必要再從頭結點遍歷,將時間的複雜度下降爲了 O(n),可是空間複雜度變爲了 O(n)。這是一種以空間換時間的作法。 (3)第三種方式,首先對原有鏈表的每一個節點進行復制,並將複製後的節點加入到原有節點的後面。當鏈表複製完成以後,再進行 random 指針的設置,因爲每一個節點後面都跟着本身的複製節點,所以咱們能夠很容易的獲取到 random 指向對應的複製節點 。最後再將鏈表分離,經過這種方法咱們也可以將時間複雜度下降爲 O(n)。
題目: 輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能建立任何新的結點,只能調整樹中結點指針的指向。 思路: 須要生成一個排序的雙向列表,那麼咱們應該經過中序遍歷的方式來調整樹結構,由於只有中序遍歷,返回纔是一個從小到大的排序 序列。 基本的思路是咱們首先從根節點開始遍歷,先將左子樹調整爲一個雙向鏈表,並將左子樹雙向鏈表的末尾元素的指針指向根節點,並 將根節點的左節點指向末尾節點。再將右子樹調整爲一個雙向鏈表,並將右子樹雙向鏈表的首部元素的指針指向根元素,再將根節點 的右節點指向首部節點。經過對左右子樹遞歸調整,所以來實現排序的雙向鏈表的構建。
題目: 輸入一個字符串,按字典序打印出該字符串中字符的全部排列。例如輸入字符串 abc,則打印出由字符 a,b,c 所能排列出來的全部 字符串 abc,acb,bac,bca,cab 和 cba。輸入描述:輸入一個字符串,長度不超過 9(可能有字符重複),字符只包括大小寫字母。 思路: 咱們能夠把一個字符串看作是兩個部分,第一部分爲它的第一個字符,第二部分是它後面的全部字符。求整個字符串的一個全排列,可 以看作兩步,第一步是求全部可能出如今第一個位置的字符,即把第一個字符和後面的全部字符交換。第二步就是求後面全部字符的一 個全排列。所以經過這種方式,咱們能夠以遞歸的思路來求出當前字符串的全排列。
詳細資料能夠參考:
《字符串的排列》
題目: 數組中有一個數字出現的次數超過數組長度的一半。請找出這個數字。例如輸入一個長度爲 9 的數組{1,2,3,2,2,2,5,4,2}。因爲數 字 2 在數組中出現了 5 次,超過數組長度的一半,所以輸出 2。若是不存在則輸出 0。 思路: (1)對數組進行排序,排序後的中位數就是所求數字。這種方法的時間複雜度取決於咱們採用的排序方法的時間複雜度,所以最快爲 O(nlogn)。 (2)因爲所求數字的數量超過了數組長度的一半,所以排序後的中位數就是所求數字。所以咱們能夠將問題簡化爲求一個數組的中 位數問題。其實數組並不須要全排序,只須要部分排序。咱們經過利用快排中的 partition 函數來實現,咱們如今數組中隨 機選取一個數字,然後經過 partition 函數返回該數字在數組中的索引 index,若是 index 恰好等於 n/2,則這個數字 即是數組的中位數,也便是要求的數,若是 index 大於 n/2,則中位數確定在 index 的左邊,在左邊繼續尋找便可,反之 在右邊尋找。這樣能夠只在 index 的一邊尋找,而不用兩邊都排序,減小了一半排序時間,這種方法的時間複雜度爲 O(n)。 (3)因爲該數字的出現次數比全部其餘數字出現次數的和還要多,所以能夠考慮在遍歷數組時保存兩個值:一個是數組中的一個數 字,一個是次數。當遍歷到下一個數字時,若是下一個數字與以前保存的數字相同,則次數加 1,若是不一樣,則次數減 1,若是 次數爲 0,則須要保存下一個數字,並把次數設定爲 1。因爲咱們要找的數字出現的次數比其餘全部數字的出現次數之和還要大, 則要找的數字確定是最後一次把次數設爲 1 時對應的數字。該方法的時間複雜度爲 O(n),空間複雜度爲 O(1)。
詳細資料能夠參考:
《出現次數超過一半的數字》
題目: 輸入 n 個整數,找出其中最小的 K 個數。例如輸入 4,5,1,6,2,7,3,8 這 8 個數字,則最小的 4 個數字是 1,2,3,4 。 思路: (1)第一種思路是首先將數組排序,排序後再取最小的 k 個數。這一種方法的時間複雜度取決於咱們選擇的排序算法的時間複雜 度,最好的狀況下爲 O(nlogn)。 (2)第二種思路是因爲咱們只須要得到最小的 k 個數,這 k 個數不必定是按序排序的。所以咱們可使用快速排序中的 part ition 函數來實現。每一次選擇一個樞紐值,將數組分爲比樞紐值大和比樞紐值小的兩個部分,判斷樞紐值的位置,若是該樞 紐值的位置爲 k-1 的話,那麼樞紐值和它前面的全部數字就是最小的 k 個數。若是樞紐值的位置小於 k-1 的話,假設樞 紐值的位置爲 n-1,那麼咱們已經找到了前 n 小的數字了,咱們就還須要到後半部分去尋找後半部分 k-n 小的值,進行劃 分。當該樞紐值的位置比 k-1 大時,說明最小的 k 個值還在左半部分,咱們須要繼續對左半部分進行劃分。這一種方法的平 均時間複雜度爲 O(n)。 (3)第三種方法是維護一個容量爲 k 的最大堆。對數組進行遍歷時,若是堆的容量尚未達到 k ,則直接將元素加入到堆中,這 就至關於咱們假設前 k 個數就是最小的 k 個數。對 k 之後的元素遍歷時,咱們將該元素與堆的最大值進行比較,若是比最 大值小,那麼咱們則將最大值與其交換,而後調整堆。若是大於等於堆的最大值,則繼續向後遍歷,直到數組遍歷完成。這一 種方法的平均時間複雜度爲 O(nlogk)。
詳細資料能夠參考:
《尋找最小的 k 個數》
題目: HZ 偶爾會拿些專業問題來忽悠那些非計算機專業的同窗。今天測試組開完會後,他又發話了:在古老的一維模式識別中,經常須要計 算連續子向量的最大和,當向量全爲正數的時候,問題很好解決。可是,若是向量中包含負數,是否應該包含某個負數,並指望旁邊的 正數會彌補它呢?例如:{6,-3,-2,7,-15,1,2,2},連續子向量的最大和爲 8(從第 0 個開始,到第 3 個爲止)。你會不會被他忽悠 住?(子向量的長度至少是 1) 思路: (1)第一種思路是直接暴力求解的方式,先以第一個數字爲首日後開始疊加,疊加的過程當中保存最大的值。而後再以第二個數字爲首 日後開始疊加,並與先前保存的最大的值進行比較。這一種方法的時間複雜度爲 O(n^2)。 (2)第二種思路是,首先咱們觀察一個最大和的連續數組的規律,咱們能夠發現,子數組必定是以正數開頭的,中間包含了正負數。 所以咱們能夠從第一個數開始向後疊加,每次保存最大的值。疊加的值若是爲負數,則將疊加值初始化爲 0,由於後面的數加上負 數只會更小,所以須要尋找下一個正數開始下一個子數組的判斷。一直日後判斷,直到這個數組遍歷完成爲止,獲得最大的值。 使用這一種方法的時間複雜度爲 O(n)。
詳細資料能夠參考:
《連續子數組的最大和》
題目: 求出 1~13 的整數中 1 出現的次數,並算出 100~1# 300 的整數中 1 出現的次數?爲此他特別數了一下 1~13 中包含 1 的數字有 一、十、十一、 十二、13 所以共出現 6 次,可是對於後面問題他就沒轍了。ACMer 但願大家幫幫他,並把問題更加廣泛化,能夠很快的求出任意非負整 數區間中 1 出現的次數。 思路: (1)第一種思路是直接遍歷每一個數,而後將判斷每一個數中 1 的個數,一直疊加。 (2)第二種思路是求出 1 出如今每位上的次數,而後進行疊加。
詳細資料能夠參考:
《從 1 到 n 整數中 1 出現的次數:O(logn)算法》
題目: 輸入一個正整數數組,把數組裏全部數字拼接起來排成一個數,打印能拼接出的全部數字中最小的一個。例如輸入數組{3,32,321 },則打印出這三個數字能排成的最小數字爲 321323。 思路: (1)求出數組的全排列,而後對每一個排列結果進行比較。 (2)利用排序算法實現,可是比較時,比較的並非兩個元素的大小,而是兩個元素正序拼接和逆序拼接的大小,若是逆序拼接的 結果更小,則交換兩個元素的位置。排序結束後,數組的順序則爲最小數的排列組合順序。
詳細資料能夠參考:
《把數組排成最小的數》
題目: 把只包含質因子 二、3 和 5 的數稱做醜數。例如 六、8 都是醜數,但 14 不是,由於它包含因子 7。 習慣上咱們把 1 當作是第一個醜數。求 按從小到大的順序的第 N 個醜數。 思路: (1)判斷一個數是否爲醜數,能夠判斷該數不斷除以 2,最後餘數是否爲 1。判斷該數不斷除以 3,最後餘數是否爲 1。判斷不斷除以 5,最後餘數是否爲 1。在不考慮時間複雜度的狀況下,能夠依次遍歷找到第 N 個醜數。 (2)使用一個數組來保存已排序好的醜數,後面的醜數由前面生成。
題目: 在一個字符串(1<=字符串長度<=10000,所有由大寫字母組成)中找到第一個只出現一次的字符,並返回它的位置。 思路: (1)第一種思路是,從前日後遍歷每個字符。每遍歷一個字符,則將字符與後邊的全部字符依次比較,判斷是否含有相同字符。這 一種方法的時間複雜度爲 O(n^2)。 (2)第二種思路是,首先對字符串進行一次遍歷,將字符和字符出現的次數以鍵值對的形式存儲在 Map 結構中。而後第二次遍歷時 ,去 Map 中獲取對應字符出現的次數,找到第一個只出現一次的字符。這一種方法的時間複雜度爲 O(n)。
題目: 在數組中的兩個數字,若是前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對 的總數 P。 思路: (1)第一種思路是直接求解的方式,順序掃描整個數組。每掃描到一個數字的時候,逐個比較該數字和它後面的數字的大小。若是 後面的數字比它小,則這兩個數字就組成了一個逆序對。假設數組中含有 n 個數字。因爲每一個數字都要和 O(n)個數字做比 較,所以這個算法的時間複雜度是 O(n^2)。 (2)第二種方式是使用歸併排序的方式,經過利用歸併排序分解後進行合併排序時,來進行逆序對的統計,這一種方法的時間複雜 度爲 O(nlogn)。
詳細資料能夠參考:
《數組中的逆序對》
題目: 輸入兩個鏈表,找出它們的第一個公共結點。 思路: (1)第一種方法是在第一個鏈表上順序遍歷每一個結點,每遍歷到一個結點的時候,在第二個鏈表上順序遍歷每一個結點。若是在第二 個鏈表上有一個結點和第一個鏈表上的結點同樣,說明兩個鏈表在這個結點上重合,因而就找到了它們的公共結點。若是第一 個鏈表的長度爲 m,第二個鏈表的長度爲 n。這一種方法的時間複雜度是 O(mn)。 (2)第二種方式是利用棧的方式,經過觀察咱們能夠發現兩個鏈表的公共節點,都位於鏈表的尾部,以此咱們能夠分別使用兩個棧 ,依次將鏈表元素入棧。而後在兩個棧同時將元素出棧,比較出棧的節點,最後一個相同的節點就是咱們要找的公共節點。這 一種方法的時間複雜度爲 O(m+n),空間複雜度爲 O(m+n)。 (3)第三種方式是,首先分別遍歷兩個鏈表,獲得兩個鏈表的長度。而後獲得較長的鏈表與較短的鏈表長度的差值。咱們使用兩個 指針來分別對兩個鏈表進行遍歷,首先將較長鏈表的指針移動 n 步,n 爲兩個鏈表長度的差值,而後兩個指針再同時移動, 判斷所指向節點是否爲同一節點。這一種方法的時間複雜度爲 O(m+n),相同對於上一種方法不須要額外的空間。
詳細資料能夠參考:
《兩個鏈表的第一個公共結點》
題目: 統計一個數字:在排序數組中出現的次數。例如輸入排序數組{ 1, 2, 3, 3, 3, 3, 4, 5}和數字 3 ,因爲 3 在這個數組中出 現了 4 次,所以輸出 4 。 思路: (1)第一種方法是直接對數組順序遍歷的方式,經過這種方法來統計數字的出現次數。這種方法的時間複雜度爲 O(n)。 (2)第二種方法是使用二分查找的方法,因爲數組是排序好的數組,所以相同數字是排列在一塊兒的。統計數字出現的次數,咱們須要 去找到該段數字開始和結束的位置,以此來肯定數字出現的次數。所以咱們可使用二分查找的方式來肯定該數字的開始和結束 位置。若是咱們第一次咱們數組的中間值爲 k ,若是 k 值比所求值大的話,那麼咱們下一次只須要判斷前面一部分就好了,如 果 k 值比所求值小的話,那麼咱們下一次就只須要判斷後面一部分就好了。若是 k 值等於所求值的時候,咱們則須要判斷該值 是否爲開始位置或者結束位置。若是是開始位置,那麼咱們下一次須要到後半部分去尋找結束位置。若是是結束位置,那麼咱們 下一次須要到前半部分去尋找開始位置。若是既不是開始位置也不是結束位置,那麼咱們就分別到先後兩個部分去尋找開始和結 束位置。這一種方法的平均時間複雜度爲 O(logn)。
題目: 輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次通過的結點(含根、葉結點)造成樹的一條路徑,最長路徑的長度爲樹的深 度。 思路: 根節點的深度等於左右深度較大值加一,所以能夠經過遞歸遍從來實現。
題目: 輸入一棵二叉樹,判斷該二叉樹是不是平衡二叉樹。 思路: (1)在遍歷樹的每一個結點的時候,調用函數獲得它的左右子樹的深度。若是每一個結點的左右子樹的深度相差都不超過 1 ,那麼它 就是一棵平衡的二叉樹。使用這種方法時,節點會被屢次遍歷,所以會形成效率不高的問題。 (2)在求一個節點的深度時,同時判斷它是否平衡。若是不平衡則直接返回 -1,不然返回樹高度。若是一個節點的一個子樹的深 度爲-1,那麼就直接向上返回 -1 ,該樹已是不平衡的了。經過這種方式確保了節點只可以被訪問一遍。
題目: 一個整型數組裏除了兩個數字以外,其餘的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。 思路: (1)第一種方式是依次遍歷數組,記錄下數字出現的次數,從而找出兩個只出現一次的數字。 (2)第二種方式,根據位運算的異或的性質,咱們能夠知道兩個相同的數字異或等於 0,一個數和 0 異或仍是它自己。因爲數組中 的其餘數字都是成對出現的,所以咱們能夠將數組中的全部數依次進行異或運算。若是隻有一個數出現一次的話,那麼最後剩下 的就是落單的數字。若是是兩個數只出現了一次的話,那麼最後剩下的就是這兩個數異或的結果。這個結果中的 1 表示的是 A 和 B 不一樣的位。咱們取異或結果的第一個 1 所在的位數,假如是第 3 位,接着經過比較第三位來將數組分爲兩組,相同數字必定會 被分到同一組。分組完成後再按照依次異或的思路,求得剩餘數字即爲兩個只出現一次的數字。
題目: 小明很喜歡數學,有一天他在作數學做業時,要求計算出 9~16 的和,他立刻就寫出了正確答案是 100。可是他並不知足於此,他在想究 竟有多少種連續的正數序列的和爲 100(至少包括兩個數)。沒多久,他就獲得另外一組連續正數和爲 100 的序列:18,19,20,21,22。 如今把問題交給你,你能不能也很快的找出全部和爲 S 的連續正數序列?Good Luck!輸出描述:輸出全部和爲 S 的連續正數序列。序 列內按照從小至大的順序,序列間按照開始數字從小到大的順序。 思路: 維護一個正數序列數組,數組中初始只含有值 1 和 2,而後從 3 依次日後遍歷,每遍歷到一個元素則將這個元素加入到序列數組中,而後 判斷此時序列數組的和。若是序列數組的和大於所求值,則將第一個元素(最小的元素彈出)。若是序列數組的和小於所求值,則繼續 日後遍歷,將元素加入到序列中繼續判斷。當序列數組的和等於所求值時,打印出此時的正數序列,而後繼續日後遍歷,尋找下一個連 續序列,直到數組遍歷完成終止。
詳細資料能夠參考:
《和爲 s 的連續正數序列》
題目: 輸入一個遞增排序的數組和一個數字 S,在數組中查找兩個數,是的他們的和正好是 S,若是有多對數字的和等於 S,輸出兩個數 的乘積最小的。輸出描述:對應每一個測試案例,輸出兩個數,小的先輸出。 思路: 首先咱們經過規律能夠發現,和相同的兩個數字,兩個數字的差值越大,乘積越小。所以咱們只須要從數組的首尾開始找到第一對和 爲 s 的數字對進行了。所以咱們可使用雙指針的方式,左指針初始指向數組的第一個元素,右指針初始指向數組的最後一個元素 。而後首先判斷兩個指針指向的數字的和是否爲 s ,若是爲 s ,兩個指針指向的數字就是咱們須要尋找的數字對。若是兩數的和 比 s 小,則將左指針向左移動一位後繼續判斷。若是兩數的和比 s 大,則將右指針向右移動一位後繼續判斷。
詳細資料能夠參考:
《和爲 S 的字符串》
題目: 彙編語言中有一種移位指令叫作循環左移(ROL),如今有個簡單的任務,就是用字符串模擬這個指令的運算結果。對於一個給定的 字符序列 S,請你把其循環左移 K 位後的序列輸出。例如,字符序列 S=」abcXYZdef」,要求輸出循環左移 3 位後的結果,即 「X YZdefabc」。是否是很簡單?OK,搞定它! 思路: 字符串裁剪後拼接
題目: 牛客最近來了一個新員工 Fish,天天早晨老是會拿着一本英文雜誌,寫些句子在本子上。同事 Cat 對 Fish 寫的內容頗感興趣,有 一天他向 Fish 借來翻看,但卻讀不懂它的意思。例如,「student. a am I」。後來才意識到,這傢伙原來把句子單詞的順序翻轉了 ,正確的句子應該是「I am a student.」。Cat 對一一的翻轉這些單詞順序可不在行,你能幫助他麼? 思路: 經過空格將單詞分隔,而後將數組反序後,從新拼接爲字符串。
題目: LL 今天心情特別好,由於他去買了一副撲克牌,發現裏面竟然有 2 個大王,2 個小王(一副牌本來是 54 張^\_^)...他隨機從中抽出 了 5 張牌,想測測本身的手氣,看看能不能抽到順子,若是抽到的話,他決定去買體育彩票,嘿嘿!!「紅心 A,黑桃 3,小王,大王 ,方片 5」,「Oh My God!」不是順子..... LL 不高興了,他想了想,決定大\小王能夠當作任何數字,而且 A 看做 1,J 爲 11, Q 爲 12,K 爲 13。上面的 5 張牌就能夠變成「1,2,3,4,5」(大小王分別看做 2 和 4),「So Lucky!」。LL 決定去買體育彩票啦。 如今,要求你使用這幅牌模擬上面的過程,而後告訴咱們 LL 的運氣如何。爲了方便起見,你能夠認爲大小王是 0。 思路: 首先判斷 5 個數字是否是連續的,最直觀的方法是把數組排序。值得注意的是,因爲 0 能夠當成任意數字,咱們能夠用 0 去補滿數 組中的空缺。若是排序以後的數組不是連續的,即相鄰的兩個數字相隔若干個數字,但只要咱們有足夠的。能夠補滿這兩個數字的空 缺,這個數組實際上仍是連續的。 因而咱們須要作 3 件事情:首先把數組排序,再統計數組中 0 的個數,最後統計排序以後的數組中相鄰數字之間的空缺總數。如 果空缺的總數小於或者等於 0 的個數,那麼這個數組就是連續的:反之則不連續。最後,咱們還須要注意一點:若是數組中的非 0 數字重複出現,則該數組不是連續的。換成撲克牌的描述方式就是若是一副牌裏含有對子,則不多是順子。
詳細資料能夠參考:
《撲克牌的順子》
題目: 0, 1, … , n-1 這 n 個數字排成一個圈圈,從數字 0 開始每次從圓圏裏刪除第 m 個數字。求出這個圈圈裏剩下的最後一個數 字。 思路: (1)使用環形鏈表進行模擬。 (2)根據規律得出(待深刻理解)
詳細資料能夠參考:
《圓圈中最後剩下的數字》
題目: 求 1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case 等關鍵字及條件判斷語句(A?B:C)。 思路: 因爲不能使用循環語句,所以咱們能夠經過遞歸來實現。而且因爲不可以使用條件判斷運算符,咱們能夠利用 && 操做符的短路特 性來實現。
題目: 寫一個函數,求兩個整數之和,要求在函數體內不得使用 +、-、×、÷ 四則運算符號。 思路: 經過位運算,遞歸來實現。
題目: 將一個字符串轉換成一個整數,要求不能使用字符串轉換整數的庫函數。數值爲 0 或者字符串不是一個合法的數值則返回 0。輸入描 述:輸入一個字符串,包括數字字母符號,能夠爲空。輸出描述:若是是合法的數值表達則返回該數字,不然返回 0。 思路: 首先須要進行符號判斷,其次咱們根據字符串的每位經過減 0 運算轉換爲整數和,依次根據位數疊加。
題目: 在一個長度爲 n 的數組裏的全部數字都在 0 到 n-1 的範圍內。數組中某些數字是重複的,但不知道有幾個數字重複了,也不知 道每一個數字重複了幾回。請找出數組中任意一個重複的數字。 思路: (1)首先將數組排序,排序後再進行判斷。這一種方法的時間複雜度爲 O(nlogn)。 (2)使用 Map 結構的方式,依次記錄下每個數字出現的次數,從而能夠判斷是否出現重複數字。這一種方法的時間複雜度爲 O (n),空間複雜度爲 O(n)。 (3)從數組首部開始遍歷,每遍歷一個數字,則將該數字和它的下標相比較,若是數字和下標不等,則將該數字和它對應下標的值 交換。若是對應的下標值上已是正確的值了,那麼說明當前元素是一個重複數字。這一種方法相對於上一種方法來講不須要 額外的內存空間。
題目: 給定一個數組 A[0,1,...,n-1],請構建一個數組 B[0,1,...,n-1],其中 B 中的元素 B[i]=A[0]_A[1]_...*A[i-1]*A [i+1]*...*A[n-1]。不能使用除法。 思路: (1) C[i]=A[0]×A[1]×...×A[i-1]=C[i-1]×A[i-1] D[i]=A[i+1]×...×A[n-1]=D[i+1]×A[i+1] B[i]=C[i]×D[i] 將乘積分爲先後兩個部分,分別循環求出後,再進行相乘。 (2)上面的方法須要額外的內存空間,咱們能夠引入中間變量的方式,來下降空間複雜度。(待深刻理解)
詳細資料能夠參考:
《構建乘積數組》
題目: 請實現一個函數用來匹配包括'.'和'_'的正則表達式。模式中的字符'.'表示任意一個字符,而'_'表示它前面的字符能夠出現任 意次(包含 0 次)。 在本題中,匹配是指字符串的全部字符匹配整個模式。例如,字符串"aaa"與模式"a.a"和"ab*ac*a"匹配, 可是與"aa.a"和"ab\*a"均不匹配。 思路: (1)狀態機思路(待深刻理解)
詳細資料能夠參考:
《正則表達式匹配》
題目: 請實現一個函數用來判斷字符串是否表示數值(包括整數和小數)。例如,字符串"+100","5e2","-123","3.1416"和"-1E- 16"都表示數值。 可是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。、 思路: 利用正則表達式實現
題目: 請實現一個函數用來找出字符流中第一個只出現一次的字符。例如,當從字符流中只讀出前兩個字符 "go" 時,第一個只出現一次 的字符是 "g" 。當從該字符流中讀出前六個字符 "google" 時,第一個只出現一次的字符是 "l"。 輸出描述:若是當前字符流 沒有存在出現一次的字符,返回#字符。 思路: 同第 34 題
題目: 一個鏈表中包含環,如何找出環的入口結點? 思路: 首先使用快慢指針的方式咱們能夠判斷鏈表中是否存在環,當快慢指針相遇時,說明鏈表中存在環。相遇點必定存在於環中,所以我 們能夠從使用一個指針從這個點開始向前移動,每移動一個點,環的長度加一,當指針再次回到這個點的時候,指針走了一圈,所以 經過這個方法咱們能夠獲得鏈表中的環的長度,咱們將它記爲 n 。 而後咱們設置兩個指針,首先分別指向頭結點,而後將一個指針先移動 n 步,而後兩個指針再同時移動,當兩個指針相遇時,相遇 點就是環的入口節點。
詳細資料能夠參考:
《鏈表中環的入口結點》
《《劍指 offer》——鏈表中環的入口結點》
題目: 在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。例如,鏈表 1->2->3- > 3->4->4->5 處理後爲 1->2->5 思路: 解決這個問題的第一步是肯定刪除的參數。固然這個函數須要輸入待刪除鏈表的頭結點。頭結點可能與後面的結點重複,也就是說頭 結點也可能被刪除,因此在鏈表頭額外添加一個結點。 接下來咱們從頭遍歷整個鏈表。若是當前結點的值與下一個結點的值相同,那麼它們就是重複的結點,均可以被刪除。爲了保證刪除 以後的鏈表仍然是相連的而沒有中間斷開,咱們要把當前的前一個結點和後面值比當前結點的值要大的結點相連。咱們要確保 prev 要始終與下一個沒有重複的結點鏈接在一塊兒。
題目: 給定一棵二叉樹和其中的一個結點,如何找出中序遍歷順序的下一個結點?樹中的結點除了有兩個分別指向左右子結點的指針之外, 還有一個指向父節點的指針。 思路: 這個問題咱們能夠分爲三種狀況來討論。 第一種狀況,當前節點含有右子樹,這種狀況下,中序遍歷的下一個節點爲該節點右子樹的最左子節點。所以咱們只要從右子節點 出發,一直沿着左子節點的指針,就能找到下一個節點。 第二種狀況是,當前節點不含有右子樹,而且當前節點爲父節點的左子節點,這種狀況下中序遍歷的下一個節點爲當前節點的父節 點。 第三種狀況是,當前節點不含有右子樹,而且當前節點爲父節點的右子節點,這種狀況下咱們沿着父節點一直向上查找,直到找到 一個節點,該節點爲父節點的左子節點。這個左子節點的父節點就是中序遍歷的下一個節點。
題目: 請實現一個函數來判斷一棵二叉樹是否是對稱的。若是一棵二叉樹和它的鏡像同樣,那麼它是對稱的。 思路: 咱們對一顆二叉樹進行前序遍歷的時候,是先訪問左子節點,而後再訪問右子節點。所以咱們能夠定義一種對稱的前序遍歷的方式 ,就是先訪問右子節點,而後再訪問左子節點。經過比較兩種遍歷方式最後的結果是否相同,以此來判斷該二叉樹是否爲對稱二叉 樹。
題目: 請實現一個函數按照之字形順序打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右到左的順序打印,即第一行按照 從左到右的順序打印,第二層按照從右到左順序打印,第三行再按照從左到右的順序打印,其餘以此類推。 思路: 按之字形順序打印二叉樹須要兩個棧。咱們在打印某一行結點時,把下一層的子結點保存到相應的棧裏。若是當前打印的是奇數層 ,則先保存左子結點再保存右子結點到一個棧裏;若是當前打印的是偶數層,則先保存右子結點再保存左子結點到第二個棧裏。每 一個棧遍歷完成後進入下一層循環。
詳細資料能夠參考:
《按之字形順序打印二叉樹》
題目: 從上到下按層打印二叉樹,同一層的結點按從左到右的順序打印,每一層打印一行。 思路: 用一個隊列來保存將要打印的結點。爲了把二叉樹的每一行單獨打印到一行裏,咱們須要兩個變量:一個變量表示在當前的層中還 沒有打印的結點數,另外一個變量表示下一次結點的數目。
題目: 請實現兩個函數,分別用來序列化和反序列化二叉樹。 思路: 數組模擬
題目: 給定一顆二叉搜索樹,請找出其中的第 k 小的結點。 思路: 對一顆樹首先進行中序遍歷,在遍歷的同時記錄已經遍歷的節點數,當遍歷到第 k 個節點時,這個節點即爲第 k 大的節點。
題目: 如何獲得一個數據流中的中位數?若是從數據流中讀出奇數個數值,那麼中位數就是全部值排序以後位於中間的數值。若是數據 流中讀出偶數個數值,那麼中位數就是全部數值排序以後中間兩個數的平均值。
題目: 給定一個數組和滑動窗口的大小,找出全部滑動窗口裏數值的最大值。例如,若是輸入數組{2,3,4,2,6,2,5,1}及滑動窗口的 大小 3,那麼一共存在 6 個滑動窗口,他們的最大值分別爲{4,4,6,6,6,5}; 針對數組{2,3,4,2,6,2,5,1}的滑動窗口有如下 6 個: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2 ,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。 思路: 使用隊列的方式模擬
題目: 請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串全部字符的路徑。路徑能夠從矩陣中的任意一個格子開始,每 一步能夠在矩陣中向左,向右,向上,向下移動一個格子。若是一條路徑通過了矩陣中的某一個格子,則該路徑不能再進入該格子 。例如 a b c e s f c s a d e e 矩陣中包含一條字符串"bcced"的路徑,可是矩陣中不包含"abcb"路徑,由於字符串的 第一個字符 b 佔據了矩陣中的第一行第二個格子以後,路徑不能再次進入該格子。
題目: 地上有一個 m 行和 n 列的方格。一個機器人從座標 0,0 的格子開始移動,每一次只能向左,右,上,下四個方向移動一格,可是不能 進入行座標和列座標的數位之和大於 k 的格子。 例如,當 k 爲 18 時,機器人可以進入方格(35,37),由於 3+5+3+7 = 18。可是 ,它不能進入方格(35,38),由於 3+5+3+8 = 19。請問該機器人可以達到多少個格子?
筆者再次牆裂推薦收藏這篇原文,收錄於 CavsZhouyou - 🐜 前端面試複習筆記,這個倉庫是原做者校招時的前端複習筆記,主要總結一些比較重要的知識點和前端面試問題,但願對你們有所幫助。
最後若是文章和筆記能帶您一絲幫助或者啓發,請不要吝嗇你的贊和收藏,你的確定是我前進的最大動力 😁
附筆記連接,閱讀往期更多優質文章可移步查看,喜歡的能夠給我點贊鼓勵哦:https://github.com/Wscats/art...