剑指offer(python)--数值

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

思路:
剑指offer(python)--数值_第1张图片

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index == 0:
            return 0
        # 1作为特殊数直接保存
        baselist = [1]
        min2 = min3 = min5 = 0
        curnum = 1
        while curnum < index:
            minnum = min(baselist[min2] * 2, baselist[min3] * 3, baselist[min5] * 5)
            baselist.append(minnum)
            # 找到第一个乘以2的结果大于当前最大丑数M的数字,也就是T2
            while baselist[min2] * 2 <= minnum:
                min2 += 1
            # 找到第一个乘以3的结果大于当前最大丑数M的数字,也就是T3
            while baselist[min3] * 3 <= minnum:
                min3 += 1
            # 找到第一个乘以5的结果大于当前最大丑数M的数字,也就是T5
            while baselist[min5] * 5 <= minnum:
                min5 += 1
            curnum += 1
        return baselist[-1]
        
原文:https://blog.csdn.net/qq_20141867/article/details/81060581 

数值的次方

题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

思路:

这道题看似简单,其实BUG重重。要注意的问题:

1 关于次幂的问题特殊的情况,比如次幂为负数,或者基数为0时等等复杂的情况
2. 机器中浮点数的比较是由误差的,因此double类型的比较,不能用简单的a==0来比较。一般的比较方式是,相减的差在一个很小的区间内,我们就认为是相等的。

思路:首先考虑base在-0.00001到0.00001之间的数,其接近于0,所以其指数次输出为0,对于指数为0的任意非0数结果为1,对于指数幂为1的任意数结果为base本身,对于其他的base值,当指数为正数时,直接相乘即可,对于指数为负数时,先将其转换为正的指数,相乘的结果再取倒数即可。
即分情况讨论即可

# -*- coding:utf-8 -*-
class Solution:
    def Power(self, base, exponent):
        if base==0:
            return 0
        elif exponent==0:
            return 1
        elif exponent==1:
            return base
        else:
            return pow(base,exponent)

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

问题:1. 输入整数,可能是正,可能是负,或者是0
2.整数转变为二进制如何转变?如何计算1的个数
python中 可以使用bin(n).count(‘1’)这个做法,但是需要单独考虑因为n是负数时该式子不成立
3.补码是什么? 负数如何用补码

复习:

一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1
原码:
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如8位二进制1,-1的原码如下
8----- [+1]原 = 00000001
[-1]原 = 1000 0001
第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是: [1111 1111, 0111 1111] 即 [-127 , 127]
反码:正数的反码是其本身,负数的反码是在其原码的基础上,符号位不变,其余各个位取反. [+1] = [00000001] 原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
补码:正数的补码就是其本身,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1.
(即在反码的基础上+1)

如何获得二进制数?
”方法1: 用2辗转相除直到结果为1,将余数和最后的1从下向上的组合,就是我们想要的结果

python中 可以使用bin(n).count(‘1’)这个做法,但是需要单独考虑因为n是负数时该式子不成立

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1(self, n):
        cnt = 0
        if n < 0:
            n = n & 0xffffffff
        return bin(n).count('1')

带self 参数就需要建立class类

输入一个负整数看看,程序会陷入死循环,因为在其他语言中,负数在计算机中是用补码表示的,最高位是1,在右移的过程中,高位都是用1来填补的,所以while num这个条件一直为真;在python中,根据右移的定义就可以自行推断出来。既然现在右移num不行,那我们可以左移1,在32的整数中,最多左移32位,1就会变为零,所以这可以作为判断条件,在python中,我们一起可以左移下去(到虚拟内存大小的位数),
可以使用一个mask来表示正在看到的位数,循环32次,那么就得到了每一个位置上是1的个数

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1(self, n):
        mask = 1
        cnt = 0
        for i in range(32):
            if n & mask:
                cnt += 1
            mask <<= 1
        return cnt 

https://www.jianshu.com/p/e29d64d8f1c3

