示例#1
0
    def top_view(self, aggregate_fn=_default_printfn, **kwargs):
        if not self.root:
            return

        q = Queue()
        min_width = 0
        max_width = 0
        q.enqueue((0, self.root))

        # Top-view begins with the root node
        aggregate_fn(kwargs, self.root.value)
        while q.length() != 0:
            width, node = q.dequeue()

            if width < min_width:
                min_width = width
                aggregate_fn(kwargs, node.value)
            elif width > max_width:
                max_width = width
                aggregate_fn(kwargs, node.value)

            # NOTE: width 0, is root, would already be filled in at root,
            # and in a top-view is not going to replaced

            q.enqueue((width - 1, node.left)) if node.left else None
            q.enqueue((width + 1, node.right)) if node.right else None
示例#2
0
    def width(self):
        if not self.root:
            return 0

        q = Queue()
        q.enqueue((0, self.root))

        curr_level = 0
        curr_level_nodes = 0
        max_width = 0
        while q.length() != 0:
            level, node = q.dequeue()

            q.enqueue((level + 1, node.left)) if node.left else None
            q.enqueue((level + 1, node.right)) if node.right else None

            # Start of a new level
            # Check if number of nodes at previous level > mqx nodes at any level
            if level != curr_level:
                if curr_level_nodes > max_width:
                    max_width = curr_level_nodes

                # start counting nodes for the current level
                curr_level_nodes = 0

            curr_level_nodes += 1
            curr_level = level

        # Check last level
        if curr_level_nodes > max_width:
            max_width = curr_level_nodes
        return max_width
示例#3
0
	def all_shortest_paths_by_length(self, v1, v2, aggregate_fn=None, *args, **kwargs):
		q = Queue()
		q.enqueue((v1, [], 0))

		# Maximum number of vertices wont exceed the number of vertices
		# if there are cycles, they'd not be included more than once in the path
		shortest_path_level = self.vertices

		while q:
			curr_vertex, curr_prefix, level = q.dequeue()

			# Found all the shortest paths
			if level > shortest_path_level:
				return

			# found destination vertex,
			# record this path as one of the paths
			if curr_vertex == v2:
				path = curr_prefix + [v2]
				shortest_path_level = level
				aggregate_fn(path, *args, **kwargs)

			for v,_ in self._adjlists[curr_vertex]:
				# visited[] traking doesn't yield well to BFS
				# when extracting all paths
				# Instead just check if we don't add a vertex already
				# in the current path so we don't loop endlessly
				# if there's a cycle / its an undirected graph
				if v not in curr_prefix:
					q.enqueue((v, curr_prefix + [curr_vertex], level+1))
class QueueMin(object):
    def __init__(self):
        self.q = Queue()
        self.min = None

    # All queue items are always in Q,
    def __len__(self):
        return self.q.size

    def __repr__(self):
        return "{%r}/Min: %r" % (self.q, self.min)

    def __str__(self):
        return str(self.q)

    def minimum(self):
        return self.min

    # Add to Q and return
    def enqueue(self, x):
        self.q.enqueue(x)

        # update min
        # if min is None => this is the first item to be enqueued
        self.min = min(x, self.min) if self.min else x

    # Dequeue() from Q
    # if popped item was minima, Re-enqueue all items while calculating new minima
    def dequeue(self):
        # Helper function to re-enqueue all items from Queue, Q back into itself
        # while updating new minima
        def reenqueue(q):
            minima = q.front()
            for i in range(len(q)):
                x = q.dequeue()
                q.enqueue(x)
                minima = min(x, minima)
            return minima

        # SLL raises UnderFlowError if Q is empty
        item = self.q.dequeue()

        # We just dequeued the last item from the queue
        # restore min to be None
        if not self.q:
            self.min = None
        elif item == self.min:
            # Update min if item == minima
            self.min = reenqueue(self.q)

        return item

    # Return the front of Q
    def front(self):
        return self.q.front()

    # Return the back of Q
    def back(self):
        return self.q.back()
示例#5
0
    def levelorder_traversal(self, aggregate_fn=_default_printfn, **kwargs):
        if not self.root:
            return

        q = Queue()
        q.enqueue(self.root)
        while q.length() != 0:
            tmp = q.dequeue()
            aggregate_fn(kwargs, tmp.value)

            q.enqueue(tmp.left) if tmp.left else None
            q.enqueue(tmp.right) if tmp.right else None
