剑指offer刷题总结

 
 
受到这篇文章的启发,于是我写下了这篇博客主要是记录自己准备实习前的刷题过程。

按照笔者推荐的刷题过程,剑指offer->Leetcode动态规划->面试前再过一遍剑指offer

我觉得这个刷题过程的可行性是很高的,于是在时间规定下,我决定在一个星期之内按照牛客上剑指offer的题目刷完对应的题。
        剑指offer刷题总结_第1张图片


目录

    • 1. 写在前面
    • 2. 数据结构类题目
      • 2.1 排序
      • 2.2 递归
      • 2.3 贪心
      • 2.4 动态规划
      • 2.5 分治
      • 2.6 穷举
      • 2.7 回溯
      • 2.8 数学
      • 2.9 dfs
      • 2.10 双指针
      • 2.11 二分
      • 2.13 位运算
      • 2.14 bfs
      • 2.15 数组
      • 2.16 字符串
      • 2.17 链表
      • 2.18 栈
      • 2.19 队列
      • 2.20 树
      • 2.21 哈希
      • 2.22 堆

1. 写在前面

面试季来了,不管是作为面试者还是以后作为面试官,了解算法这门程序员之间的沟通方式都是非常必要的。

找过工作的朋友应该都听说过《剑指offer》,本文主要对我这些天刷过的《剑指offer》做个简单的分类小结,方便后面专项复(练)习~

【1】剑指offer推荐刷题地址:
https://www.nowcoder.com/ta/coding-interviews

【2】我的所有AC题解(Python语言),每道题都写了点自己的思路:
笔者:https://github.com/nlpjoe/Coding4Interviews

【3】剑指offer面试题内容:https://www.cnblogs.com/yanmk/p/9130681.html
 
 


2. 数据结构类题目

2.1 排序

(1)JZ29 题目

题目描述

给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。如果K>数组的长度,那么返回一个空的数组。


"""
**排序**

题目描述:
    给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,88个数字,
    则最小的4个数字是1,2,3,4。如果K>数组的长度,那么返回一个空的数组

解题思路:
    首先使用插入排序,排序完对应的数组,然后相应切片输出。
"""

# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        length = len(tinput)  # 计算出数组的长度
        # 当k大于这类长度时,或者输入的数组为空时,返回空
        if (k > length) or tinput == []:
            return []
        # 典型的插入排序
        for i in range(1, length):
            # 输入该插入的数字(遍历从1到最后一个元素)
            key = tinput[i]
            j = i - 1
            # 如果j没有越界,同时,key比前面已经拍好数组小时,需要插入
            while j >= 0 and key < tinput[j]:
                tinput[j+1] = tinput[j]
                j = j -1
            # 找到了该插入的位点,为最后比较位点j+1
            tinput[j+1] = key
        return tinput[:k]

当然,可以直接运用python特有的函数 .sort() 直接得到排序后的数组。
这样就可以避免使用了一些排序算法。

 

(2)JZ63题目

题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。


"""
**排序**

题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,
那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,
那么中位数就是所有数值排序之后中间两个数的平均值。
我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

解题思路:
利用两个堆:

1. 用于存储输入数字中较小一半的最大堆(最大堆中的所有数字都小于或等于最大堆的top元素)
2. 用于存储输入数字的较大一半的最小堆(最小堆中的所有数字都大于或等于最小堆的顶部元素)

python 库heapq中默认的堆为小顶堆,在使用了-num传入到堆中时,此时pop出堆顶的元素后最大值的相反数。
值得提醒:最大堆的最大值<=最小堆的最小值
"""

# -*- coding:utf-8 -*-
import heapq
class Solution:
    def __init__(self):
        self.count = 0
        self.max_heap = []
        self.min_heap = []

    def Insert(self, num):
        # 统计加入数据的个数
        self.count += 1
        #将数据放到最大堆中
        heapq.heappush(self.max_heap, -num)
        #将最大堆中的堆顶元素放到最小堆中
        # 将最大堆中最小的元素放入最小堆中
        max_heap_top = heapq.heappop(self.max_heap)
        heapq.heappush(self.min_heap, -max_heap_top)

        #如果最小堆和最大堆之间的差距大于1,将最小堆中的堆顶放进最大堆
        # 这一步判断的过程:二进制判断,如果self.count为基数例如3二进制为11,此时与1进行与运算。
        # 其实这一步保证了大顶堆中元素的个数要大于小顶堆元素的个数
        if self.count & 1:
            min_heap_top = heapq.heappop(self.min_heap)
            heapq.heappush(self.max_heap, -min_heap_top)

    #题目getmedian参数少了个s
    def GetMedian(self,s):
        # write code here
        if self.count & 1:
            return -self.max_heap[0]
        else:
            return (self.min_heap[0] - self.max_heap[0]) / 2.0


 
 

2.2 递归

(1)JZ8题目

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

这道题目是典型的递归题,可以看出第N阶段的结果=第N-2阶段结果+第N-1阶段结果。

"""
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。
求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
"""

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloor(self, number):
        # write code here
        var0 = 1
        var1 = 2
        if number < 3:
            return number
        i = 3
        while i <= number:
            num = var0 + var1
            var0 = var1
            var1 = num
            i += 1
        return num

 
(2)JZ10题目

题目描述

我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

这道题也是典型的递归类的题目,可以看出第N阶段的结果=第N-2阶段结果+第N-1阶段结果。

在牛客剑指offer这道题的评论区,有位大佬画了一副图,很形象解释了第N阶段的结果=第N-2阶段结果+第N-1阶段结果。 f(n-2)阶段放两个,f(n-1)放一个。
           


"""
**递归**

题目描述
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。
请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

"""

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloor(self, number):
        # write code here
        var0 = 1
        var1 = 2
        if number < 3:
            return number
        i = 3
        while i <= number:
            num = var0 + var1
            var0 = var1
            var1 = num
            i += 1
        return num


    def rectCover(self, number):
        res = [0,1,2]
        while len(res) <= number:
            res.append(res[-1] + res[-2])
        return res[number]

 
 

