算法练习 剑指Offer全解 python

03 数组中重复的数字

题目描述:

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

方法一:暴力法

class Solution:
    def findRepeatNumber(self, nums):
        n = len(nums)
        for i in range(0,n-1):
            for j in range(i+1,n):
                if nums[j] == nums[i]:
                    return nums[i]

运行超时

方法二:哈希表

class Solution:
    def findRepeatNumber(self, nums):
        hash_set = dict()
        for item in nums:
            if item in hash_set:
                return item
            else:
                hash_set[item] = 1

04 二维数组中的查找

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

方法一:暴力法

class Solution:
    def findNumberIn2DArray(self, matrix,target):
        if matrix == []:
            return False
        flag = False
        row_len = len(matrix)
        col_len = len(matrix[0])
        for i in range(row_len):
            for j in range(col_len):
                if matrix[i][j] == target:
                    flag = True
        return flag

竟然可以通过

方法二:从右上角出发,目标比数组数大则向左找,目标比数组数小则向下找。

class Solution:
    def findNumberIn2DArray(self, matrix, target):
        if matrix == []:
            return False
        flag = False
        row_len = len(matrix)
        col_len = len(matrix[0])
        i = row_len - 1
        j = 0
        while i >= 0 and j <= col_len - 1:
            if matrix[i][j] > target:
                i = i - 1
            elif matrix[i][j] < target:
                j = j + 1
            elif matrix[i][j] == target:
                flag = True
                break
        return flag

05 替换空格

class Solution:
    def replaceSpace(self, s):
        return s.replace(" ","%20")

06 从尾到头打印列表

遍历链表后reverse即可

class Solution:
    def reversePrint(self, head):
        ret = []
        p = head
        while p != None:
            ret.append(p.val)
            p = p.next
        ret.reverse()
        return ret

07 重建二叉树

Python3重建二叉树递归解法,这个视频就够了! - 重建二叉树 - 力扣(LeetCode)https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/solution/python3zhong-jian-er-cha-shu-di-gui-jie-drv5p/

依据手动建树的方法模拟一次递归过程:

需要的信息有:从前序序列中找到根节点的值、中序序列中左子树的左边界、中序序列中右子树的左边界

思路如下:

从前序序列中找到根节点的值

根据根节点的值,找到根节点在中序序列中的位置(提前构造好{值:位置}的dict)

构建根节点(将根节点加入树中)

根据左右子树的边界信息,递归的对其左右子树进行上述操作

求解如下:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, preorder, inorder):

        # 在中序序列中,index可以根据值找到位置
        index = {val:i for i,val in enumerate(inorder)}

        # 递归函数建树:根节点在前序序列(preorder)中的下标,左、右子树在中序序列中的左右边界
        def build(preRootId,inL,inR):
            if inL > inR:  # 递归出口 左边界大于右边界
                return None
            rootVal = preorder[preRootId]  # 得到根节点的值
            inRootId = index[rootVal]  # 根据根节点的值 找到根节点在中序序列中的下标

            # 求左子树的长度,目的是找到前序序列中右子树的根节点的起始地址
            leftSize = inRootId - inL

            # 构建根节点
            root = TreeNode(rootVal)
            # 构建左子树
            root.left = build(preRootId+1, inL, inRootId-1)
            # 构建右子树
            root.right = build(preRootId+1+leftSize,inRootId+1,inR)
            return root
        return build(0,0,len(inorder)-1)

09 用两个栈实现队列

class CQueue:
    def __init__(self):
        self.Stack,self.Stack2 = [],[]

    def appendTail(self, value):
        self.Stack.append(value)

    def deleteHead(self):
        if self.Stack2:
            return self.Stack2.pop()
        if not self.Stack:
            return -1
        while self.Stack:
            self.Stack2.append(self.Stack.pop())
        return self.Stack2.pop()
        

还是要注意 Stack == [] 和 Stack is None是两个完全不同的概念。

10-1 斐波那契数列

class Solution:
    def fib(self, n):
        if n==0:
            return 0
        elif n==1:
            return 1
        else:
            return self.fib(n - 1) + self.fib(n - 2)

递归时超出时间限制。

用一个一维数组存储前项结果。