class Stack(object):
	def __init__(self):
		self.q1 = Queue()
		self.q2 = Queue()


	# peek what's at the top of the stack
	# New items are always added to Q1's end
	# Stack's top would be at the back of Q1
	def top(self):
		return self.q1.back()


	def __len__(self):
		return self.q1.size


	# Just add to Q1 and return
	def push(self, x):
		self.q1.enqueue(x)


	# Move (n-1) items from q1 to q2
	# dequeue remaining item from q1 to return
	# swap q1, q2
	def pop(self):
		# Helper function to move 'n' items from queue a to queue b
		def move(a, b, n):
			for i in range(n):
				b.enqueue(a.dequeue())

		# Move (n-1) items from q1 to q2
		move(self.q1, self.q2, self.q1.size-1)

		# Swap Q1, Q2
		self.q1, self.q2 = self.q2, self.q1

		# Dequeue and return the last item from what was q1 earlier, and now q2
		return self.q2.dequeue()



	def __str__(self):
		slist = []
		for x in self.q1:
			slist.insert(0, str(x))

		return '[%d]: ' %(self.__len__()) + ' '.join(slist) 


	def __repr__(self):
		return "{%r , %r}" %(self.q1, self.q2)
示例#7
0
	def bfs(adjacency_lists, startvertex, visited, aggregate_fn, *args, **kwargs):
		neighbors = Queue()
		neighbors.enqueue(startvertex)
		visited[startvertex] = True
		while neighbors:
			v = neighbors.dequeue()
			aggregate_fn(v, *args, **kwargs)

			# enqueue all vertices that are neighbors of v
			for n,_ in adjacency_lists[v]:
				if not visited[n]:
					visited[n] = True
					neighbors.enqueue(n)
	def bfs_i(adjacency_matrix, startvertex, visited, aggregate_fn, *args, **kwargs):
		neighbors = Queue()
		neighbors.enqueue(startvertex)
		while neighbors:
			v = neighbors.dequeue()
			if not visited[v]:
				visited[v] = True
				aggregate_fn(v, *args, **kwargs)

				# enqueue all vertices that are neighbors of v
				for n in xrange(len(visited)):
					if adjacency_matrix[v][n] is not None and not visited[n]:
						neighbors.enqueue(n)
示例#9
0
	def bfs_i(adjacency_lists, startvertex, visited, aggregate_fn, *args, **kwargs):
		neighbors = Queue()
		neighbors.enqueue(startvertex)
		while neighbors:
			v = neighbors.dequeue()
			if not visited[v]:
				visited[v] = True
				aggregate_fn(v, *args, **kwargs)

				# enqueue all vertices that are neighbors of v
				for n,_ in adjacency_lists[v]:
					if not visited[n]:
						neighbors.enqueue(n)
示例#10
0
	def bfs(adjacency_matrix, startvertex, visited, aggregate_fn, *args, **kwargs):
		neighbors = Queue()

		visited[startvertex] = True
		neighbors.enqueue(startvertex)
		while neighbors:
			v = neighbors.dequeue()
			aggregate_fn(v, *args, **kwargs)

			# enqueue all vertices that are neighbors of v
			for n in xrange(len(visited)):
				if adjacency_matrix[v][n] is not None and not visited[n]:
					visited[n] = True
					neighbors.enqueue(n)
class Stack(object):
	def __init__(self):
		self.q = Queue()


	# peek what's at the top of the stack
	# New items are always added to Q's end
	# Stack's top would be at the back of Q
	def top(self):
		return self.q.back()


	def __len__(self):
		return self.q.size


	# Just add to Q and return
	def push(self, x):
		self.q.enqueue(x)


	# re-enqueue (n-1) items from Q, and re-enqueue back to Q
	def pop(self):
		# Helper function to reenqueue 'n' items from queue a back into it
		def reenqueue(a, n):
			for i in range(n):
				a.enqueue(a.dequeue())

		# reenqueue (n-1) items from q
		reenqueue(self.q, self.q.size-1)
		# Dequeue from the front of q
		return self.q.dequeue()



	# Actual stack order would be the q in reverse order
	def __str__(self):
		slist = []
		for x in self.q:
			slist.insert(0, str(x))

		return '[%d]: ' %(self.__len__())  +  ' '.join(slist)


	def __repr__(self):
		return "%r" %(self.q)
