经典算法题之约瑟夫环与全排列

约瑟夫环

问题描述:

约瑟夫环问题的基本描述如下:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,要求找到最后一个出列的人或者模拟这个过程。

解题思路:构建循环列表

def Josephuson(n, m):
    List = list(range(1, n + 1))  #生成n序列
    index = 0
    while List:
        temp = List.pop(0)  #弹出列表的第一个值
        index += 1
        if index == m:
            print('{}-->'.format(temp), end='') #打印路径
            index = 0  #索引重新归零
            continue   #索引符合m的倍数,结束本次循环,该值不在参与循环列表的构建
        else:
            List.append(temp)  #不满足索引条件把弹出来的数值重新添加到列表尾部构建循环列表
        if len(List) == 1:  # 当列表只有1个值时结束循环
            print(List[0])
            break

if __name__ == '__main__':
    n = int(input('总人数:'))
    m = int(input('报数:'))
    Josephuson(n, m)

运行截图:

全排列

问题描述:输入一个字符串,按字典序打印出该字符串中字符的所有排列。

示例: 1 2 3的全排列如下:

1 2 3 , 1 3 2 , 2 1 3 , 2 3 1 , 3 1 2 , 3 2 1

从上面的全排列也可以看出来了,从左往右依次增大,对这就是字典序法。可是如何用算法来实现字典序法全排列呢?

2.1字典序算法

例:字典序法找124653的下一个排列

如果当前排列是124653,找它的下一个排列的方法是:

从这个序列中从右至左找第一个左邻小于右邻的数,如果找不到,则所有排列求解完成,如果找得到则说明排列未完成。

本例中将找到46,计4所在的位置为i,找到后不能直接将46位置互换,而又要从右到左到第一个比4大的数,本例找到的数是5,其位置计为j,将i与j所在元素交换125643,然后将i+1至最后一个元素从小到大排序(也可以说是反转,从643到346)得到125346,这就是124653的下一个排列。

def DictSort(value):
    global number
    s_end = len(value) - 1  #尾指针
    p = s_end               #右邻指针
    q = p - 1               #左邻指针
    while p != 0:
        if value[q] >= value[p]:  #字符串左邻大于或等于右邻
            q = q - 1  # 指针往左移动
            p = p - 1  # 指针往左动动
        else:
            i = q  # 找到左邻小于右邻,记录索引
            j = s_end  # 右邻指针重新指向末尾
            while value[i] >= value[j]:  # 从右往左找,找到记录值value[i]小于右邻的值
                j = j - 1
            value[i], value[j] = value[j], value[i]  # 找到了,交换列表中二者的值
            value[i + 1:] = value[-1:i:-1]  # 剩下的值进行反转
            number = number + 1  # 执行次数加1
            return 1
    return 0

if __name__ == '__main__':
    str = input('请输入排序的字符串:')
    number = 1    #记录执行的次数
    value = list(str)  
    value.sort()
    print("第{}种排序:{}".format(number, ''.join(value)))  # 第一次最小排序
    flag = DictSort(value)  
    while flag:
        print("第{}种排序:{}".format(number,''.join(value)) )
        flag = DictSort(value)
    print('该字符串一共有{}种排序!'.format(number))

运行效果截图:

         

 2.2全排列递归实现

全排列的递归实现解析:

为方便起见,用123来示例下。123的全排列有123、132、213、231、312、321这六种。

1、首先考虑213和321这二个数是如何得出的。显然这二个都是123中的1与后面两数交换得到的。

2、然后可以将123的第二个数和每三个数交换得到132。同理可以根据213和321来得231和312。

3、因此可以知道——全排列就是从第一个数字起每个数分别与它后面的数字交换。

# 递归回溯实现字符串全排列
def AllSort(value, s, length):
    global number
    if s == length:  # 递归出口
        number = number + 1
        print("第%d中排序:" % number, ''.join(value))

    else:
        for i in range(s, length):
            value[s], value[i] = value[i], value[s]
            AllSort(value, s + 1, length)
            value[s], value[i] = value[i], value[s] #注意,因为递归有回溯的过程,所以需要把数字重新调换回来变为初始的排列。

if __name__ == '__main__':
    str = input('请输入排序的字符串:')
    number = 0
    value = list(str)  # 字符串转列表处理
    length = len(value)
    value.sort()
    AllSort(value, 0, length)

 运行效果截图

    

 以上递归算法算然实现了全排列,但并非字典序,而且当字符串出现相同字符时,会出现相同的排列,所以如何优化呢?

优化方案:交换前先进行判断是否重复

def is_same(value, s, i):  # 判断排列的重复性
    while s < i:
        if value[s] == value[i]:
            return 0
        s += 1
    return 1

def AllSort(value, s, length):
    global number
    if s == length:  #递归出口
        number = number + 1
        print("第%d中排序:" % number, ''.join(value))
    else:
        for i in range(s, length):
            if is_same(value, s, i):  #去重全排列
                value[s], value[i] = value[i], value[s]
                AllSort(value, s + 1, length)
                value[s], value[i] = value[i], value[s]

if __name__ == '__main__':
    str = input('请输入排序的字符串:')
    number = 0
    value = list(str)
    length = len(value)
    value.sort()
    AllSort(value, 0, length)

运行效果截图:

   

原文地址:https://www.cnblogs.com/Liu928011/p/14905277.html