class Solution:
    def fib(self, n):
        if n == 0:
            return 0
        if n == 1:
            return 1
        dp = [0] * (n+1)
        dp[0] = 0
        dp[1] = 1
        for i in range(2,n+1):
            dp[i] = dp[i-1]+dp[i-2]
        return dp[n] % 1000000007

当然,也可以不用额外存储空间:

class Solution:
    def fib(self,n):
        if n == 0 :
            return 0
        a, b = 1, 1
        for i in range(n - 1):
            a, b = b, a + b
        return a % 1000000007

10-2 青蛙跳台阶

class Solution:
    def numWays(self, n):
        if n == 0:
            return 1
        elif n == 1:
            return 1
        elif n == 2:
            return 2
        else:
            return (self.numWays(n-1)+self.numWays(n-2)) % 1000000007

递归超出时间限制。

其解法与上述斐波那契数列一致。

class Solution:
    def numWays(self, n):
        if n == 0:
            return 1
        a,b = 1,2
        for i in range(n - 1):
            a, b = b, a + b
        return a % 1000000007

11 旋转数组的最小数字

检测a[i+1]比a[i]小的元素即可

class Solution:
    def minArray(self, numbers):
        for i in range(0,len(numbers)-1):
            if numbers[i+1]<numbers[i]:
                return numbers[i+1]
        return numbers[0]

12 矩阵中的路径

leetcode官方解法(DFS)

class Solution:
    def exist(self,board,word):
        directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]  # 四种前进方向
        def check(i,j,k):  #(i,j)是board中的坐标,k记录了已经比较到单词中的第k个字母
            if board[i][j] != word[k]:  # 终止条件:出现不相等
                return False
            if k == len(word) - 1:  # 终止条件:比较完了整个单词
                return True

            visited.append((i,j))  # visited数组:记录已经走过的路径,避免兜圈子

            result = False
            for di,dj in directions:
                newi,newj = i+di,j+dj  # 出发
                if 0 <= newi < len(board) and 0 <= newj < len(board[0]): # 当然只考虑board内的情况
                    if(newi, newj) not in visited:  # 而且没访问过
                        if check(newi, newj, k+1):  # 递归:只有k == len(word)-1时 才会将这些路径标记位True
                            result = True
                            break
            visited.remove((i, j))
            return result

        h, w = len(board), len(board[0])
        visited = []
        for i in range(h):
            for j in range(w):
                if check(i, j, 0):  # 初始值:k为0
                    return True

        return False

13 机器人的运动范围

本题参考了leetcode官方解法(BFS)

from queue import Queue
def digitsum(n):  # 求各位和函数
    ans = 0
    while n:
        ans += n % 10
        n //= 10
    return ans

class Solution:
    def movingCount(self, m, n, k):
        q = Queue() 
        q.put((0, 0))  # 将(0,0)加入队列
        s = set()  # s是机器人活动范围集合 集合自带去重功能
        while not q.empty():  
            x, y = q.get()  # 每次出队一个坐标分析
            if (x, y) not in s and 0 <= x < m and 0 <= y < n and digitsum(x) + digitsum(y) <= k:  #符合题目所指条件
                s.add((x, y))
                for nx, ny in [(x + 1, y), (x, y + 1)]:  # 将右侧、下册元素入队
                    q.put((nx, ny))
        return len(s)  

14 剪绳子

