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.旋转图像

image.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]
2. 纵向扫描
def longestCommonPrefix(self, s: List[str]) -> str:
        if not s:
            return ""
        res = s[0]
        i = 1
        while i < len(s):
            while s[i].find(res) != 0:
                res = res[0:len(res)-1]
            i += 1
        return res
3. python特性
def longestCommonPrefix(self, strs):
    res = ""
    for tmp in zip(*strs):
        tmp_set = set(tmp)
        if len(tmp_set) == 1:
            res += tmp[0]
        else:
            break
    return res

3. 链表

1. 删除链表中的节点

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

def deleteNode(self, node):
    """
    :type node: ListNode
    :rtype: void Do not return anything, modify node in-place instead.
    """
    node.val = node.next.val
    node.next = node.next.next

2. 删除链表的倒数第N个节点

1. 循环迭代
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
    dummy = ListNode(0)
    dummy.next = head

    #step1: 获取链表长度
    cur, length = head, 0 
    while cur:
        length += 1
        cur = cur.next 

    #step2: 找到倒数第N个节点的前面一个节点
    cur = dummy
    for _ in range(length - n):
        cur = cur.next
    
    #step3: 删除节点,并重新连接
    cur.next = cur.next.next
    return dummy.next
2. 双指针
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
    dummy = ListNode(0)
    dummy.next = head
    
    slow, fast = dummy, dummy
    for _ in range(n):
        fast = fast.next
       
    while fast and fast.next:
        slow, fast = slow.next, fast.next
        
    slow.next = slow.next.next
    
    return dummy.next
3. 递归迭代
![递归](D:\Desktop\啊这\递归.gif)def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
    if not head: # 判断是否为最后一个节点
        self.count = 0
        return head  
    head.next = self.removeNthFromEnd(head.next, n) # 递归调用
    self.count += 1 # 回溯时进行节点计数
    return head.next if self.count == n else head 

3. 反转链表

1. 双指针
def reverseList(self, head: ListNode) -> ListNode:
    pre = None
    cur = head
    while cur:
        tmp = cur.next	# 暂存
        cur.next = pre	# 倒序
        pre = cur		# 进位
        cur = tmp
    return pre
2. 递归解法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7fuhOVnK-1608036976052)(https://i.loli.net/2020/12/15/H4U9nb5gPV7hfYS.gif)]

def reverseList(self, head: ListNode) -> ListNode:
    if(head==None or head.next==None):
		return head
		# 这里的cur就是最后一个节点
		cur = self.reverseList(head.next)
		# 这里请配合动画演示理解
		# 如果链表是 1->2->3->4->5,那么此时的cur就是5
		# 而head是4,head的下一个是5,下下一个是空
		# 所以head.next.next 就是5->4
		head.next.next = head
		# 防止链表循环,需要将head.next设置为空
		head.next = None
		# 每层递归函数都返回cur,也就是最后一个节点
		return cur

4. 合并两个有序链表

1. 迭代
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
    res = ListNode(-1)
    cur = res
    while l1 and l2:
        if l1.val <= l2.val :
            cur.next = l1
            l1 = l1.next
        else:
            cur.next = l2
            l2 = l2.next
            cur = cur.next
    cur.next = l1 if l1 is not None else l2
    return res.next
2.迭代
def mergeTwoLists(self, l1, l2):
    if l1 is None:
        return l2
    elif l2 is None:
        return l1
    elif l1.val < l2.val:
        l1.next = self.mergeTwoLists(l1.next, l2)
        return l1
    else:
        l2.next = self.mergeTwoLists(l1, l2.next)
        return l2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值