python实现《剑指offer》66道题

1. 二维数组中的查找

从右上到左下 注意while中内容怎么写

class Solution:
# array 二维列表
    def Find(self,target, array):
        # write code here
        rows = len(array) - 1  #行数
        cols= len(array[0]) - 1 #列数
        i = rows
        j = 0
        while j<=cols and i>=0:     #这里是<=和>=,一开始没习惯这么写
            if target<array[i][j]:
                i -= 1
            elif target>array[i][j]:
                j += 1
            else:
                return True
        return False

a = Solution()
print(a.Find(3,[[1,2],[3,5]]))

2. 替换空格

直接用python里的函数replace(old,new)。

法一:

class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        s = s.replace(' ','%20')
        return news
a = Solution()
print(a.replaceSpace("as d "))

法二:

class Solution:
# s 源字符串
    def replaceSpace(self, s):
        news = ''
        for j in s:
            if j == ' ':
                news = news + '%20'
            else:
                news =  news + j
        return news
a = Solution()
print(a.replaceSpace("as d "))

反转链表 从尾到头打印链表

1.用stack实现;2.用递归实现,python中,a=[1,2],a=a+[3],则a=[1,2,3]

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        if listNode is None:
            return []
        return self.printListFromTailToHead(listNode.next)+[listNode.val]

node1=ListNode(10)
node2=ListNode(11)
node3=ListNode(12)
node1.next=node2
node2.next=node3
a = Solution()
print(a.printListFromTailToHead(node1))

重建二叉树

根据根节点,区分左子树、右子树。再分别对中序遍历的左子树右子树做相同操作。前序遍历的位置每次后移一位。

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None        
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        if not pre or not tin:   # 并不是None,所以if pre == None or tin == None:这是错的
            return None
        root = TreeNode(pre.pop(0))  #每次弹出一个
        index = tin.index(root.val)
        root.left = self.reConstructBinaryTree(pre,tin[:index])
        root.right = self.reConstructBinaryTree(pre,tin[index+1:])
        return root
S = Solution()
p = S.reConstructBinaryTree([1,2,4,7,3,5,6,8],[4,7,2,1,5,3,8,6])
print(p.val)

或者改成

root = TreeNode(pre[0])
index = tin.index(root.val)
root.left = self.reConstructBinaryTree(pre[1:index+1],tin[:index])
root.right = self.reConstructBinaryTree(pre[index+1:],tin[index+1:])

也是一样的,因为pre[1:]从1开始,重新进去迭代,就是下一个了。

用两个栈来实现队列

入队:将元素进栈A

出队:判断栈B是否为空,如果为空,则将栈A中所有元素pop,并push进栈B,栈B出栈;

如果不为空,栈B直接出栈。

class Solution:
    def __init__(self):
        self.stackA = []
        self.stackB = []
         
    def push(self, node):
        # write code here
        self.stackA.append(node)
         
    def pop(self):
        # return xx
        if self.stackB:
            return self.stackB.pop(0)
        elif not self.stackA:
            return None
        else:
            while self.stackA:
                self.stackB.append(self.stackA.pop(0))  #可以写pop()或pop(0)
            return self.stackB.pop(0)


S = Solution()
S.push(7)
S.push(5)
S.push(4)
print(S.pop())  

旋转数组的最小数字

数组中的第一个数大于等于最后一个数,用二分查找,看中间值和第一个数比较,如果比第一个大,那肯定是前面是递增的。

class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if len(rotateArray)==0:
            return 0
        if len(rotateArray)==1:
            return rotateArray[0]
        if len(rotateArray)==2:
            if rotateArray[0]<=rotateArray[1]:
                return rotateArray[0]
            else:
                return rotateArray[1] 
        #start = rotateArray[0]
        #stop = rotateArray[len(rotateArray[0])-1]        
        index = int(len(rotateArray)/2)
        middle = rotateArray[index]
        if middle>=rotateArray[0] :
            return self.minNumberInRotateArray(rotateArray[index:])
        else:
            return self.minNumberInRotateArray(rotateArray[:index+1]) 
S = Solution()
p = S.minNumberInRotateArray([3,3,3,1,3])
print(p)

斐波那契数列

不能用递归啊,从上向下不好,要从下向上,知道两个初始值,慢慢推上去

class Solution:      
    def Fibonacci(self, n):
        # write code here
        f1=0
        f2=1
        if n==0:
            return 0
        elif n==1:
            return 1
        else:
            for i in range(n+1)[2:]:
                f = f1+f2
                f1 = f2
                f2 = f               
            return f

S = Solution()
p = S.Fibonacci(7)
print(p)

法二:

class Solution:      
    def Fibonacci(self, n):
        result = [0,1]
        a=n
        while n>=2:
            result.append(result[-1]+result[-2])    #不是result=result.append,就是result.append
            n=n-1
        return result[a]

S = Solution()
p = S.Fibonacci(8)
print(p)

或者:
res = [0,1]
while(len(res)<=n):
res.append(res[-1]+res[-2])
return res[n]

反转链表

不要while pHead.next!=None:就用while pHead:

