算法可视化:实现没有循环但每次调用函数时增量可变的插入排序
Posted
技术标签:
【中文标题】算法可视化:实现没有循环但每次调用函数时增量可变的插入排序【英文标题】:algorithm visualization: implementing insertion sort without loops but with variable increments everytime a function is called 【发布时间】:2020-08-17 18:22:00 【问题描述】:我正在处理中构建排序算法可视化器(使用额外的可视化库扩展 java),我非常坚持这个问题,我认为其他人将能够帮助我解决这个问题。 在处理过程中,有一个名为 draw() 的函数每秒被调用 60 次。我想在这里执行,每次调用 draw() 时,插入算法的一个步骤。我已经用冒泡排序实现了它。 (见下面的代码)。 updateBubble() 在 draw() 中被调用,'colors' 是我用来保持不同颜色值排序的数组列表的名称。
图片以获得更好的理解: [![可视化算法预览][1]][1]
...
int j = 0
...
void updateBubble()
bubble.sort(j);
j++;
if (i<bubble.colors.size())
if (j >= bubble.colors.size()-i-1)
j = 0;
i++;
else
bubble.sorted = true;
这里是BubbleSort类中的函数(bubble是这个类的一个对象)
void sort(int j)
if (j<colors.size()-1)
if (colors.get(j) > colors.get(j+1))
int temp = colors.get(j);
colors.set(j, colors.get(j+1));
colors.set((j+1), temp);
通过这种方式,我能够将可视化过程减慢到我可以控制自己的帧速率的速度,而无需使用会立即执行排序算法的循环。现在我也想为插入排序算法做一个类似的实现,但我觉得我被卡住了,因为我似乎无法使用类似的实现,或者可能有更好的方法来做到这一点? 我目前所拥有的按预期立即执行它,而无法看到该过程。
void updateInsertion()
insertion.sort();
void sort()
int n = colors.size();
for (int i = 1; i < n; ++i)
int key = colors.get(i);
int j = i - 1;
while (j >= 0 && colors.get(j) > key)
colors.set(j+1, colors.get(j));
j = j - 1;
colors.set(j+1, key);
这就是我现在得到的:这仍然是错误的,但越来越接近并清楚我想要达到的目标,创建一个仅适用于增量和 if 语句而不是 while 和 fors 的函数,因此每个不同的步骤都是每次调用该方法时执行。
// i resembles for loop variable
if (i<insertion.colors.size())
if (j<0 || insertion.colors.get(j) <= insertion.colors.get(i)) // negative check to go out of while loop
insertion.colors.set(j+1, keap);
if(notSortedYet())
i++;
keap = insertion.colors.get(i);
j = i - 1;
else // resembles being in the while loop
insertion.colors.set((j+1), insertion.colors.get(j));
j = j - 1;
编辑:我修复了它,您可以在下面找到我的解决方案 :) 每次调用 updateInsertion() 时,我的代码都会在算法中执行准确的一步!感谢所有努力评论的人,我不知道这是否是最佳实践,所以如果你愿意,请随时更新!
void updateInsertion()
// i resembles for loop variable
if (i<insertion.colors.size())
if (j>=0 && insertion.colors.get(j) > firstUnsorted)
int temp = insertion.colors.get(j+1);
insertion.colors.set((j+1), insertion.colors.get(j));
insertion.colors.set(j,temp);
j = j - 1;
else
insertion.colors.set(j+1, firstUnsorted);
if (i<insertion.colors.size()-1)
i++;
firstUnsorted = insertion.colors.get(i);
j = i - 1;
【问题讨论】:
我会像 ***.com/questions/11570132/… 那样使用迭代器。现在您可以逐步执行算法,并控制绘图代码。 嘿,感谢您的快速评论!我并没有真正关注这将如何帮助我或我将如何实施?有一个循环和一个while循环,如何每次在这个过程中用迭代器调用draw()时只做一个步骤? 【参考方案1】:我喜欢这个项目。
Processing 还有一个millis()
方法,它返回自从你开始你的草图以来花费了多少毫秒。我有时用它来计时我的动画,这在这里可以派上用场。下面是一个计时器类的实现:
class Delay
int limit;
Delay (int l)
limit = millis() + l;
boolean expired ()
return (millis() > limit);
我建议你使用这个类而不是调整 FPS。通过使用延迟来减慢排序的实现,您可以让计算机按照自己的节奏工作,并且只在需要时绘制新帧。像这样(请原谅我说“做事”的部分):
Delay holdTheFrame = new Delay(1);
void draw()
if(holdTheFrame.expired())
holdTheFrame = new Delay(500); // half a second before the next frame
// Advance one step forward in your sorting
// Draw the visualization of the data
您可以微调数据的排序速度,并且仅在数据发生变化时对其进行绘制。这是双赢的!
玩得开心!
编辑
为了帮助您实现,这里有一个示例。您可以将此代码复制并粘贴到一个空的处理草图中,它会按原样运行。为了让我的工作更轻松,我打印到控制台而不是使用图形显示,但你应该能够得到我正在做的事情。
这里的秘密是我的排序算法已经过微妙的修改,所以当我调用它们时,它们总是只运行一个排序步骤。自己看:
int _numberOfItems = 10;
int _sortingStep = 0;
IntList _bubbleList = new IntList();
boolean _bubbleListSorted = false;
IntList _selectionList = new IntList();
IntList _insertionList = new IntList();
Delay _delay = new Delay(1);
void setup()
for (int i=0; i<_numberOfItems; i++)
_bubbleList.append((int)random(10, 99));
for (int i=0; i<_numberOfItems; i++)
_selectionList.append((int)random(10, 99));
for (int i=0; i<_numberOfItems; i++)
_insertionList.append((int)random(10, 99));
void draw()
if (_delay.expired())
_delay = new Delay(500);
// sort one step with every algo you want to display
if (!_bubbleListSorted)
singleStepBubbleSort(_bubbleList);
if (_sortingStep < _numberOfItems)
singleStepSelectionSort(_selectionList, _sortingStep);
singleStepInsertionSort(_insertionList, _sortingStep);
_sortingStep++;
// update the display (I'm printing to console instead for simplicity)
for (int i : _bubbleList)
print(i + " ");
print(" | ");
for (int i : _selectionList)
print(i + " ");
print(" | ");
for (int i : _insertionList)
print(i + " ");
print("\n");
// An "single-step" implementation of Insertion Sort
void singleStepInsertionSort(IntList list, int step)
int k = list.get(step);
int j = step - 1;
while (j >= 0 && list.get(j) > k)
list.set(j+1, list.get(j));
j = j - 1;
list.set(j+1, k);
// An "single-step" implementation of Bubble Sort
void singleStepBubbleSort(IntList list)
int temp;
boolean swapped = false;
for (int i=0; i<list.size()-1; i++)
if (list.get(i) > list.get(i + 1))
// swap arr[j] and arr[j+1]
temp = list.get(i);
list.set(i, list.get(i+1));
list.set(i+1, temp);
swapped = true;
if (!swapped)
_bubbleListSorted = true;
// An "single-step" implementation of Selection Sort
void singleStepSelectionSort(IntList list, int step)
int min_idx = step;
for (int j = step+1; j < list.size(); j++)
if (list.get(j) < list.get(min_idx))
min_idx = j;
int temp = list.get(min_idx);
list.set(min_idx, list.get(step));
list.set(step, temp);
class Delay
int limit;
Delay (int l)
limit = millis() + l;
boolean expired ()
return (millis() > limit);
如果您有任何问题,请告诉我。
更多编辑:
插入排序的每次交换都意味着很多很多交换。这真的很痛苦,因为这种算法很难停下来。
幸运的是,我不在乎。跳出框框思考,我选择创建一个专用于对数组进行排序的类,同时记录如何对其进行排序,然后能够“就像实时发生一样”回放它。看看:
int numberOfItems = 10;
int sortingStep = 0;
Delay delay = new Delay(1);
ManagedSelectionSort managedSelectionSort; // I created a class just to manage this madness
void setup()
IntList list = new IntList();
for (int i=0; i<numberOfItems; i++)
list.append((int)random(10, 99)); // some random numbers to sort later
managedSelectionSort = new ManagedSelectionSort(list); // take a look at the instantiation of this class
print("Step " + String.format("%02d", sortingStep) + ": ");
printArray(managedSelectionSort.list);
print("\n");
void draw()
if (delay.expired())
delay = new Delay(100); // i put a very short delay, you'll probably want to tweak this
managedSelectionSort.sortOneStep(); // this is not what it seems
sortingStep++;
print("Step " + String.format("%02d", sortingStep) + ": ");
printArray(managedSelectionSort.list);
print("\n");
// this class is where the magic happens
// we'll sort the array all at once while recording every move
// then we'll play back those moves on a copy of the array
class ManagedSelectionSort
IntList list, hiddenList; // list is the "official" list, while hiddenList is where the heavy lifting happens
ArrayList<SwapIndex> swapList; // this is where I record how to sort the array
ManagedSelectionSort(IntList baseList) // this way I can instantiate several similar objects with the same list
list = new IntList();
hiddenList = new IntList();
swapList = new ArrayList<SwapIndex>();
for (int i : baseList)
// both lists have the same initial numbers
list.append(i);
hiddenList.append(i);
// as soon as this object is instantiated, it knows how it'll sort the array
// because it already did...
hiddenSort();
// this method plays the moves which were recorded earlier according to the current sortingStep
// the swapList array was filled with every swap needed to sort the array, one by one
// now it's just a matter of playing them back on a copy of the initial array
void sortOneStep()
if (sortingStep < swapList.size())
swap(list, swapList.get(sortingStep).index1, swapList.get(sortingStep).index2);
// this is the real implementation of the insertion sort
void hiddenSort()
for (int i=1; i<hiddenList.size(); i++)
int j = i;
while (j>0 && hiddenList.get(j) < hiddenList.get(j-1))
swap(hiddenList, j, j-1, true); // swap is a class specific helper method, it swaps the numbers and also records the move
j--;
// this is an overload, i could have done without but it's confortable
void swap(IntList list, int index1, int index2)
swap(list, index1, index2, false);
void swap(IntList list, int index1, int index2, boolean recordMove)
// the swap first
int temp = list.get(index1);
list.set(index1, list.get(index2));
list.set(index2, temp);
// if the method is set on 'record', it adds this move to the swapList array
if (recordMove)
swapList.add(new SwapIndex(index1, index2));
// this class could have been a struct, but I like to start in OOP right from the bat in case things gets complicated
class SwapIndex
int index1;
int index2;
SwapIndex(int index1, int index2)
this.index1 = index1;
this.index2 = index2;
// this method is just an helper method to print to console
void printArray(IntList list)
for (int i : list)
print(i + " ");
class Delay
int limit;
Delay (int l)
limit = millis() + l;
boolean expired ()
return millis() > limit;
如果我这次理解正确的话,这应该可以解决您最初的问题!
【讨论】:
嘿!感谢您的回答,我肯定会实现一个算法计时器:)问题是,我希望 4 个算法同时以相同的速度执行步骤.. 所以他们的步骤方法都应该被调用画。如果我在抽签中延迟,我仍然无法逐步执行排序算法:) 有道理。信不信由你,但我昨晚想到了:你可以把延迟放在排序算法中。这样,每个算法都可以在您选择的时间步调。确保每个延迟对象都是不同的,否则您可能会意外放弃一些排序步骤。如果这种实现方式让你感到奇怪,请告诉我,我会向你展示一段代码 sn-p,它应该会让它更明显。 如果我在算法中放了一个延迟,是不是除非前一个算法结束,否则下一个算法不会被调用?还是我错了? 我可以假设您希望所有排序算法以相同的速率逐步推进吗? @ArnePannemans 我用一些代码更新了答案,这应该可以帮助你理解我的意思。【参考方案2】:实现此目的的一种方法是通过某种存储状态。下面是我所说的高层次内容。
// Starts the procedure. Must be called before draw().
void init()
state = "forLoop";
i = 1;
n = colors.size();
// Single iteration of a loop.
void draw()
switch(state)
case "forLoop":
doForBody();
break;
case "whileLoop":
doWhileLoopBody();
break;
...
// Executes everything in the while loop and the one or two things
// just after it.
void doWhileLoopBody()
if (isThisIterationOfWhileDone())
// Get out of the while loop and prepare for the next iteration of for.
// A better way to what I'm doing on the next couple lines here would
// be to introduce an additional state (ex: "postWhile") that would
// execute just after this method and would handle the colors.set(),
// incrementing i, etc.
state = "forLoop";
colors.set(j+1, key);
i++;
return;
// update colors, value of j, etc...
// Executes everything before the while loop.
void doForLoopBody()
if (isThisIterationOfForDone())
state = "END";
return;
// update colors, get values of key and j initialized, etc
// switch to processing the body of the while loop
state = "whileLoop";
【讨论】:
以上是关于算法可视化:实现没有循环但每次调用函数时增量可变的插入排序的主要内容,如果未能解决你的问题,请参考以下文章