6

I am trying to solve a linked list problem, to find the middle element in a single pass using python. Could someone please review my code and suggest the best manner to do this?

  class Node(object):
      def __init__(self, data=None, next=None):
          self.data = data
          self.next = next
      def __str__(self):
          return str(self.data)

  def print_nodes(node):
      while node:
          print node
          node = node.next

  def find_middle(node):
      while node:
          current = node
          node = node.next
          second_pointer = node.next
          next_pointer = second_pointer.next
          if next_pointer is None:
              return "Middle node is %s" % str(current)

  node1 = Node(1)
  node2 = Node(2)
  node3 = Node(3)
  node4 = Node(4)
  node5 = Node(5)

  node1.next = node2
  node2.next = node3
  node3.next = node4
  node4.next = node5

  print find_middle(node1)
metasequoia
  • 7,014
  • 5
  • 41
  • 54
James Sapam
  • 16,036
  • 12
  • 50
  • 73

12 Answers12

8

I merged all the methods for you creating, finding and printing.

class Node(object):
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next
    def __str__(self):
        return str(self.data)

def create_linked_list(n):
    """Creating linked list for the given
       size"""
    linked_list = Node(1)
    head = linked_list
    for i in range(2, n):
        head.next = Node(i)
        head = head.next
    return linked_list

def print_linked_list(node):
    """To print the linked list in forward"""
    while node:
        print '[',node,']','[ref] ->',
        node = node.next
    print '-> None'

def find_middle1(node):
    tick = False
    half = node
    while node:
        node = node.next
        if tick:
            half = half.next
        tick = not tick
    return "Middle node is %s" % str(half)

def find_middle2(node):
    list = []
    while node:
        list.append(node)
        node = node.next
    return "Middle node is %s" % str(list[len(list)/2])


node = create_linked_list(10)
print_linked_list(node)

print find_middle1(node)
print find_middle2(node)

Output:

[ 1 ] [ref] -> [ 2 ] [ref] -> [ 3 ] [ref] -> [ 4 ] [ref] -> [ 5 ] [ref] -> [ 6 ] [ref] -> [ 7 ] [ref] -> [ 8 ] [ref] -> [ 9 ] [ref] -> -> None
Middle node is 5
Middle node is 5
  • For your find_middle2 method you should use len(list)//2 to convert any float number into integer. – Adnan Jan 17 '19 at 10:24
  • 1
    @Adnanshah : He is using python2, so 11 / 2 is floor division that is 5. https://stackoverflow.com/questions/183853/what-is-the-difference-between-and-when-used-for-division Also, list = [] , shouldn't be used as list variable name. – aspiring1 Apr 14 '19 at 10:21
4

Here's on way, it's one pass, though probably not as efficient as you'd like:

def find_middle(node):
    list = []
    while node:
        list.append(node)
        node = node.next
    return list[len(list)/2]

does that work?

en_Knight
  • 5,301
  • 2
  • 26
  • 46
  • hmm, this is another interesting way without using two pointer algorithm. I will wait for some more suggestion. Thank you so much for the answer and suggestion. – James Sapam Dec 02 '13 at 03:49
  • No problem. Your algorithm does not appear to work - it always returns the third to last element in the list, I believe, not the middle element. The double pointer is a good idea, though it requires a decent amount more bookkeeping in your Linked List. Did you try implementing it that way? – en_Knight Dec 02 '13 at 03:54
  • Let me check once again, is it possible for you to edit my code ? – James Sapam Dec 02 '13 at 03:56
  • Maybe, but consider what you're code is doing - it keeps one pointer one ahead of the current, and one two ahead of the current. It's stops when the pointer two ahead is null, and returns the current; it's clearly going to return the Node two away from the end. Does that make sense? – en_Knight Dec 02 '13 at 04:02
4

You could keep two pointers, one that moves half as fast as the other.

def find_middle(node):
    tick = False
    half = node
    while node:
        node = node.next
        if (tick):
            half = half.next
        tick = not tick
    return "Middle node is %s" % str(half)
Jordan P
  • 1,499
  • 1
  • 10
  • 14
  • There were some errors in the original text. This seems to work for me in python3: http://pastebin.com/2vtaPHRq – Jordan P Dec 02 '13 at 04:04
  • Hi Jordan, now its working after changing `tick = not tick`, now i would like to ask you the logic, i din't clearly understand how it find out the half. – James Sapam Dec 02 '13 at 04:09
  • 1
    half = half.next is called once for every two times node = node.next is called. By the time node reaches the end of the list, half will be 1/2 way through the list. – Jordan P Dec 02 '13 at 04:15
  • Is this what the OP is really looking for? Seems to me this is essentially a pass and a half version, just done concurrently. – Travis Griggs Dec 02 '13 at 04:36
  • Good call. Depending on the size of the array and level of optimization (if half.next causes cache misses), this could produce 0(1.5n) runtime. – Jordan P Dec 02 '13 at 04:44
2

pseudo code for finding middle element of linked list : -

fast = head
slow = head


while(fast!=null) {



 if(fast.next!=null) {

      fast = fast.next.next
      slow = slow.next
 }

 else { 

  break
 }
}

// middle element
return slow
Vikram Bhat
  • 6,106
  • 3
  • 20
  • 19
1