class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        # write code here
        prev = None
        pcur = pHead
        while pcur:
            tmp = pcur.next    #暂存pcur.next
            pcur.next = prev    #反转
            prev = pcur    #移到下一位  # 不是prev.next = pcur   (.next想成一个指针 指向某处)移动的只有pcur。
            #或者想成prev是指针,只有指向的地址变了,它本身没有.next的概念的。
            pcur = tmp    #移到下一位
        return prev     #新表头 就是末尾

合并两个排序的链表

这题做了很久,用递归和非递归版本。以下是非递归实现:

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        temp = ListNode(None)   #或者ListNode(0) 这里可以引用其他的类的,可以在类中引用其他类!
        node = temp         #不能返回temp,因为temp已经到了末尾,node是一个指针,指向temp的初始地址
        #这里很奇怪,写成上面形式,或者 node = ListNode(None) temp = node都可以  
        while pHead1 or pHead2:
            if pHead1==None:
                temp.next = pHead2      #注意.next的使用,一开始就这样,忽略了初始的None
                break
            if pHead2 == None:
                temp.next = pHead1
                break
            else:
                if pHead1.val <=pHead2.val:
                    temp.next = pHead1
                    pHead1 = pHead1.next
                else:
                    temp.next = pHead2
                    pHead2 = pHead2.next
                temp = temp.next
        return node.next    #不要初始的第一个自己初始化的结点了

树的子结构

这个题要好好理清楚,多刷几遍。
思路是肯定要用两个函数的,step1.遍历 找到是否有相同的第一个结点,step2.在有第一个相同结点的基础上,才跳进新函数,去分别遍历左子树和左子树匹配,右子树和右子树匹配。

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution:
    def HasSubtree(self, pRoot1, pRoot2):
        # write code here
        result = False
        if pRoot1 and pRoot2:
            if pRoot1.val == pRoot2.val:
                result = self.zishu(pRoot1,pRoot2)
            if result == False:
                result = self.HasSubtree(pRoot1.left,pRoot2)
            if result == False:
                result = self.HasSubtree(pRoot1.right,pRoot2)                
        return result

    def zishu(self,pRoot1,pRoot2):
        if pRoot2 == None:
            return True
        if pRoot1 == None:
            return False
        if pRoot1.val != pRoot2.val:
            return False
        return (self.zishu(pRoot1.left,pRoot2.left) and self.zishu(pRoot1.right,pRoot2.right)) 

二叉树的镜像

交换左右子结点,子节点下的子树也会搬到另一边的。
代码关键点1. return。并不是有return,递归时就要一定要将结果赋给某个变量。
2.self的使用,temp = TreeNode(0)。一开始用的temp = self.TreeNode(0)。错的。

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        if root == None:
            return root
        if root.left or root.right: #这句话可有可无
            temp = TreeNode(0)  #这句话可有可无,注意self用法,不写成 self.TreeNode(0)/(None)
            temp = root.left
            root.left = root.right
            root.right = temp
        self.Mirror(root.left)  #注意 这里返回值,没有赋给某变量。直接执行语句。
        self.Mirror(root.right)
        return root  

顺时针打印矩阵

这个用python还是有点麻烦的,for循环range的使用。还有就是用res.append。这题看答案的,首先要设置top bottom left right。

class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        rows = len(matrix)-1      #行
        cols = len(matrix[0])-1   #列
        left=0
        right=cols
        top=0
        bottom=rows
        res = []
        while top<=bottom and left<=right:
            for i in range(left,right+1):
                res.append(matrix[top][i])
            for i in range(top+1,bottom+1): 
                res.append(matrix[i][right])
            if top!=bottom:                 #判断 上等于下的时候,就不需要从右到左再遍历了
                for i in range(right-1,left-1,-1):  #很复杂 从大到小 这里是left-1而不是left+1
                    res.append(matrix[bottom][i])
            if left!=right:                 #判断 左等于右的时候,就不需要从下到上再遍历了
                 for i in range(bottom-1,top,-1):   #右边取闭区间top+1,所以这里是top
                     res.append(matrix[i][left])
            left=left+1
            right = right-1
            top = top+1
            bottom = bottom-1
        return res

a = Solution()
matrix = [[1,2,3],[4,5,6]]
print(a.printMatrix(matrix))

包含mini函数的栈

栈的问题,都一个一个入栈地分析,要想成初始栈里就有元素,从空栈,一个个压入开始分析。

辅助栈 概念。

错题集:1.self;2.self.min()忘记加(),函数记得加括号

class Solution:
    def __init__(self):
        self.stack = []
        self.assist = []
    def push(self, node):
        # write code here
        if not self.min() or node < self.min():     #之前写成self.min,忘记加()了
            self.assist.append(node)
        else:
            self.assist.append(self.min())      #或self.assist.append(self.assist[-1]不过不好
        self.stack.append(node)
    def pop(self):
        # write code here
        if self.stack:
            self.assist.pop()
            return self.stack.pop()
    def top(self):
        # write code here
        if self.stack:
            return self.stack[-1]
    def min(self):
        # write code here 
        if self.assist:
            return self.assist[-1]

栈的压入、弹出序列

class Solution:
    def IsPopOrder(self, pushV, popV):
        stack, k = [], 0
        # write code here
        # 用一个辅助栈 保存pushV的元素,重要的是if判断,先判断pushV[i]和popV[k]是否相等
        #再判断stack栈顶元素是否和pop[k]相等,相等就pop,k++。
        for i in range(len(popV)):
            stack.append(pushV[i])
            if pushV[i]==popV[k]:
                while stack!=[] and stack[-1] == popV[k]:
                    stack.pop()
                    k = k+1
        if k==len(popV):
            return True
        return False


