文章目录
- 剑指offer
-
- JZ01 二维数组的查找
- JZ02 替换空格
- JZ03 从尾到头打印链表
- JZ04 重建二叉树
- JZ05:用两个栈实现队列
- JZ06 旋转数组的最小数字
- JZ7 斐波那契数列
- JZ08:跳台阶
- JZ9 跳台阶扩展问题
- JZ10-矩阵覆盖
- JZ11-二进制中1的个数
- JZ12-数值的整数次方
- JZ13-调整数组顺序使奇数位于偶数前面
- JZ14-链表中倒数第k个结点
- JZ15-反转链表
- JZ16-合并有序链表
- JZ17-树的子结构
- JZ18-二叉树的镜像
- JZ19-顺时针打印矩阵
- JZ20-包含min函数的栈
- JZ21-栈的压入、弹出序列
- JZ22-从上到下打印二叉树
- JZ23-二叉搜索树的后序遍历序列
- JZ-24-二叉树中和为某一值的路径
- JZ25-复杂链表的复制
- JZ26-二叉搜索树与双向链表
- JZ27-字符串的排列
- JZ28-数组中出现次数超过一半的数字
- JZ29-最小的K个数
- JZ30-连续子数组的最大和
- JZ31-整数中1出现的次数(从1到n整数中1出现的次数
- JZ32-把数组排成最小的数
- JZ33-丑数
- JZ34-第一个只出现一次的字符
- JZ35-数组中的逆序对
- JZ36-两个链表的第一个公共结点
- JZ37-数字在升序数组中出现的次数
- JZ38-二叉树的深度
- JZ39-平衡二叉树
- JZ40-数组中只出现一次的两个数字
- JZ41-和为S的连续正数序列
- JZ42-和为S的两个数字
- JZ43-左旋转字符串
- JZ44-翻转单词顺序列
- JZ45-扑克牌顺子
- JZ46-孩子们的游戏(圆圈中最后剩下的数字)
- JZ47-求1+2+3+...+n
- JZ48-不用加减乘除做加法
- JZ49-把字符串转换成整数
- JZ50-数组中重复的数字
- JZ51-构建乘积数组
- JZ52-正则表达式匹配
- JZ53-表示数值的字符串
- JZ54-字符流中第一个不重复的字符
- JZ55-链表中环的入口结点
- JZ56-删除链表中重复的结点
- JZ57-二叉树的下一个结点
- JZ58 对称的二叉树
- JZ59 按之字形顺序打印二叉树
- JZ60-把二叉树打印成多行
- JZ61-序列化二叉树
- JZ62-二叉搜索树的第k个结点
- JZ63-数据流中的中位数
- JZ64-滑动窗口的最大值
- JZ65-矩阵中的路径
- JZ66-机器人的运动范围
- JZ67-剪绳子
- Leecode
- 面试笔试拓展题
剑指offer
以牛客网题号为准
JZ01 二维数组的查找
题目: 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
给定 target = 7,返回 true。
给定 target = 3,返回 false。
思路: 利用二维数组的特点。采用二分的思想,从数组的左下角开始遍历。
# -*- coding:utf-8 -*-
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
if len(array) == 0 and len(array[0]) == 0:
return False
i=len(array)-1
j=0
while i >= 0 and j <= len(array[0])-1:
if target>array[i][j]:
j+=1
elif target<array[i][j]:
i-=1
else:
return True
return False
JZ02 替换空格
题目: 请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
思路: 把字符串中的每个字符一个个添加到新字符串中,如果遇到空格就把他换成%20。
class Solution:
def replaceSpace(self , s ):
# write code here
res=[]
for i in range(len(s)):
if s[i]==' ':
res.append('%20')
else:
res.append(s[i])
ss=''.join(res)
return ss
JZ03 从尾到头打印链表
**题目:**输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
思路:利用insert函数。
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回从尾部到头部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# write code here
pTempt=listNode
res=[]
while pTempt:
res.insert(0,pTempt.val)
pTempt=pTempt.next
return res
JZ04 重建二叉树
题目: 给定某二叉树的前序遍历和中序遍历,请重建出该二叉树并返回它的头结点。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。
思路:
- 首先根据前序遍历得到根节点
- 找到中序遍历中根节点的索引,即可分出左子树和右子树。
- 遍历建立左子树和右子树。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回构造的TreeNode根节点
def reConstructBinaryTree(self, pre, tin):
# write code here
if len(pre)==0 or len(tin)==0:
return None
root=TreeNode(pre[0])
ind=tin.index(pre[0])
root.left=self.reConstructBinaryTree(pre[1:ind+1], tin[0:ind])
root.right=self.reConstructBinaryTree(pre[ind+1:len(pre)], tin[ind+1:len(tin)])
return root
JZ05:用两个栈实现队列
题目: 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
思路: 入栈时数据存入栈stackIn, 出栈时数据从stackOut弹出。执行入栈操作时,将数据源源不断的压入栈stackIn;执行出栈操作时,将stackIn的数据一次性全部弹出,存入到stackOut中。当stackOut栈非空时,不断弹出stackOut栈中的数据顺序即为队列的Pop顺序;当stackOut中的数据为空后,再将新入栈stackIn的数据一次性存入stackOut中即可。相比于上一种方式,这种方式极大降低了数据在栈stackIn和栈stackOut中来回无意义的腾挪操作,占据更低的存储空间,消耗更低的运算时间。
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stackIn = []
self.stackOut = []
def push(self, node):
self.stackIn.append(node)
def pop(self):
if self.stackOut:
return self.stackOut.pop()
elif not self.stackIn:
return None
else:
while self.stackIn:
self.stackOut.append(self.stackIn.pop())
return self.stackOut.pop()
JZ06 旋转数组的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if len(rotateArray)==0:
return 0
left=0
right=len(rotateArray)-1
while left<right:
mid=(left+right)//2
if rotateArray[mid]>rotateArray[right]:
left=mid+1
elif rotateArray[mid]<rotateArray[right]:
right=mid
else:
right-=1
return rotateArray[left]
JZ7 斐波那契数列
题目: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
思路: 递归法时间复杂度过高。因此采用递推法,时间复杂度为O(n)。
# -*- coding:utf-8 -*-
class Solution:
def Fibonacci(self, n):
a,b=0,1
for i in range(n):
a,b=b,a+b
return a
JZ08:跳台阶
递归
def numWays(self, n: int) -> int:
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))
非递归:
class Solution:
def jumpFloor(self, number):
# write code here
if number<1:
return 0
a=1
b=2
for i in range(number-1):
a,b=b,a+b
return a
JZ9 跳台阶扩展问题
**题目:**一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法。
思路:
f [ n ] = f [ n − 1 ] + f [ n − 2 ] + . . . + f [ 0 ] f[n] = f[n-1] + f[n-2] + ... + f[0] f[n]=f[n−1]+f[n−2]+...+f[0]
f [ n − 1 ] = f [ n − 2 ] + f [ n − 3 ] + . . . + f [ 0 ] f[n-1] = f[n-2] + f[n-3] + ... + f[0] f[n−1]=f[n−2]+f[n−3]+...+f[0]
所以一合并, f [ n ] = 2 ∗ f [ n − 1 ] = 2 n − 1 f[n] = 2*f[n-1]=2^{n-1} f[n]=2∗f[n−1]=2n−1,初始条件f[0] = f[1] = 1
# -*- coding:utf-8 -*-
class Solution:
def jumpFloorII(self, number):
# write code here
return pow(2,number-1)
JZ10-矩阵覆盖
题目: 我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2n的大矩形,总共有多少种方法?
比如n=3时,23的矩形块有3种覆盖方法:
思路: 假设2*n的第一个矩形竖着放,则剩下的就是f(n-1).假设第一个横着放,那么第二个必须在下面横着放,剩下的就是f(n-2)
则递推公式为f(n)=f(n-1)+f(n-2),且f(1)=1,f(2)=2.
(注意n<=1的情况)
class Solution:
def rectCover(self, number):
# write code here
if number<=1:
return number
a=1
b=2
for i in range(number-1):
a,b=b,a+b
return a
JZ11-二进制中1的个数
题目: 输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
思路: 1100&1011=1000。也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0。那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
但python当超出int的32位时会自动转化为long型,上述代码“n = n &(n - 1)”在执行时,当执行到倒数第二步,即原来的整数变成 1(31个0)时,该整数减一,在c++中结果为0(31个1),相与之后结果为0,终止循环。但是python在计算该整数减一时,会得到1(32个0),陷入无限循环。因此通过“n & 0xffffffff”实现python只取32位的功能,相与之后32位之前不会再有值。
class Solution:
def NumberOf1(self, n):
# write code here
cou = 0
if n < 0:
n = n & 0xffffffff
while n:
cou = cou + 1
n = n &(n - 1)
return cou
JZ12-数值的整数次方
题目: 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
思路: 如果base为0,直接返回0。如果exponent为0,返回1.
采用递推公式,如果exponent为正数:Power(base, exponent)=base*Power(base, exponent-1)。
如果exponent为负数:Power(base, exponent)=(1/base)*Power(base, exponent+1)
class Solution:
def Power(self, base, exponent):
# write code here
if base==0:
return 0
if exponent==0:
return 1
if exponent>0:
return base*self.Power(base, exponent-1)
else:
return (1/base)*self.Power(base, exponent+1)
# -*- coding:utf-8 -*-
class Solution:
def Power(self, base, exponent):
# write code here
if base==0:
return 0
if exponent>0:
if exponent%2==0:
result=self.Power(base, exponent//2)
result=result**2
return result
elif exponent%2==1:
result=self.Power(base, exponent//2)**2
result=result*base
return result
elif exponent<0:
result=self.Power(1/base, abs(exponent))
return result
else:
return 1
JZ13-调整数组顺序使奇数位于偶数前面
**题目:**输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
思路: 遍历,若余数为1,append到奇数数组,反之append到偶数数组。最后讲奇数和偶数数组加起来即可。
class Solution:
def reOrderArray(self , array ):
# write code here
oddarray=[]
evenarray=[]
for i in array:
if i%2==0:
evenarray.append(i)
else:
oddarray.append(i)
return oddarray+evenarray
JZ14-链表中倒数第k个结点
题目: 输入一个链表,输出该链表中倒数第k个结点。
思路: 在链表中:倒数的+顺数的长度等于链表总长度,所以可以设置两个指针,一个先走K步,剩下的到链表的末尾要走的步数就是倒数第k个节点需要从头开始走的步数。
注意在先移动k步快指针时,需要先判断k是否为空再挪指针,因为当快指针第一次为空时,慢指针不是None。否则,若第k步正好快指针指向None时慢指针也会返回None,是不对的。
class Solution:
def FindKthToTail(self , pHead , k ):
# write code here
fast=pHead
slow=pHead
for i in range(k):
if not fast:
return None
fast=fast.next
while fast:
fast=fast.next
slow=slow.next
return slow
JZ15-反转链表
题目: 输入一个链表,反转链表后,输出新链表的表头。
思路: 先保留现有链表的第二个节点,将现有链表的头指向已反转的链表。从现有链表第二个节点开始为新的链表,更新已反转的链表。迭代至尾结点。
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
if pHead==None:
return None
last=None
while pHead:
tmp=pHead.next
pHead.next=last
last=pHead
pHead=tmp
return last
JZ16-合并有序链表
题目: 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路: 利用递归,首先如果有一个链表为空,则直接返回另一个链表。比较两个链表的值,将表头小的链表指向下一个,并递归调用。
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
if pHead1==None:
return pHead2
if pHead2==None:
return pHead1
if pHead1.val<pHead2.val:
rec=pHead1
rec.next=self.Merge(pHead1.next,pHead2)
else:
rec=pHead2
rec.next=self.Merge(pHead1,pHead2.next)
return rec
JZ17-树的子结构
题目: 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
思路: 使用函数嵌套递归判断是否匹配到起始节点;另写一个递归,判断左子树和右子树是否为子结构的函数。
1、 判断A当前节点开始,B是否为子结构,如果不是,看下A的左子树节点,如果也不是再看下A的右子树。
2、如果是某节点开始A与B的起始节点重合:
①判断B是否匹配完了,如果匹配完了说明为子结构
②如果A匹配完了,或者A的值和B和值不等,直接返回False
③如果当前点相同,那同时看一下左子树和右子树的情况。
class Solution:
def HasSubtree(self, pRoot1, pRoot2):
# write code here
if not pRoot1 or not pRoot2:
return None
return self.isSubtree(pRoot1,pRoot2) or self.HasSubtree(pRoot1.left, pRoot2) or self.HasSubtree(pRoot1.right, pRoot2)
def isSubtree(self, A, B):
if not B:
return True
if not A or A.val!=B.val:
return False
else:
return self.isSubtree(A.left,B.left) and self.isSubtree(A.right,B.right)
JZ18-二叉树的镜像
题目: 操作给定的二叉树,将其变换为源二叉树的镜像。
思路: