数据结构与算法(实践篇)


代码基于Python,简单易读

数组

O(1)插入删除元素

遇到需要逐个遍历判断并删除集合内元素的情况,可以逆序进行遍历,这样就能避免删除后元素减少而越界
删除一样的元素:

def removeDuplicates(self, nums: List[int]) -> int:
    for i in range(len(nums) - 1, 0, -1):
        if nums[i] == nums[i - 1]:
            del nums[i]
    return len(nums)

注意del函数比较耗时,所以有更好的办法最好不用

常数时间插入删除元素:

class RandomizedSet:

    def __init__(self):
        self.dict_ = {}
        self.nums = []

    def insert(self, val: int) -> bool:
        if val not in self.dict_:
            self.dict_[val] = len(self.nums)
            self.nums.append(val)
            return True
        return False

    def remove(self, val: int) -> bool:
        if val in self.dict_:
            index = self.dict_[val]
            self.nums[index] = self.nums[-1]
            self.dict_[self.nums[-1]] = index
            self.nums.pop()
            del self.dict_[val]
            return True
        return False

    def getRandom(self) -> int:
        return choice(self.nums)

使用栈和队列

# 都可以这样定义,前后插入删除时间复杂度O(n)
s = [] #栈
q = [] #队列
s.append(1)#1入栈
q.append(1)#1入队
s.pop()#出栈
q.pop(0)#出队

# 时间复杂度较低的方法,这是一个双向链表,可做栈和队列
# 前后插入删除的时间复杂度为O(1)
s = collections.deque()
a.append(1)  #入栈或入队
s.popleft()  #出队
s.popright() #出栈

抵消重复元素(线性时间复杂度),寻找单一元素

抵消重复的元素的时候可以用异或
找出唯一一个不重复的元素,重复的元素仅重复两次:

def singleNumber(self, nums: List[int]) -> int:
    s = 0
    for i in range(len(nums)):
        s = s ^ nums[i]
    return s

哈希表寻找单一元素
先遍历一次数组,把名称记为键,重复次数记为值。第二次遍历检查值为1的

class Solution:
    def firstUniqChar(self, s: str) -> int:
        a = {}
        for i in range(len(s)):
            if s[i] in a:
                a[s[i]] += 1
            else:
                a[s[i]] = 1

        for i in range(len(s)):
            if a[s[i]] == 1:
                return i
        return -1

只寻找键或值

a = {}

for i in a.keys()

for i in a.values()

判断数字是否在数组中

class Solution:
    def twoSum(self, nums, target):
        for i in range(len(nums)):
            res = target-nums[i]
            if res in nums[i+1:]:  
                return [i, nums[i+1:].index(res)+i+1]

使用哈希表存储数据(查询速度快,空间换时间)

哈希表在python中为{},判断键是否在哈希表中方法和数组一致

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        n = len(nums)
        mp = {}
        for i in range(n):
            t = target - nums[i]
            if t in mp:
                return [mp[t], i]
            # 存放nums[i]
            mp[nums[i]] = i
        return []

位运算检查重复元素

位运算运行速度极快,因此用位运算存储数据和检查重复很实用