pushV = [1,2,3,4,5]
popV = [4,5,3,2,1]
a = Solution()
print(a.IsPopOrder(pushV,popV))

从上往下打印二叉树

BFS广度优先搜索算法,用queue队列,这题我以为要用递归的方法,想的都是递归怎么做,原来可以通过迭代实现,那么,要怎么找完root,找left和right呢?答案是通过一个list,list放的每个元素是treenode,每次读一个,就能依次遍历了。

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

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

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

都要区分开左右子树,核心思想,重点1.怎么区分开。重点2.怎么判断true false

class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        if sequence==None or len(sequence)<=0:
            return False
        root = sequence[-1]
        for i in sequence:
            if i>root:
                break
        j = sequence.index(i)   #左右子树区分点
        for ii in sequence[j:]:
            if ii < root:
                return False
        left,right = True, True
        if j>0: #之前没写这个判断  不是全部都是右子树 也有左子树 才执行下面一步
            left = self.VerifySquenceOfBST(sequence[:j])
        if j<len(sequence)-1:    #不是全部都是左子树 也有右子树 才执行下面一步 (如果全部都是左子树, j==len(sequence)-1
            right = self.VerifySquenceOfBST(sequence[j:-1])
        return left and right

二叉树中和为某一值的路径

这题还是没能自己做出来。是dfs搜索,用递归,但不知道怎么用。

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        # write code here
        res = []
        if not root:
            return res
        path = [root]  #保存根节点
        sums = [root.val]
        def dfs(root):  #深度优先搜索的变形,不是用while,而是用递归!
            if root.left:
                path.append(root.left)
                sums.append(root.left.val+sums[-1])
                dfs(root.left)
            if root.right:
                path.append(root.right)
                sums.append(root.right.val+sums[-1])
                dfs(root.right)
            if not root.left and not root.right:
                if sums[-1] == expectNumber:
                    res.append([p.val for p in path])
            path.pop()
            sums.pop()
        dfs(root)
        return res       

pNode1 = TreeNode(10)
pNode2 = TreeNode(5)
pNode3 = TreeNode(12)
pNode4 = TreeNode(4)
pNode5 = TreeNode(7)


pNode1.left = pNode2
pNode1.right = pNode3
pNode2.left = pNode4
pNode2.right = pNode5


S = Solution()
print(S.FindPath(pNode1, 22))

复杂链表的复制

这个很难想,一开始不知道要干什么,思路是如何复制一个链表,要新建结点,复制值、next指针、random指针。

分为三步完成:

一复制每个结点,并把新结点放在老结点后面,如1->2,复制为1->1->2->2

二为每个新结点设置other指针

三把复制后的结点链表拆开

class RandomListNode:
    def __init__(self, x):
        self.label = x
        self.next = None
        self.random = None
class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if not pHead:
            return None    
        def clonenext(pHead):   #在def里又定义def,不能再加self了
            node = pHead
            while node:
                clone = RandomListNode(0)   #是一个结点 创建一个个结点
                clone.label = node.label
                clone.next = node.next
                node.next = clone
                node = clone.next

        def clonerandom(pHead):
            node = pHead
            while node:
                clone = node.next
                if node.random:
                    clone.random = node.random.next
                node = clone.next

        def separate(pHead):    #法一 自己写的
            node = pHead
            clone1 = node.next
            while node:
                clone = node.next
                if not clone.next:
                    node.next = None
                    clone.next = None
                    return clone1
                node.next = clone.next
                node = node.next
                clone.next = node.next        
        def ReconnectNodes(pHead):  #法二 剑指offer上的
            pNode = pHead
            pClonedHead = pClonedNode = pNode.next
            pNode.next = pClonedHead.next
            pNode = pNode.next

            while pNode:
                pClonedNode.next = pNode.next
                pClonedNode = pClonedNode.next
                pNode.next = pClonedNode.next
                pNode = pNode.next

            return pClonedHead        



        clonenext(pHead)
        clonerandom(pHead)
        return separate(pHead)

node1 = RandomListNode(1)
node2 = RandomListNode(3)
node3 = RandomListNode(5)
node1.next = node2
node2.next = node3
node1.random = node3

S = Solution()
clonedNode = S.Clone(node1)
print(clonedNode.random.label)  

二叉搜索树与双向链表

用list存每个结点

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution:
    def Convert(self, pRootOfTree): #中序遍历 right:前向链表 left:后向链表
        # write code here
        if not pRootOfTree: #一开始没写
            return
        a, b = [], []                
        def zhongxu(root):
            if not root:
                return 
            if pRootOfTree.left:
                zhongxu(root.left)
            a.append(root)   #访问根结点
            if root.right:
                zhongxu(root.right)

        zhongxu(pRootOfTree)
        for i in range(len(a))[:-1]:
            a[i].right = a[i+1]
        for i in range(len(a))[1:]:
            a[i].left = a[i-1]
        return a[0] #返回是a[0],而不是返回a

    #法二:l

    for i,v in enumerate(self.arr[:-1]):
        v.right = self.arr[i + 1]
        self.arr[i + 1].left = v
    return self.arr[0]