2.3 贪心

(1)JZ9题目

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。


# -*- coding:utf-8 -*-
class Solution:
    def jumpFloorII(self, number):
        # write code here
        var = [0, 1, 2]
        while len(var) <= number:
            var.append(sum(var) + 1)
        return var[number]

class Solution2:
    def jumpFloorII(self, number):
        return 2**(number-1)

还可以使用等比数列的方法来求解这,也就是第二种解决方法:
           剑指offer刷题总结_第2张图片

 
 

2.4 动态规划

(1)JZ27题目

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。


"""
**字符串 动态规划 回溯算法**

题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。
例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串:
abc,acb,bac,bca,cab和cba。

"""


# -*- coding:utf-8 -*-
class Solution:
    def Permutation(self, ss):
        # write code here
        ss = list(ss)
        def backtrack(position ,end):
            if position == end:
                var = ''.join(ss[:])
                if var not in res:
                    res.append(var)
                return


            for index in range(position, end):
                ss[index], ss[position] = ss[position], ss[index]
                backtrack(position + 1, end)
                ss[index], ss[position] = ss[position], ss[index]

        res = []
        backtrack(0, len(ss))
        return sorted(res)

 

(2)JZ30题目

题目描述

输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为 O(n).


"""
**动态规划**


题目描述
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。
求所有子数组的和的最大值。要求时间复杂度为 O(n).
"""

# -*- coding:utf-8 -*-
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        res = array[0]  # 记录当前所有子数组的和的最大值
        temp = 0  # 包含array[i]的连续数组最大值
        for i in array:
            temp = max(i,temp+i)
            res = max(res,temp)
        return res

 
 

2.5 分治

JZ26题目

题目描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

"""
**分治**

题目描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。
要求不能创建任何新的结点,只能调整树中结点指针的指向。

思路:
(1)直接在中序遍历中,进行指针的连接操作。
(2)使用中序遍历,然后再将数组中的结点与前后依次相互连接。
"""


# -*- coding:utf-8 -*-
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


class Solution1:
    def Convert(self, pRootOfTree):
        if not pRootOfTree: return
        self.head = self.pre = None
        self.MidTraversal(pRootOfTree)
        return self.head

    def MidTraversal(self, root):
        if not root: return
        self.MidTraversal(root.left)
        if not self.head:
            self.head = self.pre = root
        else:
            self.pre.right, root.left, self.pre = root, self.pre, root
        self.MidTraversal(root.right)



class Solution2:
    def Convert(self, pRootOfTree):
        # write code here
        if not pRootOfTree:return
        self.arr = []
        self.midTraversal(pRootOfTree)
        for i,v in enumerate(self.arr[:-1]):
            v.right = self.arr[i + 1]
            self.arr[i + 1].left = v
        return self.arr[0]

    def midTraversal(self, root):
        if not root: return
        self.midTraversal(root.left)
        self.arr.append(root)
        self.midTraversal(root.right)

2.6 穷举

JZ26题目

题目描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!


"""
**穷举**

题目描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。
没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
"""

# -*- coding:utf-8 -*-
class Solution:
    def FindContinuousSequence(self, tsum):
        # write code here
        # 生成一个等长的递归数组
        var = [x+1 for x in range(tsum)]
        # 中位数的坐标
        num = tsum//2 + 1
        res = []
        for i in range(num):
            j = i+2
            while j <= tsum:
                if sum(var[i:j]) == tsum:
                    res.append(var[i:j])
                j += 1
        return res

 

2.7 回溯

JZ27题目

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。


"""
**字符串 动态规划 回溯算法**

题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。
例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串:
abc,acb,bac,bca,cab和cba。

"""


# -*- coding:utf-8 -*-
class Solution:
    def Permutation(self, ss):
        # write code here
        ss = list(ss)
        def backtrack(position ,end):
            if position == end:
                var = ''.join(ss[:])
                if var not in res:
                    res.append(var)
                return


            for index in range(position, end):
                ss[index], ss[position] = ss[position], ss[index]
                backtrack(position + 1, end)
                ss[index], ss[position] = ss[position], ss[index]

        res = []
        backtrack(0, len(ss))
        return sorted(res)

 

2.8 数学

(1)JZ11

题目描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。


"""
**数字**
题目描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
"""

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1(self, n):
        # write code here
        # 当n为负数时,用补码
        if n < 0:
            n = n & 0xffffffff
        var = "{:032b}".format(n)
        res = list(var)
        sum = 0
        for i in res:
            if i == '1':
                sum += 1
        return sum

    # 方法二使用位运算
    def NumberOf1_2(self, n):
        # write code here
        count = 0
        if n < 0:
            n = n & 0xffffffff
        while n:
            if (n & 1):
                count += 1
            n = n >> 1
        return count

 
(2)JZ12

题目描述

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0

"""
**数字**

题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0

"""
# -*- coding:utf-8 -*-
class Solution:
    def fast_power(self, base, exponent):
        if base == 0:
            return 0
        if exponent == 0:
            return 1
        e = abs(exponent)
        tmp = base
        res = 1
        while(e > 0):
            #如果最后一位为1,那么给res乘上这一位的结果,也就是分成奇数个
            if (e & 1 == 1):  
                res =res * tmp 
            e = e >> 1
            tmp = tmp * tmp
        return '%.5f'%res if exponent > 0 else 1/res

    def Power(self, base, exponent):
        # write code here
        if base == 0 and exponent == 0:
            return 0
        return pow(base, exponent)

上面的很简单,没有使用快速幂算法,下面使用一下快速幂算法,快速幂算法参考下面的博客
https://blog.csdn.net/hkdgjqr/article/details/5381028

 
(3)JZ31

题目描述

求出1 ~ 13的整数中1出现的次数,并算出100 ~ 1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)


"""
**数字**

题目描述
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?
"""

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        ls = [str(i+1) for i in range(n)]
        res = ''.join(ls)
        count = 0
        for i in res:
            if i == '1':
                count += 1
        return count

 
(4)JZ33

题目描述

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。


