练习题 Day5

剑指 Offer 第二版

剑指 Offer 15. 二进制中1的个数

输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。

输入:00000000000000000000000000001011

输出:3

# 简单思路,二进制转化,随后统计
n = 99
res = []
while n > 0:
    res.append(n % 2)
    n = n // 2
binary = []
while res:
    binary.append(res.pop())
print(binary)
count = 0
for i in range(len(binary)):
    if binary[i] == 1:
        count += 1
print(count)
[1, 1, 0, 0, 0, 1, 1]
4

剑指 Offer 18. 删除链表的节点

链表中节点的值互不相同

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

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

class Solution:
    
    def deletNode(self, head, val):
        
        if head.val == val:
            return head.next
        pred = head
        
        while pred.next:
            if pred.next.val == val:
                pred.next = pred.next.next
                return head
            else:
                pred = pred.next

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

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

nums = [1,6,2,5,3,4]
res = []
for i in nums:
    if i % 2:
        res.append(i)
for j in nums:
    if j % 2 == 0:
        res.append(j)
print(res)
[1, 5, 3, 6, 2, 4]

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

本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。

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

class Solution:
    def getKthFromEnd(self, head, k):
        nodelist = []
        current = head
        while current:
            nodelist.append(current)
            current = current.next
        for i in range(k - 1):
            nodelist.pop()
        return nodelist.pop()

剑指 Offer 24. 反转链表

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

class Solution:
    def reverseList(self, head):
        pre, cur = None, head
        while cur:
            tmp = cur.next
            cur.next = pre
            pre = cur
            cur = tmp
        return pre

剑指 Offer 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):
        start = ListNode()
        node = start
        while (l1 or l2):
            # 只有一个
            if not (l1 and l2):
                node.next = l1 or l2
                break
            # 都非空,比大小
            elif l1.val < l2.val:
                node.next = l1
                l1 = l1.next
            else:
                node.next = l2
                l2 = l2.next
            node = node.next
        return start.next

剑指 Offer 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):
        return self.mirror(root)

    def mirror(self, node):
        if node:  # 非空
            if node.left and node.right:  # 左右都有
                node.right, node.left = self.mirror(node.left), self.mirror(node.right)
            elif node.left:  # 只有一边
                node.left, node.right = None, self.mirror(node.left)
            elif node.right: # 只有一边
                node.right, node.left = None, self.mirror(node.right)
        return node

# 解法二(更简洁)
    def mirrorTree(self, root):
        if root:
            # 关键点在于先左右子节点交换!!!,就不用考虑赋值None了
            root.left, root.right = root.right, root.left 
            if root.left:
                root.left = self.mirrorTree(root.left)
            if root.right:
                root.right = self.mirrorTree(root.right)
        return root

剑指 Offer 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):
        if not root:   # none,直接返回T
            return True
        else: 
            return self.ifmatch(root.left, root.right)
        
    def ifmatch(self, a, b):
        if (not a) and (not b):  # 都是none
            return True
        elif a and b and a.val == b.val:    # 都存在且数值相等,对子节点递归!
            return self.ifmatch(a.left, b.right) and self.ifmatch(a.right, b.left) 
        # 只有一个存在或者数值不相等
        return False

剑指 Offer 29. 顺时针打印矩阵

按照从外向里以顺时针的顺序依次打印出每一个数字。

第一步,p维矩阵

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]

1  2  3  4
5  6  7  8
9  10 11  12
13 14 15 16 

输出:[1,2,3,6,9,8,7,4,5]

def printmatrix(res, matrix):
    p = len(matrix)
    # 停止条件
    
    if p == 1:
        res.append(matrix)
        return
    
    for i in range(0, p):
        res.append(matrix[0][i])
    for i in range(1, p - 1):
        res.append(matrix[i][p - 1])
    for i in range(1, p):
        res.append(matrix[p - 1 ][p - i])
    for i in range(1, p):
        res.append(matrix[p - i][0])
        
    if p >= 3:
        return printmatrix(res, [x[1: len(matrix)-1] for x in matrix[1:len(matrix)-1]])
    
res = []
matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
printmatrix(res, matrix) 
print(res)
[1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10]

第二步,m x n 维矩阵

1  2  3  4
5  6  7  8
9 10 11 12

6 7 行

class Solution:
    
    def printmatrix(self, res, matrix):
        # 空
        if (not matrix) or (not matrix[0]):
            return
        # 非空
        m, n = len(matrix), len(matrix[0])
        # 向量
        if m == 1:       
            res +=  matrix[0]
            return
        if n ==1:
            res += [x[0] for x in matrix]
            return
        # 矩阵
        for i in range(0, n):
            res.append(matrix[0][i])
        for i in range(1, m):
            res.append(matrix[i][n - 1])
        for i in range(1, n):
            res.append(matrix[m - 1][n - i -1])
        for i in range(1, m - 1):
            res.append(matrix[m - i -1][0])
        # 递归
        self.printmatrix(res, [x[1: (n-1)] for x in matrix[1:(m-1)]])
        return
    
    def spiralOrder(self, matrix):
        res = []
        self.printmatrix(res, matrix) 
        return res
matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
s = Solution()
s.spiralOrder(matrix)
[1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7]

剑指 Offer 30. 包含min函数的栈

class MinStack:

    def __init__(self):
        self.stack = [] 
        self.mins = []  # 存每次push之后最小值的栈

    def push(self, x: int) -> None:
        self.stack.append(x)
        if not self.mins or x <= self.mins[-1]:
            self.mins.append(x)
            
    def pop(self) -> None:
        x = self.stack.pop()
        tmp = self.mins.pop()
        if tmp != x:
            self.mins.append(tmp)
            
    def top(self) -> int:
        tmp = self.stack.pop()
        self.stack.append(tmp)
        return tmp

    def min(self) -> int:
        tmp = self.mins.pop()
        self.mins.append(tmp)
        return tmp
s = MinStack()
s.push(-3),s.push(0),s.push(-2)
s.min(),s.pop(),s.top(),s.min()
(-3, None, 0, -3)

剑指 Offer 32 打印二叉树

这个部分的关键在于理解——队列的思想!

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

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

# 定义测试用例
t1 = TreeNode(3)
t2 = TreeNode(9)
t3 = TreeNode(20)
t1.left = t2; t1.right = t3
t4 = TreeNode(15)
t5 = TreeNode(7)
t6 = TreeNode(0)
t2.left = t4; t2.right = t5; t3.right = t6

从上到下、每一层从左到右

def print_tree(root):
    res = []
    
    if not root:  # root 是空
        return res

    nodelist = [root]
    while nodelist:
        current = nodelist.pop(0)
        res.append(current.val)
        if current.left:
            nodelist.append(current.left)
        if current.right:
            nodelist.append(current.right)
    
    return res

print_tree(t1)
[3, 9, 20, 15, 7, 0]

每一层打印到一行

# 将本层全部节点打印到一行,并将下一层全部节点加入队列,以此类推,即可分为多行打印。
def print_row(root):
    row_list = [root]
    res = []
    
    while row_list:
        row_res = []         # 本层的打印结果
        n = len(row_list)   # 本层有多少节点
        
        for i in range(n):
            cur = row_list.pop(0)
            row_res.append(cur.val)
            if cur.left:
                row_list.append(cur.left)
            if cur.right:
                row_list.append(cur.right)
        
        res.append(row_res)
    
    return res
print_row(t1)
[[3], [9, 20], [15, 7, 0]]

之字形顺序打印二叉树

即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

def print_row(root):
    row_list = [root]
    res = []
    row = 1     # 记录z字,即打印顺序
    while row_list:
        row_res = []         # 本层的打印结果
        n = len(row_list)   # 本层有多少节点

        for i in range(n): 
            cur = row_list.pop(0)   
            row_res.append(cur.val)
            if cur.left:
                row_list.append(cur.left)
            if cur.right:
                row_list.append(cur.right)
        
        if row % 2 == 0:
            row_res.reverse()
        res.append(row_res)

        row += 1
        
    return res
print_row(t1)
[[3], [20, 9], [15, 7, 0]]

剑指 Offer 39. 出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

# 解法一:字典计数
nums = [1, 0, 0, 2, 2, 3, 2, 2, 2, 5, 4, 2, 2]
d = dict()
for num in nums:
    if num in d.keys():
        d[num] += 1
    else:
        d[num] = 1
for k in d.keys():
    if d[k] > (len(nums)/2):
        print(k)

# 解法二:摩尔投票,技巧性
votes = 0
for num in nums:
    if votes == 0: 
        x = num
    if num == x:
        votes += 1
    else:
        votes -= 1
if votes != 0:
    print(x)
2
2

剑指 Offer 40. 最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

topk 堆排序 示意图

class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        if k == 0:
            return list()

        hp = [-x for x in arr[:k]]
        heapq.heapify(hp)
        for i in range(k, len(arr)):
            if -hp[0] > arr[i]:
                heapq.heappop(hp)
                heapq.heappush(hp, -arr[i])
        ans = [-x for x in hp]
        return ans
  • 我们用一个大根堆实时维护数组的前k最小值
  • 从第 k+1 个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。最后将大根堆里的数存入数组返回即可。
  • 注意:Python 语言中是小根堆,因此要对数组中所有的数取其相反数,才能使用小根堆维护前 k 小值。

剑指 Offer 42. 连续子数组的最大和

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]

输出: 6

解释: 连续子数组 [4,-1,2,1] 的和最大为 6。

动态规划
当前最大 = max(当前值加上前一个最大,当前值)

nums = [-2,1,-3,4,-1,2,1,-5,4]
max_list = nums
for i in range(1, len(nums)):
    max_list[i] = max(max_list[i-1] + nums[i], nums[i])
print(max(max_list))
6