简体   繁体   English

如何将第一个节点的上一个节点设置为最后一个节点的下一个节点以创建循环双向链表?

[英]How do I set the prev of my first node to the next of my last node to create a circular doubly linked list?

I am creating the Josephus problem using a circular doubly linked list.我正在使用循环双向链表创建 Josephus 问题。 I am getting an Attribute error, which I assume is because my current_node (first node) does not have a .prev yet.我收到一个属性错误,我认为这是因为我的 current_node(第一个节点)还没有 .prev。

I understand that the prev of my first node should point to the next of my last node to create a circular doubly linked list.我知道我的第一个节点的上一个节点应该指向我最后一个节点的下一个节点来创建一个循环双向链表。

Can someone guide me on whether I have correctly identified the error?有人可以指导我是否正确识别错误? If yes, how can I rectify it?如果是,我该如何纠正?

If not, then what are the other ways I can correct the error?如果没有,那么我可以纠正错误的其他方法是什么?

#Initialize the node
    def __init__(self, data):
        self.data = data
        self.next = None
        self.prev = None
        
    def remove(self, n):
        print("Student " +str(n)+ " was removed")
        
class Circle:
# Initializing the DLL
    def __init__(self):
        self.head = None
        self.tail = None
#Inserting elements 2 to n in the dll              
    def insert_after(self, x, data):          
        y = Student(data) # make a new Node object.
        z = Student(data)
        z = x.next
        
        y.prev = x
        y.next = z
        x.next = y
        
        z.prev = y       
    
    def josephus_solution(self, dllist, n, k):
            no_of_active_nodes = n
            current_node = Student(1)
            #last_node = Student(n)
            #print(current_node.prev)
            for i in range(2, n + 1):
                dllist.insert_after(current_node, i)
                count = 0
            #print(current_node.data)
            while (current_node.next != current_node.prev):
                #print(current_node.next.prev)
                current_node = current_node.next
                count += 1
                #print(current_node.data)
                if (count == k):
                    current_node.remove(current_node.data)
                    current_node.prev.next = current_node.next
                    current_node.next.prev = current_node.prev
                    count = 0 
                    no_of_active_nodes -= 1
                    #print(no_of_active_nodes)
                if (no_of_active_nodes == 1):
                    print("Student" + str(current_node.data) + "Recieves the scholarship")
            return current_node.data

dllist = Circle()
n = 5 #int(input('Input number of people (n): '))
k = 2 #int(input('The nth person will be executed. Input k: '))
ans = dllist.josephus_solution(dllist, n, k)

Error错误

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_24/3762059582.py in <module>
     54 n = 5 #int(input('Input number of people (n): '))
     55 k = 2 #int(input('The nth person will be executed. Input k: '))
---> 56 ans = dllist.josephus_solution(dllist, n, k)

/tmp/ipykernel_24/3762059582.py in josephus_solution(self, dllist, n, k)
     32             #print(current_node.prev)
     33             for i in range(2, n + 1):
---> 34                 dllist.insert_after(current_node, i)
     35                 count = 0
     36             #print(current_node.data)

/tmp/ipykernel_24/3762059582.py in insert_after(self, x, data)
     24         x.next = y
     25 
---> 26         z.prev = y
     27 
     28     def josephus_solution(self, dllist, n, k):

AttributeError: 'NoneType' object has no attribute 'prev'

The direct reason for the error is that z is None and your code can therefore not access any prev attribute of it in the line z.prev = y .错误的直接原因是zNone ,因此您的代码无法在z.prev = y行中访问它的任何prev属性。 The cause is that when the first node is created, its prev and next attributes are initialised to None , and when this node is passed as x argument to insert_after , then with z = x.next a None value is assigned to z .原因是当第一个节点被创建时,它的prevnext属性被初始化为None ,当这个节点作为x参数传递给insert_after ,然后z = x.next一个None值被分配给z