All of the above answers are right but for me, this worked best:

        def middleNode(self, head: ListNode) -> ListNode:
            list=[]
            while head:
                list.append(head)
                head=head.next
            return list[floor(len(list)/2)]

Here, using floor helped me otherwise my code gave me errors.

Areeha
  • 823
  • 7
  • 11
0

OK, this is NOT a good idea. But it DOES satisfy the constraint of traversing only once. Instead of traversing once (and a secondary half traversal), it (ab)uses the stack to emulate the half traversal (backwards instead of forwards). It's not a good idea, because Python doesn't have an infinitely growable stack (I wish Python would take a cue from the Smalltalk guys on this one), so you really can only handle lists in the size of hundreds, definitely not thousands (this is Python3, btw).

First I modified your script to build bigger lists with the change of a value:

last = root = Node(1)
for i in range(2, 312):
    node = Node(i)
    last.next = node
    last = node

Since we're using the stack and recursion, we need a way to return abruptly out of a deep call stack. So we create an Exception subclass, which is really more of a "notification" than an "exception".

class FoundMiddleNode(Exception):
    def __init__(self, node):
        super().__init__()
        self.node = node

Now for the recursive function:

def probeForMiddle(node, length):
    if node.next is None: #recursion stopper
        return length
    #call recursively
    lengthToEnd = probeForMiddle(node.next, length + 1)
    #is my distance from start half of what is being returned recursively as the full length?
    if (lengthToEnd // 2) - length == 0: 
        raise FoundMiddleNode(node) #throw an exception to abort the rest of the stack walk
    return lengthToEnd

And to use it we do:

try:
    probeForMiddle(root, 0)
except FoundMiddleNode as e:
    print(e.node)

Not pretty. Not a good idea in anything approximating production code. But a decent example of (ab)using recursion and exceptions to fill the requirement of only traversing once.

Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
0

The best way to find the middle node is to have two pointers:

 P1 = root
    P2 = root
    While not p2.next == Null:
    P1 =p1.next
    P2 =P2.next.next
0
//Linked list

ll = {'A': ["data", "B"],
 'B': ["data", "C"],
 'C': ["data", "D"],
 'D': ["data", "E"],
 'E': ["data", None]}


def find_next_node(node="A"):
    return ll[node][1] if ll[node][1] else None

def find_mid_node(head="A"):
    slow = head
    fast = head
    while(fast!=None):
        for i in range(2):
            if find_next_node(fast):
                fast = find_next_node(node=fast)
            else:
                return slow

        for j in range(1):
            slow = find_next_node(node=slow)


print (find_mid_node())
anushjay
  • 95
  • 2
  • 8
0

You can write a smaller code for finding the middle node. Showing the snippet below:

   def find_middle(node):
      half = node
      while node and node.next is not None:
         node = node.next.next
         half = half.next
      return half

Few important points:

  1. Logic will remain the same of keeping two pointers, one fast and other slow.
  2. Write a Linked list class to create a linked list with the help of loop rather than explicitly setting next pointers.
Shagun Pruthi
  • 1,813
  • 15
  • 19
0

This is very similar to what James and Jordan have posted already, it's just little simpler in terms of what it does and I've added explanation as comments to what's actually doing

class Node:
  def __init__(self, data=None, next=None):
    self.data = data
    self.next = next 
# loop through the items and check for next items using two pointers (fast and slow)
# set the speed for fast twice higher than the slow pointer so when when fast reaches
# the end the slow would be in the middle
def find_middle(head ):
    fast = slow = head 
    #slow = head
    while fast.next != None and fast.next.next != None:
        fast = fast.next.next
        slow = slow.next

    # slow is now at the middle :)
    print (slow.data  )


#setup data
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)
node5 = Node(5)

node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5

find_middle(node1)
grepit
  • 21,260
  • 6
  • 105
  • 81
0

I might be little late but this works best for me. Writing complete code for creating, finding the midpoint & printing the linked list.

class Node:
    '''
    Class to create Node and next part of the linked list
    '''
    def __init__(self,data):
        self.data = data
        self.next = None
        

def createLL(arr):
    '''
    linked list creation
    '''
    head = Node(arr[0])
    tail = head
    for data in arr[1:]:
        tail.next = Node(data)
        tail = tail.next
        
    return head

def midPointOfLL(head):
    '''
    Here, i am using two pointers slow and fast, So idea is fast will be 2 times faster than slow pointer
    and when fast reaches the end of the linked list slow pointer will be exactly in the middle.
    '''

    slow = head
    fast = head
    if head is not None:
        while (fast.next is not None) and (fast.next.next is not None):
            slow = slow.next
            fast = fast.next.next
        
    return slow

def printLL(head):
    curr = head
    while curr :
        print(curr.data,end = "-->")
        curr = curr.next
    print('None')


arr  = list(map(int,input().split())) 
head = createLL(arr)       
midPoint = midPointOfLL(head)
print(midPoint.data)
printLL(head)
-1
list=['ok','go','no','poi','duo','ok','go','nah']
b=0
b=int(len(list)/2) #print the middle element change .5 values 
print(list[b])
if((len(list)%2)==0): #if list is of even number print both middle values
    print(list[b+1])

when i searched this question i was looking for something like this so i can get both middle values when entered even num of elements

there must be a better way to do it i just start coding from like 3,4 day

Muhammad Yusuf
  • 563
  • 4
  • 20