剑指offer1

参考:https://blog.csdn.net/fuxuemingzhu/article/details/79654813 

一、丑数

思路1:比较巧妙的方式是使用空间换时间。我们使用一个数组保存每个丑数,然后生成下一个丑数。

使用了3个指针,分别指向最后一个进行×2,×3,×5操作后会大于当前最大的丑数的位置。那么需要找到下一个丑数的时候,一定会是这三个指针指向的丑数进行对应操作的结果之一。因此,每次都更新三个指针指向下一次操作就会变成最大值的位置。就能一直生成下一丑数。

class Solution:
    def GetUglyNumber_Solution(self, index):
        if index<1:
            return 0
        res=[1]
        t2=t3=t5=0
        nextnum=1
        while nextnum<index:
            minshu=min(res[t2]*2,res[t3]*3,res[t5]*5)
            res.append(minshu)
            if res[t2]*2<=minshu:
                t2=t2+1

            if res[t3]*3<=minshu:
                t3=t3+1
            if res[t5]*5<=minshu:
                t5=t5+1
            nextnum=nextnum+1
        return res[index-1]

思路2:把每个数字逐个遍历判断是否是丑数的方式,这样的话效率不高

二、二进制中1的个数

思路1:判断整数二进制中最右边是不是1

class Solution(object):
    def hammingWeight(self, n):
 
        count=0
        
        while n!=0:
            if(n&1):
                count++
            n=n>>1
        return(count)

会出现死循环,因为当把负数0x80000000右移一位的时候。并不是简单的把最高位的1移到第二位变成0x40000000,而是0xc0000000。这是因为移位前是一个负数,仍然要保证移位后也是一个负数,因此移位后的最高位会设为1,如果一直做右移运算,最终会使得数字变成0xFFFFFFFF 而陷入死循环、

常规解法:为了避免死循环,我们可以不右移输入的数字n,首先把n和1做与运算,判断n的最低位是不是1,接着,把1左移一位得到2,再和n做与运算,就能判断n的次低位是不是1,反复左移,每次都可以判断n的其中一位是不是1,但是这种解法,循环的次数等于整数的二进制的位数,32位的整数需要循环32次

class Solution:
    def NumberOf1(self, n):
        if n==0:
            return 0
        count=0
        flag=1
        i=32
        while(i>0):
            i=i-1
            if (n&flag):
                count=count+1
            flag=flag<<1
        return count

思路2:给面试官带来惊喜

参考:https://blog.csdn.net/u010005281/article/details/79851154

总结:把一个整数减去1,再和原整数做与运算,会把该整数最右边的1变成0

class Solution:
    def NumberOf1(self, n):
        count = 0
        while n&0xffffffff != 0:
            count += 1
            n = n & (n-1)
        return count

三、矩阵中的路径

遇到矩阵搜索的问题就是递归和回溯,两者差不多

# -*- coding:utf-8 -*-
class Solution:
    def hasPath(self, matrix, rows, cols, path):
        assistMatrix = [True]*rows*cols
        for i in range(rows):
            for j in range(cols):
                if(self.hasPathAtAStartPoint(matrix,rows,cols, i, j, path, assistMatrix)):
                    return True
        return False

    def hasPathAtAStartPoint(self, matrix, rows, cols, i, j, path, assistMatrix):
        
        if not path:    #说明已经找到路径,可以返回True
            return True
        index = i*cols+j
        if i<0 or i>=rows or j<0 or j>=cols or matrix[index]!=path[0] or assistMatrix[index]==False:
            #下标不符合,index对应的值不为和字符数组中的不一致,或者该index已经被访问,这些情况只要有符合的就返回false
            #只有上面的所有情况都不符合,也就是值相等,且没有访问过,下标不符合
            return False
        assistMatrix[index] = False
        #进行上下左右的递归
        if(self.hasPathAtAStartPoint(matrix,rows,cols,i+1,j,path[1:],assistMatrix) or
               self.hasPathAtAStartPoint(matrix,rows,cols,i-1,j,path[1:],assistMatrix) or
               self.hasPathAtAStartPoint(matrix,rows,cols,i,j-1,path[1:],assistMatrix) or
               self.hasPathAtAStartPoint(matrix,rows,cols,i,j+1,path[1:],assistMatrix)):
            return True
        
        assistMatrix[index] = True#表示递归不到这一步,则回溯
        return False#返回失败

四、机器人的运动范围

思路:

回溯算法:当准备进入坐标(i,j)时,通过检查坐标的数位来判断机器人能否进入。如果能进入的话,接着判断四个相邻的格子。

# -*- coding:utf-8 -*-
class Solution:
    def movingCount(self, threshold, rows, cols):
        # write code here
        matrix = [[True for i in range(cols)] for j in range(rows)]
        result = self.findgrid(threshold, rows, cols, matrix, 0, 0)
        return result
    
    def judge(self, threshold, i, j):
        if sum(map(int,str(i)+str(j))) <= threshold:
            return True
        else:
            return False
        
    def findgrid(self, threshold, rows, cols, matrix, i, j):
        count = 0
        if i < rows and i>=0 and j<cols and j>=0 and self.judge(threshold, i, j) and matrix[i][j]:
            matrix[i][j] = False
            count = 1+ self.findgrid(threshold, rows, cols, matrix, i-1, j) \
                    + self.findgrid(threshold, rows, cols, matrix, i+1, j) \
                    + self.findgrid(threshold, rows, cols, matrix, i, j-1) \
                    + self.findgrid(threshold, rows, cols, matrix, i, j+1)
        return count