There are several issues with your approach:你的方法有几个问题:

  • In insert_after it makes no sense to call Student twice, since you want to insert one node, not twoinsert_after中调用Student两次是没有意义的,因为你想插入一个节点,而不是两个
  • Although your Circle class has a head and a tail attribute, they are never used after their initialisation to None , so there is actually nothing in the Circle instance that helps you to maintain the list.尽管您的Circle类有一个head和一个tail属性,但它们在初始化为None后就不再使用了,因此 Circle 实例中实际上没有任何内容可以帮助您维护列表。 You might as well define all methods on the Student class.您不妨在Student类上定义所有方法。
  • The while condition in josephus_solution is probably intended to test that only one node remains in the list, but it actually verifies whether there are two left. josephus_solution的 while 条件可能是为了测试列表中只剩下一个节点,但它实际上是验证是否还剩下两个节点。 Admittedly, this works when current_node is a node that was just deleted, but then the returned data is the data of the deleted node and not the remaining active node, and current_node is not a just-deleted node, then this condition will make the loop exit when it still has two active nodes.诚然,当current_node是刚删除的节点时,这是有效的,但是返回的数据是被删除节点的数据而不是剩余的活动节点,并且current_node不是刚删除的节点,那么这种情况将使循环当它还有两个活动节点时退出。

Some other remarks:其他一些注意事项:

  • As you call josephus_solution as a method, it should not be necessary to also pass an instance as argument.当您将josephus_solution作为方法调用时,也不必将实例作为参数传递。 self is already available to that method self已经可用于该方法
  • I would split the creation of the nodes into a separate -- generic -- method, and let josephus_solution work on an existing linked list.我会将节点的创建拆分为一个单独的 -- 通用 -- 方法,并让josephus_solution处理现有的链表。
  • As in a circular list none of the next or prev attributes should ever be None , you'll make things easier when initialising them to self instead of None .就像在循环列表中一样, nextprev属性都不应该是None ,当将它们初始化为self而不是None时,您会让事情变得更容易。
  • The remove method should not be responsible for printing a message, but should on the other hand be responsible for rewiring the prev and next references.remove方法不应该负责打印的消息,而是应该在另一方面负责重新布线的prevnext引用。 At any rate, there is no need for this method to get an argument, as it knows its own attributes through self .无论如何,此方法不需要获取参数,因为它通过self知道自己的属性。
  • In the original Josephus problem, the first node is numbered as 1, so with k=2 the first node to remove is the one with number 2. Your code would first delete the one with number 3. To align with the original problem, move the current_node = current_node.next statement to be the last line in the body of the loop.在最初的约瑟夫斯问题中,第一个节点编号为 1,因此当 k=2 时,要删除的第一个节点是编号为 2 的节点。您的代码将首先删除编号为 3 的节点。要与原始问题对齐,请移动current_node = current_node.next语句是循环体中的最后一行。 This also helps to be sure that current_node is never a just-deleted node when the while condition is evaluated.这也有助于确保在评估while条件时current_node永远不会是刚刚删除的节点。
  • As your while condition already takes care of when to stop, there should be no reason to keep track with a no_of_active_nodes variable.由于您的while条件已经处理了何时停止,因此没有理由使用no_of_active_nodes变量进行跟踪。

There are more remarks to make, but these are what I believe the most important.还有更多的评论要说,但这些是我认为最重要的。

Here is a correction of your code, taking all of the above into account:考虑到以上所有因素,以下是对您的代码的更正:

class Student:
    def __init__(self, data):
        self.data = data
        # Better make prev/next self references to avoid None:
        self.next = self
        self.prev = self
        
    def remove(self):
        # Perform the remove here
        self.prev.next = self.next
        self.next.prev = self.prev
        
    def insert(self, data):          
        # Should only create one node, not two
        node = Student(data)
        node.next = self
        node.prev = self.prev
        self.prev = node.prev.next = node

    # Handy method to create a circular list from values
    @classmethod
    def fromiterator(cls, iterator):
        head = cls(next(iterator))
        for value in iterator:
            head.insert(value)
        return head

    def josephus_solution(self, k):
        count = 0
        current_node = self
        while current_node.next is not current_node:  # This is how to test for 1 node
            count += 1
            if count == k:
                current_node.remove()  # No argument
                print("Student " +str(current_node.data) + " was removed")
                count = 0
            current_node = current_node.next # do this at the end of the iteration
            # No need to keep track of active nodes ...
        return current_node

# These are the parameters of the "original" problem (see Wikipedia)
n = 41
k = 3
dllist = Student.fromiterator(iter(range(1, n+1)))
# Don't pass the list as argument: it already is available as `self`
dllist = dllist.josephus_solution(k)
# Perform output in the main code, not inside method
print("Student " + str(dllist.data) + " recieves the scholarship")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM