Lintcode題目總結
方法技巧題:
Complete Binary Tree: http://www.lintcode.com/en/problem/complete-binary-tree/
用一個queue進行層序遍歷,如果q.front()為NULL,則pop並break,否則將其左右兒子加入queue。然後檢查queue中剩餘的元素,如果全是NULL則返回true,否則返回false
Stack Sorting: http://www.lintcode.com/en/problem/stack-sorting/
用另一個stack名為s。在!stk.empty()時,如果s為空或stk.top() <= s.top()則將stk中的元素pop出並加入s;否則令value = stk.top(),令stk進行pop但並不加入s,然後在!s.empty() && s.top() <= value時將s中的元素pop並加入stk,然後將value加入stk,再將s中剩餘的元素pop並加入stk。最後再將s中的元素pop並加入stk即可
Reverse Pairs: http://www.lintcode.com/en/problem/reverse-pairs/
用divide & conquer + merge sort,將陣列分成兩部分分別排序,然後再merge,令用 i 遍歷前半部分,j 遍歷後半部分,如果nums[i] > nums[j],則nums[j]一定小於前半部分剩下的所有元素(包括 i),所以此時result += mid - i + 1
Swap Two Nodes in Linked List: http://www.lintcode.com/en/problem/swap-two-nodes-in-linked-list/
要注意特殊判斷一下兩個節點相鄰的情況。只要保證這種情況下pre1在pre2前面即可
LFU Cache: http://www.lintcode.com/en/problem/lfu-cache/
用unordered_map + map + doubly linked list:
1)unordered_map<int, ListNode*> hash:儲存key和對應雙向連結串列節點的對應關係
2)map<int, cacheNode*> freq:int型變數是一個cache被訪問的次數,cacheNode是一個新的struct,裡面只有兩個變數:ListNode *head, *newest,分別儲存某個固定訪問次數的所有cache,head->next是最舊的。注意這裡的第二個元素必須用指標
具體的演算法是:
set:首先判斷如果要加入新的key是否會超出capacity,如果超出則將freq中第一個元素最老的cache刪除;然後檢查要set的key是否存在,如果已存在則將其從原來freq對應的雙向連結串列中移出,但並不刪除這個節點,只是將其frequency++,如果不存在則建立新的節點;最後將更改/新建立的節點加入到freq中正確的出現次數對應的位置。需要注意兩點:a) 在從雙向連結串列中移出節點時,要判斷cur->next是不是空,不為空才更新cur->next->prev;b) 如果當雙向連結串列移出某個節點後head = newest,則說明當前出現次數下沒有對應的cache了,此時應該將對應的cacheNode*進行delete並從freq中erase
get:如果hash中不存在key則返回-1,否則將對應節點從雙向連結串列中移出並將其出現次數+1再插入到合適的位置
注意在定義ListNode時要包括key,val和frequency,其中包括key是為了在從雙向連結串列中刪除某個元素時能夠從hash中也進行erase,frequency是為了保證將某個節點從雙向連結串列中移出時能對出現次數+1以找到它新的合適的位置。另外注意在destructor中對動態分配的變數進行釋放。對於所有的操作,由於不需要進行查詢,所以時間複雜度都是O(1)
Dynamic Programming:
Coins In a Line II: http://www.lintcode.com/en/problem/coins-in-a-line-ii/
用dp[i]表示從 i 到最後一個硬幣,某個玩家所能獲得的最大和。則對於dp[i]:
1)如果當前玩家取一個,則另一個玩家可能取一個或兩個:val1 = values[i] + min(dp[i + 2], dp[i + 3])
2)如果當前玩家取兩個,則另一個玩家可能取一個或兩個:val2 = values[i] + values[i + 1] + min(dp[i + 3], dp[i + 4])
dp[i] = max(val1, val2)
最後再返回dp[0] > total - dp[0]即可。total和dp[i]的計算可以在一個for迴圈中完成
Backpack: http://www.lintcode.com/en/problem/backpack/
宣告一個二維vector<vector<bool>> (size + 1, vector<bool>(bag + 1, false))名為can,can[i + 1][j]表示用了前 i 個元素後能否獲得值 j,則can[i + 1][j] = can[i][j] || (j >= items[i] && can[i][j - items[i]]),最後再返回 j 從大到小第一個can[size][j]為true時的 j 即可。注意初始化can[i][0] = true。另外空間複雜度可以優化為O(bag),只要宣告兩個一維的vector即可,初始化時令[0] = true即可
Copy Books: http://www.lintcode.com/en/problem/copy-books/
O(k *n^2):最多隻需要書的本數的人,所以k = min(size, k)。用dp[i][j]表示前 i 本書分給 j 個人時的最小值,計算從第 i 本([i - 1])到前面第一本,如果都給第 j 個人所能獲得的最小值,這裡面最小的那個就是結果,即dp[i][j] = min(dp[i][j], max(sum, dp[l][j - 1])),其中sum是[l] ~ [i - 1]的和。注意初始化:1)當 i = 0即沒有書時,dp[0][j] = 0;2)當只有一個人時,dp[i][1]應該等於前 i 本書的和。初始化完成後,令 j = 2 ~ k即可
O(k * n):仍然用dp[i][j]表示前 i 本書分給前 j 個人時的結果,初始化dp[0][i]和dp[i][1],令 i = 2 ~ k,對於每個 i 計算dp[right][i]的值,令left = 0,right = 1,在right <= size時,令sum = dp[right][1] - dp[left][1]表示從pages[left + 1]到pages[right]的和,則dp[right][i] = min(dp[right][i], max(sum, dp[left][i - 1]))。因為隨著left的增加,dp[left][i - 1]會增加而sum會減少,所以如果left < right && sum > dp[left][i - 1],則令left++;否則令right++,此外因為當前的left是invalid的,而之前的left是valid,所以應該令left--。最後返回dp[size][k]即可