自己写的:

在代码中加入assistMatrix[index]==False的判断是因为不能让判别重复的位置计算两次

class Solution:
    def movingCount(self, threshold, rows, cols):
        if rows<0 or cols<0:
            return 0
        assistMatrix=[True]*rows*cols
        i=0
        j=0
        
        num=self.digui(threshold,rows,cols,i,j,assistMatrix)
        return num
    
    def panbie(self,m,n,threshold):
        
        return sum(map(int,str(m)+str(n)))>threshold
        
        

    def digui(self,threshold,rows,cols,m,n,assistMatrix):
        count=0
        
        index = m*cols+n
        if m<0 or m>=rows or n<0 or n>=cols or self.panbie(m,n,threshold) or assistMatrix[index]==False:
            return count
        
        assistMatrix[index] = False
        
        count= 1+ self.digui(threshold,rows, cols,m+1,n,assistMatrix)\
            + self.digui(threshold,rows, cols,m-1,n,assistMatrix)\
            + self.digui(threshold,rows, cols,m,n+1,assistMatrix)\
            + self.digui(threshold,rows, cols,m,n-1,assistMatrix)
        return count

五、剪绳子(动态规划、贪婪算法)

解题思路:

动态规划法:

动态规划求解问题的四个特征: 
①求一个问题的最优解; 
②整体的问题的最优解是依赖于各个子问题的最优解; 
③小问题之间还有相互重叠的更小的子问题; 
④从上往下分析问题,从下往上求解问题;

def rope_cut(length):
    # 最优解数组,当长度为0是为0,当长度为1是为1,当长度为2时为2,当长度大于3时,3就不能切开了,因为3>1*2,最优解数组为3
    li=[0,1,2,3]
    if length==0:#当长度为0时,返回0
        return 0
    if length==1:#当长度为1时,返回1
        return 1
    if length==2:#当长度为2时,返回2
        return 2
    if length==3:#当长度为3时,返回2,虽然最优解数组里为2,但是每次必须得切一刀,这样1*2=2,所以长度为3时还是2
        return 2
    for j in range(4,length+1):
        max = 0
        for i in range(1,j):
            # 思路:每次求解值时将其他小于需要求解的长度是都列出来放在一个数组里
            #如:求长度为5,最优解数组里必须得有长度为1,2,3,4的最优解值
            #注:此处使用列表保存最优解数组是为了性能优化,虽然递归求解也能解出,但会造成大量重复执行
            temp=li[i]*li[j-i]
            if temp>max:
                max=temp
        li.append(max)#每次将上次所得最优解追加在列表里
    return li[-1]
print(rope_cut(8))

贪婪算法:

def cutRope(length):
    if length==0:#当长度为0时,返回0
        return 0
    if length==1:#当长度为1时,返回1
        return 1
    if length==2:#当长度为2时,返回2
        return 2
    if length==3:#当长度为3时,返回2,虽然最优解数组里为2,但是每次必须得切一刀,这样1*2=2,所以长度为3时还是2
        return 2
    if length==4:
        return 4
    timeOfThree = length // 3
    if length - timeOfThree * 3 == 1:  
    # 如果减去3,还剩下4,这时候就不能在减去3了,2*2 > 3 * 1
        timeOfThree -= 1
    timeOfTwo = (length - timeOfThree * 3) // 2
    return pow(3,timeOfThree) * pow(2,timeOfTwo)
print(cutRope(8))

六、 删除链表中的重复节点

方法一
考虑借助一个辅助list,每次放入一个node,记录当前结点的值currValue。当下一个结点值与currValue相等,则将结点pop()出栈,移动指针直到找到一个值不同的结点。当下一个结点与currValue值不等,则将该结点append进去,然后更新currValue值,并后移。

class Solution:
    def deleteDuplication(self, pHead):
        # write code here
        if not pHead:
            return pHead
        node_stack = []
        curVal = pHead.val
        node_stack.append(pHead)
        node = pHead.next
        while node:
            if node.val == curVal:
                n = node_stack.pop()
                while node.val == curVal:
                    node = node.next
                    if not node:
                        break
            else:
                node_stack.append(node)
                curVal = node.val
                node = node.next
        phead = ListNode(-1)
        head = phead
        while node_stack:
            node = node_stack.pop(0)
            head.next = node
            head = head.next
        head.next = None
        return phead.next

方法二、(三指针法)书中介绍的方法,新建一个head作为头指针,设置两个指针p1p2一前一后,比较两个结点的值,分为两种情况:

  1. 如果相等,则移动p2找到第一个不相等的结点,令p1指向该结点,p2依旧指向p1.next
  2. 如果不相等,则直接将p1结点连接到新的链表后,将p1p2分别后移。
class Solution:
    def deleteDuplication(self, pHead):
        # write code here
        if not pHead:
            return pHead
        p0 = ListNode(-1)
        head = p0
        p1 = pHead
        p2 = pHead.next
        while p2:
            # 如果p1与p2值相等,则向后移动p2直到找到不相等的值
            if p1.val == p2.val:
                # 找到该重复结点后的第一个结点
                while p2 and p1.val == p2.val:
                    p2 = p2.next
                p1 = p2
                if p1:
                    p2 = p1.next
                else:
                    break
            # 如果p1与p2值不相等
            else:
                p0.next = p1
                p0 = p0.next
                p1 = p1.next
                p2 = p2.next
        p0.next = p1
        return head.next

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值