node1 = TreeNode(7)
node2 = TreeNode(5)
node3 = TreeNode(8)
node4 = TreeNode(4)
node5 = TreeNode(6)

node1.left = node2
node1.right = node3
node2.left = node4
node2.right = node5


S = Solution()
print(S.Convert(node1))

递归核心思想:可以写成诸如f(n) = f(n+*) ….的表达式

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

class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        for i in numbers:
            if numbers.count(i)>len(numbers)/2:
                return i
        return 0
S = Solution()
print(S.MoreThanHalfNum_Solution([1,2,3,2,2,2,5,4,2]))

## 连续子数组的最大和
这题有两种解法,一种是我们熟知的动态规划算法,还有一种挺巧妙的,看的剑指 offer里的。

法一:

 class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        a = False
        if not array:
            a = True
            return 0
        a = False
        cur = 0
        Sum = -1000
        for i in array:
            if cur <= 0:    #cur<=0 说明前面累加的白加了,可以从下一个重新加起
                cur = i
            else:
                cur = cur + i
            if cur > Sum:
                Sum = cur
        return Sum

S = Solution()
print(S.FindGreatestSumOfSubArray([-3,5,-2]))

法二:动态规划!!!!!!一定要会啊

f[i]表示以i结尾的子数组的最大和,只需求出max(f[i]),递归公式:

f[i] = array[i]             ( f[i-1]<=0 )   #前面累加小于0,所以不要前面的了,只保留最后这个
f[i] = array[i] + f[i-1]    ( f[i-1>0] )

解法:

class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        f = [None]*len(array)   #不能写f = np.zeros(len(array),在开头import numpy as np,牛客网不支持import numpy
        f[0] = array[0]
        for i in range(len(array))[1:]:
            if f[i-1]<=0:
                f[i] = array[i]
            else:
                f[i] = array[i] + f[i-1]
        return max(f)

整数中1出现的次数(从1到n整数中1出现的次数)

参考http://www.cnblogs.com/qiaojushuang/p/7780445.html

对于1-9,10-99,100-999,每个n位数中包含1的个数公式为:

f(1) = 1

f(2) = 9 * f(1) + 10 ** 1

f(3) = 9 * f(2) + 10 ** 2

f(n) = 9 * f(n-1) + 10 ** (n-1)

n位数一共的1个数:

s(1) = f(1)
s(2) = 9*s(1)+ 10 ** 1 + s(1)
s(3) = 9*s(2)+ 10 ** 1 + s(2)

高位数:分两种:
1.最高位==1时,最高位为1时的次数(eg.12345)为:2346次(联想:10000-19999的次数为9999+1,因此12345的次数为2346)
2.最高位>1时,最高位为1 时的次数(eg.22345)为):10000次,因为包含了最高位为1时所有可能(当最高位为1时)。

class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        if n < 10:
            return 1 if n >= 1 else 0
        #判断最高位为1时,1的个数
        gaowei = int(str(n)[0])
        weishu = len(str(n))
        if gaowei == 1:
            high = n - (10**(weishu-1))* gaowei + 1 #这个加1别忘了
                #high1 = high
        else:
            high = 10**(len(str(n))-1)

        def fn(n):
            if n <= 0:
                return 0
            if n == 1:
                return 1
            current = 9 * fn(n-1) + 10 ** (n-1)
            return fn(n-1) + current    #这步很重要
        """        
        def sum1(n):   #记录f(n)的和    #我自己写的,错误的fn
            if n <= 0:
                return 0
            def fn(n): #   n :位数
                if n <= 0:
                    return 0
                if n == 1:
                    return 1
                if n > 1:
                    return fn(n-1)*9 + 10**(n-1)
            summ=0
            for i in range(n+1)[1:]:
                summ = summ + fn(i) 
        """
        low = fn(weishu-1) * gaowei
        return high+low+ self.NumberOf1Between1AndN_Solution(n - gaowei * 10 ** (weishu-1))   


S = Solution()
print(S.NumberOf1Between1AndN_Solution(13))

把数组排成最小的数

这题和“字符串的排列”那题是一样的,都是全排列。
把数组变成一串数字:int(''.join([str(t) for t in arrSs]))

class Solution:
    def PrintMinNumber(self, numbers):
        if not numbers:
            return ""

        def Permutation(startIdx):
            if startIdx >= len(arrSs):
                clone = int(''.join([str(t) for t in arrSs]))
                res.append(clone)
            else:
                changeIdx = startIdx
                while changeIdx < len(arrSs):
                    arrSs[changeIdx], arrSs[startIdx] = arrSs[startIdx], arrSs[changeIdx]
                    Permutation(startIdx + 1)
                    arrSs[changeIdx], arrSs[startIdx] = arrSs[startIdx], arrSs[changeIdx]
                    changeIdx += 1

        res = []
        arrSs = numbers
        Permutation(0)
        res = list(set(res))
        return min(sorted(res))        
S = Solution()
print(S.PrintMinNumber([12,34,56]))

法二:拼接成的数组mn和nm比较,马上能得出大小(cmp的用法)。拼接用字符串拼接(数字转换成字符串)。

from functools import cmp_to_key

