剑指offer:其他思维方式(python版本)

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

这里需要弄清楚为什么python中负数需要和 0xFFFFFFFF 做与操作?
在计算机中,所有的数字都是使用补码存储起来的。由于Python没有位数这个概念,所以得到二进制表示需要多一点操作,即将位数限制在32位,通过和一个32位的全1数字按位与运算即可。对于正数来说,上面的按位与操作可以不做,因为正数的符号位为0,补码即原码,所以前面的数字全为0,按位与没有意义。但对于负数来说,直接bin(-1)是不能得到其补码的,而是得到了1的原码前面加上了负号,即-0b1。则通过和一个32位的全1数字按位与运算可得到其补码二进制表示对应的十进制数(按位与运算把符号位的1视为了数字)。

补码:正数不变,负数是它正数的反码+1
例如:-2:111…1101+1即111…110
在python中比较特殊,它储存二进制的-2是-ob10,所以 0xFFFFFFFF&n 直接得到负数的正数

#只取n的前32位,n&32个1(0xFFFFFFFF)
#一个F是四位在十六进制里面
#简易做法:
n = 0xFFFFFFFF&n
count = 0
for c in str(bin(n)):
	if c == "1":
		court +=
return coun t 
#做法二:
#每一次进行n-1&n的操作时n中的一个1都会被干掉
#到最后就会变成0
def NumberOf1(self,n):
	count = 0
	while n:
		n=n&0xFFFFFFFF
		n=n&(n-1)
		count += 1
	return count

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

异或^:相等为0,不等为1
先把两个数变成二进制,然后让这两个数进行与运算,得到的结果左移一位(因为要进行进位操作),然后再将;两个数进行异或操作(此时的两个数相加则是结果),然后把新得到的两个数一直循环下去直至进行与操作得到的数为0,最后进行异或操作得到的那个数就是答案。

加法操作可以转化为二进制数中先进行与操作,再进行异或操作,因为与操作是相同为1,不同为0,这样可以找到二进制数中需要进位的数的位置,然后在进行左移一位因为需要进位,然后再将这两个数进行异或操作,得到需要相加数的位置,因为异或操作是相同为0,不同为1。最后将这两个位操作的到的结果相加则可。但是不能相加所以可以继续进行这个操作,直至与操作得到的结果为0为止。
在这里插入图片描述

def Add(self,num1,num2):
	xorNum = num1 ^ num2
	andNum = (num1 & num2) << 1
	while andNum != 0:
		tmp1 = xorNum ^ andNum
		tmp2=(xorNum & andNum) << 1
		#python中存储数字是无限位,在进行异或操作的时候会得到很多个1,所以要把它变为32位的,但是在与操作的时候不会有这种情况
		tmp1 = tmp1 & 0xFFFFFFFF
		xorNum = tmp1
		andNum = tmp2
	
	#F在二进制里面是1111,7在二进制里面是0111
	#在这里要判断是否为负数的情况,ox7FFFFFFF是在32位中正数的最大值,
	#~为按位取反的意思
	return xorNum if xorNum <= 0x7FFFFFFF else ~(xorNum ^ 0xFFFFFFFF)

题目:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

遇到不相同的数字就相互抵消掉,最终剩下的数字就可能是大于一半的数字长度的数字

def MoreThanHalfNum_Solution(self,numbers):
	last = 0
	lastCount = 0
	for num in numbers:
		if lastCount == 0:
			last = num
			lastCount = 1
		else:
			if num == last:
				lastCount +=1
			else:
				lastCount -= 1
	
	if lastCount == 0:
		return 0
	else:
		#这种情况就是last可能大于一半的数字
		lastCount = 0
		for num in numbers:
			if num == last:
				lastCount += 1
		if lastCount > (len(numbers) >> 1):
			return last
	return 0

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

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

方法一:(时间复杂度太高)
死循环找丑数,判断一个数是不是丑数先一直循环除以2,直到不能整除,然后是3,然后是5,直到最后除完得1。

count = 0
def isUglyNumber(num):
	while num % 2 == 0:
		num = num//2
	while num % 3 ==0:
		num = num//3
	while num % 5 ==0:
		num = num//5
	if num == 1:
		return True
	else:
		return False
	num = 1
	while True:
		if isUglyNumber(num):
			count += 1
		if count == index:
			return num
		num += 1

方法二:
创建一个装丑数的列表,然后根据index找到丑数。
生成三个指针分别是乘以2,3,5,得出来的数比较大小,小的放下一个,然后乘完的指针向下移一位,一直循环下去。

def GetUglyNumber_solution(self,index):
	if index < 1:
		retuen 0
	uglyList = [1]
	twoPointer = 0
	threePointer = 0
	fivePointer = 0
	count = 1
	while count != index:
		minValue = min(2*uglyList[twoPointer],3*uglyList[threePointer],5*uglyList[fivePointer])
		uglyList.append(minValue)
		count += 1
		if minValue == 2*uglyList[twoPointer]:
			twoPointer += 1
		if minValue == 3*uglyList[threePointer]:
			threePointer += 1
		if minValue == 5*uglyList[fivePointer]:
			fivePointer += 1
	return uglyList[count-1]

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

两个相同的数字异或为0,并且a^b ^c=a ^c ^b。
1。>> 和 <<都是位运算,对二进制数进行移位操作。<< 是左移,末位补0,相当于原数x乘2。>>是右移,右移1位相当于除以2。

找到那两个数的异或和之后,找数字里面的1的位置(用除以2,有余数的那个就是1的位置或者向右移一位)。再用掩码生成一个数,把这个数与数组中所有的数进行与操作,会得到两种数,一种与操作完为0,另一个与操作完为1。
在这里插入图片描述

def findNumsAppearOnce(self,array):
	if len(array) < 2:
		return None
	twoNumXor = None
	for num in array:
		if twoNumXor == None:
			twoNumXor == num
		else:
			twoNumXor = twoNumXor ^ num
	#得到的twoNumXor就是两个不同数的异或,
	#此时去找这个二进制数最后位上是1就可以判断出它们这些位上是不同的
	#除以2去掉一个0,一直到有余数就找到那个1,因为2在二进制中是10
	count = 0
	while twoNumXor %2 == 0 :
		twoNumXor = twoNumXor >> 1
		count += 1
	#1向左移count位
	mask = 1 << count
	#此时拿mask与数组进行与操作(相当于掩码操作,只判断mask中1那位上的数是否为1),
	#把数组分为两组,每一组上都有一个只出现一次的数和其他相同的数
	firstNum = None
	secondNum = None
	for num in array:
		if mask & num == 0:
			if firstNum == None:
				firstNum = num
			else:
				firstNum == firstNum ^ num
		else:
			if secondNum == None:
				secondNum == num
			else:
				secondNum = secondNum ^ num
	return firstNum,secondNum

扑克牌顺子
题目: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。

算一下列表里面0的个数,然后按照大小进行排序,把0都去掉,算一下每个数之间的空隙,如果空隙大于0的个数则False,反之则True。

    def IsContinuous(self, numbers):
        # write code here
        if numbers == []:
            return False
        zeroNum = numbers.count(0)
        numbers.sort()
        gap_number = 0
        for i in range(zeroNum,len(numbers)-1):
            if numbers[i] == numbers[i+1]:
                return False
            gap_number += (numbers[i+1] - numbers[i] -1)
        if gap_number > zeroNum:
            return False
        else:
            return True
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值