"""
**数字 二分**

题目描述
把只包含质因子235的数称作丑数(Ugly Number)。
例如68都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。
求按从小到大的顺序的第N个丑数。
"""

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index<7:
            return index
        p1,p2,p3 = 0,0,0
        array = [1]
        while len(array)<index:
            newnum = min(array[p1]*2,array[p2]*3,array[p3]*5)
            array.append(newnum)
            if newnum == array[p1]*2:
                p1 += 1
            if newnum == array[p2]*3:
                p2 += 1
            if newnum == array[p3]*5:
                p3 +=1
        return array[-1]

 

2.9 dfs

(1)JZ4

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

"""
题目描述:

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

解题思路:
    使用递归、树相关的知识点
"""


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


class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        if len(pre) == 0:
            return None
        # 判断是否是最后一个节点
        if len(pre) == 1:
            Root = TreeNode(pre[0])
            Root.left = None
            Root.right = None
            return Root
        Root = TreeNode(pre[0])
        loc = tin.index(pre[0])
        length = len(tin[:loc])

        if loc == 0:
            # 无左节点
            Root.left = None
        else:
            Root.left = self.reConstructBinaryTree(pre[1:length+1], tin[:loc])

        if length+1 < len(pre):
            Root.right = self.reConstructBinaryTree(pre[length+1:], tin[loc+1:])
        else:
            # 无右节点
            Root.right = None
        return Root

 
(2)JZ39
题目描述

输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

"""
题目描述

输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

解题思路:

"""


class Solution:
    def IsBalanced_Solution(self, pRoot):
        # write code here
        if pRoot is None:
            return True
        else:
            res = abs(self.dapth(pRoot.left) - self.dapth(pRoot.right)) <=1 \
                  & self.IsBalanced_Solution(pRoot.left) & \
                  self.IsBalanced_Solution(pRoot.right)
            return res

    # 存储每个结点的高度
    def dapth(self, root):
        if root is None:
            return 0
        else:
            return 1 + max(self.dapth(root.left), self.dapth(root.right))

 

(3)JZ65
题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
例如 剑指offer刷题总结_第3张图片
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。


"""
题目描述:
   请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。

解题思路:
   dfs
"""


class Solution:
    def hasPath(self, matrix, word):
        if word == '':
            return True
        if not matrix:
            return False

        def dfs(i, j, k):
            # 判断路径是否存在
            if not 0 <= i < len(matrix) or not 0 <= j < len(matrix[0]) or \
                    matrix[i][j] != word[k]:
                return False

            if len(word) - 1 == k:
                return True

            matrix[i][j] = ""
            res = dfs(i + 1, j, k + 1) or dfs(i - 1, j, k + 1) or dfs(i, j - 1, k + 1) or dfs(i, j + 1, k + 1)
            matrix[i][j] = word[k]
            return res

        # 遍历入口
        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                if dfs(i, j, k=0):
                    return True

 

2.10 双指针

 
(1)JZ42
题目描述

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。


"""
题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

解题思路:
双指针、数组
"""

# -*- coding:utf-8 -*-
class Solution:
    def FindNumbersWithSum(self, array, tsum):
        # write code here
        low = 0
        high = len(array)-1
        if len(array)<=1 or tsum ==0 :
            return []
        while low <= high:
            if array[low] + array[high] == tsum:
                return [array[low], array[high]]
            if array[low] + array[high] > tsum:
                high -= 1
            else:
                low += 1
        return []

 
(2)JZ64
题目描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
窗口大于数组长度的时候,返回空.


"""
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。

思路:
双指针,数组
"""

# -*- coding:utf-8 -*-
class Solution:
    def maxInWindows(self, num, size):
        # write code here
        if len(num) < size or not num or size == 0:
            return []
        var = []
        i = 0
        while i <= len(num)-size:
            var.append(max(num[i:i+size]))
            i += 1
        return var

 

2.11 二分

 
(2)JZ6
题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

"""
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

思路:
二分,旋转
"""

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if len(rotateArray) == 0:
            return 0
        low = 0
        high = len(rotateArray) -1
        while low < high:
            mid = (low + high) // 2
            if rotateArray[mid] > rotateArray[high]:
                low = mid + 1
            elif rotateArray[mid] < rotateArray[high]:
                high = mid

        return rotateArray[low]

 
(2)JZ33
题目描述

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

"""
**数字 二分**

题目描述
把只包含质因子235的数称作丑数(Ugly Number)。
例如68都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。
求按从小到大的顺序的第N个丑数。

"""

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        if index < 7:
            return index
        p1, p2, p3 = 0, 0, 0
        array = [1]
        while len(array) < index:
            var = min(array[p1]*2, array[p2]*3, array[p3]*5)
            array.append(var)
            if array[p1]*2 == var:
                p1 += 1
            if array[p2]*3 == var:
                p2 += 1
            if array[p3]*5 == var:
                p3 += 1
        return array[-1]

 
(3)JZ39
题目描述

统计一个数字在升序数组中出现的次数。

"""
题目描述
统计一个数字在升序数组中出现的次数。

思路:
双指针,二分法
"""
# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        if len(data) == 0:
            return 0

        def getLower(data, k):
            low = 0
            high = len(data) -1
            mid = (low + high)//2

            # 获取k第一次出现的下标
            while low <= high:
                if data[mid] < k:
                    low = mid + 1
                else:
                    high = mid - 1
                mid = (low + high)//2

            return low
        # 获得k最后一次出现的下标

        def getHigher(data, k):
            low = 0
            high = len(data) - 1
            mid = (low + high) // 2

            # 获取k第一次出现的下标
            while low <= high:
                if data[mid] <= k:
                    low = mid + 1
                else:
                    high = mid - 1
                mid = (low + high) // 2

            return high
        # 获得k最后一次出现的下标

        return getHigher(data, k) - getLower(data, k) + 1

 

2.13 位运算

 
(1)JZ40

题目描述

一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。


"""
题目描述
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

思路:
使用位运算
"""