`剑指offer(python)--数值_第2张图片
https://www.jianshu.com/p/e29d64d8f1c3

方法2:是用位运算,将1每次左移,和数字进行&运算,如果成功,则返回1

技巧:python中的左移和右移与其他C/C++等的定义和结果都是不一样的,大家可以自行做实验,python中的定义:右移n位定义为除以pow(2,n),左移n位定义为乘以pow(2,n),而且没有溢出(移除的int会升级为long类型)

`n-1会让n最右边的1变为0,其左边所有的数字都不会改变,所以每 n = (n-1)&n一次,n就少一个1

class Solution:
    def NumberOf1(self, n):
        total =0
        if n==0:
            return 0
        if n<0:
            n=n & 0xffffffff  # 这一步是关于负数运算的
        while n:
            n=n&(n-1)  #按位与 
            total+=1
        return total
        

class Solution:
    def numberOf1(self, n):
        total = 0
        if n == 0:
            return 0
            if n < 0:
                n = n & 0xffffffff
                while n:
                    n = n & (n - 1)
                    total += 1
                    return total


if __name__ == '__main__':
    demo=Solution()

    print(demo.numberOf1(9))

为什么这个输出是none

拓展:
位运算n&(n-1)的使用

按位与的知识
n&(n-1)作用:将n的二进制表示中的最低位为1的改为0,先看一个简单的例子: n = 10100(二进制),则(n-1) = 10011
==》n&(n-1) = 10000 可以看到原本最低位为1的那位变为0。
1、 判断一个数是否是2的方幂
n > 0 && ((n & (n - 1)) == 0 )

解释((n & (n-1)) == 0):

如果A&B==0,表示A与B的二进制形式没有在同一个位置都为1的时候。

那么本题到底啥意思??

不妨先看下n-1是什么意思。

令:n=1101011000(二进制,十进制也一样),则

n-1=1101010111。

n&(n-1)=1101010000

由此可以得出,n和n-1的低位不一样,直到有个转折点,就是借位的那个点,从这个点开始的高位,n和n-1都一样,如果高位一样这就造成一个问题,就是n和n-1在相同的位上可能会有同一个1,从而使((n
& (n-1)) != 0),如果想要

((n & (n-1)) == 0),则高位必须全为0,这样就没有相同的1。

所以n是2的幂或0

  1. 求某一个数的二进制表示中1的个数 while (n >0 ) {
    count ++;
    n &= (n-1); }

  2. 计算N!的质因数2的个数。 容易得出N!质因数2的个数 = [N / 2] + [N / 4] + [N / 8] + …. 下面通过一个简单的例子来推导一下过程:N = 10101(二进制表示) 现在我们跟踪最高位的1,不考虑其他位假定为0, 则在 [N / 2]
    01000 [N / 4] 00100 [N / 8] 00010 [N / 8] 00001 则所有相加等于01111
    = 10000 - 1 由此推及其他位可得:(10101)!的质因数2的个数为10000 - 1 + 00100 - 1 + 00001 - 1 = 10101 - 3(二进制表示中1的个数)

推及一般N!的质因数2的个数为N-(N二进制表示中1的个数)

顺时针打印矩阵

题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
则依次打印出数字
1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

解析:由于题目是以从外圈到内圈的顺序依次打印,在矩阵中标注一圈作为分析的目标。设矩阵的宽度为cols,而其高度为rows。选取左上角坐标为(startX, startY),右下角坐标为(endX, endY)的一个圈来分析。
剑指offer(python)--数值_第3张图片
由于endX和endY可以根据startX、startY以及columns、rows来求得,因此此时我们只需要引入startX和startY两个变量。我们可以想象有一个循环,在每一次循环里我们从(startX, startY)出发按照顺时针打印数字。
接着分析这个循环结束的条件。对一个5×5的矩阵而言,最后一圈只有一个数字,对应的坐标为(2, 2)。我们发现5 > 2 * 2。对一个6×6的矩阵而言,最后一圈有四个数字,对应的坐标仍然为(2, 2)。我们发现6 > 2 * 2依然成立。于是我们可以得出,让循环继续的条件是“cols > startX * 2 && rows > startY * 2”。

  接下来我们分析如何按照顺时针的顺序打印一圈的数字。我们可以分四步来打印:第一步是从左到右打印一行,第二步是从上到下打印一列,第三步从右到左打印一行,最后一步是从下到上打印一列。也就是我们把打印一圈数字这个问题,分解成四个子问题。