class Solution:
    def cuttingRope(self, n):
        dp = [0] * (n+1)
        if n <= 2:
            return 1
        if n == 3:
            return 2
        # dp[x]定义为将绳子切成若干段的最大乘积
        # 注意:当i≥4的时候 2、3段不切的乘积收益更高
        dp[1] = 1
        dp[2] = 2
        dp[3] = 3
        for i in range(4,n+1):  # i 是绳子的长度
            for j in range(1,i//2+1):  # j 是刀的位置
                dp[i] = max(dp[i],dp[j]*dp[i-j])
        return dp[n]

15 二进制中1的个数

简单循环遍历

class Solution:
    def hammingWeight(self, n):
        n = bin(n)
        cnt = 0
        for i in str(n):
            if i == '1':
                cnt = cnt + 1
        return cnt

位运算的巧妙推论:n & (n−1)的结果是将n二进制中最低位的1变为0

class Solution:
    def hammingWeight(self, n):
        ret = 0
        while n:
            n &= n - 1
            ret += 1
        return ret

16 数值的整数次方(重要)

常规思路:超时(0.00001 2147483647 )

class Solution:
    def myPow(self, x, n):      
        if n < 0:
            x = 1/x
            n = abs(n)
        if n == 0:
            return 1.0
        temp = x
        while n-1:
            x = x * temp
            n = n - 1
        return x

分治思路:

class Solution:
    def myPow(self, x, n):
        if n == 0:
            return 1
        def quickMul(n):
            if n == 0:
                return 1
            y = quickMul(n // 2)
            if n % 2 == 0:
                return y*y
            else:
                return y*y*x

        if n>0:
            return quickMul(n)
        elif n<0:
            return 1/quickMul(-n)                

17 打印从1到最大的n位数

class Solution:
    def printNumbers(self, n):
        limit = 1*pow(10,n)
        lst = list()
        for i in range(1,limit):
            lst.append(i)
        return lst

18 删除链表的节点(按值)

考虑到删除第一个元素的情况即可

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def deleteNode(self, head, val):
        p = head
        if head.val == val:
            head = head.next
            return head
        while p.next.val != val:
            p = p.next
        p.next = p.next.next
        return head

21.调整数组顺序使奇数位于偶数前面

class Solution:
    def exchange(self, nums):
        temp_1 = []
        temp_2 = []
        for i in nums:
            if i%2 != 0:
                temp_1.append(i)
            else:
                temp_2.append(i)
        temp_1.extend(temp_2)
        return temp_1

python合并两个列表用extend

22.链表中倒数第k个节点

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getKthFromEnd(self, head, k):
        def linklist_length(head):
            length = 0
            while head:
                length+=1
                head = head.next
            return length
            
        length = linklist_length(head)
        n = length - k
        cnt = 0
        while cnt<n:
            head = head.next
            cnt = cnt + 1
        return head

24.反转链表

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head):
        if head is None:
            return None
        cur = head
        pre = None
        # 四步走
        while cur.next:
            nex = cur.next # 存储cur的next
            cur.next = pre
            pre = cur
            cur = nex
        cur.next = pre
        return cur

        # 四步走
        while cur:
            nex = cur.next # 存储cur的next
            cur.next = pre
            pre = cur
            cur = nex
        return pre

25.合并两个排序的链表

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def mergeTwoLists(self, l1, l2):
        prehead = ListNode(-1)

        prev = prehead
        while l1 and l2:
            if l1.val <= l2.val:
                prev.next = l1
                l1 = l1.next
            else:
                prev.next = l2
                l2 = l2.next            
            prev = prev.next

        prev.next = l1 if l1 is not None else l2
        return prehead.next

26.树的子结构

递归思路

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    # B是A的子结构:B从A的某个节点出发的遍历序列完全相同
    # 递归的对A的每一个节点进行判定
    def isSubStructure(self, A, B):
        def Judge(A,B): # 依次对比(遍历)从该节点出发的A的某棵子树与B
            if B is None: # 递归终止条件:B已经访问完
                return True
            if A is None:# 由于上一个判断 这里已经默认了B不为空 B不为空而A为空 证明B不是A的子结构
                return False
            if B.val != A.val:
                return False
            return Judge(A.left,B.left) and Judge(A.right,B.right)
        if A is None or B is None:
            return False
        return Judge(A,B) or self.isSubStructure( A.left, B) or self.isSubStructure( A.right, B)

27.二叉树的镜像

递归是要找到最小问题 从最小问题开始解决

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def mirrorTree(self, root):
        if root is None:
            return;     
        left = self.mirrorTree(root.left)
        right = self.mirrorTree(root.right)
        root.left, root.right = right, left
        return root

28.对称的二叉树

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isSymmetric(self, root):
        def Judge(root1,root2):
            if root1 is None and root2 is None:
                return True
            if root1 is None or root2 is None:
                return False
            return root1.val == root2.val and Judge(root1.left,root2.right) and Judge(root1.right,root2.left)
        if root is None:
            return True;
        return Judge(root.left,root.right)

29.顺时针打印矩阵

换方向的条件:是模拟走后看是否有问题,而不是走之前判断四周;

class Solution:
    def spiralOrder(self, matrix):
        if not matrix:
            return list()

        rows = len(matrix)
        columns = len(matrix[0])

        directions = [[0,1],[1,0],[0,-1],[-1,0]]
        index = 0

        visited = [[False for col in range(columns)] for row in range(rows)]

        ret = []
        row = 0
        column = 0
        for i in range(0,columns*rows):
            ret.append(matrix[row][column])
            visited[row][column] = True
            nextRow,nextCol = row +  directions[index][0],column +  directions[index][1]
            # 换方向条件:这样走会有问题
            if nextRow == rows or nextCol == columns or nextRow<0 or nextCol<0 or visited[nextRow][nextCol]:
                index = (index+1)%4
            row = row +  directions[index][0]
            column = column +  directions[index][1]

    
        return ret

30.包含min函数的栈

我们可以在每个元素 a 入栈时把当前栈的最小值 m 存储起来。在这之后无论何时,如果栈顶元素是 a,我们就可以直接返回存储的最小值 m

class MinStack:

    def __init__(self):
        self.stack = []
        self.mini_stack = [math.inf]


    def push(self, x: int) -> None:
        self.stack.append(x)
        self.mini_stack.append(min(self.mini_stack[-1],x))

    def pop(self) -> None:
        self.stack.pop()
        self.mini_stack.pop()

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

    def min(self) -> int:
        return self.mini_stack[-1]


# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.min()

31.栈的压入、弹出序列

class Solution:
    def validateStackSequences(self, pushed, popped):
        if pushed is None:
            return False
        stack = []
        length = len(pushed)
        i,j = 0,0
        while j!= length and i!=length:
            if pushed[i] != popped[j]:
                stack.append(pushed[i])
                i = i + 1
            elif pushed[i] == popped[j]:
                i = i + 1
                j = j + 1

            while j< length and stack and stack[-1] == popped[j] : # 这里的判断顺序不能变 否则会出现越界问题
                j = j + 1
                stack.pop()

        return len(stack) == 0                

可以简化为:

class Solution:
    def validateStackSequences(self, pushed, popped) :
        st, j = [], 0
        for x in pushed:
            st.append(x)
            while st and st[-1] == popped[j]:
                st.pop()
                j += 1
        return len(st) == 0

32.从上到下打印二叉树(重要)

层次遍历二叉树需要借助队列完成

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root):
        if root is None: 
            return []
        res, queue = [],collections.deque() # 队列
        queue.append(root)
        while queue:
            node = queue.popleft()
            res.append(node.val)
            if node.left: queue.append(node.left)
            if node.right: queue.append(node.right)
        return res

但是在一些公司的面试考核中,是不允许使用python队列的库函数deque()的,可以用pop(0)代替popleft方法

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root):
        if root is None:
            return []
        
        ret = []
    # 层次遍历(广度优先)
        def bfs(root):
            queue = [root]
            while queue:
                currentNode = queue.pop(0) # 等价于popleft
                ret.append(currentNode.val)
                if currentNode.left:
                    queue.append(currentNode.left)
                if currentNode.right:
                    queue.append(currentNode.right)
        bfs(root)
        return ret

变式:以列表套列表的形式打印,思路是对于每一层的结果单独生成列表(tmp),用nxt存储下一层的结果,queue清零就是每层结束的标志。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root):
        if root is None:
            return []
        
        ret = []
        def bfs(root):
            queue = [root]
            while queue:
                nxt = []
                tmp = []
                for node in queue:
                    tmp.append(node.val)
                    if node.left:
                        nxt.append(node.left)
                    if node.right:
                        nxt.append(node.right)
                ret.append(tmp)
                queue = nxt
        bfs(root)
        return ret

33.二叉搜索树的后序遍历序列

[1,3,2,6,5]

观察一个正确的序列,可以发现的关系是 [1,3,2] [6] [5]依次为比根小、比根大、根;

用两个指针分别指向位置0和len-1(根)处;

未完待续

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值