class Solution:
    def FindNumsAppearOnce(self , array ):
        # write code here
        XORsum = 0
        for i in array:
            XORsum ^= i
        t = 1
        while (XORsum&t)==0:
            t = t<<1
        ans1 = 0
        ans2 = 0
        for i in array:
            if (t&i)==0:
                ans1 ^= i
            else:
                ans2 ^= i
        if ans1<ans2:
            return [ans1,ans2]
        else:
            return [ans2,ans1]

 

2.14 bfs

 
(1)JZ60
题目描述

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

"""
题目描述:

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

思路:
使用bfs
"""

# -*- coding:utf-8 -*-

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


# 使用层次遍历来输出结果
class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        # write code here
        if not pRoot:
            return []

        res = []
        queue = [pRoot]
        while queue:
            temp = []
            for _ in range(len(queue)):
                cur = queue.pop(0)
                temp.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            res.append(temp)
        return res

 

2.15 数组

(1)JZ1 二维数组中的查找

描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[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 not array:
            return False
        m = len(array)
        n = len(array[0])
        # 在右上角往左下角进行二分查找
        row, col = 0, n-1
        while row<m and col>=0:
            if target == array[row][col]:
                return True
            elif target > array[row][col]:
                row += 1
            else:
                col -=1
        return False    

 

(2)JZ4 重建二叉树

描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        # write code here
        if len(pre) == 0:
            return None
        elif len(pre) == 1:
            return TreeNode(pre[0])
        if len(pre) == 1:
            head = TreeNode(pre[0])
            head.left = None
            head.right = None
            return head
        head = TreeNode(pre[0])      
        temp1 = tin.index(pre[0])
        length = len(tin[:temp1])
        if temp1 == 0:
            head.left = None
        else:
            head.left = self.reConstructBinaryTree(pre[1:length+1], tin[:temp1])
        if length + 1 < len(pre):
            head.right = self.reConstructBinaryTree(pre[length+1:], tin[temp1+1:])
        else:
            head.right = None
        return head

 
(3)JZ7 斐波那契数列

描述

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。

# -*- coding:utf-8 -*-
class Solution:
    def Fibonacci(self, n):
        # write code here
        a = [0, 1, 1, 2]
        if n <= 3:
            return a[n]
        i = 0
        a2 = 1
        a3 = 2
        while i+4 <= n:
            temp = a2 + a3
            a2 = a3
            a3 = temp
            i += 1
        return temp

 
(4)JZ13 调整数组顺序使奇数位于偶数前面

描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
 

class Solution:
    def reOrderArray(self , array ):
        a = []
        b = []
        for i in array:
            if i%2 == 0:
                b.append(i)
            else:
                a.append(i)
        return a + b

 

(5)JZ19 顺时针打印矩阵
剑指offer刷题总结_第4张图片

# -*- coding:utf-8 -*-
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        if not matrix:
            return []
        # 定义了dir行走的轨迹
        # 按照右->->-上的顺序遍历
        dx = [0, 1, 0, -1]
        dy = [1, 0, -1, 0]
        x = y = dir = 0

        # 矩阵的长宽
        n= len(matrix)
        m = len(matrix[0])
        vis = [[0 for i in range(m)] for i in range(n)]

        list = []
        while 0<=x<n and 0<=y<m and not vis[x][y]:
            list.append(matrix[x][y])
            vis[x][y] = 1

            # 试着向dir方向行走
            while (0<= x+dx[dir] <n) and (0 <= y+dy[dir] <m) and not vis[x+dx[dir]][y+dy[dir]]:
                x += dx[dir]
                y += dy[dir]
                list.append(matrix[x][y])
                vis[x][y] = 1

            # 走不动时
            dir = (dir + 1) % 4
            x += dx[dir]
            y += dy[dir]
        return list

 

(6)JZ28 数组中出现次数超过一半的数字

描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。你可以假设数组是非空的,并且给定的数组总是存在多数元素。1<=数组长度<=50000

class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        # sort the list
        if not numbers:
            return []
        if len(numbers) == 1:
            return numbers[0]
        numbers.sort()
        i ,count = 1,1
        while i < len(numbers):
            j = i
            while numbers[j] == numbers[j-1]:
                count += 1
                j += 1
                if count>len(numbers)/2:
                    return numbers[j-1]
            i = j
            i += 1
        return None

 
(7)JZ32 把数组排成最小的数

描述

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

# -*- coding:utf-8 -*-
class Solution:
    def PrintMinNumber(self, numbers):
        # write code here
        # 给序列标号
        if not numbers:
            return ""
        if len(numbers) == 1:
            return numbers[0]
        num = [i for i in range(len(numbers)-1)]

        # 比较两个列表大小
        def judge_min(var_a, var_b):
            """
            输入:两个数
            输出:1表示前者大于后者,0表示后者大于前者
            """
            var_a = [int(i) for i in list(str(var_a))]
            var_b = [int(i) for i in list(str(var_b))]
            max_length = max(len(var_a), len(var_b))
            while len(var_a) < max_length:
                var_a.append(var_a[-1])
            while len(var_b) < max_length:
                var_b.append(var_b[-1])
            # flag = 1 表示var_a更大些
            i = 0
            while i <= max_length-1:
                if var_a[i] == var_b[i]:
                    i += 1
                elif var_a[i] < var_b[i]:
                    return 0
                else:
                    return 1
            return i


        # 冒泡排序
        for i in range(len(numbers)):
            for j in range(0, len(numbers)-i-1):
                if judge_min(numbers[j], numbers[j+1]) == 1:
                    numbers[j], numbers[j+1] = numbers[j+1], numbers[j]

        return "".join(map(str, numbers))

 
(8)JZ35 数组中的逆序对

描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

class Solution:
    def __init__(self):
        self.c = 0

    def InversePairs(self, data):
        self.merge_sort(data)
        return self.c

    def merge_sort(self, A):
        if len(A) <=1:
            return A
        mid = len(A) // 2
        left = self.merge_sort(A[:mid])
        right = self.merge_sort(A[mid:])
        return self.merge(left, right)

    def merge(self, left, right):
        n1 ,n2 = len(left), len(right)
        i = j = 0
        res = []

        while i < n1 and j < n2:
            if left[i] < right[j]:
                res.append(left[i])
                i += 1
            else:
                res.append(right[j])
                j += 1
                # 因为返回的值为有序的序列所有left[i:]后面的元素都是大于right[j]的元素
                self.c += len(left) - i
                self.c %= 10 ** 9 + 7

        if i < n1:
            res += left[i:]
        elif j < n2:
            res += right[j:]
        return res

 
(9)JZ37 数字在升序数组中出现的次数

描述

统计一个数字在升序数组中出现的次数。

class Solution:
    def GetNumberOfK(self, data, k):
        if len(data) == 0:
            return 0

        def getLower(data, k):
            low = 0
            high = len(data) -1
            mid = (low + high)//2

            # 获取k第一次出现的下标
            while low <= high:
                if data[mid] < k:
                    low = mid + 1
                else:
                    high = mid - 1
                mid = (low + high)//2

            return low
        # 获得k最后一次出现的下标

        def getHigher(data, k):
            low = 0
            high = len(data) - 1
            mid = (low + high) // 2

            # 获取k第一次出现的下标
            while low <= high:
                if data[mid] <= k:
                    low = mid + 1
                else:
                    high = mid - 1
                mid = (low + high) // 2

            return high
        # 获得k最后一次出现的下标

        return getHigher(data, k) - getLower(data, k) + 1

 

(10)JZ42 和为S的两个数字

描述

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,返回两个数的乘积最小的,如果无法找出这样的数字,返回一个空数组即可。

# -*- coding:utf-8 -*-
class Solution:
    def FindNumbersWithSum(self, array, tsum):
        # write code here
        low = 0
        high = len(array)-1
        if len(array)<=1 or tsum ==0 :
            return []
        while low <= high:
            if array[low] + array[high] == tsum:
                return [array[low], array[high]]
            if array[low] + array[high] > tsum:
                high -= 1
            else:
                low += 1
        return []

 

(11)JZ50 数组中重复的数字

描述

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1

class Solution:
    def duplicate(self, numbers):
        if not numbers:
            return -1
        var = sorted(numbers)
        for i in range(len(numbers)-1):
            if var[i] == var[i+1]:
                return var[i]
        return -1

 

(12)JZ51 构建乘积数组
描述

给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)
对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在。