剑指offer(python)--数值_第4张图片
值得注意的是,最后一圈可能退化成只有一行、只有一列、甚至只有一个数字,因此打印这样的一圈就不需要四步了。
原文:https://blog.csdn.net/yanxiaolx/article/details/52254590

 def printMatrix(self, matrix):
        # write code here
        if matrix==[[]]:
            return
        row=len(matrix)
        column=len(matrix[0])
        left=0
        right=column-1
        top=0
        boom=row-1
        res=[]
        while right>left and toptop:
            for i in range(top,boom+1):
                res.append(matrix[i][left])
        #剩下一个
        if boom==top and left==right:
            res.append(matrix[left][top])
        return res

原文:https://blog.csdn.net/yangnianjinxin/article/details/79243110 

和为s的连续正数序列

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

输出描述:

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

思路

# -*- coding:utf-8 -*-
class Solution:
    def FindContinuousSequence(self, tsum):
        res=[]
        for i in range(1,tsum//2+1):
            sumRes=i
            for j in range(i+1,tsum//2+2):
                sumRes+=j
                if sumRes==tsum:
                    res.append(list(range(i,j+1)))
                    break
                elif sumRes>tsum:
                    break
        return res

-和为s的两个数字

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

输出描述: 对应每个测试案例,输出两个数,小的先输出。

不需要判断最小的

# -*- coding:utf-8 -*-
class Solution:
    def FindNumbersWithSum(self, array, tsum):
        for i in array:
            if tsum-i in array:
                if tsum-i==i:
                    if array.count(i)>1:
                        return [i,i]
                else:
                    return [i,tsum-i]
        return []

下面这个是我自己写的,通过

# -*- coding:utf-8 -*-
class Solution:
    def FindNumbersWithSum(self, array, tsum):
        list=[]
        n=len(array)
        for i in range(0,n-1):
            for j in range(i+1,n):
                if array[i]+array[j]==tsum:
                    list.append([array[i],array[j]])
        if list==[]:
            return []
        return min(list)

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


思路:
1,使用空格或者特殊字符把字符串转换为多个单词字符串,然后存在列表里,
2,列表翻转,再进行连接


-- coding:utf-8 --

class Solution:
def ReverseSentence(self, s):
str1=s.split(’ ') #
lists=[]
for i in str1:
lists.append(i)
return ’ '.join(lists[::-1])


## 矩阵中的路径*** 机器人的运动范围
题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路:
回溯法,遍历矩阵中的每一个位置

-- coding:utf-8 --

class Solution:
def hasPath(self, matrix, rows, cols, path):
for i, s in enumerate(matrix):
if s==path[0] and self.visit([(i//cols, i%cols)], matrix, rows, cols, path):
return True
return False

def visit(self, ans, matrix, rows, cols, path):
    if len(ans)==len(path):
        return True
    i,j = ans[-1]
    nex = [(ii,jj) for ii,jj in [(i,j-1),(i,j+1),(i-1,j),(i+1,j)]
           if 0<= ii 

题目描述
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

思路:将地图全部置1,遍历能够到达的点,将遍历的点置0并令计数+1.这个思路在找前后左右相连的点很有用,比如leetcode中的海岛个数问题/最大海岛问题都可以用这种方法来求解。

class Solution:
def init(self):
self.count = 0

def movingCount(self, threshold, rows, cols):
    # write code here
    arr = [[1 for i in range(cols)] for j in range(rows)]
    self.findway(arr, 0, 0, threshold)
    return self.count

def findway(self, arr, i, j, k):
    if i < 0 or j < 0 or i >= len(arr) or j >= len(arr[0]):
        return
    tmpi = list(map(int, list(str(i))))
    tmpj = list(map(int, list(str(j))))
    if sum(tmpi) + sum(tmpj) > k or arr[i][j] != 1:
        return
    arr[i][j] = 0
    self.count += 1
    self.findway(arr, i + 1, j, k)
    self.findway(arr, i - 1, j, k)
    self.findway(arr, i, j + 1, k)
    self.findway(arr, i, j - 1, k)
## 求1到N加法,做加法
题目描述
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

思路:直接套公式

-- coding:utf-8 --

class Solution:
def Sum_Solution(self, n):
return n*(n+1)/2
# write code here


题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

思路:可以采用位操作来实现,利用异或运算来计算不带进位的加法结果,利用与运算计算进位的标志,然后将这两个结果进行不带进位相加,重复上述过程,直至进位标志位0
链接:https://www.nowcoder.com/questionTerminal/59ac416b4b944300b617d4f7f111b215
来源:牛客网

同样我们可以用三步走的方式计算二进制值相加: 5-101,7-111 第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。

第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。

第三步重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010)<<1。
     继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。
  
计算两个数字的异或值,相当于只计算每一位的和,不计算进位,得出结果sum;

2.计算两个数字的与值,相当于求出两个数字的进位,然后左移一位,相当于进位,得出结果jw

-- coding:utf-8 --

class Solution:
def Add(self, num1, num2):
# write code here
while num2 != 0:
temp = num1 ^ num2
num2 = (num1 & num2) << 1
num1 = temp & 0xFFFFFFFF
return num1 if num1 >> 31 == 0 else num1 - 4294967296

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


思路:
1,使用空格或者特殊字符把字符串转换为多个单词字符串,然后存在列表里,
2,列表翻转,再进行连接


-- coding:utf-8 --

class Solution:
def ReverseSentence(self, s):
str1=s.split(’ ') #
lists=[]
for i in str1:
lists.append(i)
return ’ '.join(lists[::-1])

## 扑克牌顺子****
题目描述
LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

思路:

> 链接:https://www.nowcoder.com/questionTerminal/762836f4d43d43ca9deb273b3de8e1f4
来源:牛客网
> 
> 这道题很简单,注意两点 
> 1、如果输入为空,返回false
>  2、除了王的任何某个特定数值的牌出现两张或者更多,那么一定凑不齐顺子。
> 思路,先统计王的数量,再把牌排序,如果后面一个数比前面一个数大于1以上,那么中间的差值就必须用王来补了。看王的数量够不够,如果够就返回true,否则返回false。
思路:
1)没有大小王的时候即判断数是否连续;
2)有大小王的时候,判断数的间隔是否小于王的数量。小于返回true,大于返回false;
3)有相等的牌则直接返回false。


链接:https://www.nowcoder.com/questionTerminal/762836f4d43d43ca9deb273b3de8e1f4
来源:牛客网

    class Solution:
        def IsContinuous(self, numbers):
            # write code here
            if len(numbers) < 5:
                return False
            #计算0的个数
            nOfZero = numbers.count(0)
            #排序
            numbers.sort()
            #序列中间隔的值初始化为0
            sumOfGap=0
            #遍历非0部分的递增序列
            for i in range(nOfZero, len(numbers) - 1):
                small = numbers[i]
                big = numbers[i + 1]
                #当前与下一个值的比较,若相等则说明存在对子
                if small == big:
                    return False
                else:
                    #若不同,则得到二者的差再减1,若为0则说明连续,否则二者之间存在空缺
                    sumOfGap+= (big-small - 1)
                    #判断0的个数及序列中非0部分间隔值,若0不小于间隔值,则说明满足连续条件
            if nOfZero >= sumOfGap:
                return True
            else:
                return False
## 孩子们的游戏
题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

思路:
约瑟夫问题
一个重要的公式
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190223193426255.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI0NDI5MzMz,size_16,color_FFFFFF,t_70)
链接:https://www.nowcoder.com/questionTerminal/f78a359491e64a50bce2d89cff857eb6
来源:牛客网

    class Solution:
        def LastRemaining_Solution(self, n, m):
            if n < 1:
                return -1
             
            con = range(n)
             
            final = -1
            start = 0
            while con:
                k = (start + m - 1) % n
                final = con.pop(k)
                n -= 1
                start = k
                 
            return final

你可能感兴趣的:(剑指offer)