示例#12
0
    def top_view_LR(self, aggregate_fn=_default_printfn, **kwargs):
        if not self.root:
            return

        # Queue to help with level-order traversal
        q = Queue()

        # Store L/R width of a node and its value if it is visible in the top-view, in a sorted list
        # ordered by the width
        # at the end of the level-order traversal the list would contain
        # [(-max_left_width, node value), ...., (0, root.value), ... (max_right_width, node value)]
        slist = SLL()

        # pairs a(w1, node_val1) vs b(w2, node_val2) are valued against each other
        # depending on w1 vs w2 (where w1 and w2 are widths, so slist is kept sorted by the nodes' L/R widths)
        pair_comparator = lambda (w1, node_val1), (w2, node_val2): cmp(w1, w2)

        min_width = 0
        max_width = 0
        q.enqueue((0, self.root))

        # Top-view begins with the root node
        slist.place((0, self.root.value))
        while q.length() != 0:
            width, node = q.dequeue()

            if width < min_width:
                min_width = width
                slist.place((width, node.value))
            elif width > max_width:
                max_width = width
                slist.place((width, node.value))

            # NOTE: width 0, is root, would already be filled in at root,
            # and in a top-view is not going to replaced

            q.enqueue((width - 1, node.left)) if node.left else None
            q.enqueue((width + 1, node.right)) if node.right else None

        # At the end of the level-order traversal,
        # slist is ordered by width, so
        # (-max_left_width, ..., 0, ..., max_right_width)
        # Just retrieve the SLL L-R for the top view (L-R)
        while slist.size != 0:
            width, item = slist.pop_front()
            aggregate_fn(kwargs, item)
示例#13
0
class Stack(object):
    def __init__(self):
        self.q1 = Queue()
        self.q2 = Queue()

    # peek what's at the top of the stack
    # New items are always added to Q1's end
    # Stack's top would be at the back of Q1
    def top(self):
        return self.q1.back()

    def __len__(self):
        return self.q1.size

    # Just add to Q1 and return
    def push(self, x):
        self.q1.enqueue(x)

    # Move (n-1) items from q1 to q2
    # dequeue remaining item from q1 to return
    # swap q1, q2
    def pop(self):
        # Helper function to move 'n' items from queue a to queue b
        def move(a, b, n):
            for i in range(n):
                b.enqueue(a.dequeue())

        # Move (n-1) items from q1 to q2
        move(self.q1, self.q2, self.q1.size - 1)

        # Swap Q1, Q2
        self.q1, self.q2 = self.q2, self.q1

        # Dequeue and return the last item from what was q1 earlier, and now q2
        return self.q2.dequeue()

    def __str__(self):
        slist = []
        for x in self.q1:
            slist.insert(0, str(x))

        return '[%d]: ' % (self.__len__()) + ' '.join(slist)

    def __repr__(self):
        return "{%r , %r}" % (self.q1, self.q2)
class Stack(object):
	def __init__(self):
		self.q1 = Queue()
		self.q2 = Queue()


	# peek what's at the top of the stack
	# Stack-order is preserved in Q1
	# Stack's top would be at the front of Q1
	def top(self):
		return self.q1.front()


	def __len__(self):
		return self.q1.size


	# Add x to Q2
	# Move everything from Q1 to Q2, so Q2 preserves stack-order
	# swap Q1, Q2
	def push(self, x):
		# Helper function to move all items from queue a to queue b
		def move(a, b):
			while a:
				b.enqueue(a.dequeue())

		self.q2.enqueue(x)
		# Move items from q1 to q2
		move(self.q1, self.q2)
		# Swap Q1, Q2
		self.q1, self.q2 = self.q2, self.q1


	# Q1 has items in stack-order
	# Just dequeue from Q1 and return
	def pop(self):
		return self.q1.dequeue()


	def __str__(self):
		return str(self.q1)


	def __repr__(self):
		return "{%r , %r}" %(self.q1, self.q2)
示例#15
0
    def left_view(self, aggregate_fn=_default_printfn, **kwargs):
        if not self.root:
            return

        q = Queue()
        levels_done = None
        q.enqueue((0, self.root))
        while q.length() != 0:
            curr_level, node = q.dequeue()

            # Nothing has been printed in this level so far
            # NOTE: (None < 0) in python
            if curr_level > levels_done:
                aggregate_fn(kwargs, node.value)
                levels_done = curr_level

            q.enqueue((curr_level + 1, node.left)) if node.left else None
            q.enqueue((curr_level + 1, node.right)) if node.right else None