shift = 1 << 8   # 如果第一位表示1,这里左移8位存一个9进去
if((line[i] & shift) > 0   # 如果之前的数据与shift重复,则与不为0
line[i] |= shift  # 把数据保存

检查数独是否有效(不检查是否有解):

def isValidSudoku(self, board: List[List[str]]) -> bool:
    shift = 0
    line = [0,0,0,0,0,0,0,0,0]
    column = [0,0,0,0,0,0,0,0,0]
    cell = [0,0,0,0,0,0,0,0,0]
    for i in range(9):
        for j in range(9):
            if(board[i][j] == '.'): continue
            shift = 1 << int(int(board[i][j]) - 1)
            k = int(int(i / 3) * 3 + j / 3)
            if((line[i] & shift) > 0 or (column[j] & shift) > 0 or (cell[k] & shift) > 0):
                return False
            line[i] |= shift
            column[j] |= shift
            cell[k] |= shift
    return True

矩阵的旋转

矩阵旋转90度相当于上下翻转后对角线翻转

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        l = len(matrix)
        for i in range(int(l/2)):
            for j in range(l):
                temp = matrix[i][j]
                matrix[i][j] = matrix[l-i-1][j]
                matrix[l-i-1][j] = temp
        for i in range(l):
            for j in range(l):
                if i > j:
                    temp = matrix[i][j]
                    matrix[i][j] = matrix[j][i]
                    matrix[j][i] = temp

数组数字和字符串排序

调用API的两种方式

st = "asldfjslkdjf"
# 第一种
st = sorted(st)
# 第二种
st.sort()

自己写的话参考快速排序

倒序

数组倒序:

s[:] = s[::-1]

数字倒序
假设输入数字c

res = 0
while c != 0:
    res = res * 10 + c % 10
    c = int(c / 10)

三数之和,排序加双指针

我的遍历和双指针写法,时间复杂的较高:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums = sorted(nums)
        answer = []
        for i in range(len(nums)-2):
            begin = i + 1
            end = len(nums) - 1
            while  begin < end:
                if nums[begin] + nums[end] == -nums[i]:
                    if [nums[i],nums[begin],nums[end]] not in answer:
                        answer.append([nums[i],nums[begin],nums[end]])
                    begin += 1
                    end -= 1
                elif nums[begin] + nums[end] < -nums[i]:
                    begin += 1
                elif nums[begin] + nums[end] > -nums[i]:
                    end -= 1
        return answer

去重优化写法,不要用not in:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        if(not nums or n<3):    return []
        nums.sort()
        answer = []
        for i in range(n-2):
            if nums[i]>0:  return answer
            if i!=0 and nums[i] == nums[i-1]: continue
            begin = i + 1
            end = n - 1
            while  begin < end:
                if nums[begin] + nums[end] == -nums[i]:
                    answer.append([nums[i],nums[begin],nums[end]])
                    while begin < end and nums[begin] == nums[begin+1]:
                        begin += 1
                    while begin < end and nums[end] == nums[end-1]:
                        end -= 1
                    begin += 1
                    end -= 1

                elif nums[begin] + nums[end] < -nums[i]:
                    begin += 1
                else:
                    end -= 1
        return answer

字典存数据方法,用了二分查找,耗时是上面的五十分之一:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        ans = []
        counts = {}
        for i in nums:
            counts[i] = counts.get(i, 0) + 1
        nums = sorted(counts)
        for i, num in enumerate(nums):
            if counts[num] > 1:
                if num == 0:
                    if counts[num] > 2:
                        ans.append([0, 0, 0])
                        continue
                else:
                    if -num * 2 in counts:
                        ans.append([num, num, -2 * num])
            if num < 0:
                two_sum = -num
                left = bisect.bisect_left(nums, (two_sum - nums[-1]), i + 1)
                for j in nums[left: bisect.bisect_right(nums, (two_sum // 2), left)]:
                    k = two_sum - j
                    if k in counts and k != j:
                        ans.append([num, j, k])

        return ans

除自身以外的乘积

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        l = len(nums)
        ans = [1] * l
        currentleft = 1
        currentright = 1
        for i in range(l):
            ans[i] *= currentleft
            currentleft *= nums[i]
            ans[l-1-i] *= currentright
            currentright *= nums[l-1-i]
        return ans

螺旋矩阵

我的方法:用hashset存格子

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        a = set()
        row = 0
        column = 0
        thisposition = (row,column)
        outputs = [matrix[0][0]]
        a.add((0,0))
        islast = False
        while len(outputs) < len(matrix) * len(matrix[0]):
            if not islast:
                if column + 1 < len(matrix[0]) and (row,column+1) not in a:
                    column += 1
                elif row + 1 < len(matrix) and (row+1,column) not in a:
                    row += 1
                elif column - 1 >= 0 and (row,column-1) not in a:
                    column -= 1
                elif (row-1,column) not in a:
                    row -= 1
                    islast = True
            else:
                if (row-1,column) not in a:
                    row -= 1
                else:
                    islast = False
                    continue
            outputs.append(matrix[row][column])
            a.add((row,column))
        return outputs

方法二:存行和列开始和结束位置,性能更优

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        rowbegin = 0
        columnbegin = 0
        rowend = len(matrix) - 1
        columnend = len(matrix[0]) - 1
        outputs = [matrix[0][0]]
        state = 0  # 右,下,左,上
        row = 0
        column = 0
        while len(outputs) < len(matrix) * len(matrix[0]):
            if state == 0:
                if column + 1 <= columnend:
                    column += 1
                else:
                    state += 1
                    rowbegin += 1
                    continue
            elif state == 1:
                if row + 1 <= rowend:
                    row += 1
                else:
                    state += 1
                    columnend -= 1
                    continue
            elif state == 2:
                if column - 1 >= columnbegin:
                    column -= 1
                else:
                    state += 1
                    rowend -= 1
                    continue
            elif state == 3:
                if row - 1 >= rowbegin:
                    row -= 1
                else:
                    state = 0
                    columnbegin += 1
                    continue
            outputs.append(matrix[row][column])

        return outputs

字符串

字符串保留字母数字、转换为大小写、处理空白

s = "saldjfgs1324343"
s.upper() # 大写
s.lower() # 小写
for i in s:
    if i.isdigit():  # 判断是否为数字

    if i.isalpha():  # 判断是否为字母

s = s.strip()  # 处理前后空白

合理利用截断字符串的方式判断相同前缀

def longestCommonPrefix(self, strs: List[str]) -> str:
       ans = strs[0]
       for i in range(1,len(strs)):
           while (ans != strs[i][0:len(ans)]):
               ans = ans[:-1]
       
       return ans


字典解决两数之和

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        d  = {}
        for i in range(len(nums)):
            if target - nums[i] in d:
                return [i,d[target - nums[i]]]
            d[nums[i]] = i

defaultdict分类问题

将 字母异位词 组合在一起。字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
defaultdict 是 dict 的一个子类。通常 Python 中字典(dict)这种数据类型是通过键值对来存取的,当索引一个不存在的键时,就会引发 keyerror 异常。那么,defaultdict 就可以解决这个问题,它可以实现为不存的键值返回一个默认值。defaultdict(list),会构建一个默认value为list的字典
defaultdict接受一个工厂函数作为参数,这个factory_function可以是list、set、str等等,作用是当key不存在时,返回的是工厂函数的默认值,比如list对应[ ],str对应的是空字符串,set对应set( ),int对应0

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        # 利用字典
        dict_ = defaultdict(list)
        for i in strs:
            key = ''.join(sorted(list(i)))  # 可简化为str(sorted(i))
            dict_[key].append(i)
        return list(dict_.values())

无重复最长子串

双指针我的数组存储写法:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        maxlen = 0
        begin = 0
        end = 0
        strs = []
        while end < len(s):
            end += 1
            while s[end-1] in strs:
                strs.pop(0)
                begin += 1
            strs.append(s[end-1])
            if len(strs) > maxlen:
                maxlen = len(strs)
        return maxlen

字典存储(速度快):

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        match = {} # 每个字符的最大位置
        maxx = 0 # 最长长度
        left = 0 # 无重复字符的最小位置
        for i in range(len(s)):
            if s[i] in match:
                left = max(match[s[i]]+1, left)
            match[s[i]] = i
            maxx = max(maxx, i-left+1) # 以当前位置为结尾的最长无重复字符的长度
        return maxx

最长回文子串

三种解法,暴力判断每个子串,两边扩散法,动态规划,两边扩散法速度最快

class Solution:
    def longestPalindrome(self, s: str) -> str:
        if s == "": return ""
        maxlen = 1
        maxstr = s[0]
        # # 暴力解法
        # for i in range(len(s)):
        #     for j in range(len(s)-1,i,-1):
        #         if j-i+1 > maxlen and self.judge(s[i:j+1]):
        #             maxlen = j-i+1
        #             maxstr = str(s[i:j+1])
        # return maxstr
        #两边扩散法
        for i in range(1,len(s)):
            if (len(s)-i)*2+1 <= maxlen: break
            begin = i
            end = i
            while begin-1 >= 0 and s[begin-1] == s[begin]:
                begin -= 1
            while end+1 <= len(s)-1 and s[end+1] == s[end]:
                end += 1
            while begin >= 0 and end <= len(s)-1:
                if begin-1 < 0 or end+1 > len(s)-1:
                    if maxlen < end - begin + 1:
                        maxlen = end - begin + 1
                        maxstr = s[begin:end+1]
                    break
                begin -= 1
                end += 1
                if s[begin] != s[end]:
                    if maxlen < end - begin - 1:
                        maxlen = end - begin - 1
                        maxstr = s[begin+1:end]
                    break
        return maxstr

        # # 动态规划
        # if len(s) < 2:
        #     return s
        # start = 0
        # length = len(s)
        # # 创建二维数组
        # dp = [([False] * length) for i in range(length) ]
        # for right in range(1,length):
        #     for left in range(right):
        #         if (s[left] != s[right]):
        #             continue
        #         if  right == left:
        #             dp[left][right] = True
        #         elif right - left <= 2:
        #             dp[left][right] = True
        #         else :
        #             dp[left][right] = dp[left + 1][right - 1]
                
        #         if dp[left][right] and right - left + 1 > maxlen:
        #             maxlen = right - left + 1
        #             start = left
        # return s[start:start+maxlen]

    def judge(self,s:str) -> bool:
        if len(s) == 1: return True
        begin = 0
        end = len(s) - 1
        while end > begin:
            if s[begin] != s[end]:
                return False
            begin += 1
            end -= 1
        return True

链表

链表中每一个节点为一个对象,对象中包含两个成员变量,第一个是val,代表链表的值,第二个是next,它指向下一个节点,是下一个节点对象的引用。

删除节点

只知道一个节点的情况:

class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

反转链表

普通方法:

def reverseList(head: Optional[ListNode]) -> Optional[ListNode]:
    newHead = None
    while head != None:
        temp = head.next
        head.next = newHead
        newHead = head
        head = temp
    return newHead

1
使用栈解决:
一定要注意让尾巴节点next指针为空,否则将形成环:

def reverseList(head: Optional[ListNode]) -> Optional[ListNode]:
    if head == None:
        return None
    a = []
    while head != None:
        a.append(head)
        head = head.next
    head = a.pop()
    b = head
    while len(a) > 0:
        n = a.pop()
        b.next = n
        b = n
    b.next = None

        return head

合并两个有序链表

我的答案,全部放入数组中进行排序后重新生成链表:

class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        if list1 == None:
            return list2
        elif list2 == None:
            return list1

        if list1.val > list2.val:
            head = list2
        else:
            head = list1
        a = []
        while list1 != None:
            a.append(list1)
            list1 = list1.next
        while list2 != None:
            a.append(list2)
            list2 = list2.next
        a = sorted(a,key=lambda x:x.val)
        h = a.pop(0)
        while len(a)> 0:
            next = a.pop(0)
            h.next = next
            h = next
        return head

递归:

def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
    if list1 is None: return list2
    if list2 is None: return list1
    if list1.val <= list2.val:
        list1.next = self.mergeTwoLists(list1.next, list2)
        return list1
    else:
        list2.next = self.mergeTwoLists(list1,list2.next)
        return list2

判断链表是否有环

我的直观解法:

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        a = []
        while head != None:
            if head not in a:
                a.append(head)
                head = head.next
            else:
                return True
        return False

快慢指针:

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        head1 = head
        head2 = head
        while head1 != None and head2 != None and head2.next != None:
            head1 = head1.next
            head2 = head2.next.next
            if head1 == head2:
                return True
        return False

二叉树

序列化和反序列化

前序遍历,时间较长

class Codec:
    def serialize(self, root):
        def xianxu(root):
            if root == None: 
                self.a.append("None")
            else:
                self.a.append(str(root.val))
                xianxu(root.left)
                xianxu(root.right)
            
        self.a = []
        xianxu(root)
        return ",".join(self.a)

    def deserialize(self, data):
        def reserialize(array):
            if not array: return
            if array[0] == "None":
                array.pop(0)
                return
            head = TreeNode(int(array.pop(0)))
            head.left = reserialize(array)
            head.right = reserialize(array)
            return head

        a = data.split(",")
        return reserialize(a)

层序遍历,使用collection.deque,时间大大减少;

class Codec:
    def serialize(self, root):
        # 层序遍历
        q = collections.deque()
        a = []
        q.append(root)
        while q:
            node = q.popleft()
            if node == None:
                a.append("None")
            else:
                a.append(str(node.val))
                q.append(node.left)
                q.append(node.right)
        return ",".join(a)


    def deserialize(self, data):
        # 层序遍历
        data = data.split(",")
        if not data or data[0] == "None":
            return None
        q = collections.deque()
        head = TreeNode(int(data[0]))
        q.append(head)
        index = 1
        while q:
            node = q.popleft()
            if node != None:
                if data[index] != "None":
                    node.left = TreeNode(int(data[index]))                    
                    q.append(node.left)
                index += 1
                if data[index] != "None":
                    node.right = TreeNode(int(data[index]))
                    q.append(node.right)
                index += 1
        return head

最大深度

自己写的直观递归:

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        leftdepth = 1
        rightdepth = 1
        if root.left != None:
            leftdepth += self.maxDepth(root.left)            
        if root.right != None:
            rightdepth += self.maxDepth(root.right)
        
        return max(leftdepth,rightdepth)

一行递归:

return 0 if root == None else (max(self.maxDepth(root.left),self.maxDepth(root.right)) + 1)

用队列一层层遍历:

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        q = []
        count = 0
        q.append(root)
        while len(q) > 0:
            # 重新计算下一层的节点个数
            l = len(q)
            while l > 0:
                a = q.pop(0)
                l = l - 1
                if a.left != None:
                    q.append(a.left)
                if a.right != None:
                    q.append(a.right)
            # 这一层的元素遍历完毕
            count += 1
        return count

验证二叉搜索树

中序递归

class Solution:
    def __init__(self):
        self.prev = None
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        # 中序遍历递归
        if root == None:
            return True

        if not self.isValidBST(root.left):
            return False
        if self.prev != None and self.prev.val >= root.val:
            return False
        self.prev = root
        if not self.isValidBST(root.right):
            return False

        return True

中序非递归,利用栈做存储

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        s = []
        s.append(root)
        pre = None
        while len(s) > 0:
            while root.left != None:
                s.append(root.left)
                root = root.left

            a = s.pop()
            if pre != None and a.val <= pre.val:
                return False
            pre = a
            
            if a.right != None:
                root = a.right
                s.append(root)

        return True

直接递推

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        minn = -(1<<32)
        maxx = 1<<32
        return self.evaluate(root,minn,maxx)

    def evaluate(self,root,minn,maxx):
        if root == None:
            return True
        if root.val <= minn or root.val >= maxx:
            return False

        return self.evaluate(root.left,minn,root.val) and self.evaluate(root.right,root.val,maxx)

对称二叉树

递归法,从上往下一对对遍历:

class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        return self.bijiao(root.left,root.right)
        

    def bijiao(self,left,right):
        if left == None and right == None:
            return True
        if left == None or right == None or left.val != right.val:
            return False
        return self.bijiao(left.left,right.right) and self.bijiao(left.right,right.left)

非递归法,使用队列的方式,两两添加进队列中:

def isSymmetric(self, root: Optional[TreeNode]) -> bool:
    if root.left == None and root.right == None:
        return True
    q = []
    q.append(root.left)
    q.append(root.right)
    while len(q) > 0:
        a = q.pop(0)
        b = q.pop(0)
        if a == None and b == None:
            continue
        if (a != None and b == None) or (a == None and b != None):
            return False
        if a.val != b.val:
            return False
        q.append(a.left)
        q.append(b.right)
        q.append(a.right)
        q.append(b.left)
    return True
            

层序遍历

参考深度检测中的代码可以进行层序遍历,这其实是宽度优先搜索:

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        q = []
        l = []
        q.append(root)
        while len(q) > 0:
            thisfloor = []
            for i in range(len(q)):
                a = q.pop(0)
                if a == None: continue
                thisfloor.append(a.val)
                q.append(a.left)
                q.append(a.right)
            if thisfloor != []:
                l.append(thisfloor)
        return l

填充右侧指针节点:

class Solution:
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        if root == None: return None
        q = []
        q.append(root)
        while len(q) > 0:
            lastone = None
            for i in range(len(q)):
                node = q.pop(0)
                if node.left != None: q.append(node.left)
                if node.right != None: q.append(node.right)
                if lastone != None: lastone.next = node
                lastone = node
        return root

不用队列的方式(每个节点右指针赋值):

class Solution:
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        if root == None or root.left == None: return root
        origin = root
        head = root
        while head.left != None:
            lastone = None
            headn = head
            while headn != None:
                thisone = headn.left
                headn.left.next = headn.right
                if lastone != None:
                    lastone.next = headn.left
                lastone = headn.right
                headn = headn.next
            head = head.left
        return origin

数组构建平衡二叉树

要求每个节点左右子树高度差不超过1
递归法:

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
        # nums = sorted(nums)
        headindex = int(len(nums) / 2) 
        head = TreeNode(nums[headindex])
        if headindex > 0:
            head.left = self.sortedArrayToBST(nums[:headindex])
        if headindex+1 < len(nums):
            head.right = self.sortedArrayToBST(nums[headindex+1:])
        return head

中序遍历

中序遍历相当于左根右

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        outputs = []
        self.bianli(root,outputs)
        return outputs

    def bianli(self,root,outputs):
        if root == None: return
        self.bianli(root.left,outputs)
        outputs.append(root.val)
        self.bianli(root.right,outputs)

递归法

前序和中序遍历中得到二叉树

递归方法

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
        if len(inorder) == 0: return None
        #if len(inorder) == 1: return TreeNode(inorder[0])
        head = TreeNode(preorder[0])
        index = inorder.index(preorder[0])
        head.left = self.buildTree(preorder[1:index+1],inorder[:index])
        head.right = self.buildTree(preorder[index+1:],inorder[index+1:])

        return head

电话号码的数字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

这里可以看成是一颗n叉树,有3到4个子节点,返回所有的叶子节点的值,可以用广度搜索(层序遍历)

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        dict_ = {'2':"abc",'3':"def",'4':"ghi",'5':"jkl",'6':"mno",'7':"pqrs",'8':"tuv",'9':"wxyz"}
        if len(digits) == 0: 
            return []
        elif len(digits) == 1: 
            return list(dict_[digits[0]])  
        # n叉数层序遍历
        q = list(dict_[digits[0]])
        a = 1
        while len(q) != 0:
            # 遍历一层
            for i in range(len(q)):
                out = q.pop(0)
                for j in range(len(dict_[digits[a]])):
                    q.append(out+dict_[digits[a]][j])
            a += 1
            if a == len(digits):
                return q

岛屿数量

给定数组如
a = [[“1”,”1”,”1”,”1”,”0”],
[“1”,”1”,”0”,”1”,”0”],
[“1”,”1”,”0”,”0”,”0”],
[“0”,”0”,”0”,”0”,”0”]]
1代表陆地,0代表海水,求岛屿数量,被上下左右围绕的一整块陆地为一个岛屿
方法是采用深度优先搜索,搜索到1后把1和相邻的1都置0,完成一个岛屿的搜索,再去搜索下一个岛屿

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        counts = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == '0': continue
                self.zhiling(grid,i,j)
                counts += 1
        return counts


    def zhiling(self,grid,row,column):
        if row > 0 and grid[row-1][column] == '1':
            grid[row-1][column] = '0'
            self.zhiling(grid,row-1,column)
        if column > 0 and grid[row][column-1] == '1':
            grid[row][column-1] = '0'
            self.zhiling(grid,row,column-1)
        if row < len(grid)-1 and grid[row+1][column] == '1':
            grid[row+1][column] = '0'
            self.zhiling(grid,row+1,column)
        if column < len(grid[0])-1 and grid[row][column+1] == '1':
            grid[row][column+1] = '0'
            self.zhiling(grid,row,column+1)

回溯算法

电话号码的数字母组合

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        if not digits: return []

        phone = {'2':['a','b','c'],
                 '3':['d','e','f'],
                 '4':['g','h','i'],
                 '5':['j','k','l'],
                 '6':['m','n','o'],
                 '7':['p','q','r','s'],
                 '8':['t','u','v'],
                 '9':['w','x','y','z']}
                
        def backtrack(conbination,nextdigit):
            if len(nextdigit) == 0:
                res.append(conbination)
            else:
                for letter in phone[nextdigit[0]]:
                    backtrack(conbination + letter,nextdigit[1:])

        res = []
        backtrack('',digits)
        return res

括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        # 选取n个左括号和n个右括号,任何时候选取的右括号个数不超过左括号
        # # 逐个选取括号递归
        # self.outputs = []
        # self.selectkuohao(n,n,'')
        # return self.outputs
        # 递推公式 dp[i]="("+dp[m]+")"+dp[k]   其中m+k=i-1
        if n == 0: return [""]
        outputs = []
        for i in range(n):
            a = self.generateParenthesis(i) 
            b = self.generateParenthesis(n-1-i)
            for j in a:
                for k in b:
                    outputs.append("("+j+")"+k)
        return outputs


    def selectkuohao(self,right,left,strs):
        if left == 0 and right == 0:
            self.outputs.append(strs)
        if left > 0:
            self.selectkuohao(right,left-1,strs+"(")
        if left != right:
            self.selectkuohao(right-1,left,strs+")")

生成所有子集

逐个选择递归,这题可以用递推递归

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        self.outputs = []
        self.selectnumber(nums,[],0)
        return self.outputs

    def selectnumber(self,nums,l,start):
        self.outputs.append(l)
        for i in range(start,len(nums)):
            self.selectnumber(nums,l+[nums[i]],i+1)

位运算方法,子集个数为2的n次方,n为数组长度

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        outputs = []
        length = 1 << len(nums)
        for i in range(length):
            a = []
            for j in range(len(nums)):
                if i >> j & 1 == 1:
                    a.append(nums[j])
            outputs.append(a)c
        return outputs

图搜索组合成单词

给定图board和单词word,如果单词存在图中,则返回true,否则为false,单词在图中可以沿着上下左右遍历,遍历的格子不允许重复

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        self.a = 0
        for i in range(len(board)):
            for j in range(len(board[0])):
                if board[i][j] == word[0]:
                    self.select(board,i,j,word,1)
                if self.a == 1: return True
        return False

    def select(self,board,row,column,word,i):
        if i == len(word): 
            self.a = 1 
            return
        board[row][column] = ""
        if row > 0 and word[i] == board[row-1][column]:
            self.select(board,row-1,column,word,i+1)
        if column > 0 and word[i] == board[row][column-1]:
            self.select(board,row,column-1,word,i+1)
        if row < len(board)-1 and word[i] == board[row+1][column]:
            self.select(board,row+1,column,word,i+1)
        if column < len(board[0])-1 and word[i] == board[row][column+1]:
            self.select(board,row,column+1,word,i+1)
        board[row][column] = word[i-1]

排序和搜索

前k个高频元素(字典内部进行排序)

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        dict_ = defaultdict(int)
        for i in range(len(nums)):
            dict_[nums[i]] += 1
        sortdict = sorted(dict_.items(),key=lambda x:x[1],reverse=True)
        outputs = []
        for i in range(k):
            outputs.append(sortdict[i][0])
        return outputs

第k大元素(n时间复杂度、构建堆)

堆排序:

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
      # 堆排序
        length = len(nums)

        def max_heap(nums, start, end):
            dad = start
            son = dad * 2 + 1
            while son <= end:
                if son + 1 <= end and nums[son] < nums[son+1]:
                    son += 1
                if nums[dad] >= nums[son]:
                    return
                else:
                    nums[dad], nums[son] = nums[son], nums[dad]
                    dad = son
                    son = dad * 2 + 1
        # 构建大顶堆
        for i in range(length//2, -1, -1):
            max_heap(nums, i, length-1)
        coun = 0
        # 堆顶与最后元素交换
        for i in range(length-1, 0, -1):
            nums[0], nums[i] = nums[i], nums[0]
            coun += 1
            if coun == k:
                break
                # 再次构建堆
            max_heap(nums, 0, i-1)
        return nums[-k]

优化方法:构建容量为k的小顶堆,更换堆顶元素即可

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        # 构建容量为k的小顶堆
        def min_heap(nums, start, end):
            dad = start
            son = dad * 2 + 1
            while son <= end:
                if son + 1 <= end and nums[son] > nums[son+1]:
                    son += 1
                if nums[dad] <= nums[son]:
                    return
                else:
                    nums[dad], nums[son] = nums[son], nums[dad]
                    dad = son
                    son = dad * 2 + 1

        for i in range(k//2, -1, -1):
            min_heap(nums, i, k-1)
        for i in range(k,len(nums)):
            if nums[i] < nums[0]:
                continue
            nums[0],nums[i] = nums[i],nums[0]
            min_heap(nums, 0, k-1)
        return nums[0]

搜索有序矩阵

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        column = len(matrix[0]) - 1
        row = 0
        while row < len(matrix) and column >= 0:
            if matrix[row][column] < target:
                row += 1
            elif matrix[row][column] > target:
                column -= 1
            else:
                return True
        return False 

二分法

找bug

用start和end不断逼近范围,最后输出start的index

class Solution:
    def firstBadVersion(self, n: int) -> int:
        start = 1
        end = n
        while start < end:
            mid = int((start+end) / 2)
            if isBadVersion(mid):
                end = mid
            else:
                start = mid + 1
        return start

找非递减序列中的目标值开始和结束的位置

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if len(nums) == 0: return [-1, -1]
        left, right, first = 0, len(nums) - 1, -1
        # 先找第一个出现的位置
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] == target:
                first = mid
                right = mid - 1    # 往前找还有没有
            if nums[mid] < target:
                left = mid + 1
            if nums[mid] > target:
                right = mid - 1
        if first == -1: return [-1, -1]  # 如果找不到,就不用找了
        # 再找最后出现的位置,这时候边界为[first,len-1]
        left, right, last = first, len(nums) - 1, -1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] == target:
                last = mid
                left = mid + 1   # 往后找还有没有
            if nums[mid]<target:
                left = mid+1
            if nums[mid]>target:
                right = mid-1
        return [first,last]

找一个经过一次旋转的有序数组中的元素下标

有序数组从中断开左右进行过一次交换

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        begin = 0
        end = len(nums) - 1
        while end >= begin:
            mid = (begin+end)//2
            if nums[mid] == target:
                return mid
            elif nums[mid] > target:
                if nums[end] >= nums[mid] or (nums[mid] >= nums[begin] and target >= nums[begin]):
                    end = mid - 1
                else:
                    begin = mid + 1
            else:
                if nums[mid] >= nums[begin]or (nums[mid] <= nums[end] and target <= nums[end]):
                    begin = mid + 1
                else:
                    end = mid - 1
        return -1

动态规划

走楼梯

爬n阶楼梯,每次走一到两阶,共有几种走法:

class Solution:
    def climbStairs(self, n: int) -> int:
        dp = []
        dp.append(0)
        dp.append(1)
        dp.append(2)
        for i in range(n - 2):
            dp.append(dp[-1] + dp[-2])
        return dp[n]

买卖股票

计算什么时候获买卖一次股票得最大收益

def maxProfit(self, prices: List[int]) -> int:
    maxPro = 0
    minprice = 10000
    for i in range(len(prices)):
        if prices[i] < minprice:
            minprice = prices[i]
        elif prices[i] - minprice > maxPro:
            maxPro = prices[i] - minprice
    return maxPro

数相加

连续n个数相加的最大值,这里需要额外存一个包含最后一个元素的最大值用来比较:

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        maxsum = nums[0]
        lastmaxsum = nums[0]
        for i in range(1,len(nums)):
            lastmaxsum = nums[i] if nums[i] > lastmaxsum + nums[i] else lastmaxsum + nums[i]
            maxsum = maxsum if maxsum > lastmaxsum else lastmaxsum
        return maxsum

计算全部不连续的n个正数相加的最大值:
我的直观写法:

class Solution:
    def rob(self, nums: List[int]) -> int:
        maxprofit = nums[0]
        lastmax = nums[0]
        preonemax = 0
        pretwomax = 0
        for i in range(1,len(nums)):
            temp = preonemax
            preonemax = lastmax
            lastmax = temp + nums[i] if temp + nums[i]>pretwomax + nums[i] else pretwomax + nums[i]
            maxprofit = max(lastmax, preonemax, maxprofit)
            pretwomax = temp

        return maxprofit

零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

本质需要计算dp[amount],dp[i] = min(dp[i-coin[0]],dp[i-coin[1]]…,dp[i-coin[j]])+1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        a = [0] + [1<<31] * amount
        for i in range(1,len(a)):
            minn = 1 << 31
            for j in range(len(coins)):
                if i-coins[j] >= 0 and a[i-coins[j]]+1 < minn:
                    minn = a[i-coins[j]]+1
            a[i] = minn
        if a[amount] != 1<<31:
            return a[amount]
        else:
            return -1

找递增子集最大长度(二分法)

动态规划,时间复杂度n^2

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        dp = [1] * len(nums)
        for i in range(len(nums)):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i],dp[j]+1)
        return max(dp)

贪心算法+二分法:

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        d = [nums[0]]
        for i in range(1, len(nums)):
            if nums[i] > d[-1]:
                d.append(nums[i])
            else:
                begin = 0
                end = len(d) - 1
                while end > begin:
                    mid = (begin + end) // 2
                    if d[mid] >= nums[i]:
                        end = mid
                    elif d[mid] < nums[i]:
                        begin = mid + 1
                d[begin] = nums[i]
        return len(d)

最优解法,不用append极限省时间:

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        d,res = [0] * len(nums), 0
        for i in range(len(nums)):
            begin,end = 0,res
            while end > begin:
                mid = (begin + end) // 2
                if d[mid] >= nums[i]:
                    end = mid
                else:
                    begin = mid + 1
            d[begin] = nums[i]
            if begin == res:
                res += 1
        return res
                

设计问题

等可能打乱数组

class Solution:
    def __init__(self, nums: List[int]):
        self.nums = nums

    def reset(self) -> List[int]:
        return self.nums

    def shuffle(self) -> List[int]:
        newlist = self.nums.copy()
        for i in range(len(newlist)-1):
            a = random.randint(i, len(newlist)-1)
            if a == i: continue
            temp = newlist[i]
            newlist[i] = newlist[a]
            newlist[a] = temp
        return newlist

设计一个检索到最小元素的栈

无脑方法,耗时超越5%Python程序

class MinStack:
    def __init__(self):
        self.s = []

    def push(self, val: int) -> None:
        self.s.append(val)

    def pop(self) -> None:
        self.s.pop()

    def top(self) -> int:
        return self.s[-1]

    def getMin(self) -> int:
        m = 1 << 31
        for i in range(len(self.s)):
            if self.s[i] < m:
                m = self.s[i]
        return m

使用链表,用时64ms,超过70%Python程序

class Node:
    def __init__(self,val=None,nxt=None):
        self.val = val
        self.next = nxt
        self.min = val

class MinStack:
    def __init__(self,val=None,nex=None):
        self.head = None

    def push(self, val: int) -> None:
        if self.head == None:
            n = Node(val)
        else:
            n = Node(val,self.head)
            if n.min > self.head.min:
                n.min = self.head.min
        self.head = n

    def pop(self) -> None:
        if self.head != None:
            self.head = self.head.next

    def top(self) -> int:
        return self.head.val

    def getMin(self) -> int:
        return self.head.min

数学

加减乘除的本质

应对python无限制扩展位数的方法

# 限制变量为32位
def int32(value):
    return -(value & 0x80000000) | (value & 0x7FFFFFFF)

加法,本位+进位

def add(a, b):
    while b:
        carry = a & b
        a = a ^ b
        b = carry << 1
    return a

减法,本位-借位

def subtract(a, b):
    while b:
        borrow = (~a) & b
        a = a ^ b
        b = borrow << 1
    return a

乘法

def multiply(a, b):
    result = 0
    neg = a < 0 and b > 0 or a > 0 and b < 0
    a, b = abs(a), abs(b)
    while b:
        if b & 1:
            result = add(result, a)
        a <<= 1
        b >>= 1
    return -result if neg else result

除法

def divide(a, b):
    if b == 0:
        raise ValueError("除数不能为零")
    neg = a < 0 and b > 0 or a > 0 and b < 0
    a, b = abs(a), abs(b)
    result = 0
    while a >= b:
        temp, i = b, 1
        while a >= (temp << 1):
            temp <<= 1
            i <<= 1
        a = subtract(a, temp)
        result = add(result, i)
    return -result if neg else result

选出n以内的质数

如果按照一般质数的定义(不能被前面所有的质数整除)逐一筛选,时间太长,算法超时

埃氏筛选法:
默认所有数为质数,把合数筛出来,剩下质数。筛选方法如下,从2开始循环,默认质数,那么22每隔2都是合数。3没被筛,所以是质数,33开始,每隔3是合数筛选出来,以此类推。

class Solution:
    # 埃式筛选法
    def countPrimes(self, n: int) -> int:
        a = [True] * n
        count = 0
        for i in range(2,n):
            if a[i]:
                count += 1
                for j in range(i*i,n,i):
                    a[j] = False
        return count

读罗马数字

class Solution:
    def romanToInt(self, s: str) -> int:
        dic = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000}
        ret = 0
        for i in range(len(s)-1,-1,-1):
            if ret>4*dic[s[i]]:
                ret -= dic[s[i]]
            else:
                ret += dic[s[i]]
        return ret

乘阶后有几个零

class Solution:
    def trailingZeroes(self, n: int) -> int:
        # 计算有几个5和10,25记作两个
        count = 0
        while n >= 5:
            count += int(n/5)
            n /= 5
        return count

分数到小数

给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。如果小数部分为循环小数,则将循环的部分括在括号内。

用字典存余数和对应商在数组的下标,用数组存商,判断余数是否重复出现,在数组对应的位置加括号即可

class Solution:
    def fractionToDecimal(self, numerator: int, denominator: int) -> str:
        if numerator == 0: return "0"
        if numerator % denominator == 0:
            return str(numerator//denominator)
        neg = (numerator>0) ^ (denominator>0)
        numerator,denominator = abs(numerator),abs(denominator)
        a = []
        if neg: a.append("-")
        a.append(str(int(numerator//denominator)))
        a.append(".")
        d = {}

        Quotient = numerator // denominator
        remainder = numerator % denominator
        while remainder != 0:
            remainder = remainder * 10
            Quotient = remainder // denominator
            remainder = remainder % denominator
            if remainder in d:
                a.insert(d[remainder]+1,"(")
                a.append(str(Quotient))
                a.append(")")
                return "".join(a)
            d[remainder] = len(a)
            a.append(str(Quotient))

        return "".join(a)

其他

倒转二进制

class Solution:
    def reverseBits(self, n: int) -> int:
        sum = 0
        for i in range(32):
            sum <<= 1
            sum |= n & 1
            n >>= 1

        return sum

杨辉三角

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        outputs = [[1]]
        lastrow = [1]
        for i in range(2,numRows+1):
            thisrow = []
            for j in range(len(lastrow)+1):
                if j == 0 or j == len(lastrow):
                    thisrow.append(1)
                else:
                    thisrow.append(lastrow[j-1] + lastrow[j])
            outputs.append(thisrow)
            lastrow = thisrow
        return outputs

有效括号

class Solution:
    def isValid(self, s: str) -> bool:
        d = {")":"(","]":"[","}":"{"}
        stack = []
        for i in range(len(s)):
            if s[i] == "(" or s[i] == "[" or s[i] == "{":
                stack.append(s[i])
            elif not stack or d[s[i]] != stack[-1]:
                    return False
            else:
                stack.pop()
        if stack: return False
        return True

寻找缺失数字,可用异或抵消大法:

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        a = 0
        for i in range(len(nums)):
            a = a ^ nums[i] ^ (i+1)
        return a

任务调度器:
输入:tasks = [“A”,”A”,”A”,”B”,”B”,”B”], n = 2
输出:8
解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B
在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。

class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        dict_ = defaultdict(int)
        for i in range(len(tasks)):
            dict_[tasks[i]] += 1
        sortdict = sorted(dict_.values(),reverse=True)
        count = sortdict[0] + (sortdict[0] - 1) * n
        for i in range(1,len(sortdict)):
            if sortdict[0] != sortdict[i]:
                break
            count += 1
        return max(len(tasks),count)

文章作者: 微笑紫瞳星
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 微笑紫瞳星 !
  目录