# -*- coding:utf-8 -*-
class Solution:
    def multiply(self, A):
        # write code here
        if not A:
            return []
        res = []
        for i in range(len(A)):
            var = A[:]
            var.pop(i)
            temp = 1
            for j in var:
                temp *= j
            res.append(temp)
        return res

 

2.16 字符串

 
(1)JZ2 替换空格

描述

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

class Solution:
    def replaceSpace(self , s):
        return '%20'.join(s.split(' '))

 

(2)JZ27 字符串的排列

描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

# -*- coding:utf-8 -*-
# 使用回溯的方法

class Solution:
    def Permutation(self, ss):
        # write code here
        ss = list(ss)
        def backtrack(position ,end):
            if position == end:
                var = ''.join(ss[:])
                if var not in res:
                    res.append(var)
                return


            for index in range(position, end):
                ss[index], ss[position] = ss[position], ss[index]
                backtrack(position + 1, end)
                ss[index], ss[position] = ss[position], ss[index]

        res = []
        backtrack(0, len(ss))
        return sorted(res)

 

(3)JZ34 第一个只出现一次的字符

描述

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)

# -*- coding:utf-8 -*-
class Solution:
    def FirstNotRepeatingChar(self, s):
        # write code here
        return s.index(list(filter(lambda c: s.count(c) == 1, s))[0]) if len(s)>0 else -1

 

(4)JZ43 左旋转字符串

描述

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列 S,请你把其循环左移 K 位后的序列输出(保证 K 小于等于 S 的长度)。例如,字符序列S=”abcXYZdef”,要求输出循环左移 3 位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

# -*- coding:utf-8 -*-
class Solution:
    def LeftRotateString(self, s, n):
        # write code here
        if len(s) == 0:
            return ""
        length = len(s)
        n = n % length
        s += s
        return s[n:length+n]

 

(5)JZ44 翻转单词序列

描述

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“nowcoder. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a nowcoder.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

# -*- coding:utf-8 -*-
class Solution:
    def ReverseSentence(self, s):
        # write code here
        if len(s) == 0:
            return ''
        str = s.split(' ')
        # 将str中的元素对调
        i = 0
        j = len(str) - 1
        while i < j:
            str[i], str[j] = str[j], str[i]
            i += 1
            j -= 1
        return ' '.join(str)

 

(6)JZ49 把字符串转换成整数

描述

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0

# -*- coding:utf-8 -*-
class Solution:
    def StrToInt(self, str):
        # write code here
        str = str.strip()
        if not str:
            return 0
        number, flag = 0, 1
        # 符号位的判断是否有正负号
        if str[0] == '-':
            str = str[1:]
            flag = -1
        elif str[0] == '+':
            str = str[1:]
        # 遍历除了+-以外所有字符,如果遇到非数字,则返回0
        for c in str:
            if '0' <= c <= '9':
                number = 10*number + int(c)
            else:
                return 0
        number *= flag
        return number

 

(7)JZ52 正则表达式匹配

描述

请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配

# 使用的是动态规划方法

class Solution:
    def match(self , str , pattern ):
        m, n = len(str) + 1, len(pattern) + 1
        dp = [[False] * n for _ in range(m)]
        dp[0][0] = True
        # 初始化首行
        for j in range(2, n, 2):
            dp[0][j] = dp[0][j - 2] and pattern[j - 1] == '*'
        # 状态转移
        for i in range(1, m):
            for j in range(1, n):
                if pattern[j - 1] == '*':
                    if dp[i][j - 2]: dp[i][j] = True                              # 1.
                    elif dp[i - 1][j] and str[i - 1] == pattern[j - 2]: dp[i][j] = True   # 2.
                    elif dp[i - 1][j] and pattern[j - 2] == '.': dp[i][j] = True        # 3.
                else:
                    if dp[i - 1][j - 1] and str[i - 1] == pattern[j - 1]: dp[i][j] = True # 1.
                    elif dp[i - 1][j - 1] and pattern[j - 1] == '.': dp[i][j] = True    # 2.
        return dp[-1][-1]

 

