1
\$\begingroup\$

Problem is straightforward, reverse a double linkedlist, any advice on functional bug, performance in terms of algorithm time complexity and code style are highly appreciated.

Code written in Python 2.7.

class DoubleLinkedListNode:
 def __init__(self, value, next_node):
 self.value = value
 self.pre_node = None
 if next_node:
 next_node.pre_node = self
 self.next_node = next_node
 def reverse_list(self):
 new_list = self
 cur = new_list.next_node
 new_list.next_node = None
 new_list.pre_node = None
 while cur:
 node = cur.next_node
 new_list.pre_node = cur
 cur.next_node = new_list
 new_list = cur
 cur = node
 return new_list
 def print_list(self):
 cur = self
 result = []
 while cur:
 result.append(cur.value)
 cur = cur.next_node
 return result
if __name__ == "__main__":
 head = DoubleLinkedListNode(1, DoubleLinkedListNode(2, DoubleLinkedListNode(3, DoubleLinkedListNode(4, DoubleLinkedListNode(5, None)))))
 print head.print_list()
 new_head = head.reverse_list()
 print new_head.print_list()
200_success
146k22 gold badges190 silver badges479 bronze badges
asked Jan 1, 2017 at 7:20
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Firstly, the line

new_list.pre_node = None

doesn't do anything and can be removed. Secondly, your code can't handle circular double-linked lists.

Circular lists

When called on a circular double-linked list:

  • Print_list goes into an endless loop.

  • Reverse_list terminates late, giving a partially reversed list.

Your code could be easily improved to handle these issues. Preventing an endless loop requires keeping track of the identity of each node. Below, I've done this by incrementing a global variable node_count, but there are more Pythonic ways to keep a count of instances of objects.

The unique id is used to track when the cursor id matches self.id, terminating the while loops in print_list and reverse_list at the appropriate time for circular lists.

Lastly, PEP8 specifies methods within a class should be separated by a single line.

See below:

class DoubleLinkedListNode:
 def __init__(self, value, next_node):
 global node_count # added unique node id
 self.id = node_count
 node_count += 1
 self.value = value
 self.pre_node = None # removed unnecessary line
 if next_node:
 next_node.pre_node = self
 self.next_node = next_node
 def reverse_list(self):
 new_list = self
 cur = new_list.next_node
 new_list.next_node = None
 while cur:
 node = cur.next_node
 new_list.pre_node = cur
 cur.next_node = new_list
 if cur.id == self.id and not node: break # for circular lists
 new_list = cur
 cur = node
 return new_list
 def print_list(self):
 cur = self
 result = []
 while cur:
 result.append([cur.value])
 next_id = cur.next_node.id if cur.next_node else None
 if next_id == self.id: break # for circular lists
 cur = cur.next_node
 return result
if __name__ == "__main__":
 node_count = 0
 head = DoubleLinkedListNode(1, DoubleLinkedListNode(2, DoubleLinkedListNode(3, DoubleLinkedListNode(4, DoubleLinkedListNode(5, None)))))
 print(head.print_list())
 new_head = head.reverse_list()
 print(new_head.print_list())

(Bonus: For simple testing, here's my method to make the lists circular. Add it to the DoubleLinkedListNode class.)

 def circularize(self):
 cur = self
 while cur.next_node:
 cur = cur.next_node
 cur.next_node = self

Complexity

Reversing lists, double-linked or not, scales according to \$\mathcal{O}(n)\,ドル where n is the length of the list.

The only way to speed it up significantly is to make each list an object, with a Boolean direction flag. Any process moving through the list first refers to the direction flag, which either calls next_node or pre_node depending on how it's set. All that's needed to reverse the list is to reverse the direction flag.

Update:

Below is an example of how \$\mathcal{O}(1)\$ reversal of double-linked lists might be implemented using a direction flag. Points relevant to OP's code are:

  • A dedicated reverse_list method is not really necessary.

  • An __iter__ method is more useful than a dedicated print_list method (thanks ChatterOne).

  • Circular lists are also reversed neatly this way (note that __iter__ cycles endlessly for circular lists).

See below:

class DoubleLinkedListNode:
 def __init__(self, value, plus_node, dir=None):
 self.value = value
 self.minus_node = None
 if plus_node:
 plus_node.minus_node = self
 if dir == None:
 self.dir = plus_node.dir
 if dir != None: 
 self.dir = dir
 self.plus_node = plus_node
 def __iter__(self):
 self.cur = self
 return self
 def __next__(self):
 if self.cur:
 current = self.cur
 if self.cur.dir:
 if self.cur.dir[0] > 0:
 self.cur = self.cur.plus_node
 elif self.cur.dir[0] < 0:
 self.cur = self.cur.minus_node
 return current
 else:
 raise StopIteration
if __name__ == "__main__":
 tail = DoubleLinkedListNode(5, None, [1]) # [1] sets direction for all nodes in list.
 head = DoubleLinkedListNode(1, DoubleLinkedListNode(2, DoubleLinkedListNode(3, DoubleLinkedListNode(4, tail))))
 for i in head: print(i.value) # Prints node values forwards from head.
 head.dir[0] = -1 # Reverses direction for all nodes in the list.
 for i in tail: print(i.value) # Prints node values in reverse from tail.
answered Jan 1, 2017 at 10:20
\$\endgroup\$
3
  • 1
    \$\begingroup\$ You can probably expand your answer easily by adding an __iter__ and using the direction flag that you suggested to yield either the next or the previous node. \$\endgroup\$ Commented Jan 2, 2017 at 10:50
  • \$\begingroup\$ Love your code and your ideas to use direction flag, and also handle circular double linkedlist. Mark your reply as answer. \$\endgroup\$ Commented Jan 5, 2017 at 6:44
  • \$\begingroup\$ @ChatterOne, nice catch! vote up. \$\endgroup\$ Commented Jan 5, 2017 at 6:45

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.