演算法題題集
㈠ 阿裡面試演算法題合集一
0,1,,n-1這n個數字排成一個圓圈,從數字0開始,每次從這個圓圈裡刪除第m個數字。求出這個圓圈裡剩下的最後一個數字。
例如,0、1、2、3、4這5個數字組成一個圓圈,從數字0開始每次刪除第3個數字,則刪除的前4個數字依次是2、0、4、1,因此最後剩下的數字是3。
示例 1:
輸入: n = 5, m = 3
輸出: 3
示例 2:
輸入: n = 10, m = 17
輸出: 2
請實現一個函數,輸入一個整數,輸出該數二進製表示中 1 的個數。例如,把 9 表示成二進制是 1001,有 2 位是 1。因此,如果輸入 9,則該函數輸出 2。
示例 1:
輸入:
輸出:3
解釋:輸入的二進制串 中,共有三位為 '1'。
數字以0123456789101112131415…的格式序列化到一個字元序列中。在這個序列中,第5位(從下標0開始計數)是5,第13位是1,第19位是4,等等。
請寫一個函數,求任意第n位對應的數字。
示例 1:
輸入:n = 3
輸出:3
示例 2:
輸入:n = 11
輸出:0
注意這里必須是long 類型
輸入一個非負整數數組,把數組里所有數字拼接起來排成一個數,列印能拼接出的所有數字中最小的一個。
示例 1:
輸入: [10,2]
輸出: "102"
示例 2:
輸入: [3,30,34,5,9]
輸出: "3033459"
老師想給孩子們分發糖果,有 N 個孩子站成了一條直線,老師會根據每個孩子的表現,預先給他們評分。
你需要按照以下要求,幫助老師給這些孩子分發糖果:
每個孩子至少分配到 1 個糖果。
相鄰的孩子中,評分高的孩子必須獲得更多的糖果。
那麼這樣下來,老師至少需要准備多少顆糖果呢?
示例 1:
輸入: [1,0,2]
輸出: 5
解釋: 你可以分別給這三個孩子分發 2、1、2 顆糖果。
示例 2:
輸入: [1,2,2]
輸出: 4
解釋: 你可以分別給這三個孩子分發 1、2、1 顆糖果。
第三個孩子只得到 1 顆糖果,這已滿足上述兩個條件。
在一條環路上有 N 個加油站,其中第 i 個加油站有汽油 gas[i] 升。
你有一輛油箱容量無限的的汽車,從第 i 個加油站開往第 i+1 個加油站需要消耗汽油 cost[i] 升。你從其中的一個加油站出發,開始時油箱為空。
如果你可以繞環路行駛一周,則返回出發時加油站的編號,否則返回 -1。
說明:
如果題目有解,該答案即為唯一答案。
輸入數組均為非空數組,且長度相同。
輸入數組中的元素均為非負數。
示例 1:
輸入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
輸出: 3
貪心的思路是,只要總和大於0 就可以繞一圈,
開始位置的貪心思路是,如果從剛開始sum小於0,一定不是從之前開始,而是從當前下一個位置,sum = 0 清空
給定一個非負整數數組,你最初位於數組的第一個位置。
數組中的每個元素代表你在該位置可以跳躍的最大長度。
你的目標是使用最少的跳躍次數到達數組的最後一個位置。
示例:
輸入: [2,3,1,1,4]
輸出: 2
解釋: 跳到最後一個位置的最小跳躍數是 2。
從下標為 0 跳到下標為 1 的位置,跳 1 步,然後跳 3 步到達數組的最後一個位置。
給定一個非負整數數組,你最初位於數組的第一個位置。
數組中的每個元素代表你在該位置可以跳躍的最大長度。
判斷你是否能夠到達最後一個位置。
示例 1:
輸入: [2,3,1,1,4]
輸出: true
解釋: 我們可以先跳 1 步,從位置 0 到達 位置 1, 然後再從位置 1 跳 3 步到達最後一個位置。
一條包含字母 A-Z 的消息通過以下方式進行了編碼:
'A' -> 1
'B' -> 2
...
'Z' -> 26
給定一個只包含數字的非空字元串,請計算解碼方法的總數。
示例 1:
輸入: "12"
輸出: 2
解釋: 它可以解碼為 "AB"(1 2)或者 "L"(12)。
這里一定注意 第一個數為0 的情況,s.charAt(0) == '0' 第一個為0 要直接返回0 才行
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。
如果你最多隻允許完成一筆交易(即買入和賣出一支股票一次),設計一個演算法來計算你所能獲取的最大利潤。
注意:你不能在買入股票前賣出股票。
示例 1:
輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格;同時,你不能在買入前賣出股票。
給定三個字元串 s1, s2, s3, 驗證 s3 是否是由 s1 和 s2 交錯組成的。
示例 1:
輸入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
輸出: true
給你一個字元串 s 和一個字元規律 p,請你來實現一個支持 '.' 和 '*' 的正則表達式匹配。
'.' 匹配任意單個字元
'*' 匹配零個或多個前面的那一個元素
所謂匹配,是要涵蓋 整個 字元串 s的,而不是部分字元串。
說明:
s 可能為空,且只包含從 a-z 的小寫字母。
p 可能為空,且只包含從 a-z 的小寫字母,以及字元 . 和 *。
示例 1:
輸入:
s = "aa"
p = "a"
輸出: false
解釋: "a" 無法匹配 "aa" 整個字元串。
給定一個整數矩陣,找出最長遞增路徑的長度。
對於每個單元格,你可以往上,下,左,右四個方向移動。 你不能在對角線方向上移動或移動到邊界外(即不允許環繞)。
示例 1:
輸入: nums =
[
[9,9,4],
[6,6,8],
[2,1,1]
]
輸出: 4
解釋: 最長遞增路徑為 [1, 2, 6, 9]。
使用帶記憶的可以避免超時
使用動態規劃解題
給出一個由無重復的正整數組成的集合,找出其中最大的整除子集,子集中任意一對 (Si,Sj) 都要滿足:Si % Sj = 0 或 Sj % Si = 0。
如果有多個目標子集,返回其中任何一個均可。
示例 1:
輸入: [1,2,3]
輸出: [1,2] (當然, [1,3] 也正確)
給定一些標記了寬度和高度的信封,寬度和高度以整數對形式 (w, h) 出現。當另一個信封的寬度和高度都比這個信封大的時候,這個信封就可以放進另一個信封里,如同俄羅斯套娃一樣。
請計算最多能有多少個信封能組成一組「俄羅斯套娃」信封(即可以把一個信封放到另一個信封裡面)。
說明:
不允許旋轉信封。
示例:
輸入: envelopes = [[5,4],[6,4],[6,7],[2,3]]
輸出: 3
解釋: 最多信封的個數為 3, 組合為: [2,3] => [5,4] => [6,7]。
標準的動態規劃
一隻青蛙想要過河。 假定河流被等分為 x 個單元格,並且在每一個單元格內都有可能放有一石子(也有可能沒有)。 青蛙可以跳上石頭,但是不可以跳入水中。
給定石子的位置列表(用單元格序號升序表示), 請判定青蛙能否成功過河(即能否在最後一步跳至最後一個石子上)。 開始時, 青蛙默認已站在第一個石子上,並可以假定它第一步只能跳躍一個單位(即只能從單元格1跳至單元格2)。
如果青蛙上一步跳躍了 k 個單位,那麼它接下來的跳躍距離只能選擇為 k - 1、k 或 k + 1個單位。 另請注意,青蛙只能向前方(終點的方向)跳躍。
請注意:
石子的數量 ≥ 2 且 < 1100;
每一個石子的位置序號都是一個非負整數,且其 < 231;
第一個石子的位置永遠是0。
示例 1:
[0,1,3,5,6,8,12,17]
true
使用數組+ 鏈表枚舉所有的可能
給你兩個單詞 word1 和 word2,請你計算出將 word1 轉換成 word2 所使用的最少操作數 。
你可以對一個單詞進行如下三種操作:
插入一個字元
刪除一個字元
替換一個字元
示例 1:
輸入:word1 = "horse", word2 = "ros"
輸出:3
解釋:
horse -> rorse (將 'h' 替換為 'r')
rorse -> rose (刪除 'r')
rose -> ros (刪除 'e')
給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1。
示例 1:
輸入: coins = [1, 2, 5], amount = 11
輸出: 3
解釋: 11 = 5 + 5 + 1
示例 2:
輸入: coins = [2], amount = 3
輸出: -1
給定一個字元串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度為 1000。
示例 1:
輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個有效答案。
給定一個字元串 S 和一個字元串 T,計算在 S 的子序列中 T 出現的個數。
一個字元串的一個子序列是指,通過刪除一些(也可以不刪除)字元且不幹擾剩餘字元相對位置所組成的新字元串。(例如,"ACE" 是 "ABCDE" 的一個子序列,而 "AEC" 不是)
題目數據保證答案符合 32 位帶符號整數范圍。
示例 1:
輸入:S = "rabbbit", T = "rabbit"
輸出:3
給定一個無序的整數數組,找到其中最長上升子序列的長度。
示例:
輸入: [10,9,2,5,3,7,101,18]
輸出: 4
使用二分查詢
在一個由 0 和 1 組成的二維矩陣內,找到只包含 1 的最大正方形,並返回其面積。
示例:
輸入:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
輸出: 4
給定正整數 n,找到若干個完全平方數(比如 1, 4, 9, 16, ...)使得它們的和等於 n。你需要讓組成和的完全平方數的個數最少。
示例 1:
輸入: n = 12
輸出: 3
解釋: 12 = 4 + 4 + 4.
你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。
給定一個代表每個房屋存放金額的非負整數數組,計算你 不觸動警報裝置的情況下 ,一夜之內能夠偷竊到的最高金額。
示例 1:
輸入:[1,2,3,1]
輸出:4
你是一個專業的小偷,計劃偷竊沿街的房屋,每間房內都藏有一定的現金。這個地方所有的房屋都圍成一圈,這意味著第一個房屋和最後一個房屋是緊挨著的。同時,相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。
給定一個代表每個房屋存放金額的非負整數數組,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金額。
示例 1:
輸入: [2,3,2]
輸出: 3
思路是忽略第一個求一個結果,忽略最後一個求一個結果,只要一個時一個結果
// 可以使用rangeCopy 將其放入一個函數中求解
給定一個三角形,找出自頂向下的最小路徑和。每一步只能移動到下一行中相鄰的結點上。
相鄰的結點 在這里指的是 下標 與 上一層結點下標 相同或者等於 上一層結點下標 + 1 的兩個結點。
例如,給定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自頂向下的最小路徑和為 11(即,2 + 3 + 5 + 1 = 11)。
給定一個包含非負整數的 m x n 網格,請找出一條從左上角到右下角的路徑,使得路徑上的數字總和為最小。
說明:每次只能向下或者向右移動一步。
示例:
輸入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
輸出: 7
一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記為「Start」 )。
機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記為「Finish」)。
現在考慮網格中有障礙物。那麼從左上角到右下角將會有多少條不同的路徑?
示例 1:
輸入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
輸出: 2
一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記為「Start」 )。
機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記為「Finish」)。
問總共有多少條不同的路徑?
假設你正在爬樓梯。需要 n 階你才能到達樓頂。
每次你可以爬 1 或 2 個台階。你有多少種不同的方法可以爬到樓頂呢?
注意:給定 n 是一個正整數。
示例 1:
輸入: 2
輸出: 2
㈡ 數據結構與演算法題需要回答
《數據結構與演算法》模擬題
一、填空題:(共15分)(每空一分)
按照排序時,存放數據的設備,排序可分為<1> 排序和<2> 排序。內部排序和外部排序
圖的常用的兩種存儲結構是<3> 和<4> 。鄰接矩陣和鄰接表
數據結構中的三種基本的結構形式是<5> 線性結構 和<6> 樹型結構 、圖型結構<7> 。
一個高度為6的二元樹,最多有<8> 63 個結點。
線性查找的時間復雜度為:<9> O(n^2) ,折半查找的時間復雜度為:<10> O(nlogn) 、堆分類的時間復雜度為:<11> O(nlogn) 。
在採用散列法進行查找時,為了減少沖突的機會,散列函數必須具有較好的隨機性,在我們介紹的幾種散列函數構造法中,隨機性最好的是<12> 隨機數 法、最簡單的構造方法是除留余數法<13> 。
線性表的三種存儲結構是:數組、<14> 鏈表 、<15> 靜態鏈表 。
二、回答下列問題:(共30分)
現有如右圖的樹,回答如下問題:看不見圖
根結點有:
葉結點有:
具有最大度的結點:
結點的祖先是:
結點的後代是:
棧存放在數組A[m]中,棧底位置是m-1。試問:
棧空的條件是什麼?top=m-1
棧滿的條件是什麼?top=-1
數據結構和抽象數據型的區別與聯系:
數據結構(data structure)—是相互之間存在一種或多種特定關系的數據元素的集合。數據元素相互之間的關系稱為結構。
抽象數據類型(ADT):是指一個數學模型(數據結構)以及定義在該模型(數據結構)上的一組操作。
㈢ 八:聚類演算法K-means(20191223-29)
學習內容:無監督聚類演算法K-Means
k-means:模型原理、收斂過程、超參數的選擇
聚類分析是在數據中發現數據對象之間的關系,將數據進行分組,組內的相似性越大,組間的差別越大,則聚類效果越好。
不同的簇類型: 聚類旨在發現有用的對象簇,在現實中我們用到很多的簇的類型,使用不同的簇類型劃分數據的結果是不同的。
基於原型的: 簇是對象的集合,其中每個對象到定義該簇的 原型 的距離比其他簇的原型距離更近,如(b)所示的原型即為中心點,在一個簇中的數據到其中心點比到另一個簇的中心點更近。這是一種常見的 基於中心的簇 ,最常用的K-Means就是這樣的一種簇類型。 這樣的簇趨向於球形。
基於密度的 :簇是對象的密度區域,(d)所示的是基於密度的簇,當簇不規則或相互盤繞,並且有早上和離群點事,常常使用基於密度的簇定義。
關於更多的簇介紹參考《數據挖掘導論》。
基本的聚類分析演算法
1. K均值: 基於原型的、劃分的距離技術,它試圖發現用戶指定個數(K)的簇。
2. 凝聚的層次距離: 思想是開始時,每個點都作為一個單點簇,然後,重復的合並兩個最靠近的簇,直到嘗試單個、包含所有點的簇。
3. DBSCAN: 一種基於密度的劃分距離的演算法,簇的個數有演算法自動的確定,低密度中的點被視為雜訊而忽略,因此其不產生完全聚類。
不同的距離量度會對距離的結果產生影響,常見的距離量度如下所示:
優點:易於實現
缺點:可能收斂於局部最小值,在大規模數據收斂慢
演算法思想:
選擇K個點作為初始質心
repeat
將每個點指派到最近的質心,形成K個簇
重新計算每個簇的質心
until 簇不發生變化或達到最大迭代次數
這里的「重新計算每個簇的質心」,是根據目標函數來計算的,因此在開始時要考慮 距離度量和目標函數。
考慮歐幾里得距離的數據,使用 誤差平方和(Sum of the Squared Error,SSE) 作為聚類的目標函數,兩次運行K均值產生的兩個不同的簇集,使用SSE最小的那個。
k表示k個聚類中心,ci表示第幾個中心,dist表示的是歐幾里得距離。
這里有一個問題就是為什麼,我們更新質心是讓所有的點的平均值,這里就是SSE所決定的。
k均值演算法非常簡單且使用廣泛,但是其有主要的兩個缺陷:
1. K值需要預先給定 ,屬於預先知識,很多情況下K值的估計是非常困難的,對於像計算全部微信用戶的交往圈這樣的場景就完全的沒辦法用K-Means進行。對於可以確定K值不會太大但不明確精確的K值的場景,可以進行迭代運算,然後找出Cost Function最小時所對應的K值,這個值往往能較好的描述有多少個簇類。
2. K-Means演算法對初始選取的聚類中心點是敏感的 ,不同的隨機種子點得到的聚類結果完全不同
3. K均值演算法並不是很所有的數據類型。 它不能處理非球形簇、不同尺寸和不同密度的簇,銀冠指定足夠大的簇的個數是他通常可以發現純子簇。
4. 對離群點的數據進行聚類時,K均值也有問題 ,這種情況下,離群點檢測和刪除有很大的幫助。
下面對初始質心的選擇進行討論:
當初始質心是隨機的進行初始化的時候,K均值的每次運行將會產生不同的SSE,而且隨機的選擇初始質心結果可能很糟糕,可能只能得到局部的最優解,而無法得到全局的最優解。
多次運行,每次使用一組不同的隨機初始質心,然後選擇一個具有最小的SSE的簇集。該策略非常的簡單,但是效果可能不是很好,這取決於數據集合尋找的簇的個數。
關於更多,參考《數據挖掘導論》
為了克服K-Means演算法收斂於局部最小值的問題,提出了一種 二分K-均值(bisecting K-means)
將所有的點看成是一個簇
當簇小於數目k時
對於每一個簇
計算總誤差
在給定的簇上進行K-均值聚類,k值為2 計算將該簇劃分成兩個簇後總誤差
選擇是的誤差最小的那個簇進行劃分
在原始的K-means演算法中,每一次的劃分所有的樣本都要參與運算,如果數據量非常大的話,這個時間是非常高的,因此有了一種分批處理的改進演算法。
使用Mini Batch(分批處理)的方法對數據點之間的距離進行計算。
Mini Batch的好處:不必使用所有的數據樣本,而是從不同類別的樣本中抽取一部分樣本來代表各自類型進行計算。n 由於計算樣本量少,所以會相應的減少運行時間n 但另一方面抽樣也必然會帶來准確度的下降。
聚類試圖將數據集中的樣本劃分為若干個通常是不相交的子集,每個子集成為一個「簇」。通過這樣的劃分,每個簇可能對應於一些潛在的概念(也就是類別);需說明的是,這些概念對聚類演算法而言事先是未知的,聚類過程僅能自動形成簇結構,簇對應的概念語義由使用者來把握和命名。
聚類是無監督的學習演算法,分類是有監督的學習演算法。所謂有監督就是有已知標簽的訓練集(也就是說提前知道訓練集里的數據屬於哪個類別),機器學習演算法在訓練集上學習到相應的參數,構建模型,然後應用到測試集上。而聚類演算法是沒有標簽的,聚類的時候,需要實現的目標只是把相似的東西聚到一起。
聚類的目的是把相似的樣本聚到一起,而將不相似的樣本分開,類似於「物以類聚」,很直觀的想法是同一個簇中的相似度要盡可能高,而簇與簇之間的相似度要盡可能的低。
性能度量大概可分為兩類: 一是外部指標, 二是內部指標 。
外部指標:將聚類結果和某個「參考模型」進行比較。
內部指標:不利用任何參考模型,直接考察聚類結果。
對於給定的樣本集,按照樣本之間的距離大小,將樣本集劃分為K個簇。讓簇內的點盡量緊密的連在一起,而讓簇間的距離盡量的大
初學者會很容易就把K-Means和KNN搞混,其實兩者的差別還是很大的。
K-Means是無監督學習的聚類演算法,沒有樣本輸出;而KNN是監督學習的分類演算法,有對應的類別輸出。KNN基本不需要訓練,對測試集裡面的點,只需要找到在訓練集中最近的k個點,用這最近的k個點的類別來決定測試點的類別。而K-Means則有明顯的訓練過程,找到k個類別的最佳質心,從而決定樣本的簇類別。
當然,兩者也有一些相似點,兩個演算法都包含一個過程,即找出和某一個點最近的點。兩者都利用了最近鄰(nearest neighbors)的思想。
優點:
簡單, 易於理解和實現 ;收斂快,一般僅需5-10次迭代即可,高效
缺點:
1,對K值得選取把握不同對結果有很大的不同
2,對於初始點的選取敏感,不同的隨機初始點得到的聚類結果可能完全不同
3,對於不是凸的數據集比較難收斂
4,對噪點過於敏感,因為演算法是根據基於均值的
5,結果不一定是全局最優,只能保證局部最優
6,對球形簇的分組效果較好,對非球型簇、不同尺寸、不同密度的簇分組效果不好。
K-means演算法簡單理解,易於實現(局部最優),卻會有對初始點、雜訊點敏感等問題;還容易和監督學習的分類演算法KNN混淆。
參考閱讀:
1.《 深入理解K-Means聚類演算法 》
2.《 K-Means 》