1. 程式人生 > 其它 >LeetCode初級演算法程式碼

LeetCode初級演算法程式碼

技術標籤:leetcode演算法

初級演算法

1. 陣列

1. 雙指標

​ 刪除排序陣列中的重複項:雙指標一快一慢,慢指標為最終輸出陣列

def removeDuplicates(self, nums: List[int]) -> int:
        l = len(nums)
        i, j = 0, 1
        if l < 2:return l;
        for a in range(l-1):
            if nums[i] != nums[j]:
                i += 1
                nums[
i] = nums[j] j +=1 nums = nums[0:i+1] return len(nums);

2. 貪心演算法

​ 問題求解時,總是做出目前最優的方案

def maxProfit(self, prices: List[int]) -> int:
        income = 0
        for i in range(1,len(prices)):
            income += max(0, prices[i]-prices[i-1])
        return income

3. 旋轉陣列

1. 暴力法

		每次移動一個元素
def rotate(self, nums: List[int], k: int) -> None:
    temp ,previous = 0, 0
    for i in range(k):
        previous = nums[len(nums) - 1]
        for j in range(len(nums)):
            temp = nums[j]
            nums[j] = previous
            previous = temp
    return nums

​ 時間複雜度:O(n*k) 空間複雜度O(1)

2. 反轉

		當我們旋轉陣列 k 次, *k%n* 個尾部元素會被移動到頭部,剩下的元素會被向後移動
def rotate(self, nums: List[int], k: int) -> None:
    n = len(nums)
    k %= n
    nums[:] = nums[::-1]		#反轉整個陣列
    nums[:k] = nums[:k][::-1]	#反轉前k個元素
    nums[k:] = nums[k:][::-1]	#反轉剩下的元素

​ 時間複雜度:O(n) 空間複雜度O(1)

3. 環狀替代

​ 陣列內元素的移動可以形成一個或多個閉環,只需一次遍歷即可

	def rotate(self, nums: List[int], k: int) -> None:
	    count,index,temp = 0,0,nums[0]
	    done_index = [0]
	    while count < len(nums):
	        count,target = count+1, (index + k) % len(nums)
	        temp,nums[target] = nums[target],temp
	        if target not in done_index:
	            index = target
	        elif target + 1 < len(nums):
	            index,temp = target + 1,nums[target + 1]
	        done_index.append(index)

​ 時間複雜度:O(n) 空間複雜度O(1)

4. 存在重複元素

1. 樸素線性查詢

​ 依次逐個檢查列表中的元素,直到找到滿足的元素

def containsDuplicate(self, nums: List[int]) -> bool:
    for i in range(len(nums)):
        for j in range(i):
            if nums[j] == nums[i]:
                return True
    return False

​ 時間複雜度 : O(n2) 空間複雜度 : O(1)

2. 排序

​ 將陣列排列後,掃描是否粗壯乃連續的重複元素

def containsDuplicate(self, nums: List[int]) -> bool:
    nums.sort()
    for i in range(len(nums)-1):
        if nums[i] == nums[i + 1]:
            return True
    return False

​ 時間複雜度 : O(n log n) 空間複雜度:O(1)

3. 雜湊表

​ 利用支援快速搜尋和插入操作的動態資料結構。

def containsDuplicate(self, nums: List[int]) -> bool:
	return len(nums) != len(set(nums))		#set()可以自動去重,自動排序

​ 時間複雜度:O(n) 空間複雜度:O(n)

5. 只出現一次的數字(重複兩次)

1. 位運算

​ 陣列中的全部元素的異或運算結果即為陣列中只出現一次的數字 eg:6 ^4 = 4^6

def singleNumber(self, nums: List[int]) -> int:
    return reduce(lambda x, y: x ^ y, nums)

​ 時間複雜度:O(n) 空間複雜度:O(1)

2.排序

def singleNumber(nums):
    if len(nums)==1:   #如果陣列長度為1,則直接返回即可
        return nums[0]   
    nums.sort()     #對陣列進行排序,使其相同元素靠在一起
    for i in range(1,len(nums),2):   #迴圈陣列,驗證前後是否相同,由於原始出現兩次,因此可跳躍判斷
        if nums[i-1] != nums[i]:
            return nums[i-1]
        if (i+2) == len(nums):   #判斷單一元素在排序後陣列的最後面
            return nums[-1]

