import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
added = [5, 2, 7, 3] # the numbers, in the order they are added
gap = 0.15 # gap between boxes, and between boxes and walls
length = len(added) + (len(added) - 1) * gap # length of a line of boxes
middle = length / 2 # the queue and the priority queue are centered on the stack
arrow = dict(arrowstyle="-|>", color="black", mutation_scale=12, shrinkA=0, shrinkB=0)
wall = dict(color="black", linewidth=1.2)
label_style = dict(va="center", fontsize=11)
title_style = dict(ha="center", va="center", fontsize=12)
def draw_box(ax, x, y, number, gray):
"""Draw a unit box with the number and its lower-left corner at (x, y)."""
ax.add_patch(
mpatches.Rectangle(
(x, y),
1,
1,
facecolor="lightgray" if gray else "white",
edgecolor="black",
linewidth=1,
)
)
ax.text(x + 0.5, y + 0.5, str(number), ha="center", va="center", fontsize=12)
def draw_arrow(ax, tail, head):
"""Draw an arrow from tail to head."""
ax.annotate("", xy=head, xytext=tail, arrowprops=arrow)
# Computer Modern fonts, as in LaTeX. The last setting avoids a warning about cmr10.
latex_style = {
"font.family": "cmr10",
"mathtext.fontset": "cm",
"axes.formatter.use_mathtext": True,
}
with plt.rc_context(latex_style):
fig, ax = plt.subplots(figsize=(6.5, 2.7))
# Queue: a number enters at the right end and leaves at the left end
x0, y0 = 1.7, middle - 0.5
for j, number in enumerate(added):
draw_box(ax, x0 + j * (1 + gap), y0, number, gray=(j == 0))
left, right = x0 - gap - 0.25, x0 + length + gap + 0.25 # ends of the walls
for y in (y0 - gap, y0 + 1 + gap):
ax.plot([left, right], [y, y], **wall)
draw_arrow(ax, (left - 0.3, middle), (left - 1.6, middle))
ax.text(left - 0.95, middle + 0.4, "dequeue", ha="center", **label_style)
draw_arrow(ax, (right + 1.6, middle), (right + 0.3, middle))
ax.text(right + 0.95, middle + 0.4, "enqueue", ha="center", **label_style)
ax.text(x0 + length / 2, -0.75, "Queue", **title_style)
# Stack: a number enters and leaves at the top
x0 = right + 3.2
for j, number in enumerate(added):
draw_box(ax, x0, j * (1 + gap), number, gray=(j == len(added) - 1))
left, right, top = x0 - gap, x0 + 1 + gap, length + 0.25 # walls
ax.plot([left, left, right, right], [top, -gap, -gap, top], **wall)
draw_arrow(ax, (x0 + 0.27, top + 1.2), (x0 + 0.27, top + 0.15))
ax.text(x0 + 0.1, top + 0.7, "push", ha="right", **label_style)
draw_arrow(ax, (x0 + 0.73, top + 0.15), (x0 + 0.73, top + 1.2))
ax.text(x0 + 0.9, top + 0.7, "pop", ha="left", **label_style)
ax.text(x0 + 0.5, -0.75, "Stack", **title_style)
# Priority queue: the numbers are in no particular order, and the smallest leaves
step = 1 + 3 * gap # distance between neighboring boxes
size = 1 + step # width and height of the 2 x 2 grid of boxes
x0, y0 = x0 + 3.6, middle - size / 2
for j, number in enumerate(added):
row, col = divmod(j, 2) # filled row by row from the top left
x, y = x0 + col * step, y0 + (1 - row) * step
draw_box(ax, x, y, number, gray=(number == min(added)))
pad = 0.3 # gap between the grid and the wall
ax.add_patch(
mpatches.FancyBboxPatch(
(x0 - pad, y0 - pad),
size + 2 * pad,
size + 2 * pad,
boxstyle="round,pad=0,rounding_size=0.4",
facecolor="none",
edgecolor="black",
linewidth=1.2,
)
)
top = y0 + size + pad # top of the wall
draw_arrow(ax, (x0 + 0.5, top + 1.15), (x0 + 0.5, top + 0.1))
ax.text(x0 + 0.33, top + 0.65, "insert", ha="right", **label_style)
x = x0 + step + 0.5 # above the smallest number, 2, at the top right
draw_arrow(ax, (x, y0 + size), (x, top + 1.15))
ax.text(x + 0.17, top + 0.65, "extract", ha="left", **label_style)
ax.text(x0 + size / 2, -0.75, "Priority queue", **title_style)
ax.set(xlim=(-0.75, x0 + size + 1.3), ylim=(-1.15, length + 1.7), aspect="equal")
ax.axis("off")
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
plt.show()