Google面試題專題2 - leetcode48. Rotate Image/31. Next Permutation/23. Merge k Sorted Lists
leetcode48. Rotate Image
題目描述
給定一個n*n的2維矩陣,順時針旋轉矩陣90度。(原地旋轉)
原地旋轉:直接修正矩陣,不要建立額外的2維矩陣。
例子
Example 1:
Given input matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
rotate the input matrix in-place such that it becomes:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
Example 2:
Given input matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],
rotate the input matrix in-place such that it becomes:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
思想
拆分旋轉90°的操作。關於軸、對角線的操作容易實現。
首先以輔對角線為軸進行翻轉;然後以中心豎線為軸進行翻轉。
解法
複雜度:時間O(n^2),空間O(1)
class Solution(object):
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: void Do not return anything, modify matrix in-place instead.
"""
n = len( matrix)
# 以輔對角線為軸翻轉
for i in range(n):
for j in range(i+1, n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
# 以中心豎線為軸翻轉
for i in range(n):
for j in range(n//2):
matrix[i][j], matrix[i][-j-1] = matrix[ i][-j-1], matrix[i][j]
leetcode31. Next Permutation
題目描述
實現下一個序列:重組數字得到比當前值大的下一個字典序。
如果該重組序列不存在,則重組至字典序最小的序列。
要求:原地調整,使用常數空間。
例子
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
思想
下一個序列,肯定是要把大數移到小數前面。
1)從後往前找第一個逆序的數對(a, b),a<b。則b為要移動的大數,記boundary為a;
2)在boundary後面的數中,找到大於boundary的最小值,移到a前面;
3)boundary後的元素升序排列。
解法
複雜度:時間O(n),空間O(1)
class Solution(object):
def nextPermutation(self, nums):
"""
:type nums: List[int]
:rtype: void Do not return anything, modify nums in-place instead.
"""
bound = -1
for i in range(len(nums)-1, 0, -1):
if nums[i] > nums[i-1]:
bound = i-1
break
if bound == -1:
nums.reverse() # nums[:] = nums[::-1]
else:
num = nums[bound]
for i in range(len(nums)-1, bound, -1):
if nums[i] > num:
nums[i], nums[bound] = nums[bound], nums[i]
nums[bound+1:] = nums[bound+1:][::-1]
return
leetcode23. Merge k Sorted Lists
題目描述
合併k個有序連結串列,返回成一個有序連結串列。
例子
Input:
[
1->4->5,
1->3->4,
2->6
]
Output: 1->1->2->3->4->4->5->6
思想
假設每個連結串列的平均長度是n。
(法1 - 遞迴)
合併2個有序連結串列時,可以兩兩比較,時間複雜度O(m+n)。
合併k個連結串列時,類似歸併排序。
(法2 - 非遞迴)
合併k個有序連結串列,步驟肯定如下:
1)比較k個頭指標,找到最小的一個,作為最終連結串列的頭指標;
2)比較k-1個頭指標和候選連結串列的第二個指標,找到最小的一個,作為連結串列的第二個元素
…
【方法】
k個元素找出最小的一個,考慮堆排序,時間複雜度O(klogk)
然後在k-1個元素中插入一個元素,調整堆,時間複雜度O(logk)
…
(python最小堆heap)
import heapq、heapq.heappush(heap, num) #插入元素、heapq.heappop(heap) # 彈出最小元素
解法1
遞迴,類似歸併排序。
第一次兩兩合併進行了k/2次,每次處理2n個值;
第二次兩兩合併進行了k/4次,每次處理4n個值;
…
最後一次兩兩合併進行了k/(2^logk)次,每次處理2^logk*n個值。
所以總時間複雜度:
O((2n) * (k / 2) + (4n) * (k / 4) + (8n) * (k / 8) + … + (2^logk*n) * (k / (2 ^logk)) )=O(nklogk)
空間複雜度O(1)。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def mergeKLists(self, lists):
"""
:type lists: List[ListNode]
:rtype: ListNode
"""
if not lists:
return None
if len(lists) == 1:
return lists[0]
mid = len(lists) // 2
left = self.mergeKLists(lists[:mid])
right = self.mergeKLists(lists[mid:])
return self.merge2Lists(left, right)
def merge2Lists(self, head1, head2):
p = ListNode(-1)
pNode = p
while head1 and head2:
if head1.val < head2.val:
p.next = head1
head1 = head1.next
else:
p.next = head2
head2 = head2.next
p = p.next
if head1:
p.next = head1
else:
p.next = head2
return pNode.next
解法2
堆調整時間複雜度為O(logk),每個元素都要取一次O(nk)。
所以總時間複雜度為O(nklogk),空間複雜度O(k)。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
import heapq
class Solution(object):
def mergeKLists(self, lists):
"""
:type lists: List[ListNode]
:rtype: ListNode
"""
heap = []
for head in lists:
if head: # ★
heapq.heappush(heap, (head.val, head))
p = ListNode(-1)
pNode = p
while heap:
node = heapq.heappop(heap)[1]
if node.next:
heapq.heappush(heap, (node.next.val, node.next))
p.next = node
p = p.next
return pNode.next