You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迭代式快速排序Matplotlib动画报错修复求助

修复迭代式快速排序Matplotlib动画的TypeError问题

我想实现一个带Matplotlib动画演示的迭代式快速排序算法,用来展示排序进度。但用matplotlib.animation实现时,程序在首次排序并设置新基准点后停止,抛出错误:

for rect, val in zip(rects, A):
TypeError: zip argument #2 must support iteration

原排序函数代码

def partition(a, l, r):
    i = (l - 1)
    x = a[r]

    for j in range(l, r):
        if a[j] <= x:
            # increment index of smaller element
            i = i + 1
            a[i], a[j] = a[j], a[i]

    a[i + 1], a[r] = a[r], a[i + 1]
    return (i + 1)


def quickSortIterative(a, l, r):
    # Create an auxiliary stack
    size = r - l + 1
    stack = [0] * (size)

    # initialize top of stack
    top = -1

    # push initial values of l and h to stack
    top = top + 1
    stack[top] = l
    top = top + 1
    stack[top] = r

    # Keep popping from stack while is not empty
    while top >= 0:

        # Pop h and l
        r = stack[top]
        top = top - 1
        l = stack[top]
        top = top - 1

        # Set pivot element at its correct position in
        # sorted array
        if l >= r:
            return
        x = a[l]
        j = l
        for i in range(l + 1, r + 1):
            if a[i] <= x:
                j += 1
                a[j], a[i] = a[i], a[j]
            yield a
        a[l], a[j] = a[j], a[l]
        yield a
        # If there are elements on left side of pivot,
        # then push left side to stack
        for i in a:
            if i - 1 > l:
                top = top + 1
                stack[top] = l
                top = top + 1
                stack[top] = i - 1
                yield stack[top]

            # If there are elements on right side of pivot,
            # then push right side to stack

            if i + 1 < r:
                top = top + 1
                stack[top] = i + 1
                top = top + 1
                stack[top] = r
                yield stack[top]

原绘图代码

def draw2(n=None):
    n = int(n)
    a = [x + 1 for x in range(n)]
    random.shuffle(a)
    datasetName = 'Random'

    # generator object returned by the function
    generator = quickSortIterative(a, 0, n - 1)
    algoName = 'Quick Sort2'

    # style of the chart
    plt.style.use('fivethirtyeight')

    fig, ax = plt.subplots()

    # bar container
    bar_rects = ax.bar(range(len(a)), a, align="edge",
                       color='Blue')

    # setting the limits of x and y axes
    ax.set_xlim(0, len(a))
    ax.set_ylim(0, int(1.1 * len(a)))
    ax.set_title("ALGORITHM : " + algoName + "\n" + "DATA SET : " +
                 datasetName)

    text = ax.text(0.01, 0.95, "", transform=ax.transAxes, color="#E4365D")
    iteration2 = [0]

    def animate(A, rects, iteration2):
        for rect, val in zip(rects, A):
            # setting the size of each bar equal to the value of the elements
            rect.set_height(val)
        iteration2[0] += 1
        text.set_text("iterations : {}".format(iteration2[0]))

    # call animate function repeatedly
    anim = FuncAnimation(fig, func=animate,
                         fargs=(bar_rects, iteration2), frames=generator, interval=50,
                         repeat=False)
    plt.show()

错误原因

你的quickSortIterative生成器函数在后续步骤中yield了单个整数(栈的元素),而非排序过程中的数组a。FuncAnimation会将生成器的每个输出作为animate的第一个参数,当传入整数时,zip(rects, A)必然报错——因为整数不可迭代。

具体问题出在这段逻辑:

for i in a:
    if i - 1 > l:
        top = top + 1
        stack[top] = l
        top = top + 1
        stack[top] = i - 1
        yield stack[top]  # 此处yield单个整数,而非数组

    if i + 1 < r:
        top = top + 1
        stack[top] = i + 1
        top = top + 1
        stack[top] = r
        yield stack[top]  # 同样问题