3.刪除元素

​ 依次刪除列表的元素

def singleNumber(nums):
    while True:
        d = nums[0]
        nums.remove(d)
        try:
            nums.remove(d)
        except:
            return d

6. 兩個陣列的交集

1.雜湊表

​ 用雜湊表儲存每個數字出現的次數

def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
    if len(nums1) > len(nums2):
        return self.intersect(nums2, nums1)
    m = collections.Counter()
    for num in nums1:
        m[num] += 1
    intersection = list()
    for num in nums2:
        if (count := m.get(num, 0)) > 0:
            intersection.append(num)
            m[num] -= 1
            if m[num] == 0:
                m.pop(num)
    return intersection

​ 時間複雜度:O(m+n) 空間複雜度:O(min(m,n))

2. 雙指標

​ 先對兩個陣列進行排序,然後使用兩個指標遍歷兩個陣列。

def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
    nums1.sort()
    nums2.sort()

    length1, length2 = len(nums1), len(nums2)
    intersection = list()
    index1 = index2 = 0
    while index1 < length1 and index2 < length2:
        if nums1[index1] < nums2[index2]:
            index1 += 1
        elif nums1[index1] > nums2[index2]:
            index2 += 1
        else:
            intersection.append(nums1[index1])
            index1 += 1
            index2 += 1
	return intersection

​ 時間複雜度:O(m log m+n log n) 空間複雜度:O(min(m,n))

7. 陣列最後一位加一,每個元素為 0-9

1.數組合成數字

 def plusOne(digits) :
        nums_s = ''.join([str(x) for x in digits])   #先將其陣列中的值轉換為字串,然後進行拼接 
        nums = str(int(nums_s) + 1)  
        r = [int(x) for x in nums]   
        return [0]*(len(digits)-len(r)) + r   #陣列前面可能存在多個0,防止這種情況出現

2.從陣列尾部遍歷

def plusOne(self, digits: List[int]) -> List[int]:
    for i in range(len(digits),0,-1):    #迴圈陣列,從最後一位開始計算
        if digits[i-1] == 9:    #如果為9,則相加為十,需要向前進一位
            digits[i-1] = 0
        	if i == 1 :       #如果滿十進位到陣列第一個,則陣列需要增加一位
            	digits = [1] + digits
        else:
            digits[i-1] += 1   #不等於9就終止迴圈結束
            break
     return digits

8. 移動陣列中零

def moveZeroes(self, nums: List[int]) -> None:
    i = 0
    for a in range(len(nums)):
        if nums[i] == 0:
            del nums[i]
            nums.append(0)
        else:
            i += 1
     return nums

9.兩數之和

雜湊表

​ 建立一個雜湊表,對於每一個x,我們首先查詢雜湊表中是否存在target - x,然後將x插入到雜湊表中

def twoSum(self, nums: List[int], target: int) -> List[int]:
    hashtable = dict()
    for i, num in enumerate(nums):
        if target - num in hashtable:
            return [hashtable[target - num], i]
        hashtable[nums[i]] = i
    return []

10. 有效的數獨

