Design a data structure that supports all following operations in average O(1) time.
|
|
提示 | 解題應用 |
---|---|
HashTable | HashMap |
|
|
如果要儲存、刪除及查找資料,並且都在時間複雜度O(1)完成的話,那麼當然馬上就會想到要用hashmap來儲存資料,不過唯一的問題是隨機取出資料時(所有資料取出的機率皆相等),也要在時間複雜度O(1)完成,在hashmap儲存中需要key才能取出value,而key又不像陣列的index值一樣能夠確定範圍,因此為了能夠順利隨機取出hashmap的資料,總共需要兩個hashmap來儲存同一份資料,一個是儲存資料與index的對應關係,另一個則是儲存index與資料的對應關係,當然每次新增資料時index的值就需要我們自行計算並賦予,而在移除資料時除了從兩份hashmap移除之外,要記得將最後index的資料移至有空缺的位置,好比移除了index為4的資料就將index最大值的資料移至4,如此一來全部資料的index範圍就能維持在[0~資料長度-1]之間而中間不會有空缺,最後當要隨機取出資料時,只要產生正值的隨機數與資料長度取餘數,其值做為index回傳對應的資料即可。
既然需要兩個hashmap來儲存同一份資料,當然在初始化的時就指派兩個hashmap來做儲存,其中一個是儲存資料與index的對應關係,另一個則是儲存index與資料的對應關係
|
|
接著在新增資料的時候,先檢查該資料是否已存在於hashmap之中(資料對應到index的hashmap),如果存在就直接回傳false,否則才新增資料至兩個hashmap之中,而新資料的index值就正好是目前資料的長度,新增完畢之後才回傳true
|
|
而在移除資料的時候,一樣也是先檢查該資料是否已存在於hashmap之中,如果不存在就直接回傳false,否則才將資料從兩個hashmap之中移除,接著再檢查最後index的資料(刪除後最後的index是全部資料的長度;如果是刪除前取則是要將長度-1)是否存在,如果存在就將最後一筆資料移至刪除後產生的空缺位置(記得最後index對應到的資料在hashmap中要完全移除,至於資料對應到的最後index則用先前空缺的index取代即可)
|
|
前面兩個新增與刪除的函數如此費功夫,正是為了在最後要隨機取出資料時能夠變的非常容易,此時就只要產生正值的隨機數與資料長度取餘數,其值做為index回傳對應的資料即可(在index對應到資料的hashmap)
|
|
|
|
如果要儲存、刪除及查找資料,並且都在時間複雜度O(1)完成的話,那麼當然馬上就會想到要用hashmap來儲存資料,不過如果還要隨機取出資料時(所有資料取出的機率皆相等),也要在時間複雜度O(1)完成的話,就需要兩個hashmap來儲存同一份資料,一個是儲存資料與index的對應關係,另一個則是儲存index與資料的對應關係,每次新增資料時index的值就需要我們自行計算並賦予,而在移除資料時除了從兩份hashmap移除之外,要記得將最後index的資料移至有空缺的位置,如此一來全部資料的index範圍就能維持在[0~資料長度-1]之間而中間不會有空缺,最後當要隨機取出資料時,只要產生正值的隨機數與資料長度取餘數,其值做為index回傳對應的資料即可。
]]>Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.
Note that it is the kth smallest element in the sorted order, not the kth distinct element.
|
|
Note:
You may assume k is always valid, 1 ≤ k ≤ n^2.
提示 | 解題應用 |
---|---|
BinarySearch | BinarySearch |
|
|
最初也是乾脆全數取出並由小至大做排序,再取第k個位置的值做回傳,不過發現討論區有人用二元搜尋似乎也是不錯的選擇,這裡的二分法跟一般的做法不太一樣,原本是取兩個邊界”位置”正中間index上的值來做為中間值進行比較,而這次則是取兩個邊界的”值”來取平均值做為中間值,之所以這麼做就是因為整個二元陣列的資料並非完全照順序排,也就是說每列的最後一個值並不一定比下一列的第一個小,唯一可以確定的就是最左上角與最右下角的值必定為最小與最大值,因此有了平均數之後接下來就只要與整個二元陣列的資料進行比較,如果比平均數小或相等就將計數器+1,最後計數器的統計與k比較大小,如果比k小平均數+1的值就放置開頭邊界,而如果相等或比k大則平均數放置結尾邊界,不斷重覆上述動作直到兩邊界值相等,其相等值就會是我們要找的結果,跑完整個測資比先前的做法稍為快了10ms。
如思路所述,一開始便將二元陣列最左上角與最右下角的值分別放置開頭邊界與結尾邊界,接著取平均數與接下來整個二元陣列的資料進行比較(記得每次比較前都要將計數器歸0),如果比平均數小或相等就將計數器+1,比平均數大的話同列後頭的數字肯定也是如此,便跳開內層的迴圈繼續檢查下一列的數字,最後計數器的統計與k比較大小,如果比k小平均數+1的值就放置開頭邊界,而如果相等或比k大則平均數放置結尾邊界,不斷重覆上述動作直到兩邊界值相等,其相等值就會是我們要找的結果
|
|
|
|
有一nxn的二元陣列,每橫排右邊的值必定比左邊大,每直列下面的值必定比右邊大,要找出整個二元陣列中第k小的值,其做法是用二元搜尋,不過這裡的二分法是取兩個邊界的”值”來取平均值做為中間值,因為整個二元陣列的資料並非完全由左上小至右下大排序,唯一可以確定的就是最左上角與最右下角的值必定為最小與最大值,因此有了平均數之後接下來就只要與整個二元陣列的資料進行比較,如果比平均數小或相等就將計數器+1,最後計數器的統計與k比較大小,如果比k小平均數+1的值就放置開頭邊界,而如果相等或比k大則平均數放置結尾邊界,不斷重覆上述動作直到兩邊界值相等,其相等值就會是我們要找的結果。
]]>You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k.
Define a pair (u,v) which consists of one element from the first array and one element from the second array.
Find the k pairs (u1,v1),(u2,v2) …(uk,vk) with the smallest sums.
|
|
|
|
|
|
|
|
看到題目很自然的就用巢狀迴圈列出所有數對的組合,並自行定義排序規則將上述組合做排序(數對總合比大小),最後只要回傳前k個小的數對即可,不過題目給的數據已經有排序過了,所以相信可以再優化至更佳的結果,目前暫時先做保留。
這部分為自行定義的排序規則,最主要是以數對的總合來比大小,其中Len(),Swap(),Less()三個function前面兩個與一般排序規則並無差別,而複寫的便是Less()的比較,取出數對中的兩個值做加總才與另一對總合做比較
|
|
有了自訂的排序規則之後,再來就只要用巢狀迴圈列出所有數對的組合,將其做排序之後便能回傳前k個小的數對,不過如果是整個數對的組合數小於等於k就只要回傳整個排序的組合即可
|
|
|
|
給兩個已排序陣列要找出最小的k個總合數對(兩陣列各別取一個值作為數對),最直覺的做法是利用巢狀迴圈列出所有數對的組合,並自行定義排序規則將上述組合做排序(數對總合比大小),最後只要回傳前k個小的數對即可。
]]>Given a set of distinct positive integers, find the largest subset such that every pair (Si, Sj) of elements in this subset satisfies: Si % Sj = 0 or Sj % Si = 0.
If there are multiple solutions, return any subset is fine.
|
|
|
|
提示 | 解題應用 |
---|---|
Math | 規律觀查 |
|
|
這題最主要是要先知道一個觀念,數組之間的數字存在互相整除的狀況下,如果有一較大的值要判斷是否能放入數組之中,確定該值能整除目前數組中的最大值即可,反之如果有一較小的值要判斷,確定目前數組中的最小值能整除該值即可,所以只要先將整個數列做排序,接著遍歷已排序後的數列,每次將取出的數字作為中心,往左(更小的值)右(更大的值)兩側尋找可以放入數組的目標,並且記得放入時要更新目前的最大、最小值,如此一來在遍歷結束後就可以在數列裡頭找出最大且數字間存在能互相整除的數組。
如思路所述一開始便將整個數列做排序,每次將取出的數字作為中心(最大、最小的初始值皆為取出的數字),往左(更小的值)右(更大的值)兩側尋找可以放入數組的目標(存在互相整除的狀況),並且記得放入時要更新目前的最大、最小值,接著比較暫存的數組大小是否比要回傳的結果數組大,如果比較大就將其做取代,最後待遍歷結束後就可以直接回傳結果陣列
|
|
|
|
如果要在數列裡頭找出最大且數字間存在能互相整除的數組,最主要是要先知道一個觀念,如果有一較大的值要判斷是否能放入數組之中,確定該值能整除目前數組中的最大值即可,反之如果有一較小的值要判斷,確定目前數組中的最小值能整除該值即可,所以只要先將整個數列做排序,接著遍歷已排序後的數列,每次將取出的數字作為中心,往左(更小的值)右(更大的值)兩側尋找可以放入數組的目標,並且記得放入時要更新目前的最大、最小值,如此一來在遍歷結束後就能找出最大的數組。
]]>You are given two jugs with capacities x and y litres. There is an infinite amount of water supply available. You need to determine whether it is possible to measure exactly z litres using these two jugs.
If z liters of water is measurable, you must have z liters of water contained within one or both buckets by the end.
Operations allowed:
|
|
|
|
提示 | 解題應用 |
---|---|
Math | 規律觀查 |
|
|
這題也許以前在數學課時曾經碰過,與其說是寫程式考數學的成分佔大多數,也因此留下了一堆負評,總而言之結論就是要用兩個不同大小的容器x,y來確認是否能量出特定體積的液體z,可以整理成以下兩點:
剩下就是驗證x,y,z帶入之後是否能符合上述條件就能確認是否能量出特定體積的液體。
一開始先檢查兩個容器的總體積是否比要求的目標體積大,如果不是就不符合題目所給予的條件回傳false,而如果任一個容器的體積或是兩個容器的總體積剛好與目標體積相等則回傳true,最後如果上述條件都不符合,就只要檢查z是不是x,y最大公因數的倍數(相除餘數為0),便能得知是否量得出特定體積的液體
|
|
如果要用程式來實作求最大公因數,首先要先知道輾轉相除法的做法,基本上就是不斷的在兩數之間取餘數並做取代,直到其中一方歸0而另一方剩餘的值就會是最大公因數
|
|
|
|
要用兩個不同大小的容器x,y來確認是否能量出特定體積的液體z,可以整理成以下兩點:
剩下就是驗證x,y,z帶入之後是否能符合上述條件就能確認是否能量出特定體積的液體。
]]>Given a non-negative integer n, count all numbers with unique digits, x, where 0 ≤ x < 10^n.
|
|
提示 | 解題應用 |
---|---|
Math | 規律觀查 |
|
|
一開始的想法是先找出所有數字出現相同的情況再用總數減去就好,不過這樣一來反而會變的更複雜,其實就只要直接用排列組合的方式去找出各長度數字皆不同的組合就好,當k(當作出現的數字長度)為1時有10種組合,為2時有9*9種組合(開頭不得為0,結尾不得與開頭重覆故[10-1]*[10-1]),為3時則是9*9*8(結尾不得與前面重覆再減1),為4時是9*9*8*7其它以此類推,而當數字的位數超過10位數,到第十一位數時必定出現重覆只會有0種組合(因為0~9只有10個),最後只要將各長度數字不同的組合數量做加總就會是我們要的結果。
在理解了整個規律之後便能很容易的將程式碼完成,先篩選掉n大於10(0種組合)與等於0(1種組合: 1)及等於1(10種組合: 0~9)的情況,最後就是加總9*9+9*9*8+9*9*8*7+…至結果之中(n介於2~10之間),要注意到的是不要忘了加上n為1的情況(10種組合),所以一開始才將結果的初始值設為10
|
|
|
|
要找出0到10^n之間有多少個值不會出現重覆的數字,其實就只要直接用排列組合的方式去找出各長度數字皆不同的組合就好,當k(當作出現的數字長度)為1時有10種組合,為2時有9*9種組合(開頭不得為0,結尾不得與開頭重覆故[10-1]*[10-1]),為3時則是9*9*8(結尾不得與前面重覆再減1),為4時是9*9*8*7其它以此類推,而當數字的位數超過10位數,到第十一位數時必定出現重覆只會有0種組合(因為0~9只有10個),最後只要將各長度數字不同的組合數量做加總就會是我們要的結果。
]]>Design a simplified version of Twitter where users can post tweets, follow/unfollow another user and is able to see the 10 most recent tweets in the user’s news feed. Your design should support the following methods:
|
|
提示 | 解題應用 |
---|---|
HashTable | HashMap |
|
|
因為曾經在產品開發上實作過類似的東西,所以很快就有了想法,總而言之就是利用兩個hashmap來分別儲存各自的文章與追隨的對象,並記得文章在發送出去之前要包含時間戳記(這邊以發文編號替代),當需要取出個人的動態時報時,就從所有追隨的對象(包含自己)取出文章在依時間戳記做排序,最後只要取出最新的前十篇文章做回傳即可。
因為最後要在個人的動態時顯示最新的前十篇文章(包含有追隨對象的文章),因此需要將每則tweet依時間戳記(這邊以發文編號替代)做排序,這邊就自行定義了tweet的結構陣列所要排序的規則
|
|
接著Twitter的結構則是要包含兩個hashmap來分別儲存各自的文章與追隨的對象,與一個計數器以作為每篇文章發表的時間戳記,並在建構函數上初始化分配這三個變數的儲存空間
|
|
當需要發表文章的時候記得先將Twitter物件的計數器+1,並將記數器的值連同tweetId作為tweet一起儲存到自己id在hashmap所對應到的陣列之中
|
|
如思路所述當需要取出個人的動態時報時,就先從所有追隨的對象(包含自己)取出文章,這邊是先取出自己的文章,才將追隨對象的文章放在後頭再依時間戳記做排序,最後只要取出最新的前十篇文章的的TweetId放到結果陣列做回傳即可
|
|
如果想要追隨特定的對象,要先過濾掉自己追隨自己的情況,接著檢查自己已追隨的名單,確定此人是否已經有追隨,如果沒有追隨才將其加入自己的追隨清單之中
|
|
而如果不想要再追隨特定的對象,就從自己已追隨名單中找出對象的index位置,接著在重新組合追隨清單的陣列即可(跳過該元素)
|
|
|
|
要簡單實作twitter所包含的功能(發表文章、追隨陌生人並使其文章出現在自己的動態時報上),做法就是利用兩個hashmap來分別儲存各自的文章與追隨的對象,並記得文章在發送出去之前要包含時間戳記(這邊以發文編號替代),當需要取出個人的動態時報時,就從所有追隨的對象(包含自己)取出文章在依時間戳記做排序,最後只要取出最新的前十篇文章做回傳即可。
]]>Given a non-empty array of integers, return the k most frequent elements.
|
|
Note:
提示 | 解題應用 |
---|---|
HashTable | HashMap |
|
|
這題我的做法很單純,就是先利用hashmap來統計各個數字所出現的次數之後,再對hashmap統計的次數做排序,當然最糟的情況(也就是所有元素都沒有重覆)時間複雜度會是O(nlogn),除此之外都小於O(nlogn),也許依討論上較有效率的解法是用上heap會更好,但目前就暫時先做保留。
由於hashmap並沒有辦法直接做排序,因此這邊的做法是將hashmap的統計結果搬至自行定義的結構陣列之後再做排序
|
|
而實際做法正如思路所述,先利用hashmap來統計各個數字所出現的次數之後,再將hashmap的統計搬至自行定義的結構陣列做排序(由大排至小),最後只要從結構陣列開頭開始取出k個數字放至結果陣列之中便能向上做回傳
|
|
|
|
這題我的做法很單純,就是先利用hashmap來統計各個數字所出現的次數之後,再對hashmap統計的次數做排序,當然最糟的情況(也就是所有元素都沒有重覆)時間複雜度會是O(nlogn),除此之外都小於O(nlogn),也許依討論上較有效率的解法是用上heap會更好,但目前就暫時先做保留。
]]>Given a positive integer n, break it into the sum of at least two positive integers and maximize the product of those integers. Return the maximum product you can get.
|
|
Note:
You may assume that n is not less than 2 and not larger than 58.
提示 | 解題應用 |
---|---|
Math | 規律觀查 |
|
|
這題有關解法主要有兩個結論:
至於這個結論是怎麼來的可以參考該題討論有相關的舉例與證明,唯一要注意上述第二點的情況是n需要大於4,因為4是例外而且最大值的因數也不會包含1: 2 2 > 1 3
一開始先初始化暫存值為1以用來儲存乘積的最大值,接著判斷如果n為2就回傳1(1+1),n為3就回傳2(1+2),而如果n大於4則利用一迴圈找出該數最多能有多少個3來做為乘積的因數,並同時記得每次都要將n減去3直到n小於等於4為止,最後乘積的暫存值與剩餘的n相乘就會是我們要的結果
|
|
|
|
某數由數個數字加總而來,要找出這些數字乘積的最大值,其解法最主要有兩個結論:
Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1’s in their binary representation and return them as an array.
For num = 5 you should return [0,1,1,2,1,2].
Follow up:
提示 | 解題應用 |
---|---|
BitManipulation | AND |
|
|
要計算某數的二進制有多少個1出現,有一個小技巧是不斷的將”該數”與”該數-1”做AND直到0為止共做了多少次就會是有多少個1,而這次要求的是0~某數的二進制分別有多少個1出現,當然就是利用一迴圈取出0~某數來分別做計算,如下:
|
|
而這樣的時間複雜度會是O(n*[Number of 1’s]),不過題目希望我們能在O(n)之內,既然有一路記下先前數字的二進制有多少個1,那麼當然就要好好重覆使用,每次求某數i的二進制有多少個1時,陣列中取出index為i&(i-1)的值再+1就會是我們要的結果,如此一來便能在規定的時間內找出0~某數的所有個數。
一開始先初始化一個n+1的陣列用來儲存0~n的結果,接著就是利用迴圈分別取出1~n(0的個數為0不需計算),每次求某數i的二進制有多少個1時,陣列中取出index為i&(i-1)的值再+1就會是我們要的結果,最後個數全數找出後便能向上回傳整個陣列
|
|
|
|
要求出0~n的二進制分別有多少個1出現,則要先計算某數的二進制有多少個1出現,有一個小技巧是不斷的將”該數”與”該數-1”做AND直到0為止共做了多少次就會是有多少個1,至於0~n當然就是利用一迴圈取出來分別做計算,而這樣的時間複雜度會是O(n*[Number of 1’s]),如果要在O(n)之內,既然有一路記下先前數字的二進制有多少個1,那麼當然就要好好重覆使用,每次求某數i的二進制有多少個1時,陣列中取出index為i&(i-1)的值再+1就會是我們要的結果,如此一來便能在規定的時間內找出0~某數的所有個數。
]]>The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the “root.” Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that “all houses in this place forms a binary tree”. It will automatically contact the police if two directly-linked houses were broken into on the same night.
Determine the maximum amount of money the thief can rob tonight without alerting the police.
|
|
Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.
|
|
Maximum amount of money the thief can rob = 4 + 5 = 9.
提示 | 解題應用 |
---|---|
DepthFirstSearch | 樹的遍歷 |
|
|
剛看到題目時很容易因為範例而被誤導使用廣度優先遍歷,求出每列的總合再做組合找最大值,然而仔細看題目的話只要不是連偷兩間屋子,也就是節點跟子節點連續就不會有問題,所以節點的左子樹與右子樹間是互相獨立的,因此在做樹的遞回遍歷時,只要比較”該節點+左子節點的子樹(左右子樹)遞回結果+右子節點的子樹(左右子樹)遞回結果”與”左子樹的遞回結果+右子樹的遞回結果”兩個值誰大再向上回傳即可,如下:
|
|
不難發現到在遞回節點子樹的最大結果時,與另一個遞回節點子樹的子樹似乎有重覆遞回的情況,這將導致花費時間大幅度的上升,因此針對這種情況只要把遞回修改成每次都回傳兩個值,包含該節點的最大結果與不包含該節點的最大結果,剩下的工作交由上一層去組合並比較大小,就能只經由遞回遍歷各節點一次找出最終的最大竊取金額。
一開始就將根節點帶入遞回函數之中,接著只要比較回傳的”包含根節點的最大結果”與”不包含根節點的最大結果”誰比較大,其值就會是最終能竊取的最大金額
|
|
至於遞回函數的細節,如果帶入的節點為空就回傳兩個0(因為遞回每次都會回傳兩個值),否則就分別將左右子節點再次帶入遞回函數之中,最後向上回傳包含該節點的最大結果(該節點+左子節點的子樹遞回結果[不包含左子節點]+右子節點的子樹遞回結果[不包含右子節點])與不包含該節點的最大結果(左子樹的遞回結果[從包含/不包含左子節點中取較大的值]+右子樹的遞回結果[從包含/不包含右子節點中取較大的值])即可
|
|
這部分的函數就只是單純比較哪個值最大,並向上回傳比較大的那一個值
|
|
|
|
二元樹其每個節點分別代表一間屋子,而節點值則是屋子所擁有的現金,要竊取最大金額而不觸發警報(不連偷兩間屋子,也就是節點跟子節點連續),做法是在進行樹的遞回遍歷時,只要比較”該節點+左子節點的子樹(左右子樹)遞回結果+右子節點的子樹(左右子樹)遞回結果”與”左子樹的遞回結果+右子樹的遞回結果”兩個值誰大再向上回傳即可,然而在遞回節點子樹的最大結果時,與另一個遞回節點子樹的子樹似乎有重覆遞回的情況,這將導致花費時間大幅度的上升,因此針對這種情況只要把遞回修改成每次都回傳兩個值,包含該節點的最大結果與不包含該節點的最大結果,剩下的工作交由上一層去組合並比較大小,就能只經由遞回遍歷各節點一次找出最終的最大竊取金額。
]]>Given an unsorted array return whether an increasing subsequence of length 3 exists or not in the array.
Formally the function should:
Return true if there exists i, j, k
such that arr[i] < arr[j] < arr[k] given 0 ≤ i < j < k ≤ n-1 else return false.
Your algorithm should run in O(n) time complexity and O(1) space complexity.
|
|
|
|
最初看到題目是想到Longest Increasing Subsequence的做法,然而這次只要確認是否存在至少有三個元素的遞增子數列,所以肯定是比先前簡單多了,只要一邊遍歷一邊找出最小且不重覆的兩個值,當再取出的值比目前所找到的兩個值都來的大,就可以直接回傳true(此三個元素能組出遞增子數列),否則到最後遍歷結束若還是沒有找到較大的值才回傳false。
因為要找出最小且不重覆的兩個值,所以一開始便初始化兩個極大值以找出兩個最小值,接著一邊遍歷一邊判斷,當取出的值比最小值還小或相等則將其取代,取出的值比第二小的值還小或相等同樣也是做取代,而當再取出的值比目前所找到的兩個值都來的大,就可以直接回傳true(此三個元素能組出遞增子數列),否則到最後遍歷結束若還是沒有找到較大的值才回傳false
|
|
|
|
要確認是否存在至少有三個元素的遞增子數列(詳細規則請看題目),只要一邊遍歷一邊找出最小且不重覆的兩個值,當再取出的值比目前所找到的兩個值都來的大,就可以直接回傳true(此三個元素能組出遞增子數列),否則到最後遍歷結束若還是沒有找到較大的值才回傳false。
]]>Given a list of airline tickets represented by pairs of departure and arrival airports [from, to], reconstruct the itinerary in order. All of the tickets belong to a man who departs from JFK. Thus, the itinerary must begin with JFK.
Note:
|
|
|
|
Another possible reconstruction is [“JFK”,”SFO”,”ATL”,”JFK”,”ATL”,”SFO”]. But it is larger in lexical order.
提示 | 解題應用 |
---|---|
Graph | 相鄰表製作 |
|
|
題目有提到要依最小詞彙順序的解答為主,因此倒不如在一開始就先將資料做排序,然而資料是一個二元陣列(每份資料由起點與終點的機場縮寫組成),這邊就只好自行定義一個排序的規則,先比較起點資料間的字串,而如果起點相同再比較終點的字串,排序之後就可以得到一個由小排至大的機票,接下來就可以開始來處理搭乘順序的問題了,不過如果就這樣依序從”JFK”開始遵從先前排序的機票來搭乘(碰到有多個終點時選擇詞彙最小的機票),還是會碰上如下的情況:
|
|
也就是中間略過數個班機而沒使用上全部的機票,所以最好的辦法是從終點找回起點,不過可惜的是我們沒有辦法預先知道最終的目地是哪一站,因此從JFK開始如果發現有以該點做為起點的班機(有終點站),就先將其放入stack之中,接著取出對應的終點(從最小詞彙順序開始取)當作下一個起點,而如果該點作為起點時並沒有終點(也就表示已到達最終的目地),此時便將該點放入結果陣列(要從結尾開始往前放,因為是從最終目地開始往回找起)並從stack取出下一個起點,不斷重覆上述動作直到stack為空為止就可以找出整個搭乘順序的過程了。
因為需要自行定義一個排序的規則來排序機票,一開始便定義Len(),Swap(),Less()三個function,前面兩個function與一般排序規則並無差別,而主要複寫的便是Less()的比較,注意資料是一個二元陣列(每份資料由起點與終點的機場縮寫組成),先比較起點資料間的字串,而如果起點相同再比較終點的字串
|
|
再來是定義stack的結構,這邊是以LinkedList來實作包含字串(儲存機場縮寫)與下一個stack節點的位置
|
|
接著就可以來找出整個搭乘順序的過程,先初始化用來儲存結果的陣列與放入第一個節點至stack(從JFK開始),再將資料的起點與終點整理成相鄰表、關係表之前把機票代入先前自訂的規則來做排序,排序後才一一取出機票的起點與終點整理成相鄰表,這邊是以hashmap來儲存,其中key值為起點而value則是一陣列包含所能抵達的終點(依最小詞彙排序)
|
|
從stack取出起點JFK開始如果發現該班機有終點站,就先將對應的終點(從最小詞彙順序開始取)放入stack之中(記得將對應的終點從相鄰表移除),而如果該點作為起點時並沒有終點(對應的終點陣列長度為0,表示已到達最終的目地),此時便將該點放入結果陣列(要從結尾開始往前放,因為是從最終目地開始往回找起)並從stack取出下一個起點,不斷重覆上述動作直到stack為空為止就可以找出整個搭乘順序的過程了
|
|
|
|
有數張從A往B地的機票,由特定的起點開始要找出整個搭乘順序的過程(使用全部的機票),如果有多個解答的情況則以最小詞彙順序的解答為主,因此一開始就先將資料做排序(二元陣列:每份資料由起點與終點的機場縮寫組成),這邊自行定義一個排序的規則,先比較起點資料間的字串,而如果起點相同再比較終點的字串,排序之後就可以得到一個由小排至大的機票,接下來就可以開始來處理搭乘順序的問題,為了避免中間略過數個班機而沒使用上全部的機票,所以最好的辦法是從終點找回起點,不過可惜的是無法預先知道最終的目地是哪一站,因此從特定的起點開始如果發現有以該點做為起點的班機(有終點站),就先將其放入stack之中,接著取出對應的終點(從最小詞彙順序開始取)當作下一個起點,而如果該點作為起點時並沒有終點(也就表示已到達最終的目地),此時便將該點放入結果陣列(要從結尾開始往前放,因為是從最終目地開始往回找起)並從stack取出下一個起點,不斷重覆上述動作直到stack為空為止就可以找出整個搭乘順序的過程了。
]]>One way to serialize a binary tree is to use pre-order traversal. When we encounter a non-null node, we record the node’s value. If it is a null node, we record using a sentinel value such as #.
|
|
For example, the above binary tree can be serialized to the string “9,3,4,#,#,1,#,#,2,#,6,#,#”, where # represents a null node.
Given a string of comma separated values, verify whether it is a correct preorder traversal serialization of a binary tree. Find an algorithm without reconstructing the tree.
Each comma separated value in the string must be either an integer or a character ‘#’ representing null pointer.
You may assume that the input format is always valid, for example it could never contain two consecutive commas such as “1,,3”.
|
|
|
|
|
|
提示 | 解題應用 |
---|---|
Stack | LinkedList |
|
|
有了前序遍歷的紀錄照理說應該也需要有中序遍歷的紀錄才能構建出一棵樹,不過這次的前序遍歷的紀錄有包含空節點,因此單前序遍歷便能構建回一棵樹,然而題目希望我們能不以重建樹的方式來確認此樹是否為合乎規定的二元樹(也就是空節點不應該存在有子節點),一開始先遍歷前序遍歷的紀錄,每次都將取出的節點值放入stack之中,當取出的是空節點而且在stack頂端的也是空節點(表示左右子節點皆為空節點),此時便從stack中取出兩個節點(空節點與空節點的父節點)再放入遍歷到的空節點以取代整個子樹,如下(以”9,3,4,#,#,1,#,#,2,#,6,#,#”為例):
出現兩個空節點就以一個空節點取代整個子樹(4,#,#)
|
|
接下來因為沒有再連續出現兩個空節點就繼續遍歷前序的紀錄,並重覆上述動作連續取代了兩組子樹
|
|
最後若是為一棵合法的二元樹,就會只剩下一個空節點在stack之中
|
|
一開始先定義好stack的結構,這邊是以LinkedList來實作
|
|
接著就來確認前序遍歷的紀錄,因為整份紀錄是字串的關係,所以要先以逗號為區隔做切割,再來便能利用迴圈一一取出每個節點值,當取出的是空節點而且在stack頂端的也是空節點(表示左右子節點皆為空節點),此時便從stack中取出兩個節點(空節點與空節點的父節點)再放入遍歷到的空節點以取代整個子樹,如果取出時stack中不包含父節點便直接回傳false,並不斷重覆上述動作直到最後若只剩下一個空節點在stack之中,其便為一棵合法的二元樹
|
|
|
|
要透過前序遍歷的紀錄(有包含空節點),以不重建樹的方式來確認此樹是否為合乎規定的二元樹(也就是空節點不應該存在有子節點),其做法就是先遍歷前序遍歷的紀錄,每次都將取出的節點值放入stack之中,當取出的是空節點而且在stack頂端的也是空節點(表示左右子節點皆為空節點),此時便從stack中取出兩個節點(空節點與空節點的父節點)再放入遍歷到的空節點以取代整個子樹,並不斷重覆上述動作直到最後若只剩下一個空節點在stack之中,其便為一棵合法的二元樹。
]]>Given a singly linked list, group all odd nodes together followed by the even nodes. Please note here we are talking about the node number and not the value in the nodes.
You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity.
|
|
Note:
The relative order inside both the even and odd groups should remain as it was in the input.
The first node is considered odd, the second node even and so on …
提示 | 解題應用 |
---|---|
LinkedList | Pointer |
|
|
非常單純的一題,只要以兩個指標分別儲存奇數與偶數的LinkedList,在遍歷原LinkedList的同時一邊將節點作分配,最後待遍歷結束後將偶數的LinkedList接上奇數的LinkedList後頭即可。
一開始先初始化用來分別儲存奇數與偶數LinkedList的頭節點,再以兩個指標指向兩串LinkedList的結尾以利後續新增節點,另外也需要一個計數器來分辨節點是奇數還是偶數,接下來就可以用迴圈來將原LinkedList節點一一取出,同時一邊將節點作分配(計數器為奇數的節點接到儲存奇數LinkedList的後頭,反之偶數也是如此),待遍歷結束後將偶數的LinkedList接上奇數的LinkedList後頭,並記得將偶數LinkedList結尾的下一個節點改為nil以防產生環(cycle),最後回傳整串LinkedList的開頭,也就是奇數頭節點的下一個節點(原LinkedList的第一個節點)
|
|
|
|
要將原LinkedList順序改為奇數節點在前面,而偶數節點接在後頭,只要以兩個指標分別儲存奇數與偶數的LinkedList,在遍歷原LinkedList的同時一邊將節點作分配,最後待遍歷結束後將偶數的LinkedList接上奇數的LinkedList後頭即可。
]]>You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
|
|
|
|
Note:
You may assume that you have an infinite number of each kind of coin.
提示 | 解題應用 |
---|---|
DynamicProgramming | Recursive |
|
|
非常典型的Dynamic Programming題目,一開始只用遞回的方式來做排列組合試著找出符合條件的結果,不出所料後面越長的測資組合越容易導致超時,所以還需要在利用空間換取時間,初始化陣列來紀錄一路上出現值的最小組合數,並由後續加以重覆使用以節省時間,直到全數組合均遍歷完畢才向上回傳最小組合數。
一開始便將參數帶入遞回函數之中,其中第三個參數陣列用來紀錄一路上出現值的最小組合數,index代表出現的值,而value則是表示其值的組合數,當然也可以使用hashmap來儲存,不過考量到測資組合有1出現,還是使用一連續空間會比較佳(當然浪費的空間也相當多)
|
|
接下來就是處理遞回的細節,需要的金額為0的話便回傳0,如果金額為負數便回傳-1(表示無法組合出來),而如果該金額剛好先前曾找出最小組合數(不為0)便直接回傳,上述都不符合的話就用迴圈取出所有硬幣面額做組合並再次向下遞回,同時從每次的回傳中找出最小值(初始值為int的32位元極大值,且最小組合數不得為-1),如果最後最小值不為極大值,便紀錄該出現值的最小組合數,否則將該值的組合數紀錄為-1
|
|
|
|
給予數個硬幣的面額值,利用最少的硬幣數組出符合目標條件的金額,一開始只用遞回的方式來做排列組合,並利用空間換取時間初始化陣列來紀錄一路上出現值的最小組合數,並由後續加以重覆使用以節省時間,直到全數組合均遍歷完畢才向上回傳最小組合數。
]]>There are n bulbs that are initially off. You first turn on all the bulbs. Then, you turn off every second bulb. On the third round, you toggle every third bulb (turning on if it’s off or turning off if it’s on). For the ith round, you toggle every i bulb. For the nth round, you only toggle the last bulb. Find how many bulbs are on after n rounds.
|
|
提示 | 解題應用 |
---|---|
Math | 規律觀查 |
|
|
一開始是直接照規則用暴力法的方式一輪一輪修正,最後再統計開關打開的數量,而當然不出所以然超過時間,透過規律觀查我們發現到最後開關打開的情況都剛好是燈泡落在平方數編號的位置,因式分解之後就不難發現原因,如編號36來說:
|
|
如果燈泡的開關是打開的表示一共使用了奇數次才有可能,而每個因數正好代表每次使用其開關的時機,而唯有平方數才會讓因數個數為奇數個(重覆的數字只算一次),也就是說1~n個燈泡最終有多少個開關是打開的取決於這個範圍之間有多少個平方數,所以將n做開根號就可以得知有多少個平方數,同時也是我們要找的結果。
這邊就只是單純使用內建數學函式庫的開根號函數來將n做開根號
|
|
|
|
有1~n個燈泡(並且都賦予1~n的編號),一開始開關皆為關閉的狀態,並遵循下列規則:
|
|
透過規律觀查我們發現到最後開關打開的情況都剛好是燈泡落在平方數編號的位置(因為因數個數為奇數個,等同於使用了開關奇數次),也就是說1~n個燈泡最終有多少個開關是打開的取決於這個範圍之間有多少個平方數,所以將n做開根號就可以得知有多少個平方數,同時也是我們要找的結果。
]]>Given a string array words, find the maximum value of length(word[i]) * length(word[j]) where the two words do not share common letters. You may assume that each word will contain only lower case letters. If no such two words exist, return 0.
|
|
|
|
|
|
提示 | 解題應用 |
---|---|
BitManipulation | 左移運算子 |
|
|
這題最主要是需要能夠快速的比較兩個字串的字母組合是否出現重覆,然而不管是用hashmap或者是陣列等方式將字母分別做儲存,都免不然要逐一比較而導致超時,但如果使用二進位的方式來儲存字母的話不但可以大幅減少使用的空間,兩個字串在判斷是否出現字母重覆更只要O(1)的時間複雜度,其做法是利用int所擁有的32位元分別代表32個位置,而a~z只會用上26個位置正好足以使用,如果該字母存在就在對應的位元位置上註記1(利用<<左移運算子將1移至目標位置),如果要判斷兩字串是否出現字母重覆就只要將對應的兩個int做&AND,結果為0的話表示兩字串的字母皆未重覆,最後就是字串間不斷兩兩比較判斷找出相乘的最大長度為止。
一開始便初始化一個與字串陣列相同長度的數字陣列,其為利用二進位的方式來儲存字母的位置,因此接著便利用巢狀迴圈遍歷每個字串及字母,每次取出字母便減去97(ASCII值轉為1~26的值),而該值便決定1要向左位移多少次才能移至位元的目標位置上(字母的對應位置),接著再與先前的值做|OR以保留之前的字母位置
|
|
有了所有字串的字母位置二進位紀錄後,最後就是字串間不斷兩兩比較判斷,如果取出同一個字串或是兩個二進位紀錄做&AND結果不為0(表示兩字串的字母出現重覆),又或者字串長度相乘不比最大值大則通通跳過,直到出現完全符合條件的結果才將相乘的值放入最大值之中,最後待全數組合遍歷完畢才向上回傳最大值
|
|
|
|
給一字串陣列要找出兩個字串不包含彼此之間的字母,且兩字串長度相乘要為最大值,最主要是需要能夠快速的比較兩個字串的字母組合是否出現重覆,其做法是利用int所擁有的32位元分別代表32個位置,而a~z只會用上26個位置正好足以使用,如果該字母存在就在對應的位元位置上註記1(利用<<左移運算子將1移至目標位置),如果要判斷兩字串是否出現字母重覆就只要將對應的兩個int做&AND,結果為0的話表示兩字串的字母皆未重覆,最後就是字串間不斷兩兩比較判斷找出相乘的最大長度為止。
]]>Write a program to find the nth super ugly number.
Super ugly numbers are positive numbers whose all prime factors are in the given prime list primes of size k. For example, [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] is the sequence of the first 12 super ugly numbers given primes = [2, 7, 13, 19] of size 4.
Note:
|
|
建議可以先參考先前Ugly Number II的解法,解說較為詳細,基本上概念完全一樣,只是將先前題的程式碼稍做修改而已,如果能找出第n個Ugly Number(由2,3,5組成),那麼一樣要找出第n個Ugly Number(由給予的數所組成)就不會太困難。
要找出第n個由小至大排序的Ugly Number(該數完全由給予的數所組成),每次決定下一個值的方式就是由Ugly Number的組成數乘上目前各別所遞增到順序,並比較誰最小來作為下一個值,此時被當作下一個值所對應到的組合,其順序就繼續向下遞增,而如果剛好有其它組合值也相同便也一起將順序向下遞增。
其實應該不需要多做什麼解釋,與先前相比Ugly Number從原本由2,3,5組成變成題目指定,因此就都只是將綁定的三個變數處理改為由指定組合所對應到的相同長度陣列來儲存並用迴圈處理而已
|
|
這部分的函數就只是單純比較陣列中哪個元素最小,並向上回傳最小的那一個元素
|
|
|
|
建議可以先參考先前Ugly Number II的解法,解說較為詳細,基本上概念完全一樣,要找出第n個由小至大排序的Ugly Number(該數完全由給予的數所組成),藉由觀查找出規律會發現由Ugly Number的組成數各別遞增所乘上的值正好是整個Ugly Number由小至大的順序,每次決定下一個值的方式就是由Ugly Number的組成數乘上目前各別所遞增到順序,並比較誰最小來作為下一個值,此時被當作下一個值所對應到的組合,其順序就繼續向下遞增,而如果剛好有其它組合值也相同,便也一起將順序向下遞增,知道了上述的規則之後就可以很容易的找出第n個Ugly Number。
]]>For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels.
Format
The graph contains n nodes which are labeled from 0 to n - 1. You will be given the number n and a list of undirected edges (each edge is a pair of labels).
You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.
Given n = 4, edges = [[1, 0], [1, 2], [1, 3]]
|
|
return [1]
Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]
|
|
return [3, 4]
Note:
(1) According to the definition of tree on Wikipedia): “a tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.”
(2) The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.
提示 | 解題應用 |
---|---|
Graph | 相鄰表製作 |
|
|
給一無向圖(其實就是樹的形狀,因此不包含相鄰成圈的環)是由n個節點與n-1條邊所組成,找出從哪些點出發到其它節點的最長距離最小,這題的概念與Course Schedule非常相近,都是要先製做相鄰表或關係表再逐步篩選出結果,如果是算出所有節點彼此的距離來找出結果,會花費超過規定內的時間,透過觀查發現出發到其它節點的最長距離最小的那些節點,通常落在整個圖最中間的位置(也就是非葉子節點:相鄰節點數大於1),而且最中間的節點數最多不超過2個,因此藉由整理出的關係表找出葉子節點再從中移除,最後不斷重覆上述動作直到關係表剩餘的節點數小於等於2就會是我們要的結果。
一開始先判斷圖所擁有的邊是否為空,如果是便回傳包含一個0的數字陣列,否則初始化hashmap來作為節點間的關係表(這題以hashmap來存取較為方便),其中key為節點值而value則是一陣列包含與此節點有關聯的其它節點值,接著再將邊取出並將兩端節點存於關係表中(因為邊是無方向性,所以hashmap上要分別做儲存),而在不斷篩選掉葉子節點以找出結果之前,要從先前整理好的關係表之中找出葉子節點(相鄰節點數等於1)才能進入下一個步驟
|
|
藉由整理關係表找出葉子節點再從中移除,不斷重覆上述動作直到關係表剩餘的節點數小於等於2為止,其中每次都記得要將總節點數減去葉子節點數,以此來得知剩餘節點數,至於將葉子節點從關係表移除,由於葉子節點只會有一個關聯的節點,因此在hashmap取值時只要取陣列的第一個值也就是index為0的位置,接著反過來在該關聯的節點移除對應到陣列的葉子節點值,這邊是再用一迴圈逐一遍歷直到找出葉子節點的index位置再移除(重新組合陣列以跳過葉子節點值),而如果該關聯的節點對應到的陣列在移除葉子節點後長度變為1,此時該節點就變為下一組新的葉子節點之一,最後如果剩餘的節點數小於等於2,便向上回傳剩餘的(葉子)節點值
|
|
|
|
給一無向圖(其實就是樹的形狀,因此不包含相鄰成圈的環)是由n個節點與n-1條邊所組成,找出從哪些點出發到其它節點的最長距離最小,先製做相鄰表或關係表再逐步篩選出結果,如果是算出所有節點彼此的距離來找出結果,會花費超過規定內的時間,透過觀查發現出發到其它節點的最長距離最小的那些節點,通常落在整個圖最中間的位置(也就是非葉子節點:相鄰節點數大於1),而且最中間的節點數最多不超過2個,因此藉由整理出的關係表找出葉子節點再從中移除,最後不斷重覆上述動作直到關係表剩餘的節點數小於等於2就會是我們要的結果。
]]>