def cmp(a, b):
    return int(str(a)+str(b)) - int(str(b)+str(a))


def print_mini(nums):
    if not nums:
        return
    print (int(''.join([str(num) for num in sorted(nums, key=cmp_to_key(cmp))])))


if __name__ == '__main__':
    test = [3,32,321]
    print_mini(test)    

丑数

这题很巧妙。下次要再做一次才行。下面是看别人的答案

class Solution:
    def GetUglyNumber_Solution(self,n):
        if n <= 0:
            return 0
        ugly = [1]
        t2, t3, t5 = 0, 0, 0  # 分别标记乘以2,3,5的丑数索引
        while n > 1:
            while ugly[t2] * 2 <= ugly[-1]:
                t2 += 1
            while ugly[t3] * 3 <= ugly[-1]:
                t3 += 1
            while ugly[t5] * 5 <= ugly[-1]:
                t5 += 1
            ugly.append(min([ugly[t2]*2, ugly[t3]*3, ugly[t5]*5]))
            n -= 1
        return ugly[-1]

数组中的逆序对

归并排序的变形。注意count,设置全局变量,在class外,挺巧妙的。(在牛客网上不能在规定时间内执行完,归并+递归的复杂度还是比较高)

count = 0 
class Solution:
    def InversePairs(self, data):
        global count
        def merge_sort(lists):
            if len(lists) <= 1:
                return lists
            global count 
            def merge(left, right):
                global count
                i, j = 0, 0
                result = []
                while i < len(left) and j < len(right):
                    if left[i] <= right[j]:
                        result.append(left[i])
                        i += 1
                    else:
                        result.append(right[j])
                        j += 1
                        count += len(left)-i #right[j]比left[i]小,就比left剩余的都小,因为left是排好序的了
                result += left[i:]
                result += right[j:]
                return result
            # 归并排序
            num = len(lists) >> 1
            left = merge_sort(lists[:num])
            right = merge_sort(lists[num:])
            return merge(left, right)
        merge_sort(data)
        return count%1000000007

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

链表问题,出发点1.长度;2.双指针

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        p1, p2 = pHead1, pHead2
        n1, n2 = pHead1, pHead2
        l1, l2 = 0, 0
        while p1:
            l1+=1
            p1 = p1.next
        while p2:
            l2+=1
            p2 = p2.next
        if l1>l2:
            s = l1-l2
            while s:
                s-=1
                n1 = n1.next
        else:
            s = l2 - l1
            while s:
                s -=1
                n2 = n2.next
        while n2:
            if n1 == n2:    
                return n1
            n1 = n1.next
            n2 = n2.next

Node1 = ListNode(0)
Node2 = ListNode(1)
Node3 = ListNode(2)
Node1.next = Node2
Node2.next = Node3

Node4 = ListNode(4)
Node5 = ListNode(1)

Node4.next = Node5
Node5.next = Node2

S = Solution()
print(S.FindFirstCommonNode(Node1,Node4).val)  

二叉树的深度

这题看了剑指 offer的讲解,太巧妙了,哎,自己写不出来。
我一直在考虑的都是只有左子树或只有右子树时,深度加1。
没考虑到左右子树都有的时候,要考虑二者的深度大小对比了,二者都有的情况下,左深度大于右边深度,深度为左子树+1!反之亦然。

class Solution:
    def TreeDepth(self, pRoot):
        # write code here
        if not pRoot:
            return 0
        nl = self.TreeDepth(pRoot.left)
        nr = self.TreeDepth(pRoot.right)
        if nl > nr:
            return nl+1
        else:
            return nr+1

或者直接写成:

    def maxDepth(p):
        if not p:
            return 0
        return 1 + max(maxDepth(p.left),maxDepth(p.right))

平衡二叉树

看的牛客网其他人的回答。

方法一:自顶向下,对于每个节点,都计算一下左子树以及右子树的深度差的绝对值,即每个节点都判断一下。(借鉴了上一题)
算法复杂度为O(N*2)

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def IsBalanced_Solution(self, p):
        if p is None:
            return True
        left = self.depth(p.left)
        right = self.depth(p.right)
        return abs(left - right) <=1 and self.IsBalanced_Solution(p.left) and self.IsBalanced_Solution(p.right)
    def depth(self, p):
        if p is None:
            return 0
        return 1 + max(self.depth(p.left), self.depth(p.right))

方法二:自下往上
算法复杂度O(N)

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def IsBalanced_Solution(self, p):
        return self.dfs(p) != -1
    def dfs(self, p):
        if p is None:
            return 0
        left = self.dfs(p.left)
        if left == -1:
            return -1
        right = self.dfs(p.right)
        if right == -1:
            return -1
        if abs(left - right) > 1:
            return -1
        return max(left, right) + 1

递增数组,和为某个数字

复杂度O(n)

class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array,target):
        i,j=0,len(array)-1 #两指针 指向头和尾
        res = []
        while i<j:
            if array[i]+array[j]==target:
                res.append([array[i],array[j]])
                i += 1
                j -= 1
            if array[i]+array[j] > target:
                j -= 1
            if array[i]+array[j] < target:
                i +=1
        return res

和为S的连续整数序列

这题看了剑指 offer的讲解,

