【问题标题】:p5.js Recursive Bubble Sortp5.j​​s 递归冒泡排序
【发布时间】:2021-03-24 07:25:28
【问题描述】:

我正在尝试修改 this solution 以适用于冒泡排序,但我有点超出我的深度,尤其是对于整个 async function 业务。该代码在一定程度上可以工作,但并未遵循我对冒泡排序所期望的确切模式,并且仅对数组​​进行了部分排序。

谁能帮帮我?

let values = [];
let startSort = true;

function bubbleSort( a ) {
    // create copy of the array 
    clone = a.slice();
    // asynchronous sort the copy
    recursiveBubbleSort( clone, clone.length );
    return;
}

//Recursive Bubble Sort
async function recursiveBubbleSort( arr, n ) {
    //If there is only single element 
    //the return the array
    if ( n === 1 ) {
    return arr;
    }

    await recursiveBubbleSort( arr, n - 1 );

    //Swap the elements by comparing them
    for ( let j = 0; j < n - 1; j++ ) {
    if ( arr[j] > arr[j + 1] ) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
    }
    }

    // copy back the current state of the sorting
    values = arr.slice();

    // slow down
    await sleep( 500 );
}

async function sleep( ms ) {
    return new Promise( resolve => setTimeout( resolve, ms ) );
}

function setup() {
    createCanvas( 600, 190 );
    frameRate( 60 );
}

let numOfRects = 15;
let rectWidth;
function draw() {
    if ( startSort ) {
    startSort = false;

    rectWidth = floor( width / numOfRects );
    values = new Array( floor( width / rectWidth ) );
    for ( let i = 0; i < values.length; i++ ) {
        values[i] = random( height );
    }

    bubbleSort( values );
    }

    background( 23 );
    stroke( 0 );
    fill( 255 );
    for ( let i = 0; i < values.length; i++ ) {
    rect( i * rectWidth, height - values[i], rectWidth, values[i] );
    }
}
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"&gt;&lt;/script&gt;

【问题讨论】:

  • 谢谢,但我的问题中引用了该答案。

标签: algorithm visualization p5.js bubble-sort


【解决方案1】:

在这个答案中,我不会关注asyncawait 的工作方式,因为这似乎不是您的目标。我将向您展示如何制作冒泡排序的工作版本。

首先让我们摆脱你拥有的这个startSort变量:你用它来初始化你的值,更好的方法是使用p5使用的setup()函数:

function setup() {
    createCanvas(600, 190);

    rectWidth = floor(width / numOfRects);
    // Generate the values
    values = new Array(floor(width / rectWidth));
    for (let i = 0; i < values.length; i++) {
        values[i] = random(height);
    }
    // The number of iterations is equal to the number of values
    n = values.length;
}

我们首先用随机值填充您的数组并定义一个全局变量n,它将保存剩余的迭代次数(与值的数量相同)。

然后让我们修改你的冒泡排序函数。在这里,我们不需要它是递归的,因为正如我稍后将展示的那样,我们将简单地调用它几次,直到我们完成所有迭代。

// Bubble Sort
function bubbleSort(arr, n) {
    // If there is no remaining iterations do nothing
    if (n <= 1) {
        return 0;
    }

    // Swap the elements by comparing them
    for (let j = 0; j < n - 1; j++) {
        if (arr[j] > arr[j + 1]) {
            [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        }
    }

    // We did one more iteration return the remaining number of iterations
    return n - 1;
}

这里有 3 件重要的事情:

  • 该函数在原地修改 arr:因此,当您使用数组调用该函数时,您将在同一个数组中得到结果。
  • 函数返回剩余的迭代次数(每次调用函数为一次迭代)
  • 如果你用n 调用它,剩余的迭代次数是10,它将什么都不做。否则它将在数组上进行一次传递。

现在您可以更改您的draw() 函数。此函数由 p5 X 次以秒为单位自动调用(默认为 60)。所以每次运行它都会调用冒泡排序函数,更新数组和剩余迭代次数:

function draw() {
    // Define the "speed" at which we do the iterations
    frameRate(10);
    background(23);
    stroke(0);
    fill(255);

    // Show the values
    for (let i = 0; i < values.length; i++) {
        rect(i * rectWidth, height - values[i], rectWidth, values[i]);
    }

    // Make one new iteration
    n = bubbleSort(values, n);
}

请注意我们如何使用frameRate() 来减少调用它的频率,以便让用户看到排序的不同步骤。

您只需在草图开始时声明全局变量,将所有内容放在一起即可。

let values = [];
let numOfRects = 15;
let rectWidth;
let n;

你可以看到完整的代码here你会注意到我在这个实现中做了更多的事情:

  • 我提取了在它自己的函数resetArray() 中将新值放入数组中的代码
  • 我在 setup() 函数中调用此函数来初始化动画,但在 n===0 时也在 draw() 中调用此函数,以便在对数组进行排序时生成新值以获得无限动画。

关于asyncawait 的注释。这些函数用于在 javascript 中创建异步代码。这是一个完整的话题,对于刚接触编程的人来说并非易事,您可以阅读文档here。基本上async 用于表示函数需要时间才能执行,await 用于表示等待函数完成执行。

在您从中获得灵感的函数中,此异步代码用于在两次调用 bubbleSort 函数之间设置延迟,以便用户可以看到不同的迭代。在这里您并不需要它,因为 p5 为您提供了 draw()frameRate() 的机制来更轻松地处理它。

【讨论】:

  • 这在某些方面很有帮助。然而,我没有提到的更大目标是可视化“k-digit-swaps 的最小值”问题,这与冒泡排序非常相似,只是它使用回溯来找到最佳解决方案。因此,冒泡排序几乎肯定需要递归,这就是为什么我尝试将它建立在递归合并排序示例的基础上。我的希望是,如果我可以让我提供的版本正常工作,添加回溯会非常简单。
  • 是的,我应该在写答案之前在 cmets 中进行澄清,但在您的问题中指定这一点会很有用。我认为您应该能够用一堆状态“模拟”递归,并保持相同的想法,即从draw() 为每次迭代调用函数
猜你喜欢
  • 2012-05-28
  • 1970-01-01
  • 2013-10-09
  • 2021-06-22
  • 2013-12-02
  • 2011-12-09
  • 2013-11-29
  • 2023-03-24
  • 1970-01-01
相关资源
最近更新 更多