合併兩個有序數組
給定兩個有序整數數組 nums1 和 nums2,將 nums2 合併到 nums1 中,使得 num1 成爲一個有序數組。
說明:
初始化 nums1 和 nums2 的元素數量分別爲 m 和 n。
你可以假設 nums1 有足夠的空間(空間大小大於或等於 m + n)來保存 nums2 中的元素。
示例:
輸入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
輸出: [1,2,2,3,5,6]
解法1:歸併排序中的merge操作,注意因爲要修改的是nums1,所以提前用copy()把nums1中的值賦給另一個數組,不能直接用‘=’,因爲後者表達的意思是tmp和nums1指向的是同一片內存空間,如果nums1變化的話,tmp也會隨之改變。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
i = 0
j = 0
idx = 0
tmp = nums1.copy()
while i < m and j < n:
if tmp[i] < nums2[j]:
nums1[idx] = tmp[i]
i += 1
else:
nums1[idx] = nums2[j]
j += 1
idx += 1
while i < m:
nums1[idx] =tmp[i]
i += 1
idx += 1
while j < n:
nums1[idx] = nums2[j]
j += 1
idx += 1
解法2:比較簡單,直接用sort()
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
nums1[m:] = nums2[:n]
nums1.sort()
雞蛋掉落
你將獲得 K 個雞蛋,並可以使用一棟從 1 到 N 共有 N 層樓的建築。
每個蛋的功能都是一樣的,如果一個蛋碎了,你就不能再把它掉下去。
你知道存在樓層 F ,滿足 0 <= F <= N 任何從高於 F 的樓層落下的雞蛋都會碎,從 F 樓層或比它低的樓層落下的雞蛋都不會破。
每次移動,你可以取一個雞蛋(如果你有完整的雞蛋)並把它從任一樓層 X 扔下(滿足 1 <= X <= N)。
你的目標是確切地知道 F 的值是多少。
無論 F 的初始值如何,你確定 F 的值的最小移動次數是多少?
示例 1:
輸入:K = 1, N = 2
輸出:2
解釋:
雞蛋從 1 樓掉落。如果它碎了,我們肯定知道 F = 0 。
否則,雞蛋從 2 樓掉落。如果它碎了,我們肯定知道 F = 1 。
如果它沒碎,那麼我們肯定知道 F = 2 。
因此,在最壞的情況下我們需要移動 2 次以確定 F 是多少。
示例 2:
輸入:K = 2, N = 6
輸出:3
示例 3:
輸入:K = 3, N = 14
輸出:4
提示:
1 <= K <= 100
1 <= N <= 10000
解法:動態規劃
令二維數組dp[K][Step], K表示雞蛋個數,Step表示第幾次摔落。dp[i][j] 表示i個雞蛋經過j次摔落最多可以確定多少層樓。顯然j <= N。所以初始化列表的時候應該初始化[K+1][N+1]
當第j次摔落雞蛋不破,我們可以繼續往上確定dp[i][j - 1]層
當第j次摔落雞蛋破了,我們最多隻能確定dp[i - 1][j - 1]層
狀態方程 d[i][j] = dp[i - 1][j - 1] + ( dp[i][j - 1] + 1 ) 最後的1表示本層
class Solution(object):
def superEggDrop(self, K, N):
"""
:type K: int
:type N: int
:rtype: int
"""
dp = [[0 for i in range(N+1)] for k in range(K+1)]
for i in range(1, K+1):
for j in range(1, N+1):
dp[i][j] = dp[i-1][j-1] + dp[i][j-1] +1
if dp[K][j]>=N:
return j
return N
拓展:谷歌題目
有一棟樓共100層,一個雞蛋從第N層及以上的樓層落下來會摔破, 在第N層以下的樓層落下不會摔破。給你2個雞蛋,設計方案找出N,並且保證在最壞情況下, 最小化雞蛋下落的次數。
思路:
先假設,最小的次數爲x次。
首先在x層摔,那麼會出現兩個結果:
1、碎了,爲了找出那一層碎了,第二個雞蛋必須從1~x-1進行遍歷的摔
2、沒碎,那麼第二次就在x+(x-1)樓層摔。
爲什麼是x+x-1樓層呢?
首先我們已經假設了通過x步我們就能得到答案,現在我們在x層已經用了一次了,那麼就只剩下x-1步了。所以我們選擇x+(x-1)層,如果碎了,我們就能通過x-2步,遍歷x+1~x+(x-1)-1的所有樓層。
3、如果在x+(x-1)樓碎了,那麼同1,遍歷x+1~x+(x-1)-1
4、沒碎,那麼同2,就在x+(x-1)+(x-2)層摔
…
最後我們將會得出這樣一個樓層公式x+(x-1)+(x-2)+…+1 = x(x+1)/2。
這個公式有什麼意義呢?
有, x(x+1)/2 >= 100,這樣才能順利的解除x。
有人說,x(x+1)/2 = 99就可以,如果雞蛋在99層都沒碎,那麼必定是100層。 我想說誰告訴你記得一定會碎!
那麼我們就順利的解除 x=14。
只出現一次的數字
給定一個非空整數數組,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。
說明:
你的算法應該具有線性時間複雜度。 你可以不使用額外空間來實現嗎?
示例 1:
輸入: [2,2,1]
輸出: 1
示例 2:
輸入: [4,1,2,1,2]
輸出: 4
利用異或操作的特性
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
tmp = 0
for x in nums:
tmp = tmp ^ x
return tmp
求衆數
給定一個大小爲 n 的數組,找到其中的衆數。衆數是指在數組中出現次數大於 ⌊ n/2 ⌋ 的元素。
你可以假設數組是非空的,並且給定的數組總是存在衆數。
示例 1:
輸入: [3,2,3]
輸出: 3
示例 2:
輸入: [2,2,1,1,1,2,2]
輸出: 2
解法1:因爲出現次數大於n/2,所以如果列表是有序的,肯定出現在列表的中間位置
解法2:摩爾投票算法,先將第一個數字假設爲衆數,然後把計數器設爲1,比較下一個數和此數是否相等,若相等則計數器加一,反之減一。然後看此時計數器的值,若爲零,則將當前值設爲候選衆數。以此類推直到遍歷完整個數組,當前候選衆數即爲該數組的衆數。
class Solution(object):
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
cand = nums[0]
cnt = 1
for i in range(1, len(nums)):
if cnt == 0:
cand = nums[i]
cnt =1
else:
if cand == nums[i]:
cnt += 1
else:
cnt -= 1
return cand
搜索二維矩陣 II
編寫一個高效的算法來搜索 m x n 矩陣 matrix 中的一個目標值 target。該矩陣具有以下特性:
每行的元素從左到右升序排列。
每列的元素從上到下升序排列。
示例:
現有矩陣 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
給定 target = 5,返回 true。
給定 target = 20,返回 false。
解法:爲了節省比較的次數,可以和左下角位置的數字進行比較。如果tmp>target,說明最後一行可以不用比較了;如果tmp<target,則第一列不需要再比較了
class Solution(object):
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if matrix == []:
return False
a = len(matrix) - 1
b = len(matrix[0])
i = 0
while a >= 0 and i < b:
tmp = matrix[a][i]
if tmp == target:
return True
elif tmp > target:
a -= 1
else:
i += 1
return False
驗證迴文串
給定一個字符串,驗證它是否是迴文串,只考慮字母和數字字符,可以忽略字母的大小寫。
說明:本題中,我們將空字符串定義爲有效的迴文串。
示例 1:
輸入: "A man, a plan, a canal: Panama"
輸出: true
示例 2:
輸入: "race a car"
輸出: false
str.isalnum方法檢測字符串是否由字母和數字組成。
filter() 函數用於過濾序列,過濾掉不符合條件的元素,返回一個迭代器對象,如果要轉換爲列表,可以使用 list() 來轉換。
該接收兩個參數,第一個爲函數,第二個爲序列,序列的每個元素作爲參數傳遞給函數進行判,然後返回 True 或 False,最後將返回 True 的元素放到新列表中。
class Solution:
def isPalindrome(self, s: str) -> bool:
s = list(filter(str.isalnum, s.lower()))
return True if s == s[::-1] else False
編輯距離的實現
在計算文本的相似性時,經常會用到編輯距離。編輯距離,又稱Levenshtein距離,是指兩個字串之間,由一個轉成另一個所需的最少編輯操作次數。通常來說,編輯距離越小,兩個文本的相似性越大。這裏的編輯操作主要包括三種:
插入:將一個字符插入某個字符串;
刪除:將字符串中的某個字符刪除;
替換:將字符串中的某個字符替換爲另外一個字符。
解法
動態規劃的思想是將問題劃分爲子問題,這個問題的迭代式爲:
(1) 當s1[i]=s2[j]時,DP[i][j]=DP[i-1][j-1]
(2) 當s1[i]!=s2[j]時,DP[i][j]=min{DP[i-1][j],DP[i][j-1],DP[i-1][j-1]}+1
DP[i][j]表示s1中以s1[i]結尾的字符串和s2中以s2[j]結尾的子串的編輯距離
關於上面(2)中的子問題的解釋:
求以s1[i]和s2[j]結尾的連個子串的編輯距離的問題,可以轉換爲求解三個子問題:
- 求解以s1[i-1]和s2[j]結尾的子串的編輯距離;對應的操作是在s1中刪除元素s1[i]
- 求解以s1[i]和s2[j-1]結尾的子串的編輯距離;對應的操作是在s1中元素s1[i]的後面插入一個元素s2[j]
- 求解以s1[i-1]和s2[j-1]結尾的子串的編輯距離;對應的操作是將元素s1[i]替換爲s2[j]
def EditDis(s1,s2):
m=len(s1)
n=len(s2)
DP=[[0 for i in range(m)] for j in range(n)]
for i in range(n):
for j in range(m):
if i==0:
DP[i][j]=j
elif j==0:
DP[i][j]=i
else:
if s1[j]==s2[i]:
if i>0 and j>0:
DP[i][j]=DP[i-1][j-1]
else:
DP[i][j]=min(DP[i][j-1],DP[i-1][j],DP[i-1][j-1])+1
return DP[n-1][m-1]
最長迴文子串
給定一個字符串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度爲 1000。
示例 1:
輸入: “babad”
輸出: “bab”
注意: “aba” 也是一個有效答案。
示例 2:
輸入: “cbbd”
輸出: “bb”
解法
注意一些邊界條件。以及這裏設置的i是start,j是end位置,j要從左往右,i也是從左到i,是爲了讓dp[i+1][j-1]提前已經得到結果
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if len(s) < 2:
return s
res = ""
max_len = -1
dp = [[False for i in range(len(s))] for j in range(len(s))]
for j in range(1, len(s)):
for i in range(0, j + 1):
if i == j:
dp[i][j] == True
if j - i < 2 and s[i] == s[j]:
dp[i][j] = True
else:
if dp[i + 1][j - 1] == True and s[i] == s[j]:
dp[i][j] = True
if j - i >= max_len and dp[i][j] == True:
max_len = j - i
res = s[i: j + 1]
return res
最長公共子序列
給定兩個字符串 text1 和 text2,返回這兩個字符串的最長公共子序列。
一個字符串的 子序列 是指這樣一個新的字符串:它是由原字符串在不改變字符的相對順序的情況下刪除某些字符(也可以不刪除任何字符)後組成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。兩個字符串的「公共子序列」是這兩個字符串所共同擁有的子序列。
若這兩個字符串沒有公共子序列,則返回 0。
示例 1:
輸入:text1 = “abcde”, text2 = “ace”
輸出:3
解釋:最長公共子序列是 “ace”,它的長度爲 3。
示例 2:
輸入:text1 = “abc”, text2 = “abc”
輸出:3
解釋:最長公共子序列是 “abc”,它的長度爲 3。
示例 3:
輸入:text1 = “abc”, text2 = “def”
輸出:0
解釋:兩個字符串沒有公共子序列,返回 0。
提示:
1 <= text1.length <= 1000
1 <= text2.length <= 1000
輸入的字符串只含有小寫英文字符。
解法
class Solution(object):
def longestCommonSubsequence(self, text1, text2):
"""
:type text1: str
:type text2: str
:rtype: int
"""
if not text1 or not text2:
return 0
res = 0
dp = [[0 for i in range(len(text2) + 1)] for j in range(len(text1) + 1)]
for i in range(len(text1)):
for j in range(len(text2)):
x, y = text1[i], text2[j]
if x == y:
dp[i + 1][j + 1] = dp[i][j] + 1
else:
dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1])
if dp[i + 1][j + 1] > res:
res = dp[i + 1][j + 1]
return res
使數組有序的最小交換次數
問題是給出一個數組,然後數組裏面的元素可以交換,任意位置都可以交換,然後求使得該數組有序的最小交換次數。
Sample Input 1
2
4
4 3 2 1
5
1 5 4 3 2
Sample Output 1
2
2
示例輸入第一個2是隻有2個測試用例,接下來每個測試用例第一個數是4,代表數組長度是4,下一行就是數組的元素(對python來說這個數組長度這個不太需要)。
解法: 目的是去找數組中互換閉包的個數,若數組本來有n個數,現在找到了一個包含x個數的互換閉包,互換閉包的意思就是在閉包內只需要交換x-1次就可以(其實就是最大交換次數),剩下n-x就得交換n-x次,那麼總交換次數就是x-1+n-x=n-1次。所以,若找到a個這樣的閉包,那麼總交換次數就是n-a次。
注意,互換閉包的長度可以爲1,也就是若當前位置就是該有的數字,那就不需要交換了。
n = int(input().strip())
for repeat_i in range(n):
length = int(input().strip())
line = input()
str_list = line.split(' ')
arr = [int(x) for x in str_list]
# 先排個序,找到每個數字對應的位置
new_arr = sorted(arr)
dict_position = {}
for i in range(len(new_arr)):
dict_position[new_arr[i]] = i
print("dict_position", dict_position)
# 中心思想很簡單,就是針對每一個位置上的數,
# 我假設這個數被換到對應位置上,
# 現在我看一下對應位置上的數有沒有被訪問過
# 如果對應位置上的數沒有被訪問過,
# 那我把對應位置上的數再交換到它對應的位置上,
# 現在我再看看對應位置上的數應該在的位置上那個數有沒有被訪問過
res_closure = 0
flag = [0 for x in arr]
for i in range(len(arr)):
if flag[i] == 0:
print("i",i)
j = i
while flag[j] == 0:
print("j",j)
flag[j] = 1
j = dict_position[arr[j]]
res_closure += 1
print(len(arr) - res_closure)