class Solution:
    def FindContinuousSequence(self, tsum):
        # write code here
        i, j = 1, 2
        summ = [1,2]
        res = []
        while i < (1+tsum)>>1:
            temp = sum(summ)
            if temp == tsum:
                b=[]
                for ii in summ: #这里注意 不能直接res.extend([summ]),这样下面的summ改变了,res里头的元素也会跟着改变。
                    b.append(ii)
                res.extend([b])
                j+=1
                if j>=tsum:
                    break
                summ += [j]
                continue
            if temp < tsum:
                j+=1
                summ+=[j]
                continue
            if temp > tsum:
                summ = summ[1:]
                i+=1
        return res

翻转的单词顺序列

class Solution:
    def ReverseSentence(self, s):
        # write code here
        b = s.split(' ')    #python中,''和""是等价的 #之前一直写的s.split()就不行,是按照""的空格区分的 
        b.reverse() #或者写成[::-1]
        str1 = ''
        str1 = ' '.join(b)
        return str1

孩子们的游戏(圆圈中最后剩下的数)约瑟夫环??

这题好难,想用直接法,复杂度O(m*n)的方法,都没做出来。
看了剑指 offer的解析,映射的,很难想到。

# 牛客网显示maximum recursion depth exceeded 未完全AC
class Solution:
    def LastRemaining_Solution(self, n, m):
        # write code here
        if n < 1:
            return -1
        if n==1:
            return 0
        else:
            return (self.LastRemaining_Solution(n-1,m)+m)%n
#用迭代做
class Solution:
    def LastRemaining_Solution(self, n, m):
        # write code here
        if n < 1 or m < 1:
            return -1
        if n==1:
            return 0
        last = 0 # n=1时,返回0
        for i in range(n+1)[2:]:
            last = (last + m)%i
        return last

不用加减乘除做加法

python要进行越界检查 & 0xFFFFFFFF

正负数检查 num1 if num1<=0x7FFFFFFF else ~(num1^0xFFFFFFFF)

class Solution:
    def Add(self, num1, num2):
        # write code here
        while num2:
            summ = num1 ^ num2
            carry = (num1 & num2)<<1
            num1 = summ & 0xFFFFFFFF
            num2 = carry & 0xFFFFFFFF
        return num1 if num1<=0x7FFFFFFF else ~(num1^0xFFFFFFFF)

把字符串转换成整数

要点在于如何把str转换成int型。
设置一个number = ['0','1','2','3','4','5','6','7','8','9']
然后number.index(i),比如i是 ‘1’,返回对应的下标1,这个是int型,很巧妙。

class Solution:
    def StrToInt(self, s):
        # write code here
        if not s:
            return 0
        if len(s)==1:   
            if s <= '0' or s >= '9':
                return 0
        lable = 1
        start = 0
        if s[0] == '+' :
            lable = 1
            start = 1
        if s[0] == '-':
            lable = -1
            start = 1
        summ = 0
        number = ['0','1','2','3','4','5','6','7','8','9']
        for i in s[start:]:
            if i <= '0' or i >= '9':
                return 0
            summ = summ*10 + number.index(i)

        return summ*lable

数组中重复的数字

这题一开始写成if a[i] in a这是错的,因为一开始a[i]还没赋值,也就没有等不等于的概念。这里in a就是在a.keys中搜索。

class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        a={}
        for i in numbers:
            if i in a:
                duplication[0] = i
                return True
            a[i] = 1    
        return False

构建乘积数组

这题脑回路好难,必须要看答案。

class Solution:
    def multiply(self, A):
        if A == None or len(A) <= 0:
            return
        length = len(A)
        aList = [1] * length
        for i in range(1, length):
            aList[i] = aList[i-1] * A[i-1]
        temp = 1
        for i in range(length-2, -1, -1):
            temp *= A[i+1]  #一开始不懂为什么要设置temp
            aList[i] *= temp
        return aList

之所以要设置temp。分成两个矩阵,左边C[i]=C[i-1]*A[i-1]右边D[i]=D[i+1]*A[i+1]