def isValidSudoku(self, board: List[List[str]]) -> bool:
row = [{} for _ in range(9)]
col = [{} for _ in range(9)]
grid = [[{} for _ in range(3)] for _ in range(3)]
for i in range(9):
    for j in range(9):
        if board[i][j] != '.':
            tmp = int(board[i][j])
            row[i][tmp] = row[i].get(tmp, 0) + 1		#get("位置", “不存在時返回值”)
            col[j][tmp] = col[j].get(tmp, 0) + 1
            grid[i//3][j//3][tmp] = grid[i//3][j//3].get(tmp, 0) + 1
            if row[i].get(tmp) > 1 or col[j].get(tmp) > 1 or grid[i//3][j//3].get(tmp) > 1:
                return False
return True

11.旋轉影象

48_rectangles.png
def rotate(self, matrix: List[List[int]]) -> None:
n = len(matrix[0])        
for i in range(n // 2 + n % 2):
    for j in range(n // 2):
        tmp = matrix[n - 1 - j][i]							    #左上角
        matrix[n - 1 - j][i] = matrix[n - 1 - i][n - j - 1]		  #右上角
        matrix[n - 1 - i][n - j - 1] = matrix[j][n - 1 -i]		  #右下角
        matrix[j][n - 1 - i] = matrix[i][j]					     #左下角
        matrix[i][j] = tmp									   #左上角

2.字串

1. 反轉字串

def reverseString(self, s: List[str]) -> None:
    for i in range(len(s),len(s)//2,-1):
        s[i-1],s[len(s)-i] = s[len(s)-i],s[i-1]
def reverseString(self, s: List[str]) -> None:
    s.reverse()
def reverseString(self, s: List[str]) -> None:
    s = s[::-1]

2. 整數反轉

​ 將整數轉換為字串

def reverse(self, x: int) -> int:
        s = str(x)
        if s[0] == '-':
            x = int('-' + s[1:][::-1])
        else:
            x = int(s[::-1])
        if (-2147483648 <= x <= 2147483647):
            return x
        else:
            return 0

3. 字串中的第一個唯一字元

def firstUniqChar(self, s: str) -> int:
    count = collections.Counter(s)		#統計s中元素出現頻率
    for idx, ch in enumerate(s):		#idx 為s中內容的序號,ch為s中內容
        if count[ch] == 1:
            return idx
    return -1

4. 有效的字母異位詞

1.排序法

將字串儲存到容器,進行sort排序,然後依次對比

def isAnagram(self, s: str, t: str) -> bool:
    if len(s) != len (t):
        return False
    a = 0
    s_l = list(s)
    t_l = list(t)
    s_l.sort()
    t_l.sort()
    s_0 = "".join(s_l)
    t_0 = "".join(t_l)
    for i in range(len(s)):
        if s_0[i] == t_0[i]:
            a += 1
    if a == len(s):
    	return True
    else:
    	return False

2.雜湊表

利用collections.Counter函式

def isAnagram(self, s: str, t: str) -> bool:
    return collections.Counter(s) == collections.Counter(t)

5. 驗證迴文串

1.篩選判斷

先篩選出所有字母與數字,並將字母全部小寫,然後判斷

def isPalindrome(self, s: str) -> bool:
    sgood = "".join(ch.lower() for ch in s if ch.isalnum())	#lower()將字母小寫,ch.isalnum()判斷是否為字母與數字
    return sgood == sgood[::-1]

3. 在原字串上直接判斷

利用雙指標

def isPalindrome(self, s: str) -> bool:
    n = len(s)
    left, right = 0, n - 1
        
    while left < right:
        while left < right and not s[left].isalnum():
            left += 1
        while left < right and not s[right].isalnum():
            right -= 1
        if left < right:
            if s[left].lower() != s[right].lower():
                return False
            left, right = left + 1, right - 1
     return True

4. 字串轉換整數 (atoi)

1.正常遍歷

class Solution:
    def myAtoi(self, s: str) -> int:
        i=0
        n=len(s)
        while i<n and s[i]==' ':
            i=i+1
        if n==0 or i==n:
            return 0
        flag=1
        if s[i]=='-':
            flag=-1
        if s[i]=='+' or s[i]=='-':
            i=i+1
        INT_MAX=2**31-1
        INT_MIN=-2**31
        ans=0
        while i<n and '0'<=s[i]<='9':
            ans=ans*10+int(s[i])-int('0')
            i+=1
            if(ans-1>INT_MAX):
                break

        ans=ans*flag
        if ans>INT_MAX:
            return INT_MAX
        return INT_MIN if ans<INT_MIN else ans

2.有限狀態機

' '+/-numberother
startstartsignedin_numberend
signedendendin_numberend
in_numberendendin_numberend
endendendendend
INT_MAX = 2 ** 31 - 1
INT_MIN = -2 ** 31

class Automaton:
    def __init__(self):
        self.state = 'start'
        self.sign = 1
        self.ans = 0
        self.table = {
            'start': ['start', 'signed', 'in_number', 'end'],
            'signed': ['end', 'end', 'in_number', 'end'],
            'in_number': ['end', 'end', 'in_number', 'end'],
            'end': ['end', 'end', 'end', 'end'],
        }
        
    def get_col(self, c):
        if c.isspace():
            return 0
        if c == '+' or c == '-':
            return 1
        if c.isdigit():
            return 2
        return 3

    def get(self, c):
        self.state = self.table[self.state][self.get_col(c)]
        if self.state == 'in_number':
            self.ans = self.ans * 10 + int(c)
            self.ans = min(self.ans, INT_MAX) if self.sign == 1 else min(self.ans, -INT_MIN)
        elif self.state == 'signed':
            self.sign = 1 if c == '+' else -1

class Solution:
    def myAtoi(self, str: str) -> int:
        automaton = Automaton()
        for c in str:
            automaton.get(c)
        return automaton.sign * automaton.ans

4.正則表達

def myAtoi(self, str: str) -> int:
        INT_MAX = 2147483647    
        INT_MIN = -2147483648
        str = str.lstrip()      #清除左邊多餘的空格
        num_re = re.compile(r'^[\+\-]?\d+')   #設定正則規則
        num = num_re.findall(str)   #查詢匹配的內容
        num = int(*num) #由於返回的是個列表,解包並且轉換成整數
        return max(min(num,INT_MAX),INT_MIN)    #返回值
元字元
匹配內容
.匹配除換行符以外的任意字元
\w匹配字母或數字或下劃線
\s匹配任意的空白符
\d匹配數字
\n匹配一個換行符
\t匹配一個製表符
\b匹配一個單詞的結尾
^匹配字串的開始
$匹配字串的結尾
\W
匹配非字母或數字或下劃線
\D
匹配非數字
\S
匹配非空白符
a|b
匹配字元a或字元b
()
匹配括號內的表示式,也表示一個組
[...]
匹配字元組中的字元
[^...]
匹配除了字元組中字元的所有字元
量詞
用法說明
*重複零次或更多次
+重複一次或更多次
?重複零次或一次
{n}重複n次
{n,}重複n次或更多次
{n,m}重複n到m次

5. 實現 strStr()

1.子串逐一比較 - 線性時間複雜度

沿著字元換逐步移動滑動視窗,將視窗內的子串與 needle 字串比較。

def strStr(self, haystack: str, needle: str) -> int:
    L, n = len(needle), len(haystack)
    for start in range(n - L + 1):
        if haystack[start: start + L] == needle:
            return start
    return -1

2.雙指標 - 線性時間複雜度

def strStr(self, haystack: str, needle: str) -> int:
        L, n = len(needle), len(haystack)
        if L == 0:
            return 0
        pn = 0
        while pn < n - L + 1:
            while pn < n - L + 1 and haystack[pn] != needle[0]:	# 與第一個字母不匹配
                pn += 1
            curr_len = pL = 0
            while pL < L and pn < n and haystack[pn] == needle[pL]:	# 與第一個字母匹配
                pn += 1
                pL += 1
                curr_len += 1
            
            # if the whole needle string is found,
            # return its start position
            if curr_len == L:
                return pn - L
            
            # otherwise, backtrack
            pn = pn - curr_len + 1	# 出現不匹配字母
        return -1

3。Rabin Karp - 常數複雜度

先生成視窗內子串的雜湊碼,然後再跟 needle 字串的雜湊碼做比較。

def strStr(self, haystack: str, needle: str) -> int:
    L, n = len(needle), len(haystack)
    if L > n:
        return -1

    # base value for the rolling hash function
    a = 26
    # modulus value for the rolling hash function to avoid overflow
    modulus = 2**31

    # lambda-function to convert character to integer
    h_to_int = lambda i : ord(haystack[i]) - ord('a')
    needle_to_int = lambda i : ord(needle[i]) - ord('a')

    # compute the hash of strings haystack[:L], needle[:L]
    h = ref_h = 0
    for i in range(L):
        h = (h * a + h_to_int(i)) % modulus
        ref_h = (ref_h * a + needle_to_int(i)) % modulus
    if h == ref_h:
        return 0
              
    # const value to be used often : a**L % modulus
    aL = pow(a, L, modulus) 
    for start in range(1, n - L + 1):
        # compute rolling hash in O(1) time
        h = (h * a - h_to_int(start - 1) * aL + h_to_int(start + L - 1)) % modulus
        if h == ref_h:
            return start

h 0 = c 0 a L − 1 + c 1 a L − 2 + . . . + c L − 1 a 2 + c L a 1 h_0=c_0a^{L-1}+c_1a^{L-2}+...+c_{L-1}a^2+c_La^1 h0=c0aL1+c1aL2+...+cL1a2+cLa1

h 1 = h 0 a − c 0 a L + c L + 1 h_1=h_0a-c_0a^L+c_{L+1} h1=h0ac0aL+cL+1

6. 外觀數列

1. 雙指標實現

def countAndSay(self, n: int) -> str:
    pre = ''
    cur = '1'
    # 從第 2 項開始
    for _ in range(1, n):
        # 這裡注意要將 cur 賦值給 pre
        # 因為當前項,就是下一項的前一項。有點繞,嘗試理解下
        pre = cur
        # 這裡 cur 初始化為空,重新拼接
        cur = ''
        # 定義雙指標 start,end
        start = 0
        end = 0
        # 開始遍歷前一項,開始描述
        while end < len(pre):
            # 統計重複元素的次數,出現不同元素時,停止
            # 記錄出現的次數,
            while end < len(pre) and pre[start] == pre[end]:
                end += 1
            # 元素出現次數與元素進行拼接
            cur += str(end-start) + pre[start]
            # 這裡更新 start,開始記錄下一個元素
            start = end
    return cur

2.正則表示式(實現):提取元素

def countAndSay(self, n: int) -> str:
    if n == 1:
    	return "1"
    s = self.countAndSay(n-1)
    # 字串 (\d)\1* 可以用來匹配結果。這裡用來提取連在一塊的元素, 如 '111221',提取出的元素是 res = ['111', '22', '1']。
    # (\d)\1*解釋:
    # \1 是為了引用前面的 \d,表明 \1 是與 \d 匹配到相同的數字。
    # 只有 \d 添加了(),才能被引用,在正則裡面稱之為捕獲組。
    # * 表示重複匹配前面字元0次或多次。所以可以匹配的到像 1 、111、11 這樣連在一起並相同的數字。
    pattern = re.compile(r'(\d)\1*')
    res = [_.group() for _ in pattern.finditer(s)]
    return ''.join(str(len(c)) + c[0] for c in res)

3.正則表示式(實現):元素替換

def countAndSay(self, n: int) -> str:
    res = "1"
    pattern= re.compile(r'(\d)\1*')
    for _ in range(n-1):
        res = pattern.sub(lambda x: str(len(x.group())) + x.group(1), res) # 核心程式碼,實現元素的替換
    return res

7. 最長公共字首

1. 縱向掃描

def longestCommonPrefix(self, strs: List[str]) -> str:
    if not strs:
        return ""
    length, count = len(strs[0]), len(strs)
    for i in range(length):
        c = strs[0][i]
        if any(i == len(strs[j]) or strs[j][i] != c for j in range(1, count)):
            return strs[0][:i]
    return strs[0]
  • 表示重複匹配前面字元0次或多次。所以可以匹配的到像 1 、111、11 這樣連在一起並相同的數字。
    pattern = re.compile(r’(\d)\1*’)
    res = [_.group() for _ in pattern.finditer(s)]
    return ‘’.join(str(len©) + c[0] for c in res)

#### 3.正則表示式(實現):元素替換

```python
def countAndSay(self, n: int) -> str:
    res = "1"
    pattern= re.compile(r'(\d)\1*')
    for _ in range(n-1):
        res = pattern.sub(lambda x: str(len(x.group())) + x.group(1), res) # 核心程式碼,實現元素的替換
    return res

7. 最長公共字首

1. 縱向掃描

def longestCommonPrefix(self, strs: List[str]) -> str:
    if not strs:
        return ""
    length, count = len(strs[0]), len(strs)
    for i in range(length):
        c = strs[0][i]
        if any(i == len(strs[j]) or strs[j][i] != c for j in range(1, count)):
            return strs[0][:i]
    return strs[0]