(8)JZ53 表示数值的字符串

描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

class Solution:
    def isNumeric(self , str ):
        # write code here
        try:
            res = float(str)
            return True
        except:
            return False

 

(9)JZ54 字符流中第一个不重复的字符

描述

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
后台会用以下方式调用Insert 和 FirstAppearingOnce 函数

# -*- coding:utf-8 -*-
class Solution:
    # 返回对应char
    s = ''
    def FirstAppearingOnce(self):
        for i in self.s:
            if self.s.count(i) == 1:
                return i
        return '#'
        # write code here
    def Insert(self, char):
        # write code here
        self.s += char

 

2.17 链表

 
(1)JZ3 从尾到头打印链表

描述

输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        # write code here
        var = []
        while listNode:
            var.append(listNode.val)
            listNode = listNode.next
        return var[::-1]

 
(2)JZ14 链表中倒数最后k个结点

描述

输入一个链表,输出一个链表,该输出链表包含原链表中从倒数第k个结点至尾节点的全部节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。

class Solution:
    def FindKthToTail(self , pHead , k ):
        # write code here
        result = []
        if pHead is None:
            return None
        while pHead:
            result.append(pHead)
            pHead = pHead.next
        if k<1 or k>len(result):
            return None
        else:
            return result[-k]

 

(3)JZ15 反转链表

描述

输入一个链表,反转链表后,输出新链表的表头。

class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        # write code here
        if not pHead:
            return None
        res = None
        while pHead:
            temp = pHead.next
            pHead.next = res
            res = pHead
            pHead = temp
        return res

 

(4)JZ16 合并两个排序的链表

class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        # write code here
        if not pHead1 and not pHead2:
            return None
        tmp = ListNode(0)
        head = tmp
        while pHead1 and pHead2:
            if pHead1.val <= pHead2.val:
                head.next = pHead1
                pHead1 = pHead1.next
            else:
                head.next = pHead2
                pHead2 = pHead2.next
            head = head.next
        if pHead1:
            head.next = pHead1
        elif pHead2:
            head.next = pHead2
        return tmp.next

 

(5)JZ25 复杂链表的复制

描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。
剑指offer刷题总结_第5张图片

class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if not pHead:
            return None
        newnode = RandomListNode(pHead.label)
        newnode.random = pHead.random
        newnode.next = self.Clone(pHead.next)
        return newnode

 

(6)JZ36 两个链表的第一个公共结点
剑指offer刷题总结_第6张图片

class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        stack1 = []
        stack2 = []
        while pHead1:
            stack1.append(pHead1)
            pHead1 = pHead1.next
        while pHead2:
            stack2.append(pHead2)
            pHead2 = pHead2.next
        res = None
        while stack1 and stack2:
            p1 = stack1.pop()
            p2 = stack2.pop()
            if p1 == p2:
                res = p1
            else:
                break
        return res

 

(7)JZ55 链表中环的入口结点

描述

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        if not pHead:
            return None
        if pHead.next == None:
            return None
        pNode1 = pHead
        pNode2 = pHead
        
        while pNode2 and pNode2.next:
            pNode1 = pNode1.next
            pNode2 = pNode2.next.next
            if pNode1 == pNode2:
                break
        pNode1 = pHead
        while pNode1 and pNode2 and pNode1 != pNode2:
            pNode1 = pNode1.next
            pNode2 = pNode2.next
        return pNode1

 

(8)JZ56 删除链表中重复的结点

描述

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

class Solution:
    def deleteDuplication(self, pHead):
        if not pHead:
            return None
        if pHead and not pHead.next:
            return pHead

        current = ListNode(0)
        if pHead.next.val == pHead.val:
            current = pHead.next.next
            while current != None and current.val == pHead.val:
                current = current.next
            return self.deleteDuplication(current)
        else:
            current = pHead.next
            pHead.next = self.deleteDuplication(current)
            return pHead

 

2.18 栈

 
(1)JZ5 用两个栈实现队列

描述

用两个栈来实现一个队列,分别完成在队列尾部插入整数(push)和在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.items1 = []
        self.items2 = []
         
    def push(self, node):
        self.items1.append(node)
         
    def pop(self):
        if not self.items1 and not self.items2:
            return -1
        else:
            if self.items2:
                return self.items2.pop()
            else:
                while self.items1:
                    self.items2.append(self.items1.pop())
                return self.items2.pop()

 

(2)JZ20 包含min函数的栈

描述

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数,并且调用 min函数、push函数 及 pop函数 的时间复杂度都是 O(1)
push(value):将value压入栈中
pop():弹出栈顶元素
top():获取栈顶元素
min():获取栈中最小元素

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.item = []

    def push(self, node):
        # write code here
        return self.item.append(node)

    def pop(self):
        # write code here
        return self.item.pop()

    def top(self):
        # write code here
        return self.item[-1]
    
    def min(self):
        return min(self.item)

 

(3)JZ21 栈的压入、弹出序列

描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

# -*- coding:utf-8 -*-
class Solution:
    def IsPopOrder(self, pushV, popV):
        # write code here
        if not pushV or not popV:
            return False
        
        if pushV[0] != popV[0] and len(pushV) == len(popV) == 1:
            return False

        def get_list(ls, val):
            index = ls.index(val)
            res = []
            if index != 0:
                res.append(ls[index-1:])
            else:
                res.append(ls[index:])
            return res[0]

        while pushV:
            for i,num in enumerate(popV):
                if i+1 < len(popV) and not popV[i+1] in get_list(pushV, num):
                    return False
                pushV.pop(pushV.index(num))
        return True

 

(4)JZ23 二叉搜索树的后序遍历序列

描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。(ps:我们约定空树不是二叉搜素树)