class Stack(object):
	def __init__(self):
		self.q = Queue()


	# peek what's at the top of the stack
	# Stack-order is preserved in Q
	# Stack's top would be at the front of Q
	def top(self):
		return self.q.front()


	def __len__(self):
		return self.q.size


	# Enqueue x into Q
	# re-enqueue (n-1) items from Q, and re-enqueue back to Q
	def push(self, x):
		# Helper function to reenqueue 'n' items from queue a back into it
		def reenqueue(a, n):
			for i in range(n):
				a.enqueue(a.dequeue())

		self.q.enqueue(x)
		# reenqueue (n-1) items from q
		reenqueue(self.q, self.q.size-1)



	# Just dequeue from Q as it preserves stack-order
	def pop(self):
		# Dequeue from the front of q
		return self.q.dequeue()



	# Q preserves stack-order, Just return Q as-is
	def __str__(self):
		return str(self.q)


	def __repr__(self):
		return "%r" %(self.q)
示例#17
0
    def span(self):
        if not self.root:
            return 0

        q = Queue()
        min_width = 0
        max_width = 0
        q.enqueue((0, self.root))

        while q.length() != 0:
            width, node = q.dequeue()

            if width < min_width:
                min_width = width
            elif width > max_width:
                max_width = width

            q.enqueue((width - 1, node.left)) if node.left else None
            q.enqueue((width + 1, node.right)) if node.right else None

        return max_width - min_width
示例#18
0
    def bottom_view(self, aggregate_fn=_default_printfn, **kwargs):
        if not self.root:
            return

        q = Queue()

        # A hash table that stores a map of L/R width to a node's item
        # We traverse the tree in level-order, and keep replacing slist[width] if we find a new node with the same width
        # At the end of the traversal, every slist[width] from {-max_left_width, ...,  0, ..., max_right_width} will contain the
        # node items that would be visible from a bottom view of the binary tree
        slist = {}

        min_width = 0
        max_width = 0
        q.enqueue((0, self.root))

        # Star with placing root at width 0
        slist[0] = self.root.value
        while q.length() != 0:
            width, node = q.dequeue()

            # As we traverse level-by-level, keep replacing
            # item at L/R width
            slist[width] = node.value

            # Track max_left_width, max_right_width
            # so we don't need to sort the hash table slist,
            # Just retrieve slist[{-max_left_width, ...,  0, ..., max_right_width}]
            if width < min_width:
                min_width = width
            elif width > max_width:
                max_width = width

            q.enqueue((width - 1, node.left)) if node.left else None
            q.enqueue((width + 1, node.right)) if node.right else None

        # At the end of the level-order traversal,
        # Just 'aggregate' slist[{-max_left_width, ...,  0, ..., max_right_width}]
        for i in range(min_width, max_width + 1):
            aggregate_fn(kwargs, slist[i])
class Stack(object):
    def __init__(self):
        self.q = Queue()

    # peek what's at the top of the stack
    # New items are always added to Q's end
    # Stack's top would be at the back of Q
    def top(self):
        return self.q.back()

    def __len__(self):
        return self.q.size

    # Just add to Q and return
    def push(self, x):
        self.q.enqueue(x)

    # re-enqueue (n-1) items from Q, and re-enqueue back to Q
    def pop(self):
        # Helper function to reenqueue 'n' items from queue a back into it
        def reenqueue(a, n):
            for i in range(n):
                a.enqueue(a.dequeue())

        # reenqueue (n-1) items from q
        reenqueue(self.q, self.q.size - 1)
        # Dequeue from the front of q
        return self.q.dequeue()

    # Actual stack order would be the q in reverse order
    def __str__(self):
        slist = []
        for x in self.q:
            slist.insert(0, str(x))

        return '[%d]: ' % (self.__len__()) + ' '.join(slist)

    def __repr__(self):
        return "%r" % (self.q)
示例#20
0
class Stack(object):
    def __init__(self):
        self.q1 = Queue()
        self.q2 = Queue()

    # peek what's at the top of the stack
    # Stack-order is preserved in Q1
    # Stack's top would be at the front of Q1
    def top(self):
        return self.q1.front()

    def __len__(self):
        return self.q1.size

    # Add x to Q2
    # Move everything from Q1 to Q2, so Q2 preserves stack-order
    # swap Q1, Q2
    def push(self, x):
        # Helper function to move all items from queue a to queue b
        def move(a, b):
            while a:
                b.enqueue(a.dequeue())

        self.q2.enqueue(x)
        # Move items from q1 to q2
        move(self.q1, self.q2)
        # Swap Q1, Q2
        self.q1, self.q2 = self.q2, self.q1

    # Q1 has items in stack-order
    # Just dequeue from Q1 and return
    def pop(self):
        return self.q1.dequeue()

    def __str__(self):
        return str(self.q1)

    def __repr__(self):
        return "{%r , %r}" % (self.q1, self.q2)
