剑指offer

这一章使用python3实现《剑指offer》中的题目。有些题目答案是原创,有些则搬运LeetCode中大神的优秀解法。<未完待续。。。>

实现单例模式

1
2
3
4
5
6
7
8
9
10
# 使用__new__控制实例创建过程
class Singleton:
_instance = None
def __new__(cls, *args, **kw):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance

class MyClass(Singleton):
pass

数组中重复的数字

题目描述:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
思考题:如果只能使用 O(1) 的额外空间,该怎么做呢?
AcWing传送门

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding:utf-8 -*-
class Solution:
# 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
# 函数返回True/False
def duplicate(self, numbers, duplication):
# write code here
if not numbers:
return False

length = len(numbers)
assist = [0] * length
for i in numbers:
if assist[numbers[i]] == 0:
assist[numbers[i]] += 1
else:
duplication[0] = numbers[i]
return True
return False

二维数组中的查找

题目描述:
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例:

1
2
3
4
5
6
7
8
9
10
11
现有矩阵 matrix 如下:

[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution(object):
def searchMatrix(self, matrix, target):
column = len(matrix)
if column == 0:
return False
row = len(matrix[0])
if row == 0:
return False
i = 0
j = row - 1
while ((i <= column-1) and (j >= 0)):
if(matrix[i][j] > target):
j-=1
elif(matrix[i][j] < target):
i+=1
else:
return True
return False

替换空格

题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

Solution

1
2
3
4
5
6
# -*- coding:utf-8 -*-
class Solution:
# s 源字符串
def replaceSpace(self, s):
# write code here
return ''.join(c if c!=' ' else '%20' for c in s)

从头到尾打印链表

题目描述
输入一个链表的头结点,按照 从尾到头 的顺序返回节点的值。
返回的结果用数组存储。

1
2
3
样例
输入:[2, 3, 5]
返回:[5, 3, 2]

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def printListReversingly(self, head):
"""
:type head: ListNode
:rtype: List[int]
"""
stack, h = [], head
while h:
stack.append(h.val)
h = h.next
return stack[::-1]

重建二叉树

题目描述
根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

1
2
3
4
5
6
7
8
9
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

3
/ \
9 20
/ \
15 7

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None

class Solution(object):
def buildTree(self, preorder, inorder):
"""
:type preorder: List[int]
:type inorder: List[int]
:rtype: TreeNode
"""
if preorder == []:
return None
root_val = preorder[0]
root = TreeNode(root_val)
cut = inorder.index(root_val)
root.left = self.buildTree(preorder[1:cut+1], inorder[:cut])
root.right = self.buildTree(preorder[cut+1:], inorder[cut+1:])
return root

二叉树的下一个节点

题目描述
题目:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右
子结点,同时包含指向父结点的指针。

分析
(1) 若该节点存在右子树:则下一个节点为右子树最左子节点
(2) 若该节点不存在右子树:这时分两种情况:

  • 2.1 该节点为父节点的左子节点,则下一个节点为其父节点

  • 2.2 该节点为父节点的右子节点,则沿着父节点向上遍历,直到找到一个节点,这个节点的left子树是给出节点的father节点。比如当前节点是D,则第一个满足的就是节点F,因为F节点的left节点是D节点的father节点。故D中序遍历的下一个节点就是F节点。
    中序遍历如下:A - C - B - D - F - H - E - M - G

    "1"

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- coding:utf-8 -*-
# class TreeLinkNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
# self.next = None
class Solution:
def GetNext(self, pNode):
# write code here
if not pNode:
return None
# 有右子树,右子树中最左节点
if pNode.right:
pre = pNode.right
while pre.left:
pre = pre.left
return pre
while pNode.next:
parent = pNode.next
if parent.left == pNode:
return parent
pNode = parent
return None

用两个栈实现队列

使用栈实现队列的下列操作:

push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
示例:

1
2
3
4
5
6
7
MyQueue queue = new MyQueue();

queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false

说明:

你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class MyQueue(object):

def __init__(self):
"""
Initialize your data structure here.
"""
self.stackA = []
self.stackB = []

def push(self, x):
"""
Push element x to the back of queue.
:type x: int
:rtype: None
"""
while self.stackA:
self.stackB.append(self.stackA.pop())
self.stackA.append(x)
while self.stackB:
self.stackA.append(self.stackB.pop())

def pop(self):
"""
Removes the element from in front of queue and returns that element.
:rtype: int
"""
return self.stackA.pop()

def peek(self):
"""
Get the front element.
:rtype: int
"""
return self.stackA[-1]

def empty(self):
"""
Returns whether the queue is empty.
:rtype: bool
"""
return self.stackA == []

# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

用两个队列实现栈

题目描述
使用队列实现栈的下列操作:

  • push(x) – 元素 x 入栈
  • pop() – 移除栈顶元素
  • top() – 获取栈顶元素
  • empty() – 返回栈是否为空
    注意:
  • 你只能使用队列的基本操作– 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
  • 你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
  • 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class MyStack(object):

def __init__(self):
"""
Initialize your data structure here.
"""
from collections import deque
self.q = deque()


def push(self, x):
"""
Push element x onto stack.
:type x: int
:rtype: None
"""
self.q.append(x)
for _ in range(len(self.q) - 1):
self.q.append(self.q.popleft())


def pop(self):
"""
Removes the element on top of the stack and returns that element.
:rtype: int
"""
return self.q.popleft()


def top(self):
"""
Get the top element.
:rtype: int
"""
return self.q[0]


def empty(self):
"""
Returns whether the stack is empty.
:rtype: bool
"""
return not len(self.q)



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

斐波那契数列

斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
给定 N,计算 F(N)。

示例 1:

输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1.
示例 2:

输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2.
示例 3:

输入:4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3.

Solution 1

1
2
3
4
5
6
7
8
9
10
11
12
class Solution(object):
def fib(self, N):
"""
:type N: int
:rtype: int
"""
if N == 0:
return 0
elif N == 1:
return 1
else:
return self.fib(N - 1) + self.fib(N - 2)

Solution 2

1
2
3
4
5
def fibonacci(n):
a = b = 1
for _ in range(n-1):
a, b = b, a+b
return b

旋转数组中的最小数字

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

你可以假设数组中不存在重复元素。

1
2
3
4
5
6
7
8
示例 1:

输入: [3,4,5,1,2]
输出: 1
示例 2:

输入: [4,5,6,7,0,1,2]
输出: 0

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution(object):
def findMin(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
l, r = 0, len(nums) - 1
if nums[l] < nums[r]:
return nums[l]
while l <= r:
mid = (l+r) // 2
if nums[mid] > nums[l]:
l = mid
elif nums[mid] < nums[r]:
r = mid
else:
return nums[r]

矩阵中的路径

机器人的运动范围

剪绳子

二进制中1的个数

描述
编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

Solution

1
2
3
4
5
6
7
8
9
10
11
class Solution(object):
def hammingWeight(self, n):
"""
:type n: int
:rtype: int
"""
res = 0
while n:
res += 1
n = n & (n-1)
return res

数值的整数次方

描述
实现 pow(x, n) ,即计算 x 的 n 次幂函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
示例 1:

输入: 2.00000, 10
输出: 1024.00000
示例 2:

输入: 2.10000, 3
输出: 9.26100
示例 3:

输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25

说明:

  • -100.0 < x < 100.0
  • n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution(object):
def myPow(self, x, n):
"""
:type x: float
:type n: int
:rtype: float
"""
if n == -1:
return 1.0/x
elif n == 0:
return 1
elif n == 1:
return x
else:
res = x
tmp = Solution()
if n % 2 == 1:
res = tmp.myPow(x, n//2) ** 2 * x
else:
res = tmp.myPow(x, n//2) ** 2
return res

打印从1到最大的n位数

删除链表中的节点

描述
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表 – head = [4,5,1,9],它可以表示为:

"1"

1
2
3
4
5
6
7
8
9
10
示例 1:

输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:

输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

说明:

  • 链表至少包含两个节点。
  • 链表中所有节点的值都是唯一的。
  • 给定的节点为非末尾节点并且一定是链表中的一个有效节点。
  • 不要从你的函数中返回任何结果。

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution(object):
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

正则表达式

表示数值的字符串

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

链表中倒数第k个节点

思路:两个指针,快指针先走k-1步,然后两个一起走,快指针走到尾节点时,慢指针在倒数第k个节点。
需考虑k=0时和fast已经走到尾节点的情况。
Solution

1
2
3
4
5
6
7
8
9
10
def FindKthToTail(self, head, k):
fast = slow = head
for _ in range(k):
if fast:
fast = fast.next
else:
return None
while fast:
slow, fast = slow.next, fast.next
return slow

链表中环的入口节点

反转链表

合并两个有序链表

树的子结构

二叉树的镜像

对称的二叉树

顺时针打印矩阵

包含min函数的栈

栈的压入、弹出序列

从上到下打印二叉树

分层从上到下打印二叉树

之字型打印二叉树

是否是二叉树的后续遍历

二叉树和为某一值的路径

复杂链表的复制

二叉搜索树与双向链表

序列化二叉树

字符串的排列

数组中出现次数超过一半的数字

最小的k个数

数据流中的中位数

连续子数组的最大和

1~n整数中1出现的次数

把数组排成最小的数字

把数字翻译成字符串

礼物的最大价值

最长不包含舒服字符的子字符串

丑数

第一个只出现一次的字符

数组中的逆序对

两个链表的第一个公共节点

在排序数组宏查找数字

0~n-1中缺失的数字

数组中数值和下标相等的元素

二叉搜索树的第k大节点

二叉树的深度

平衡二叉树

数组中只出现1次的两个数字

和为s的数字

翻转字符串

滑动窗口的最大值

n个骰子的点数

扑克牌中的顺子

圆圈中最后剩下的数字

股票的最大利润

求1+2+…+n

不用加减乘除做加法

构建乘积数组

https://darktiantian.github.io/%E5%89%91%E6%8C%87Offer/
https://github.com/darkTianTian/sword-for-offer/tree/master
https://github.com/leeguandong/Interview-code-practice-python/tree/master/%E5%89%91%E6%8C%87offer