【Python】【算法题集2】

2017年08月28日    Author:Guofei

文章归类: 0x80_数据结构与算法    文章编号: 590


版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2017/08/28/someproblems.html

Edit

连续整数和问题

编写一个程序,输入一个数字n,输出n所有的连续整数和,
例如,输入27,输出:

[2, 3, 4, 5, 6, 7]
[8, 9, 10]
[13, 14]

解答

n =27

for j in range(int(n/2)+1):
    list_iter=[]
    sum_iter=0
    while 1:
        if sum_iter>n:break
        if sum_iter==n:print(list_iter)
        list_iter.append(j)
        sum_iter+=j
        j+=1

求解平方和问题

输入整数N,输出整数a,b,使得$a^2+b^2=N$

N=100
for a in range(int(N**0.5)+1):
    b=(N-a**2)**0.5
    if int(b)==b:
        print(a,b)

特殊数遍历

找到这样的4位整数,使得$abcd=(ab+cd)^2$
其中,$abcd$是4位数,$ab,cd$是2个2位数

def combine(x, y):
    return 10 * x + y


for a in range(1, 10):
    for b in range(10):
        for c in range(10):
            for d in range(10):
                if combine(combine(combine(a, b), c), d) == (combine(a, b) + combine(c, d)) ** 2:
                    print(combine(combine(combine(a, b), c), d))

输出:

2025
3025
9801

方法2

也可以遍历1000~9999这些数,用整除法提取各个位

