剑指Offer

Search a 2D matrix

把它看成是sorted list

matrixmn[i][j]=list[in+j]list[i]=matrix[i//n][i m a t r i x m ∗ n [ i ] [ j ] = l i s t [ i ∗ n + j ] l i s t [ i ] = m a t r i x [ i / / n ] [ i

def find(matrix,target):
    if not matrix:
        return False
    if not matrix[0]:
        return False
    m,n=len(matrix),len(matrix[0])
    start=0; end=m*n-1
    while start<=end:
        middle=(start+end)/2
        if matrix[middle//n][middle%n]<target:
            start=middle+1
        elif matrix[middle//n][middle%n]>target:
            end=middle-1
        else:
            return True
    return False

第二种方法:先二分搜索行index(最后一个小于等于target的number),再二分搜索列index(等于target的number)
九章算法解答

def searchMatrix(matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """

        if not matrix:
            return False
        if not matrix[0]:
            return False

        m,n=len(matrix),len(matrix[0])

        # find the row index, the last number<=target
        start=0;end=m-1
        while start+1<end:
            mid=(start+end)//2
            if matrix[mid][0]==target:
                return True
            elif matrix[mid][0]<target:
                start=mid
            else:
                end=mid

        if matrix[end][0]<=target:
            row=end
        elif matrix[start][0]<=target:
            row=start
        else:
            return False

        #find the col index, the number==target:
        start=0;end=n-1
        while start<=end:
            mid=(start+end)//2
            if matrix[row][mid]==target:
                return True
            elif matrix[row][mid]<target:
                start=mid+1
            else:
                end=mid-1
        return False

两种方法的时间复杂度都是O(log(mn))

Search a 2D matrix 2

第一种方法,每行二分搜索,时间复杂度为O(m*log(n))

def searchMatrix( matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if not matrix:
            return False
        if not matrix[0] or matrix[-1][-1]<target:
            return False

        m,n=len(matrix),len(matrix[0])
        for row in matrix:
            if row[-1]<target:
                continue
            start=0; end=n-1
            while start<=end:
                mid=(start+end)//2
                if row[mid]<target:
                    start=mid+1
                elif row[mid]>target:
                    end=mid-1
                else:
                    return True

        return False

第二种方法,从top right 到left bottom,每次排除一行或者一列,时间复杂度为O(m+n)

def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if not matrix:
            return False
        if not matrix[0]:
            return False
        m,n=len(matrix),len(matrix[0])
        rowindex=0;colindex=n-1
        while rowindex<=m-1 and colindex>=0:
            if matrix[rowindex][colindex]<target: #排除该行
                rowindex+=1
            elif matrix[rowindex][colindex]>target: #排除该列
                colindex-=1
            else:
                return True

        return False

替换空格

直接用python正则表达

\s: Matches any whitespace character; this is equivalent to the class [ \t\n\r\f\v].

def replaceSpace(s):
        # write code here
        import re
        pt=re.compile('\s')
        return pt.sub('%20',s)

def replaceSpace2(self, s):
        # write code here
        newS=""
        for letter in s:
            if letter==" ":
                newS+="%20"
            else:
                newS+=letter
        return newS

从尾到头打印链表

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        #时间复杂度O(n^2)
        # write code here
        arr=[]
        while listNode:
            arr.insert(0,listNode.val)
            listNode=listNode.next
        return arr

    def printListFromTailToHead(self, listNode):
        # 时间复杂度为O(n)
        res=[]
        def helper(res,listNode):
            if not listNode:
                return
            helper(res,listNode.next)
            res.append(listNode.val)

        helper(res,listNode)
        return res

数组中的逆序对

参考解答
利用归并的思想

class Solution:
    """
    @param A: an array
    @return: total of reverse pairs
    """
    def reversePairs(self, A):
        if not A:
            return 0
        start=0
        end=len(A)-1
        temp=[0]*len(A)
        return self.countPairs(A,start,end,temp)

    def countPairs(self,A,start,end,temp):
        count=0
        if start>=end:
            return 0
        mid=(start+end)//2
        count+=self.countPairs(A,start,mid,temp) #统计左边子数组的逆序对
        count+=self.countPairs(A,mid+1,end,temp) #统计右边子数组的逆序对
        count+=self.countPairsBetweenArrs(A,start,end,temp) #统计左右数组之间的逆序对
        return count

    def countPairsBetweenArrs(self,A,start,end,temp):
        if start>=end:
            return 0
        mid=(start+end)//2
        leftindex=start
        rightindex=mid+1
        tempindex=start
        count=0
        while leftindex<=mid and rightindex<=end:
            if A[leftindex]>A[rightindex]:
                # for i in range(leftindex,mid+1):
                #     print (A[i],A[rightindex])
                count=count+(mid-leftindex+1)
                temp[tempindex]=A[rightindex]
                rightindex+=1
                tempindex+=1
            else: # A[leftindex]<=A[rightindex]
                temp[tempindex]=A[leftindex]
                leftindex+=1
                tempindex+=1

        while leftindex<=mid:
            temp[tempindex]=A[leftindex]
            tempindex+=1
            leftindex+=1

        while rightindex<=end:
            temp[tempindex]=A[rightindex]
            rightindex+=1
            tempindex+=1

        A[start:(end+1)]=temp[start:(end+1)]
        return count

important reverse pairs

丑数

def uglynumber(num):
    for p in [2,3,5]:
        while num%p==0 and num>0:
            num=num//p
    return num==1

丑数2

def nthUglyNumber(n):
        """
        :type n: int
        :rtype: int
        """
        if n==0:
            return None

        ugly=[1]
        i2,i3,i5=0,0,0
        for i in range(n-1):
            num=min(ugly[i2]*2,ugly[i3]*3,ugly[i5]*5)
            ugly.append(num)
            if num==ugly[i2]*2:
                i2+=1
            if num==ugly[i3]*3:
                i3+=1
            if num==ugly[i5]*5:
                i5+=1
        return ugly[-1]

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

分别求出二者的长度,求出二者的差dis,而后将指向长的链表的指针先在长链表上走dis步,而两个链表同时走,二者会同时走到第一个公共节点处。

def FindFirstCommonNode(pHead1,pHead2):
        p1=pHead1
        p2=pHead2
        len1=0; len2=0
        while p1:
            len1+=1
            p1=p1.next
        while p2:
            len2+=1
            p2=p2.next

        for i in range(abs(len1-len2)):
            if len1>len2:
                pHead1=pHead1.next
            else:
                pHead2=pHead2.next

        while pHead1:
            if pHead1==pHead2:
                return pHead1
            pHead1=pHead1.next
            pHead2=pHead2.next
        return None

最小的k个数

纸上谈兵:堆
python: heapq

def GetLeastNumbers_Solution(tinput, k):
        # 利用最小堆,时间复杂度为O(n+klog(n))
        if k>len(tinput):
            return [] 
        from heapq import heappush,heappop
        q=[]
        for num in tinput:
            heappush(q,num)
        res=[]
        for i in range(k):
            res.append(heappop(q))
        return res


def GetLeastNumbers_Solution(tinput, k):
        # 维护k个最大堆,时间复杂度为O(k+(n-k)logk)
        if k>len(tinput) or k==0:
            return [] #
        import heapq
        output=heapq.nlargest(k,tinput[:k])
        for e in tinput[k:]:
            if e<output[0]:
                output[0]=e
            output=heapq.nlargest(k,output)
        return output[::-1]

连续子数组的最大和

def FindGreatestSumOfSubArray(array):
        # 动态规划:maxSum[i]:以array[i]结尾的最长连续子序列的和
        # maxSum[i]=max(maxSum[i-1]+array[i],array[i]) 要么重新开始一个子序列,要么接上前一个位置
        if not array:
            return 0
        maxSum=[-float('inf')]*len(array)
        maxSum[0]=array[0]
        maxV=-float('inf')
        for i in range(1,len(array)):
            maxSum[i]=max(maxSum[i-1]+array[i],array[i])
            maxV=max(maxV,maxSum[i])
        return maxV

整数中1出现的次数

解题思路

def NumberOf1Between1AndN_Solution(n):
        # write code here
        m=1
        count=0
        while m<=n:
            curNum=(n//m)%10
            if curNum>1:
                count+=(n//m//10+1)*m
            elif curNum==1:
                count+=(n//m//10)*m+(n%m+1)
            else:
                count+=(n//m//10)*m
            m*=10

        return count

把数组排成最小的数

def PrintMinNumber(self, numbers):
        #Bubble Sort
        strLst=[str(n) for n in numbers]
        for i in range(len(strLst)):
            for j in range(len(strLst)-i-1):
                if self.strCmp(strLst[j],strLst[j+1]):
                    strLst[j],strLst[j+1]=strLst[j+1],strLst[j]
        return "".join(strLst)

    def strCmp(self,s1,s2):
        return s1+s2>s2+s1

数字在排序数组中出现的次数

def GetNumberOfK(data, k):
        # 利用二分查找找到第一个等于k的数
        if not data:
            return 0
        start=0
        end=len(data)-1
        #find first number equal k
        while start+1<end:
            mid=(start+end)//2
            if data[mid]<k:
                start=mid
            else: #data[mid]>=target
                end=mid

        if data[start]==k:
            firstindex=start
        elif data[end]==k:
            firstindex=end
        else:
            return 0
        count=0
        #判断的先后顺序很重要,while data[firstindex]==k and firstindex<len(data)会报错
        while firstindex<len(data) and data[firstindex]==k:
            count+=1
            firstindex+=1
        return count

二叉树的深度

def TreeDepth(self,node):
        if not node:
            return 0
        leftdepth=self.TreeDepth(node.left)
        rightdepth=self.TreeDepth(node.right)
        depth=max(leftdepth,rightdepth)+1
        return depth

判断平衡二叉树

class Solution:
    def __init__(self):
        self.bal=True
    def isBalanced(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """

        def helper(node):
            if not node:
                return 0
            leftdepth=helper(node.left)
            rightdepth=helper(node.right)
            if abs(leftdepth-rightdepth)>1:
                self.bal=False      
            depth=max(leftdepth,rightdepth)+1
            return depth

        helper(root)
        return self.bal

数组中只出现一次的数字

任何一个数字异或他自己都等于0,0异或任何一个数都等于那个数。数组中出了两个数字之外,其他数字都出现两次,那么我们从头到尾依次异或数组中的每个数,那么出现两次的数字都在整个过程中被抵消掉,那两个不同的数字异或的值不为0,也就是说这两个数的异或值中至少某一位为1。我们找到结果数字中最右边为1的那一位i,然后一次遍历数组中的数字,如果数字的第i位为1,则数字分到第一组,数字的第i位不为1,则数字分到第二组。这样任何两个相同的数字就分到了一组,而两个不同的数字在第i位必然一个为1一个不为1而分到不同的组,然后再对两个组依次进行异或操作,最后每一组得到的结果对应的就是两个只出现一次的数字。

class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, lst):
        if not lst:
            return []
        xor=lst[0]
        for num in lst[1:]:
            xor^=num

        res=self.findFirstBits1(xor)
        num1=0;num2=0
        for num in lst:
            if self.isBits1(num,res):
                num1^=num
            else:
                num2^=num
        return [num1,num2]

    def findFirstBits1(self,n):
        #对于一个数字X,X&(-X)之后得到的数字,是把X中最右边的1保留下来,其他位全部为0。注意,这里的-X是X的相反数,-X=~X+1,这里的~X意思是对X所有位取反,
        return n&(-n)

    def isBits1(self,n1,n2):
        if n1&n2==0:
            return False
        else:
            return True

########方法二            
def FindNumsAppearOnce(lst):
        # 利用字典存储频率
        freq={}
        for num in lst:
            freq[num]=freq.get(num,0)+1
        res=[x for x in lst if freq[x]==1]
        res.sort()
        return res
def FindNums

和为S的连续正数序列

设定两个指针,先分别指向数字1和数字2,并设这两个指针为small和big,对small和big求和,如果和大于目标值,则从当前和中删除small值,并把small值加一,如果和小于目标值,则把big值加一,再把新的big值加入和中。如果和等于目标值,就输出small到big的序列,同时把big加一并加入和中,继续之前的操作。

def TreeDepth(self,node):
        if not node:
            return 0
        leftdepth=self.TreeDepth(node.left)
        rightdepth=self.TreeDepth(node.right)
        depth=max(leftdepth,rightdepth)+1
        return depth

和为S的连续正数序列

class Solution:
    def FindContinuousSequence(self, tsum):
        # write code here
        small=1
        big=2
        middle=(tsum+1)//2 # small必须小于middle, middle*2=tsum+1
        res=[]
        cursum=small+big
        while small<middle:
            if cursum==tsum:
                res.append(range(small,big+1))
                big+=1 #big往后走一个
                cursum+=big
            elif cursum>tsum:
                cursum-=small #small往后走一个
                small+=1
            else: #cursum<tsum
                big+=1  # big往后走一个
                cursum+=big
        return res

和为s的两个数字

class Solution:
    def FindNumbersWithSum(self, array, tsum):
        # write code here
        left=0
        right=len(array)-1
        res=[]
        while left+1<=right:
            t=array[left]+array[right]
            if t==tsum:
                res=[array[left],array[right]]
                break
            if t<tsum:
                left+=1
            else: # t>tsum
                right-=1
        return res

左旋转字符串

class Solution:
    def LeftRotateString(self,s, n):
        # 通过reverse
        # reverse(0,n-1);
        # reverse(n,lens-1);
        # reverse(0,lens-1);
        s=list(s)
        s[:n]=self.reverse(s[:n])
        s[n:]=self.reverse(s[n:])
        s=self.reverse(s)
        return "".join(s)

    def reverse(self,s):
        left = 0
        right = len(s) - 1
        while left + 1 <= right:
            s[left], s[right] = s[right], s[left]
            left+=1
            right-=1
        return s

class Solution:
    def LeftRotateString(self,s,n):
        #重新拼接
        return s[n:]+s[:n]

    def LeftRotateString2(self, s, n):
        # 找出下标之间的关系
        lens=len(s)
        newS=[""]*lens
        for i in range(lens):
            newS[(i+1-n)%lens-1]=s[i]
        return "".join(newS)

翻转单词顺序列

class Solution:
    def ReverseSentence(self,s):
        #先翻转整个句子,再逐个翻转每个单词
        s=list(s)
        s=self.reverse(s)
        left=0
        right=0
        while right<=len(s):
            if right==len(s) or s[right]==" ":
                s[left:right]=self.reverse(s[left:right])
                left=right+1

            right+=1
        return "".join(s)

    def reverse(self,s):
        left=0
        right=len(s)-1
        while left+1<=right:
            s[left],s[right]=s[right],s[left]
            left+=1
            right-=1
        return s

    def ReverseSentence2(self,s):
        #直接用python的语句
        l=s.split(" ")
        return " ".join(l[::-1])

    def ReverseSentence3(self, s):
        s=s.split(" ")
        left=0
        right=len(s)-1
        while left+1<=right:
            s[left],s[right]=s[right],s[left]
            left+=1
            right-=1
        return " ".join(s)

扑克牌顺子

最直观的方法是把数组排序。值得注意的是,由于0可以当成任意数字,我们可以用0去补满数组中的空缺。如果排序之后的数组不是连续的,即相邻的两个数字相隔若干个数字,但只要我们有足够的0可以补满这两个数字的空缺,这个数组实际上还是连续的。举个例子,数组排序之后为{0,1,3,4,5},在1和3之间空缺了一个2,刚好我们有一个0,也就是我们可以把它当成2去填补这个空缺。

于是我们需要做3件事:

  1. 首先把数组排序
  2. 再统计数组中的0的个数
  3. 最后统计排序之后的数组中相邻数字之间的空缺总数。

如果空缺的总数小于或者等于0的个数,那么这个数组就是连续的;反之则不连续。

最后,我们还需要注意一点:

如果数组中的非0数字重复出现,则该数组不是连续的。

class Solution:
    def IsContinuous(self, numbers):
        if not numbers:
            return False
        numbers.sort() #排序
        count=self.countingZero(numbers) #统计0的个数

        gap=0 #统计不连续的间隔数
        for i in range(count,len(numbers)-1):
            if numbers[i]==numbers[i+1]:
                return False
            gap+=numbers[i+1]-numbers[i]-1
        return gap<=count

    def countingZero(self,numbers):
        count=0
        for num in numbers:
            if num>0:
                break
            count+=1
        return count

圆圈中最后剩下的数

解题思路
f[i] f [ i ] 记为让 i i 个人报m后最后剩下的数, f[1]=0 f [ 1 ] = 0

f[i]=(f[i1]+m) f [ i ] = ( f [ i − 1 ] + m )

class Solution:
    def LastRemaining_Solution(self, n, m):
        # write code here
        if n<1 or m<1:
            return -1
        f=[0]*(n+1)
        f[1]=0
        for i in range(2,n+1):
            f[i]=(f[i-1]+m)%i
        return f[-1]

二叉搜索树的最低公共祖先

对于二叉搜索树而言,每个节点的左子节点都小于这个数,右子节点都大于这个数,因此,我们比较当前节点和需要比较的结点m,n的大小,如果当前节点的值均大于m,n,则在当前节点的左子树继续操作,如果当前节点均小于m,n,则在当前节点的右子树继续操作,反之,则当前结点是最小公共祖先。

'''
查找二叉树搜索树两个结点的最低公共祖先
'''
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution:
    def findCommonParent(self,node1, node2, root):
        if root is None or node1 is None or node2 is None:
            return None

        if node1 == node2:
            return node1

        val1 = node1.val
        val2 = node2.val
        while root:
            val = root.val
            if (val1 - val) * (val2 - val) <= 0:  # 在左右子树两边
                return root
            elif val1 < val and val2 < val:
                root = root.left
            else:
                root = root.right
        return None

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()
print(S.findCommonParent(pNode3, pNode7, pNode1))

二叉树中两个结点的最低公共祖先

对于普通的二叉树而言,我们如果希望找到两个结点的最低公共祖先,那么我们可以先从树的根节点开始,找到根节点到结点m和结点n的路径,这时候我们就有两个List或者两个链表,然后就像前面题中遇到的寻找两个链表的公共结点一样,从后往前遍历两个List找到最靠后的第一个公共结点即可。

class Solution:
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        path1=self.findpath(root,p)
        path2=self.findpath(root,q)
        len1,len2=len(path1),len(path2)
        for i in reversed(range(min(len1,len2))):
            if path1[i]==path2[i]:
                return path1[i]

        return None

    def findpath(self,root,node):
        if root==node:
            return [root]
        if root is None:
            return []

        left=self.findpath(root.left,node)
        right=self.findpath(root.right,node)
        if len(left):
            return [root]+left #在左子树中找到
        elif len(right):
            return [root]+right #在右子树中找到
        else:
            return [] #都没有找到

正则表达式

class Solution:
    # s, pattern都是字符串
    def match(self, s, pattern):
        # write code here
        f = [[False] * (len(pattern) + 1) for _ in range(len(s) + 1)]
        f[0][0] = True
        for i in range(len(s) + 1):
            for j in range(len(pattern) + 1):
                if j == 0 and i >= 1:
                    f[i][j] = False
                    continue
                if j>=1 and pattern[j - 1] != "*":
                    if i >= 1 and j >= 1 and (s[i - 1] == pattern[j - 1] or pattern[j - 1] == "."):
                        f[i][j] = f[i - 1][j - 1]
                else:  # pattern[j-1]=="*"
                    if j >= 2:
                        f[i][j] = f[i][j - 2]  # 匹配0次
                        if i >= 1 and (s[i - 1] == pattern[j - 2] or pattern[j - 2] == "."):
                            f[i][j] |= f[i - 1][j]

        return f[-1][-1]

删除链表中的重复结点

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

def deleteDuplication(pHead):
    dummy=ListNode(0)
    dummy.next=pHead
    p=dummy
    while p.next and p.next.next:
        if p.next.val==p.next.next.val:
            val=p.next.val
            while p.next and p.next.val==val:
                p.next=p.next.next
        else:
            p=p.next
    return p.next

链表中环的入口结点

这里写图片描述
这里写图片描述

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        fast=pHead
        slow=pHead
        meet=None
        while fast and slow:
            slow=slow.next
            fast=fast.next
            if fast:
                fast=fast.next
            if fast==slow:
                meet=fast
                break
        if not meet:
            return None
        while meet!=pHead:
            meet=meet.next
            pHead=pHead.next
        return meet           

二叉树打印成多行

# -*- 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 []
        queue=[]
        queue.append(pRoot)
        res=[]
        while queue:
            size=len(queue)
            level=[]
            for i in range(size):    
                node=queue.pop(0)
                level.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            res.append(level)
        return res

对称二叉树

class Solution:

    def isSymmetrical(self, pRoot):
        # write code here

        def helper(leftT,rightT):
            if not leftT and not rightT:
                return True
            if leftT and rightT and leftT.val==rightT.val:
                return helper(leftT.left,rightT.right) and helper(leftT.right,rightT.left) 

        return helper(pRoot,pRoot)

二叉树的镜像

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        # write code here
        if not root:
            return
        left=self.Mirror(root.left)
        right=self.Mirror(root.right)

        root.right=left
        root.left=right
        return root

参考资料

剑指offer专栏
剑指offer:python
剑指offer:GitHub
剑指offer:python实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值