这段代码混淆了数组元素和区间索引,且错误地yield栈元素,完全偏离了迭代式快排的逻辑。


修复方案

1. 将partition改为生成器,捕获每一次交换

为了展示分区过程中的每一步交换,让partition每次交换后yield数组副本(避免引用修改影响历史帧):

def partition(a, l, r):
    i = l - 1
    pivot = a[r]

    for j in range(l, r):
        if a[j] <= pivot:
            i += 1
            a[i], a[j] = a[j], a[i]
            yield a.copy()  # 每次交换后yield当前数组状态

    a[i + 1], a[r] = a[r], a[i + 1]
    yield a.copy()
    return i + 1

2. 修正迭代式快排的栈逻辑

迭代式快排的栈应存储待处理的区间(左右边界),而非数组元素。修改quickSortIterative,遍历分区生成器的每一步输出:

def quickSortIterative(a, l, r):
    stack = []
    stack.append((l, r))

    while stack:
        current_l, current_r = stack.pop()
        if current_l >= current_r:
            continue
        
        # 遍历分区过程的每一步
        partition_gen = partition(a, current_l, current_r)
        try:
            while True:
                frame = next(partition_gen)
                yield frame
        except StopIteration as e:
            pivot_idx = e.value  # 获取分区后的基准索引
        
        # 将左右子区间压入栈(先压右区间,保证左区间优先处理)
        stack.append((current_l, pivot_idx - 1))
        stack.append((pivot_idx + 1, current_r))

3. 完整修复后的代码

整合所有修改后的可运行代码:

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import random

def partition(a, l, r):
    i = l - 1
    pivot = a[r]

    for j in range(l, r):
        if a[j] <= pivot:
            i += 1
            a[i], a[j] = a[j], a[i]
            yield a.copy()

    a[i + 1], a[r] = a[r], a[i + 1]
    yield a.copy()
    return i + 1

def quickSortIterative(a, l, r):
    stack = []
    stack.append((l, r))

    while stack:
        current_l, current_r = stack.pop()
        if current_l >= current_r:
            continue
        
        partition_gen = partition(a, current_l, current_r)
        try:
            while True:
                frame = next(partition_gen)
                yield frame
        except StopIteration as e:
            pivot_idx = e.value
        
        stack.append((current_l, pivot_idx - 1))
        stack.append((pivot_idx + 1, current_r))

def draw2(n=None):
    n = int(n) if n else 10
    a = [x + 1 for x in range(n)]
    random.shuffle(a)
    datasetName = 'Random'

    generator = quickSortIterative(a, 0, n - 1)
    algoName = 'Iterative Quick Sort'

    plt.style.use('fivethirtyeight')
    fig, ax = plt.subplots()

    bar_rects = ax.bar(range(len(a)), a, align="edge", color='Blue')

    ax.set_xlim(0, len(a))
    ax.set_ylim(0, int(1.1 * len(a)))
    ax.set_title(f"ALGORITHM : {algoName}\nDATA SET : {datasetName}")

    text = ax.text(0.01, 0.95, "", transform=ax.transAxes, color="#E4365D")
    iteration2 = [0]

    def animate(A, rects, iteration2):
        for rect, val in zip(rects, A):
            rect.set_height(val)
        iteration2[0] += 1
        text.set_text(f"iterations : {iteration2[0]}")

    anim = FuncAnimation(fig, func=animate,
                         fargs=(bar_rects, iteration2), frames=generator, interval=50,
                         repeat=False)
    plt.show()

# 测试调用
draw2(10)

关键说明

  • yield数组副本:直接yield原数组会导致后续修改覆盖历史帧,必须用a.copy()生成独立副本。
  • 捕获生成器返回值:通过StopIteration的value属性获取分区后的基准索引,确保子区间正确入栈。
  • 栈的正确使用:迭代式快排的栈存储待处理的区间边界,而非数组元素,这是原代码的核心逻辑错误。

内容的提问来源于stack exchange,提问作者User

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 13:34:38