示例#21
0
	def paths_2(self, v1, v2, aggregate_fn=None, *args, **kwargs):
		if not aggregate_fn:
			aggregate_fn = Graph._default_printfn

		q = Queue()
		q.enqueue((v1, []))

		while q:
			curr_vertex, curr_prefix = q.dequeue()

			# found destination vertex,
			# record this path as one of the paths
			if curr_vertex == v2:
				aggregate_fn(curr_prefix + [v2], *args, **kwargs)

			for v,_ in self._adjlists[curr_vertex]:
				# visited[] traking doesn't yield well to BFS
				# when extracting all paths
				# Instead just check if we don't add a vertex already
				# in the current path so we don't loop endlessly
				# if there's a cycle / its an undirected graph
				if v not in curr_prefix:
					q.enqueue((v, curr_prefix + [curr_vertex]))
示例#22
0
	def shortest_path_by_length(self, v1, v2):
		q = Queue()
		retrace = {}

		q.enqueue(v1)
		retrace[v1] = None

		def retrace_path(retrace):
			path = []
			v = v2
			while v is not None:
				path.insert(0, v)
				v = retrace[v]

			return path


		# Start a BFS traversal
		while q:
			curr_vertex = q.dequeue()

			# found destination vertex,
			# retrace from last vertex using the mapping all the way to v1
			if curr_vertex == v2:
				return retrace_path(retrace)

			for v,_ in self._adjlists[curr_vertex]:
				# visited[] traking doesn't yield well to BFS
				# when extracting all paths
				# Instead just check if we don't add a vertex already
				# in the current path so we don't loop endlessly
				# if there's a cycle / its an undirected graph
				if not retrace.has_key(v):
					q.enqueue(v)
					retrace[v] = curr_vertex

		return []
示例#23
0
    def right_view(self, aggregate_fn=_default_printfn, **kwargs):
        if not self.root:
            return

        q = Queue()
        q.enqueue((0, self.root))
        while q.length() != 0:
            curr_level, node = q.dequeue()

            q.enqueue((curr_level + 1, node.left)) if node.left else None
            q.enqueue((curr_level + 1, node.right)) if node.right else None

            # peek next node in the queue to see if this is the last node in the current level
            # if yes, print it
            try:
                (level, next_entry) = q.front()
            except UnderFlowError:
                (level, next_entry) = (None, None)

            # Queue is either empty, in which case this is the rightmost node in the last level
            # *OR*, next entry in the queue is for the level after this one, so this is the rightmost in the current level
            # In both cases, current node would be visible in a right-view.
            if (not next_entry) or (level > curr_level):
                aggregate_fn(kwargs, node.value)
class Stack(object):
    def __init__(self):
        self.q = Queue()

    # peek what's at the top of the stack
    # Stack-order is preserved in Q
    # Stack's top would be at the front of Q
    def top(self):
        return self.q.front()

    def __len__(self):
        return self.q.size

    # Enqueue x into Q
    # re-enqueue (n-1) items from Q, and re-enqueue back to Q
    def push(self, x):
        # Helper function to reenqueue 'n' items from queue a back into it
        def reenqueue(a, n):
            for i in range(n):
                a.enqueue(a.dequeue())

        self.q.enqueue(x)
        # reenqueue (n-1) items from q
        reenqueue(self.q, self.q.size - 1)

    # Just dequeue from Q as it preserves stack-order
    def pop(self):
        # Dequeue from the front of q
        return self.q.dequeue()

    # Q preserves stack-order, Just return Q as-is
    def __str__(self):
        return str(self.q)

    def __repr__(self):
        return "%r" % (self.q)
