今天总算开始整理算法了, 一直都被算法搞得瑟瑟发抖。。
写在最前面
所需的知识点:
&: 按位与运算,如果两个相位都为1,则结果为1,否则为0
|: 或 只要有一个为1,则结果为1
^: 异或, 相异为1
<<: 左移,二进制左移一位,相当于乘2
>>: 右移,二进制右移一位,相当于除2
~ :按位取反运算符,对数据的每个二进制位取反,即把1变为0,把0变为1 。~x 类似于 -x-1
一 基本数字运算
如何判断一个数是不是某个数的平方
例如 16是某个数的平方,15不是某个数的平方, 实际就是问一个数能否被开方。
下面是个比较巧妙地算法, 对n依次减1, 3, 5, 7,……如果减后数大于零,继续减;如果最后等于零,则这个数可以被开方;相减后小于零,则不能被开方。
它的时间复杂度为O(n^0.5)
def isPower(n):
minus = 1
while n > 0:
n = n - minus
# n是某个数的平方
if n == 0:
return True
# n不是某个数的平方
elif n < 0:
return False
# 每次减数都加2
else:
minus += 2
return False
if __name__ == "__main__":
n1 = 16
if isPower(n1):
print(str(n1) + "是某个数的平方")
else:
print(str(n1) + "不是某个数的平方")
如何判断一个数是否为2的n次方
1. 简单粗暴的方法, 就是把1逐渐左移一位,乘2,和传入的参数对比
时间复杂度 O(logn)
def isPower(n):
if n < 1:
return False
i = 1
while i < n:
if i == n:
return True
i <<= 1
return False
2. 这个同样很巧妙,2的n次方二进制位1, 10, 100..... 发现特点就是一个数是2的平方,它的二进制就是以唯一的1开头,后面都是0。
如果一个数二进制表示只有一个1,例如num=00010000,num-1=00001111,由于num和num-1二进制每一位都不同,
因此num&(num-1)=0,可以利用它判断是不是2的n次方。
时间复杂度O(1)
def isPower(n):
if n < 1:
return False
m = n & (n-1)
return m == 0
如何不使用除法操作实现两个正整数的除法
时间复杂度为O(m/n)
def divide(m, n):
res = 0
while m > n:
m = m - n
res += 1
return "商为" + str(m) + ",余数为" + str(res)
如何不用加减乘除运算实现基本运算
1. 如何不用加减乘除运算实现加法
利用二进制进行加法运算
- 二进制先不考虑进位,不考虑进位的二进制加法可以用异或代替
- 计算进位, 由于1+1才会进位,因此进位的运算可以用于操作代替。先做与运算, 再把运算结果左移一位
- 不断对1, 2 两步得到的结果相加, 直到进位0为止
def add(n1, n2):
sums = n1 ^ n2
carry = (n1 & n2) << 1
sums += carry
return sums
if __name__ == "__main__":
print(add(1, 1))
2. 如何不用加减乘除运算实现减法
根据~的特性,~x 类似于 -x-1, 则~b+1=-b
所以如下
def add(n1, n2):
sums = n1 ^ n2
carry = (n1 & n2) << 1
sums += carry
return sums
def sub(a, b):
return add(a, add(~b, 1))
if __name__ == "__main__":
print(sub(45, 1))
3. 如何不用加减乘除运算实现乘法
4. 如何不用加减乘除运算实现除法
(待议)
如何只用+=实现加减乘除运算
def add(a, b):
"""
方法功能: 用+=实现加法操作(至少有一个非负数)
进行小数加法
"""
if a < 0 and b < 0:
return "无法用+=实现"
if b >= 0:
i = 0
while i < b: # 实践证明换成 while i != b是不行的, 当它是小数时就gg了
a += 1 # 相当于把b的一个1拿给a,
i += 1
return a
else:
i = 0
while i < a:
b += 1
i += 1
return b
def minus(a, b):
"""
方法功能: 用+=实现减法操作, 被减数大于等于减数
相当于从别的地方给减数有一点一点+1,直到和被减数相同, 最后统计加了几个1
所以它不能进行小数减法
"""
if a < b:
return "无法用+=操作实现"
result = 0
while b != a:
b += 1
result += 1
return result
def multi(a, b):
"""
方法功能: 实现乘法操作, 两个数都为整数
"""
if a <= 0 or b <= 0:
return "无法用+=操作"
result = 0
i = 0
while i < b:
result = add(result, a)
i += 1
return result
def divide(a, b):
"""
用+=实现除法, 两个数都为整数
"""
if a <= 0 or b <= 0:
return "无法用+=操作实现"
result = 1
while True:
tmpMulti = multi(b, result)
if tmpMulti <= a:
result += 1
else:
break
return result-1
如何判断1024!末尾有几个0?
看它有几个5因子就好了,时间复杂度为O(n)
def zeroCount(n):
count = 0
while n > 0:
n = n//5
count += n
return count
if __name__ == "__main__":
print(str(zeroCount(20)))
如何按要求比较两个数的大小
不能使用大于小于和if语句, 返回值为大的数
def maxs(a, b):
return ((a+b) + abs(a-b)) / 2
如何把十进制的数转换为二进制和十六进制的数
def toBinary(n):
"""
相当于短除法
"""
result = ""
while n != 0:
remainder = n % 2
result = str(remainder) + result
# 后面两个变量位置不能变
n >>= 1
# 右移除以二
return result
def toHexadecimal(n):
result = ""
while n != 0:
remainder = n % 16
if remainder < 10:
result = str(remainder) + result
else:
result = str(chr(remainder - 10 + ord('A'))) + result
n >>= 4
return result
如何求二进制数中1的个数
1 用(n&1)判断最后一位是不是1, 然后右移动 时间复杂度O(N)
def countOne(n):
count = 0
while n > 0:
# 判断n的二进制最后一位是不是1
if (n & 1) == 1:
count += 1
n >>= 1
# 右移一位
return count
2 利用n&(n-1), 其结果都会少一位1, 而且是最后一位。n=6, 二进制位110, n-1=5, 二进制位101,110&101=100,和110比少了最后一位1。
def countOne(n):
count = 0
while n > 0:
# 如果不为零一直用n & (n-1)去掉最后一个1
if n != 0:
n = n & (n-1)
count += 1
return count
如何计算一个数的n次方
利用递归,效率高。需要判断n的奇偶
def power(d, n):
if n == 0:
return 1
if n == 1:
return d
tmp = power(d, abs(n)//2)
if n > 0:
if n % 2 == 1:
return tmp*tmp*d
else:
return tmp*tmp
else:
if n % 2 == 1:
return 1/(tmp*tmp*d)
else:
return 1/(tmp*tmp)
如何在不能使用库函数的条件下计算n的平方根
利用公式ai+1 = (ai _n/ai)/2
def squareRoot(n, e):
new_one = n
last_one = 1.0
while new_one - last_one > e:
new_one = (new_one + last_one) / 2
last_one = n / new_one
return new_one
if __name__ == "__main__":
n = 500
e = 0.000001
print(str(squareRoot(n, e)))
如何不使用^操作实现异或运算
时间复杂度O(N)
def xor(x, y):
return (x | y) & (~x | ~y)
如何不使用循环打印出1到100
def prints(n):
if n > 0:
prints(n-1)
print(n)
print(prints(100))
如何找最小的不重复数
mmp这个程序折磨我一天,总是静不下心来看,还有原代码太JB冗余了,也是懵逼的原因之一
题目描述:给定任意一个整数(0123,以0开头的不行), 求比这个数大且最小的“不重复数”。“不重复数”的含义是相邻两位数不同,例如1101是“重复数”,1121不是“重复数”
整体思路:
例如给定11099,首先对它加1,开头插入0,变成011100,从左往右找到重复字符串111, 从i=2位遍历,给它加1, 变成012100,在把
2后面的100变成010101...,于是变成012010。符合题意。
但是给的是99020这种的话,99加1要进位,要特殊考虑。即给它加上进位。如下的carry函数。
def carry(num, pos):
"""
处理特殊情况, 像['9', '9', '0', '2', '0'], 有两个99,要进位的
"""
while pos > 0:
# num=['0', '9', '10', '0', '2', '1'], pos=2
if int(num[pos]) > 9:
num[pos] = '0'
# 10变成0
num[pos-1] = str(int(num[pos-1])+1)
# 前一位加1,
pos -= 1
# 一直执行,直到前面没有要进位的
return num
def findMinNonDupNum(n):
count = 0
# n = 990210
char = list(str(n+1))
# list(99020) 是这样的结构 ['9', '9', '0', '2', '0'] , 加1 变成['9', '9', '0', '2', '1']
ch = list(char)
ch.insert(0, '0')
lens = len(ch)
# 上面三行构造类似的结构 ['0', '9', '9', '0', '2', '1']
i = 2
# ['0', '9', '9', '0', '2', '1'], 从第2位开始遍历, 因为零位的0是后加的,一位的数不可能是0, 从第二位开始遍历效率高,否则前几次是白循环
while i < lens:
# 从左往右遍历
count += 1
# 记录循环次数
if ch[i-1] == ch[i]:
# 如果当前的数等于前一个数,即有重复字符串, 这里是二位的9和一位的9相等
ch[i] = str(int(ch[i])+1)
# 当前位的数加1, ch变成 ['0', '9', '10', '0', '2', '1']
carry(ch, i)
# 调用carry, 返回经过进位处理的ch
# 下面是把i后面变成01010.....
j = i + 1
while j < lens:
if (j-i) % 2 == 1:
# j=i+1, j-i=1, 所以i的后面一定会变成0
ch[j] = '0'
else:
ch[j] = '1'
j += 1
else:
i += 1
print("循环次数" + str(count))
return int(''.join(ch))
if __name__ == "__main__":
print(findMinNonDupNum(99020))