# -*- coding:utf-8 -*-
class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        if not sequence:
            return False
        return self.check(sequence)

    def check(self, arr):
        if len(arr) <= 2:
            return True

        # 寻找第一个大于root的值
        root = arr[-1]
        p = 0
        while arr[p] < root:
            p += 1
        index = p

        # 判断右边都是大于root的值
        while index < len(arr) -1:
            if arr[index] > root:
                index += 1
            else:
                return False

        return self.check(arr[:index]) and self.check(arr[index:-1])

 

(5)JZ59 按之字形顺序打印二叉树

描述

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        # write code here
        if not pRoot:
            return []

        res = []
        queue = [pRoot]
        high = 0
        while queue:
            temp = []
            for _ in range(len(queue)):
                cur = queue.pop(0)
                temp.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            high += 1
            if high % 2 == 0:
                temp = temp[::-1]
            res.append(temp)
        return res

 

2.19 队列

 
(1)JZ22 从上往下打印二叉树

描述

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

class Solution:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        # write code here
        if not root:
            return []
        res = []
        queue = [root]
        while queue:
            temp = queue
            queue = []
            for i in temp:
                res.append(i.val)
                if i.left:
                    queue.append(i.left)
                if i.right:
                    queue.append(i.right)
        return res

 
(2)JZ61 序列化二叉树

class Solution:
    def Serialize(self, root):
        if not root:
            return '#'
        return str(root.val) + ',' + self.Serialize(root.left)+ ',' + self.Serialize(root.right)

    def Deserialize(self, s):
        root, index = self.deserialize(s.split(","), 0)
        return root
    def deserialize(self,s,index):
        if s[index]=='#':
            return None, index+1
        root = TreeNode(int(s[index]))
        index += 1
        root.left, index = self.deserialize(s, index)
        root.right, index = self.deserialize(s, index)
        return root, index

 

2.20 树

 

(1)JZ4 重建二叉树

描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        # write code here
        if len(pre) == 0:
            return None
        elif len(pre) == 1:
            return TreeNode(pre[0])
        if len(pre) == 1:
            head = TreeNode(pre[0])
            head.left = None
            head.right = None
            return head
        head = TreeNode(pre[0])      
        temp1 = tin.index(pre[0])
        length = len(tin[:temp1])
        if temp1 == 0:
            head.left = None
        else:
            head.left = self.reConstructBinaryTree(pre[1:length+1], tin[:temp1])
        if length + 1 < len(pre):
            head.right = self.reConstructBinaryTree(pre[length+1:], tin[temp1+1:])
        else:
            head.right = None
        return head

 
(2)JZ17 树的子结构

描述

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

class Solution:
     def HasSubtree(self, pRoot1, pRoot2):
            result = False
            if pRoot1 and pRoot2:
                if pRoot1.val == pRoot2.val:
                    result = self.DoesTree1HaveTree2(pRoot1,pRoot2)
                if not result:
                    result = self.HasSubtree(pRoot1.left, pRoot2)
                if not result:
                    result = self.HasSubtree(pRoot1.right, pRoot2)
            return result
      # 这个也是递归,一个有着边界条件的递归
     def DoesTree1HaveTree2(self, pRoot_A, pRoot_B):
            if not pRoot_B:
                return True
            if not pRoot_A:
                return False
            if pRoot_A.val != pRoot_B.val:
                return False
            return self.DoesTree1HaveTree2(pRoot_A.left, pRoot_B.left) and self.DoesTree1HaveTree2(pRoot_A.right, pRoot_B.right)

 

(3)JZ18 二叉树的镜像

描述

操作给定的二叉树,将其变换为源二叉树的镜像。

class Solution:
    def Mirror(self , pRoot ):
        # write code here
        if pRoot == None:
            return None
        queue = [pRoot]
        res = [] # 存储多行
        while queue:
            val = queue.pop()
            val.left, val.right = val.right, val.left
            if val.left:
                queue.append(val.left)
            if val.right:
                queue.append(val.right)
        return pRoot

 
(4)JZ22 从上往下打印二叉树

描述

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

class Solution:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        # write code here
        if not root:
            return []
        res = []
        queue = [root]
        while queue:
            temp = queue
            queue = []
            for i in temp:
                res.append(i.val)
                if i.left:
                    queue.append(i.left)
                if i.right:
                    queue.append(i.right)
        return res

 
(5)JZ23 二叉搜索树的后序遍历序列

描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。(ps:我们约定空树不是二叉搜素树)

# -*- coding:utf-8 -*-
class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        if not sequence:
            return False
        return self.check(sequence)

    def check(self, arr):
        if len(arr) <= 2:
            return True

        # 寻找第一个大于root的值
        root = arr[-1]
        p = 0
        while arr[p] < root:
            p += 1
        index = p

        # 判断右边都是大于root的值
        while index < len(arr) -1:
            if arr[index] > root:
                index += 1
            else:
                return False

        return self.check(arr[:index]) and self.check(arr[index:-1])

 
(6)JZ24 二叉树中和为某一值的路径

描述

输入一颗二叉树的根节点和一个整数,按字典序打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        def dfs(root, expectNumber):

            
            path.append(root.val)
            expectNumber -= root.val
            if not root.left and not root.right and expectNumber == 0:
                ret.append(list(path))

            if root.left:
                dfs(root.left, expectNumber)
            if root.right:
                dfs(root.right, expectNumber)
            path.pop()

        # write code here
        if not root:
            return None
        ret, path = [], []
        dfs(root, expectNumber)
        return ret

 

(7)JZ38 二叉树的深度

描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

class Solution:
    def TreeDepth(self, pRoot):
        # write code here
        if not pRoot:
            return 0
        if pRoot and not pRoot.left and not pRoot.right:
            return 1
        res = 1 + max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right))
        return res

 
(8)JZ39 平衡二叉树

描述

输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

