josephus环的实现(顺序表和链接表)

josephus环问题是数据结构中一个常见的实例:假设有n个人做成一圈,现在要求从第k个人开始报数,报道m的人退出。然后从下一个人继续从头报数,并按相同的规则退出, 直到所有的退出。还有就是要按退出的顺序打印出出列人的编号。

其实看到这个“一圈”,我第一瞬间就想到了用循环链表,也确实可以。这里我分别中顺序表和循环链表进行了实现。

一、顺序表:
对于顺序表对josephus问题的实现由两种思路:
第一种:将出列人的位置设为None或0,不改变表的长度,这种实现比较复杂
代码如下:

# 自己想出来的
# list = list(range(1,101))
#
# k = 4 # 表示下标, 表示第五个人
# n = 0 # 总共选出的要出列的人数
# a_list = []
#
# for i in range(100): # 因为数组有一百个元素,每次打印一个的话要循环一百次
#     x = 0 # 表示要数到的数
#     for z in range(100):
#         k = k % 100
#             # 如果数到了100我们就将其值为0,从头开始
#         if list[k] == None:
#             # 如果下标为k的数是None,我们跳过这层循环。
#             # 而且这个元素也不能用了,所以也要跳过这个元素
#             k += 1
#             continue
#         if x == 49: # 如果数到了五十我们就退出
#             # 从0到49刚好是50个数
#             break
#         k += 1
#         x += 1
#     a_list.append(list[k])
#     list[k] = None
#     n += 1
#     if n == 50:
#         break
#     k += 1
# print(a_list, len(a_list), end='\n')


# 约瑟夫环问题的第一种解答。
def josephus_A(n, k, m):
    people = list(range(1, n+1)) # 建立要给people的数组,值为1 ~  n

    i = k - 1 # 下标
    for num in range(n): # 循环n次,n为列表的长度,每次拿出一个元素最多要n次循环
        count = 0
        while count < m : # count用来计数的, 当它小于m(规定的次数)时
            if people[i] > 0:
                # 对于数到的人,都会将其位置值为0
                count += 1
                # 到这里还是会加一
            if count == m:
                print(people[i], end='')
                people[i] = 0
                # 如果count数到了人数,就直接可以打印了,然后将位置值为0
            i = (i+1) % n
            # 指着将i重新赋值给它的下一个下标
        if num < n - 1:
            print(", ", end="")
        else:
            print("")
    return

josephus_A(100, 5, 10)

以上有两段代码,都是用来实现的

第二种思路:删除出列人的位置,用列表的pop方法就可以,这种方法改变了列表的长度。
代码如下:

# n表示开始时列表具有的长度
# k表示从第k个元素开始数起
# m表示间隔数加一
# num表示还剩余的列表长度
# def josephus_B(n, k, m):
#     listIni = list(range(1, n+1))
#     # 创建一个初始列表
#     num = n
#     # 浮动指针,num表示列表还剩余的长度
#     # for i in range(n):
#     while num > 1:
#         k = (k + m-1) % num
#         # 数到m个人,中间应该相隔m-1个人的
#         print(listIni[k-1], end=", ")
#         listIni.pop(k-1)
#         num -= 1
#     print(listIni[0], end="\n")
#
# josephus_B(100, 5, 10)
# 对于这么函数的算法复杂度的话,虽然函数体只有一层循环,但调用了pop方法,
# 这个方法的需要线性时间,所以算法复杂度死O(n2)

def josephus_L(n, k, m):
    people = list(range(1, n+1))

    num, i = n, k-1
    # i表示下标
    for num in range(n, 0, -1):
        # 精妙之处:这里随着每次循环,num都会减一,即num表示的列表的长度减一
        i = (i + m-1) % num
        print(people.pop(i), end=(", " if num > 1 else "\n"))
        # 用一个条件表达式确定一次输出的结束字符串
    return
josephus_L(100, 5, 10)


二、链接表
使用链接表,当然是使用循环链接表来实现,也非常简单,用我们前面实现的循环链接表的类,当然再加一个对任意位置删除的方法

    def pop(self, num):
        # 指定位置的删除元素
        if self.isEmpty() or self.kerear.nodenum < num:
            # 如果链表时空的,或者指定删除的位置超过了链表的长度
            # 产生一个错误
            raise LinkedListUndeflow
        pN = 0
        p = self.head
        q = None
        while pN < num:
            if num == 1:
                print(self.popfirst(), end=", ")
                return
            if num == self.kerear.nodenum:
                print(self.poplast(), end=", ")
                return
            q = p
            p = p.next
            pN += 1
        if q:
            q.next = p.next
        print(p.elem, end=", ")
        self.kerear.nodenum -= 1

就可以解决josephus环问题,,实现代码如下:

from 循环链表 import LinkedListUndeflow, LList

def josephus_C(n, k, m):
    # 建立一个长度为100的循环链表
    mlist1 = LList()
    for i in range(100):
        mlist1.append(i + 1)
    for num in range(n, 0, -1):
        k = (k + m-1) % num
        mlist1.pop(k)

josephus_C(100, 5, 10)

以上就是josephus(约瑟夫)环问题的几种实现
免不了推荐一波裘宗燕老师的书《数据结构与算法 Python语言的实现》,反正无论对于学习python或是学习数据结构和算法,都有很大的作用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值