解析(看的https://www.nowcoder.com/questionTerminal/94a4d381a68b47b7a8bed86f2975db46

链接:https://www.nowcoder.com/questionTerminal/94a4d381a68b47b7a8bed86f2975db46
来源:牛客网

对于第一个for循环
第一步:b[0] = 1;
第二步:b[1] = b[0] * a[0] = a[0]
第三步:b[2] = b[1] * a[1] = a[0] * a[1];
第四步:b[3] = b[2] * a[2] = a[0] * a[1] * a[2];
第五步:b[4] = b[3] * a[3] = a[0] * a[1] * a[2] * a[3];
然后对于第二个for循环
第一步
temp *= a[4] = a[4]; 
b[3] = b[3] * temp = a[0] * a[1] * a[2] * a[4];
第二步
temp *= a[3] = a[4] * a[3];
b[2] = b[2] * temp = a[0] * a[1] * a[4] * a[3];
第三步
temp *= a[2] = a[4] * a[3] * a[2]; 
b[1] = b[1] * temp = a[0] * a[4] * a[3] * a[2];
第四步
temp *= a[1] = a[4] * a[3] * a[2] * a[1]; 
b[0] = b[0] * temp = a[4] * a[3] * a[2] * a[1];
由此可以看出从b[4]到b[0]均已经得到正确计算。

表示数值的字符串

这题看的别人的https://www.nowcoder.com/questionTerminal/6f8c901d091949a5837e24bb82a731f2

class Solution:
    # s字符串
    def isNumeric(self, s):
        # write code here
        sign, decimal, hasE = False, False, False #正负号,小数,有没有E
        for i in range(len(s)):
            if s[i] == 'e' or s[i] == 'E' :
                if hasE:
                    return False
                if i == len(s)-1 :
                    return False
                hasE = True
            elif s[i] == '+' or s[i] =='-' :
                if sign and s[i-1]!='e' and s[i-1]!='E' :
                    return False
                if not sign and i>0 and s[i-1]!='e' and s[i-1]!='E' :
                    return False
                sign = True
            elif s[i] == '.' :
                if decimal or hasE:
                    return False
                decimal = True
            elif s[i] > '9' or s[i] < '0' :
                return False
        return True

链表中环的入口结点

class ListNode:
def __init__(self, x):
    self.val = x
    self.next = None
class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        p1, p2, p3 = pHead, pHead, pHead
        while p1:
            if not p1 or not p2 or not p1.next or not p1.next.next:
                return None
            p1 = p1.next
            p2 = p2.next.next
            if p1 == p2:
                break
        while p1:
            if p1 == p3:    #一开始把这个判断写在下面了,应该写在开头。
                return p1          
            p1 = p1.next                
            p3 = p3.next

pNode1 = ListNode(1)
pNode2 = ListNode(2)
pNode3 = ListNode(3)
pNode4 = ListNode(4)

pNode1.next = pNode2
pNode2.next = pNode3
pNode3.next = pNode4
pNode4.next = pNode1

S = Solution()
print(S.EntryNodeOfLoop(pNode1))

删除链表中重复的结点

这题想用之前写过的“在O(1)内删除链表结点”的思想,写了半天也没写出来。

看了牛客网上的:
链接:https://www.nowcoder.com/questionTerminal/fc533c45b73a41b0b44ccba763f866ef
用递归做,还是比较巧妙的。

public ListNode deleteDuplication(ListNode pHead) {
       if (pHead == null || pHead.next == null) { // 只有0个或1个结点,则返回
           return pHead;
       }
       if (pHead.val == pHead.next.val) { // 当前结点是重复结点
           ListNode pNode = pHead.next;
           while (pNode != null && pNode.val == pHead.val) {
               // 跳过值与当前结点相同的全部结点,找到第一个与当前结点不同的结点
               pNode = pNode.next;
           }
           return deleteDuplication(pNode); // 从第一个与当前结点不同的结点开始递归
       } else { // 当前结点不是重复结点
           pHead.next = deleteDuplication(pHead.next); // 保留当前结点,从下一个结点开始递归
           return pHead;
       }
   }

对称的二叉树

这题做出来了,开心。重点在于新建的’#’结点,这个想得比较久。

class Solution:
    def isSymmetrical(self, pRoot): #中序遍历 以根节点为中心,对称
        # write code here
        a = []
        def zhongxu(pRoot):
            if not pRoot:
                #newNode = TreeNode('#')
                #a.append(newNode.val)
                return
            if pRoot.left and not pRoot.right:
                pRoot.right = TreeNode('#')
            elif pRoot.right and not pRoot.left:
                pRoot.left = TreeNode('#')
            zhongxu(pRoot.left)
            a.append(pRoot.val)
            zhongxu(pRoot.right)
        zhongxu(pRoot)
        lens = len(a)
        for i in range(lens>>1):
            if a[i] != a[lens-1-i]:
                return False
        return True

按之字形顺序打印二叉树

参考了 https://github.com/Jack-Lee-Hiter/AlgorithmsByPython/blob/master/Target%20Offer/%E6%8C%89%E4%B9%8B%E5%AD%97%E5%BD%A2%E9%A1%BA%E5%BA%8F%E6%89%93%E5%8D%B0%E4%BA%8C%E5%8F%89%E6%A0%91.py

是层次遍历的变形,我知道的,但还是没能写出来。每次打印一层,不要全部一起打印。

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution:
    # 存储点的时候按照奇数层和偶数层分别存储
    def Print(self, pRoot):
        if not pRoot:
            return []
        result, nodes = [], [pRoot]
        right = True
        while nodes:
            curStack, nextStack = [], []
            if right:
                for node in nodes:
                    curStack.append(node.val)
                    if node.left:
                        nextStack.append(node.left)
                    if node.right:
                        nextStack.append(node.right)
            else:
                for node in nodes:
                    curStack.append(node.val)
                    if node.right:
                        nextStack.append(node.right)
                    if node.left:
                        nextStack.append(node.left)
            nextStack.reverse()
            right = not right
            result.append(curStack)
            nodes = nextStack
        return result
    # 转换思路,存储的时候一直从左向右存储,打印的时候根据不同的层一次打印
    def zigzagLevelOrder(self, root):
        if not root:
            return []
        level, result, lefttoright = [root], [], True
        while level :
            nextlevel ,curres = [], []
            for node in level:
                curres.append(node.val)
                if node.left:
                    nextlevel.append(node.left)
                if node.right:
                    nextlevel.append(node.right)
            if not lefttoright:
                curres.reverse()
            lefttoright = not lefttoright
            if curres:
                result.append(curres)
            level = nextlevel
        return result


pNode1 = TreeNode(8)
pNode2 = TreeNode(6)
pNode3 = TreeNode(10)
pNode4 = TreeNode(5)
pNode5 = TreeNode(7)
pNode6 = TreeNode(9)
pNode7 = TreeNode(11)

pNode1.left = pNode2
pNode1.right = pNode3
pNode2.left = pNode4
pNode2.right = pNode5
pNode3.left = pNode6
pNode3.right = pNode7

S = Solution()
aList = S.zigzagLevelOrder(pNode1)
print(aList)

矩阵的路径

这题做了大半天也没有完全AC,就差一点了,最后return的逻辑有问题。

最后看的牛客网讨论区的答案。

考虑的点:
1. 不能回到之前经历过的元素,所以要设置一个判断,比如访问过的设为1。并且之后还要复原,比如重新设为0.
2. 递归没有true,能返回到上一个点,又开启新一条路径,我就是在这里始终卡住的。总结就是要设置给false的判断,不能一味地if什么时候为true,还要判断什么时候为false。

class Solution:
    def hasPath(self, matrix, rows, cols, path):       
        # write code here
        flag = [None]*len(matrix)
        def helper(matrix, rows, cols, i, j, path, k, flag):
            index = i * cols + j
            if i<0 or i>=rows or j<0 or j>=cols or matrix[index]!=path[k] or flag[index]==1:
                return False
            if k== len(path) -1:
                return True
            flag[index] = 1
            if (helper(matrix,rows,cols, i-1, j, path, k+1, flag)
                or helper(matrix, rows, cols, i + 1, j, path, k + 1, flag)
                or helper(matrix, rows, cols, i, j - 1, path, k + 1, flag)
                or helper(matrix, rows, cols, i, j + 1, path, k + 1, flag)):
                return True
            flag[index] = 0 
            return False

        for i in range(rows):
            for j in range(cols):
                if helper(matrix,rows,cols,i,j,path,0,flag):
                    return True
        return False

或直接写成:

class Solution:
    def hasPath(self, matrix, rows, cols, path):       
        # write code here
        flag = [None]*len(matrix)
        def helper(matrix, rows, cols, i, j, path, flag):
            index = i * cols + j
            if i<0 or i>=rows or j<0 or j>=cols or matrix[index]!=path[0] or flag[index]==1:
                return False
            if len(path)==1:
                return True
            flag[index] = 1
            if (helper(matrix,rows,cols, i-1, j, path[1:], flag)
                or helper(matrix, rows, cols, i + 1, j, path[1:], flag)
                or helper(matrix, rows, cols, i, j - 1, path[1:], flag)
                or helper(matrix, rows, cols, i, j + 1, path[1:], flag)):
                return True
            flag[index] = 0 
            return False

        for i in range(rows):
            for j in range(cols):
                if helper(matrix,rows,cols,i,j,path,flag):
                    return True
        return False

a = Solution().hasPath('abcdbdef',2,4,'abb')
print(a)       

机器人的运动范围

回溯法。有了上一题“矩阵的路径”的经验,在上一题的基础上更改代码就行了。return还是遇到了问题,这里flag=1之后不用再flag=0,依然是看牛客网讨论区的回答才AC.

class Solution:
    def movingCount(self, threshold, rows, cols):
        # write code here
        flag = [0]*rows*cols
        count = [0]*rows*cols
        #或 flag = [[0 for col in range(cols)] for row in range(rows)] 下面用flag[i][j]
        def helper(threshold, rows, cols, i, j, flag, count):
            index = i * cols + j
            if i < 0 or i >= rows or j < 0 or j >= cols or \
                sum(list(map(int,str(i))))+sum(list(map(int,str(j)))) > threshold or \
                flag[index] == 1:
                return 0
            flag[index] = 1
            count[index] = 1
            helper(threshold,rows,cols, i - 1, j, flag,count)
            helper(threshold, rows, cols, i + 1, j, flag,count)
            helper(threshold, rows, cols, i, j - 1, flag,count)
            helper(threshold, rows, cols, i, j + 1, flag,count)
            return count 
        summ = helper(threshold, rows, cols, 0, 0, flag,count)
        if summ == 0:
            return 0
        else:
            return sum(summ)

牛客网上:

class Solution:
    def movingCount(self, threshold, rows, cols):
        # write code here
        flag = [0]*rows*cols
        #flag = [[0 for col in range(cols)] for row in range(rows)]
        def helper(threshold, rows, cols, i, j, flag):
            index = i * cols + j
            if i < 0 or i >= rows or j < 0 or j >= cols or \
                sum(list(map(int,str(i))))+sum(list(map(int,str(j)))) > threshold or \
                flag[index] == 1:
                return 0
            flag[index] = 1
            return helper(threshold,rows,cols, i - 1, j, flag)+ \
                helper(threshold, rows, cols, i + 1, j, flag)+ \
                helper(threshold, rows, cols, i, j - 1, flag)+ \
                helper(threshold, rows, cols, i, j + 1, flag) +1
        return helper(threshold, rows, cols, 0, 0, flag)

猜你喜欢

转载自blog.csdn.net/eqiang8848/article/details/82313655