经典算法问题 Python 实现合集

在编程学习中,算法是永恒的主题。今天给大家带来一系列经典算法问题的 Python 实现,涵盖了数学问题、游戏策略、数据结构等多个领域。每个案例都配有详细的分析和代码实现,非常适合算法初学者和面试备考的同学。

1. 五猴分桃问题

问题描述:海滩上有一堆桃子,五只猴子来分。第一只猴子把桃子分成五份,多了一个,它吃掉多的一个并拿走一份;第二只猴子把剩下的桃子又分成五份,也多了一个,它也吃掉多的一个并拿走一份;以后每只猴子都如此操作。问原来至少有多少个桃子?

def show(n):
    for i in range(1, 6):
        t = (n - 1) / 5
        print('%d. 总数%d个, 第%i只猴吃一个, 拿走%s个。' % (i, n, i, t))
        n = 4 * t

def fun():
    k = 1
    while True:
        t = k
        for i in range(4):
            t = 5 * t + 1
            if t % 4: break
            t /= 4
        else:
            print(5 * t + 1)
            show(5 * t + 1)
            break
        k += 1

fun()

算法思路:采用逆向递推,从最后一只猴子往前推,找到满足条件的最小整数解。

2. 三色旗问题

问题描述:有一个包含红、白、蓝三种颜色的数组,要求通过最少次数的交换,将数组排列成红、白、蓝的顺序。

import random

def fun(l):
    count = 0
    a = l.count(0)
    b = a + l.count(1)
    k1 = a
    k2 = len(l) - 1

    # 把第一个区域全部交换成白
    for i in range(a):
        if l[i] == 0:
            continue
        if l[i] == 1:
            while l[k1] != 0: k1 += 1
            k = k1
        elif l[i] == 2:
            while l[k2] != 0: k2 -= 1
            k = k2
        l[k] = l[i]
        l[i] = 0
        count += 1

    # 把第二个区域全部交换成红
    k = len(l) - 1
    for i in range(a, b):
        if l[i] == 2:
            while l[k] != 1: k -= 1
            l[k] = l[i]
            l[i] = 1
            count += 1
    return count

t = [random.choice([0, 1, 2]) for i in range(30)]
print(t)
steps = fun(t)
print(t, steps)

算法思路:设置三个指针,分别指向白区、红区和蓝区,一次遍历完成交换。

3. 约瑟夫环问题

问题描述:n个人围成一圈,从第一个人开始报数,数到k的人出列,下一个人继续从1开始报数,求最后剩下的人。

class Node():
    def __init__(self, value, next=None):
        self.value = value
        self.next = next

def createLink(n):
    if n <= 0:
        return False
    if n == 1:
        return Node(1)
    else:
        root = Node(1)
        tmp = root
        for i in range(2, n+1):
            tmp.next = Node(i)
            tmp = tmp.next
        tmp.next = root
        return root

def josephus(n, k):
    if k == 1:
        print('survive:', n)
        return
    root = createLink(n)
    tmp = root
    while True:
        for i in range(k-2):
            tmp = tmp.next
        print('kill:', tmp.next.value)
        tmp.next = tmp.next.next
        tmp = tmp.next
        if tmp.next == tmp:
            break
    print('survive:', tmp.value)

if __name__ == '__main__':
    josephus(10, 4)

算法思路:使用循环链表模拟报数过程,每次删除第k个节点。

4. 过桥问题

问题描述:有n个人要过桥,每次最多两人,过桥需要手电筒,每个人过桥时间不同,两人一起过按时间长的算,求最短过桥时间。

def fun(arr, time=0):
    ll = len(arr)
    if ll == 1:
        return arr[0]
    if ll == 2:
        return arr[1] + time
    if ll == 3:
        return arr[0] + arr[1] + arr[2] + time
    x = 2 * arr[0] + arr[-1] + arr[-2]
    y = arr[0] + 2 * arr[1] + arr[-1]
    if x < y:
        return fun(arr[0:ll-2], time + x)
    else:
        return fun(arr[0:ll-2], time + y)

arr = [2, 5, 6, 9, 13]
print(fun(arr))

算法思路:贪心策略,每次选择两种方案中较优的一种。

5. 取火柴游戏

问题描述:有21根火柴,两人轮流取,每次可取1-4根,取到最后一根者胜。实现一个AI对手。

import random

def fun(match):
    idx = 0
    while match > 1:
        idx += 1
        if idx % 2 == 1:
            gamer = 'A'
            choice = random.choice(range(1,5)) if match >= 5 else random.choice(range(1, match+1))
        else:
            gamer = 'B'
            if match > 5:
                for x in range(1, 5):
                    if (match - x) % 5 == 1:
                        choice = x
                        break
            else:
                choice = match - 1
        match -= choice
        print(gamer, choice, match)
    another = 'A' if gamer == 'B' else 'B'
    loser = gamer if match == 0 else another
    print('%s 胜利!' % loser)

fun(21)

算法思路:利用数学规律,保持剩余火柴数为5的倍数加1。

6. 年龄问题

问题描述:四个人年龄成等差数列,年龄和为26,乘积为880,求各自的年龄。

def sum(a, k, n):
    s = a
    for i in range(1, n):
        s += a + i * k
    return s

def mul(a, k, n):
    s = a
    for i in range(1, n):
        s *= a + i * k
    return s

for a in range(1, 26 // 4):
    find = False
    k = 1
    while True:
        t = sum(a, k, 4)
        if t >= 26:
            if t == 26 and mul(a, k, 4) == 880:
                find = True
            break
        k += 1
    if find:
        for i in range(20):
            print(a + i * k)

算法思路:枚举首项和公差,检查是否满足和与积的条件。

7. 三色球问题

问题描述:红、黄、蓝三种球,红球最多3个,黄球最多3个,蓝球最少2个,总数为8个,求所有可能的组合。

print('red\tyellow\tblue')
for red in range(0, 4):
    for yellow in range(0, 4):
        for green in range(2, 7):
            if red + yellow + green == 8:
                print(red, '\t', yellow, '\t', green)

算法思路:三重循环枚举所有可能,检查总数条件。

8. 鸡兔同笼问题

问题描述:经典的鸡兔同笼问题,输入脚的总数和头的总数,计算鸡和兔各有多少只。

while True:
    try:
        sum = eval(input("请输入鸡和兔子脚的总数: "))
        head = eval(input("请输入鸡和兔子头的总数: "))
        
        if sum < 6:
            print("输入鸡和兔子脚的总数错误请重新输入>>>")
        elif head < 2:
            print("输入鸡和兔子头的总数错误请重新输入>>>")
        else:
            j = 0
            t = 0
            flag = False
            while j < head:
                j += 1
                t = head - j
                if (sum == (j * 2 + t * 4)):
                    print("有鸡 %d只,兔子 %d只" % (j, t))
    except:
        print("能不能好好玩?")

算法思路:枚举鸡的数量,计算兔的数量,检查脚数是否匹配。

9. 井深问题

问题描述:五个人用绳子测井深,每个人测量的方式不同,求井深和各人绳长。

def fun():
    e = 0
    while True:
        e += 4
        a = 0
        while True:
            a += 5
            d = e + a / 5
            c = d + e / 4
            if c % 2 != 0 or d % 3 != 0:
                continue
            b = c + d / 3
            if b + c / 2 < a:
                break
            if b + c / 2 == a:
                deep = 2 * a + b
                print('a--> %d, b--> %d, c--> %d, d--> %d, e--> %d, deep--> %d' % (a, b, c, d, e, deep))
                return a, b, c, d, e, deep

print(fun())

算法思路:根据方程组推导,满足整除条件求解。

10. 金鱼问题

问题描述:某人卖金鱼,第一次卖掉总数的一半加1/2条,第二次卖掉剩下的1/3加1/3条,依次类推,最后剩下11条,求原来有多少条。

n = 11
while True:
    x = n
    for i in range(2, 5+1):
        x = x - (x/i + 1/i)
    if x == 11:
        print(n)
        x = n
        for i in range(2, 5+1):
            m = x/i + 1/i
            x = x - m
            print('%d: mai-->%d shend-->%d' % (i-1, m, x))
        break
    n = n + 1

算法思路:逆向递推,从最后结果往前推算初始值。

11. 借书问题

问题描述:5本书借给3个人,每人最多借一本,求所有可能的借法。

count = 0
print("假设五本书分别为1,2,3,4,5,主要借法有")

for a in range(1, 6):
    for b in range(1, 6):
        if a != b:
            for c in range(1, 6):
                if c != a and c != b:
                    count += 1
                    print("第%d种:A分到书%d,B分到书%d,C分到书%d" % (count, a, b, c))

算法思路:三重循环枚举,确保三个人拿到的书不同。

12. 分椰子问题

问题描述:海滩上有一堆椰子,五个人分,每次分成5份多一个,求原来至少有多少个椰子。

def xf(n):
    a = 1
    b = a
    while 1:
        for i in range(n-1):
            a = (a-1)/n*(n-1)*1.0
        if (a-1) % n == 0:
            return b
        b += 1
        a = b
print(xf(5))

算法思路:类似五猴分桃问题,逆向推导。

13. 传教士与野人过河问题

问题描述:n个传教士和n个野人过河,船最多载m人,任何一边传教士人数不能少于野人(除非该边没有传教士),求过河方案。

n = 3  # n个传教士、n个野人
m = 2  # 船能载m人

x = []  # 一个解
is_found = False

def get_states(k):
    if k % 2 == 0:
        s1, s2 = n - sum(s[0] for s in x), n - sum(s[1] for s in x)
    else:
        s1, s2 = sum(s[0] for s in x), sum(s[1] for s in x)
    
    for i in range(s1 + 1):
        for j in range(s2 + 1):
            if 0 < i + j <= m and (i * j == 0 or i >= j):
                yield [(-i, -j), (i, j)][k % 2 == 0]

def conflict(k):
    if k > 0 and x[-1][0] == -x[-2][0] and x[-1][1] == -x[-2][1]:
        return True
    if 0 < n - sum(s[0] for s in x) < n - sum(s[1] for s in x):
        return True
    if 0 < sum(s[0] for s in x) < sum(s[1] for s in x):
        return True
    return False

def backtrack(k):
    global is_found
    if is_found: return
    
    if n - sum(s[0] for s in x) == 0 and n - sum(s[1] for s in x) == 0:
        print(x)
        is_found = True
    else:
        for state in get_states(k):
            x.append(state)
            if not conflict(k):
                backtrack(k + 1)
            x.pop()

backtrack(0)

算法思路:状态空间搜索,使用回溯法,每次生成合法的船载状态,检查两岸是否满足约束条件。

总结

以上13个经典算法问题涵盖了数学推理、贪心策略、回溯搜索、链表操作等多种算法思想。通过实现这些案例,不仅可以加深对Python语言的理解,还能提升解决实际问题的能力。每个问题都有其独特的思维亮点,建议读者亲自运行代码,观察输出结果,理解算法背后的数学原理。