class Solution:
    def IsBalanced_Solution(self, pRoot):
        # write code here
        
        if not pRoot:
            return True
        else:
            res = abs(self.dapth(pRoot.left) - self.dapth(pRoot.right)) <=1 \
                  and self.IsBalanced_Solution(pRoot.left) \
                  and self.IsBalanced_Solution(pRoot.right) 
        return res
        
    # 计算每个结点的高度
    def dapth(self, root):
        if not root:
            return 0 
        else:
            return 1 + max(self.dapth(root.left), self.dapth(root.right))
        

 

(9)JZ57 二叉树的下一个结点
描述

给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的next指针。下图为一棵有9个节点的二叉树。树中从父节点指向子节点的指针用实线表示,从子节点指向父节点的用虚线表示.
剑指offer刷题总结_第7张图片

class Solution:
    def GetNext(self, pNode):
        if not pNode:
            return pNode
        
        # 右子树中的最左子树
        if pNode.right:
            pNode = pNode.right
            while pNode.left:
                pNode = pNode.left
            return pNode
    
        # 判断父节点是否存在
        while pNode.next:
            root = pNode.next
            if root.left == pNode:
                return root
            pNode = pNode.next
            
        return None

 

(10)JZ58 对称的二叉树

描述

请实现一个函数,用来判断一棵二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

class Solution:
    def isSymmetrical(self, pRoot):
        if not pRoot:
            return True

        def comp_value(pleft, pright):
            if not pleft and not pright:
                return True
            if not pleft or not pright:
                return False
            if pleft.val != pright.val:
                return False
            return comp_value(pleft.left, pright.right) and comp_value(pleft.right, pright.left)
        return comp_value(pRoot, pRoot)

 
(11)JZ59 按之字形顺序打印二叉树

描述

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        # write code here
        if not pRoot:
            return []

        res = []
        queue = [pRoot]
        high = 0
        while queue:
            temp = []
            for _ in range(len(queue)):
                cur = queue.pop(0)
                temp.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            high += 1
            if high % 2 == 0:
                temp = temp[::-1]
            res.append(temp)
        return res

 
(12)JZ60 把二叉树打印成多行

描述

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

# 使用层次遍历来输出结果
class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
    # write code here
        if pRoot == None:
            return None
        queue = [pRoot]
        res = [] # 存储多行
        while queue:
            val = queue.pop()
            res.append(val.val)
            if val.left:
                queue.append(val.left)
            if val.right:
                queue.append(val.right)
        return res

 

(13)JZ62 二叉搜索树的第k个结点

class Solution:
    # 返回对应节点TreeNode
    def KthNode(self, pRoot, k):
        # write code here
        def pre_order(pRoot):
            if pRoot:
                pre_order(pRoot.left)
                list.append(pRoot)
                pre_order(pRoot.right)
        list = []
        pre_order(pRoot)

        # 冒泡排序
        for i in range(len(list)):
            for j in range(0, len(list)-i-1):
                if list[j].val > list[j+1].val:
                    list[j], list[j+1] = list[j+1], list[j]


        if k == 0 or k > len(list):
            return None
        return list[k-1]

 

2.21 哈希

 

(1)JZ28 数组中出现次数超过一半的数字

描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。你可以假设数组是非空的,并且给定的数组总是存在多数元素。1<=数组长度<=50000

class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        # sort the list
        if not numbers:
            return []
        if len(numbers) == 1:
            return numbers[0]
        numbers.sort()
        i ,count = 1,1
        while i < len(numbers):
            
            j = i
            while numbers[j] == numbers[j-1]:
                count += 1
                j += 1
                if count>len(numbers)/2:
                    return numbers[j-1]
            i = j
            i += 1
        return None

 

(2)JZ40 数组中只出现一次的两个数字

描述

一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

class Solution:
    def FindNumsAppearOnce(self , array ):
        # write code here
        XORsum = 0
        for i in array:
            XORsum ^= i
        t = 1
        while (XORsum&t)==0:
            t = t<<1
        ans1 = 0
        ans2 = 0
        for i in array:
            if (t&i)==0:
                ans1 ^= i
            else:
                ans2 ^= i
        if ans1<ans2:
            return [ans1,ans2]
        else:
            return [ans2,ans1]

 

2.22 堆

 
(1)JZ29 最小的K个数

描述

给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。如果K>数组的长度,那么返回一个空的数组

# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        length = len(tinput)
        if (k > length) or tinput == []:
            return []
        var = tinput[0]
        for i in range(1, length):
            key = tinput[i]
            j = i - 1
            while j>=0 and key < tinput[j]:
                tinput[j+1] = tinput[j]
                j = j -1
            tinput[j+1] = key
        return tinput[:k]

 

(2)JZ63 数据流中的中位数

描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

import heapq
class Solution:
    def __init__(self):
        self.count = 0
        self.max_heap = []
        self.min_heap = []

    def Insert(self, num):
        # write code here
        self.count += 1
        #将数据放到最大堆中
        heapq.heappush(self.max_heap, -num)
        #将最大堆中的堆顶元素放到最小堆中
        max_heap_top = heapq.heappop(self.max_heap)
        heapq.heappush(self.min_heap, -max_heap_top)
        #如果最小堆和最大堆之间的差距大于1,将最小堆中的堆顶放进最大堆
        if self.count & 1:
            min_heap_top = heapq.heappop(self.min_heap)
            heapq.heappush(self.max_heap, -min_heap_top)

    #题目getmedian参数少了个s       
    def GetMedian(self):
        # write code here
        if self.count & 1:
            return -self.max_heap[0]
        else:
            return (self.min_heap[0] - self.max_heap[0]) / 2.0

 
(3)JZ64 滑动窗口的最大值

描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。

例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

窗口大于数组长度的时候,返回空

# -*- coding:utf-8 -*-
class Solution:
    def maxInWindows(self, num, size):
        # write code here
        if len(num) < size or not num or size == 0:
            return []
        var = []
        i = 0
        while i <= len(num)-size:
            var.append(max(num[i:i+size]))
            i += 1
        return var

 
 


 
剑指offer:https://www.nowcoder.com/ta/coding-interviews

你可能感兴趣的:(LeetCode,leetcode)