for i in range(1000, 10000):
    a = i // 1000
    b = i // 100 - 10 * (i // 1000)
    c = i // 10 - 10 * (i // 100)
    d = i - 10 * (i // 10)
    if i == (a * 10 + b + c * 10 + d) ** 2:
        print(a, b, c, d)

方法2(改进)

耦合性高点,但代码短,效率高

for i in range(1000, 10000):
    ab = i // 100
    cd = i % 100
    if i == (ab + cd) ** 2:
        print(i)

角谷猜想(小范围验证)

给定任意一个自然数N,若它是偶数则除以2,若为奇数则乘3加1,若干次后必然得到结果1

N = 10000

count = 1
while count <= 10000:
    if N == 1:
        print('end with 1, count is {}'.format(count))
        break
    elif N % 2 == 0:
        N = N / 2
    elif N % 2 == 1:
        N = N * 3 + 1
    count += 1
else:#while执行到底,没有被break,执行else内容
    print('out of count')

这个代码特色是,有一个循环上限,防止死循环(遇到非角谷数)
注意while…else…的用法

验证四方定理

四方定理是数论中一个已经被证明的定理。
四方定理的内容:任意一个自然数可以表示为4个整数和的形式:
$100=6^2+8^2+0^2+0^2, 29=2^2+3^2+4^2+0^2$

N = 134

is_four_squares = 0
inter_max = int(N ** 0.5) + 1
for a in range(inter_max):
    for b in range(a, inter_max):
        for c in range(b, inter_max):
            for d in range(c, inter_max):
                if N == a ** 2 + b ** 2 + c ** 2 + d ** 2:
                    is_four_squares = 1
                    print(a, b, c, d)
if is_four_squares == 0:
    print('{} is not a for squares!'.format(N))

寻找同构数

$n^2$的尾部是n,那么n是同构数,例如,$76^2=5776$,76是一个同构数。

问题:求1000以内所有的同构数。

for i in range(1001):
    n = 0 # 最大位数
    while i // (10 ** n):  
        n += 1
    if (i ** 2) % (10 ** n) == i:
        print(i)

特点是用整除和while求得最大位数,用余数求出尾数。

验证尼科彻斯定理

这是一个已经被证明的定理:任何一个整数的立方,都可以表示为连续奇数之和。

解答:与上面的 连续整数和问题 很像

N = 5

for i in range(N ** 3):
    sum_iter = 0
    list_iter = []
    while 1:
        if sum_iter > N ** 3:
            break
        elif sum_iter == N ** 3:
            print(list_iter)
            break
        list_iter.append(i)
        sum_iter += i
        i += 2

三重回文数问题

找到11~999之间这样的数字a,使得$a,a^2,a^3$是回文数。

解答:怎样找到数字的回文数是主要问题。

这个可以注意一下:

def reverse(num):
    j = 0
    while num:
        temp = num % 10
        j = j * 10 + temp
        num = num // 10
    return j

主程序很简单

for i in range(11, 1000):
    if reverse(i) == i:
        if reverse(i ** 2) == i ** 2:
            if reverse(i ** 3) == i ** 3:
                print(i)

渔夫捕鱼问题

有A,B,C,D,E这5个渔夫抓了一堆鱼,各自睡觉。
A第一个醒来,把鱼分5份,丢掉一条,拿一份回家了。
B第二个醒来,同样把鱼分为5份,丢掉一条,拿一份回家。
C,D,E同样,问一开始有多少条鱼。

def func(x):
    if (x - 1) % 5 == 0:
        return (x - 1) * 4 / 5, True
    else:
        return 0, False


k = 0
while 1:
    flag = True
    k += 1
    i = k
    j = 0
    while flag and j < 5:
        i, flag = func(i)
        j += 1
    if flag:
        print(k)
        break

一个字节中有多少个1

问题:给定一个变量a,计算对应的字节中有多少个1

a = 7
count = 0
for i in range(32):
    if a & (2 ** i):
        count += 1
count

有效括号问题

原题是LeetCode上的经典题目了,用 stack 解决。时间复杂度O(n),空间复杂度 O(1) 这里修改一下题目。

有效括号问题变形1

已知只有小括号,用 stack 方法的空间复杂度是 O(n),可以优化吗?

可以在定一个变量,初始为0,遇到左括号就+1,遇到右括号就-1,迭代过程中为负,或者迭代结果不是0,就都不满足。

时间复杂度O(n), 空间复杂度 O(1)

def myfunc(input_string):
    stack = 0
    for i in input_string:
        if i == '(':
            stack += 1
        elif i == ')':
            stack -= 1
        else:
            return False

        if stack < 0:
            return False
    if stack != 0:
        return False
    return True


input_string = '(())()'
myfunc(input_string)

有效括号问题变形2

已知只有小括号,求最大有效子串的长度。

(LeetCode 32题)

还是回到 stack 的思路上,压入stack的元素不是括号,而是序号。

TODO:P504没太懂

追求极致

位运算:

奇偶判断

n/2, n/4, n/8 这种可以替换成 n>>1, n>>2, n>>3 虽然大部分编译器会自动帮你这么优化,但是给面试官的印象好一些

奇偶判断,与其 n%2,不如 n&1

消位操作

n & (n - 1) 的效果是,把n最右边的一个1变成0

用途1:判断n是否是2的某个次幂
如果是,那么二进制只有一个1,因此只需要看 n & (n-1) 是否为0

用途2:判断n对应的二进制有几个1,
只需要一个wihle循环做 n & (n - 1)

异或操作

异或有以下特点:

  1. 一个数和自己异或,得到0 :n & n = 0
  2. 任何数字和 0 做异或,得到自己: n & 0 = n
  3. 支持交换律和结合律 x^(y^z) = (x^y)^z

应用举例1:一个数组,只有1个数出现1次,其它都出现2次,找到这个数。
利用以上特点,全部求异或就可以得到结果,时间复杂度 O(n),空间复杂度 O(1)
如果是常规思维,时间复杂度是 O(n) 空间复杂度是 O(n)

应用举例2:交换两个数。

x = x ^ y
y = x ^ y
x = x ^ y

应用举例3,m的n次方。如果我们使用

res = 1
for i in range(n):
    res *= m

复杂度是n

举例来说,n=13,则n对应的二进制是1101,那么原式等价于
$n^{1101}=n^{0001}n^{0100}n^{1000}$

于是给出代码:

res, tmp = 1, m
while n != 0:
    if n & 1 == 1:
        res *= tmp
    tmp *= tmp
    n = n >> 1

复杂度是logn,本质上是利用2次方去算4次方,4次方去算8次方。

应用举例4:找出不大于N的最大2的幂指数

立即可以想出一个logn复杂度的算法

while res * 2 <= n:
    res *= 2

然而,结合上面关于二次幂的分析,N对应的二进制,只要保留最高位的1,就是要的结果,就有这个算法: step1:把n最高位1的右边全变成1
step2:n右移1位,然后加1,得到结果

上面的step1比较难,具体做法是 n 右移,然后与n本身求或,反复多次。写出代码:

n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8 # n是16位
n = (n + 1) >> 1

充分利用数组下标

有限个元素的计数问题,可以把数组的位置和元素做一一对应,数组的值是计数。

例如,给定一串字符串,统计每个字母的出现次数,可以建立一个长度是26的数组,遍历一次得到计数。

题目:有0-20之间的整数,共100万个,请把排序后的结果打印出来。

import numpy as np
nums = np.random.randint(0, 20, 2000)

res = [0] * 20
for num in nums:
    res[num] += 1

for num, cnts in enumerate(nums):
    for cnt in range(cnts):
        print(num)

阶乘专题

阶乘题目1

给定一个整数 N,他的阶乘末尾有几个0

分析:只有 2x5 可以产生0,那么需要计算偶数数量,以及可以被5整除的数量。前者远多于后者,所以只需要考虑被5整除的数量。
考虑到 25 贡献2个5,50也贡献2个5,125贡献3个5,因此这样写:

n = 20
res = 0
for i in range(1, n + 1):
    j = i
    while j % 5 == 0:
        res += 1
        j //= 5

res

进一步思考可以发现通项公式:res=n//5+n//5//5+…

n = 20

res = 0
while n != 0:
    res += (n // 5)
    n = n // 5

res

阶乘题目2

求N的阶层的二进制表示中,最低位1的位置。

其实就是计算2的个数,参考上面题目

n = 20

res = 0
while n != 0:
    res += (n//2)
    n //= 2

还可以优化吗?

我们知道,n//2n>>1 其实是一样的,于是优化:

n = 20

res = 0
while  n!=0:
    n >>=1
    res += n

其实一行即可

def func(n):
    return 0 if n == 0 else n // 5 + func(n // 5)

您的支持将鼓励我继续创作!
WeChatQR AliPayQR qr_wechat