class QueueMin(object):
	def __init__(self):
		self.q = Queue()
		self.min = None


	# All queue items are always in Q, 
	def __len__(self):
		return self.q.size


	def __repr__(self):
		return "{%r}/Min: %r" %(self.q, self.min)


	def __str__(self):
		return str(self.q)


	def minimum(self):
		return self.min


	# Add to Q and return
	def enqueue(self, x):
		self.q.enqueue(x)

		# update min
		# if min is None => this is the first item to be enqueued
		self.min = min(x, self.min) if self.min else x


	# Dequeue() from Q 
	# if popped item was minima, Re-enqueue all items while calculating new minima
	def dequeue(self):
		# Helper function to re-enqueue all items from Queue, Q back into itself
		# while updating new minima
		def reenqueue(q):
			minima = q.front()
			for i in range(len(q)):
				x = q.dequeue()
				q.enqueue(x)
				minima = min(x, minima)
			return minima

		# SLL raises UnderFlowError if Q is empty
		item = self.q.dequeue()

		# We just dequeued the last item from the queue
		# restore min to be None
		if not self.q:
			self.min = None
		elif item == self.min:
			# Update min if item == minima
			self.min = reenqueue(self.q)

		return item


	# Return the front of Q
	def front(self):
		return self.q.front()


	# Return the back of Q
	def back(self):
		return self.q.back()
示例#26
0
class QueueMin(object):
	def __init__(self):
		self.q1 = Queue()
		self.dq2 = Deque()
		self.min = None


	# All queue items are always in Q1, 
	# DQ2 is merely used to store relative order of minimas
	def __len__(self):
		return self.q1.size


	def __repr__(self):
		return "{%r , %r}/Min: %r" %(self.q1, self.dq2, self.min)


	def __str__(self):
		return str(self.q1)


	def minimum(self):
		return self.min


	# Add item to Q1
	def enqueue(self, x):
		self.q1.enqueue(x)

		# update min
		# if min is None => this is the first item to be enqueued
		self.min = min(x, self.min) if self.min else x

		# Update DQ2 to maintain the order of minimas, 
		# so when a minima is removed, the next minima can be immediately retrieved
		# remove everything from the back of DQ2 as long as it is > item x
		while self.dq2 and self.dq2.back() > x:
			self.dq2.pop_back()

		# Add item x to DQ2
		self.dq2.push_back(x)



	# Dequeue() from Q1
	# DQ2 stores relative order of minimas for the current queue items
	# if popped item was minima, update minima to DQ2 new head()
	def dequeue(self):
		# SLL raises UnderFlowError if Q2 is empty
		item = self.q1.dequeue()

		# item removed was the current minima, remove it from DQ2 as well
		# item would be found in the head of DQ2
		if item == self.min:
			self.dq2.pop_front()
			# Also, update new min which would be the new head of DQ2 after removing the current minima
			# NOTE: self.min should be None, if dq2 is empty - ie, we just removed the last item in the Queue
			self.min = self.dq2.front() if self.dq2 else None

		return item


	# Return the front of Q1
	def front(self):
		return self.q1.front()


	# Return the back of Q1
	def back(self):
		return self.q1.back()
示例#27
0
class QueueMin(object):
    def __init__(self):
        self.q1 = Queue()
        self.q2 = Queue()
        self.min = None

    # All queue items are always in Q1, Q2 is merely used to move queue elements and update minima
    # after which Q2 is renamed Q1
    def __len__(self):
        return self.q1.size

    def __repr__(self):
        return "{%r , %r}/Min: %r" % (self.q1, self.q2, self.min)

    def __str__(self):
        return str(self.q1)

    def minimum(self):
        return self.min

    # Add to Q1 and return
    def enqueue(self, x):
        self.q1.enqueue(x)

        # update min
        # if min is None => this is the first item to be enqueued
        self.min = min(x, self.min) if self.min else x

    # Dequeue() from Q2
    # if popped item was minima, use Q2 to move all items while calculating new minima
    def dequeue(self):
        # Helper function to move all items from Queue, A to Queue, B
        # while updating new minima
        def move(a, b):
            minima = a.front()
            while a:
                x = a.dequeue()
                b.enqueue(x)
                minima = min(x, minima)
            return minima

        # SLL raises UnderFlowError if Q2 is empty
        item = self.q1.dequeue()

        # We just dequeued the last item from the queue
        # restore min to be None
        if not self.q1:
            self.min = None
        elif item == self.min:
            # Update min if item == minima
            self.min = move(self.q1, self.q2)
            # swap Q1, Q2
            self.q1, self.q2 = self.q2, self.q1

        return item

    # Return the front of Q1
    def front(self):
        return self.q1.front()

    # Return the back of Q1
    def back(self):
        return self.q1.back()