常见算法-三色棋(Gossip)
1、说明
三色旗的问题最早由E.W.Dijkstra所提出,他所使用的用语为Dutch Nation Flag(Dijkstra为荷兰人),而多数的作者则使用Three-Color Flag来称之。
假设有一条绳子,上面有红、白、蓝三种颜色的旗子,起初绳子上的旗子颜色并没有顺序,您希望将之分类,并排列为蓝、白、红的顺序,要如何移动次数才会最少,注意您只能在绳子上进行这个动作,而且一次只能调换两个旗子。
在一条绳子上移动,在程式中也就意味只能使用一个阵列,而不使用其它的阵列来作辅助,问题的解法很简单,您可以自己想像一下在移动旗子,从绳子开头进行,遇到蓝色往前移,遇到白色留在中间,遇到红色往后移,如下所示:
只是要让移动次数最少的话,就要有些技巧:
- 如果图中W所在的位置为白色,则W+1,表示未处理的部份移至至白色群组。
- 如果W部份为蓝色,则B与W的元素对调,而B与W必须各+1,表示两个群组都多了一个元素。
- 如果W所在的位置是红色,则将W与R交换,但R要减1,表示未处理的部份减1。
注意B、W、R并不是三色旗的个数,它们只是一个移动的指标;什么时候移动结束呢?一开始时未处理的R指标会是等于旗子的总数,当R的索引数减至少于W的索引数时,表示接下来的旗子就都是红色了,此时就可以结束移动。
2、C++代码
#include<iostream>
using namespace std;
#define Blue "Blue"
#define White "White"
#define Red "Red"
void Swap(string* x, string* y) {
string temp = *y;
*y = *x;
*x = temp;
}
void Gossip(string* flag,int n) {
int bluePtr = 0;
int whitePtr = 0;
int redPtr = n - 1;
while (whitePtr <= redPtr) {
if (flag[whitePtr] == Blue) {
Swap(&flag[bluePtr], &flag[whitePtr]);
bluePtr++;
whitePtr++;
}
else if (flag[whitePtr] == White) {
whitePtr++;
}
else{
while (whitePtr < redPtr && flag[redPtr] == Red) {
redPtr--;
}
Swap(&flag[whitePtr], &flag[redPtr]);
redPtr--;
}
}
}
void PrintGossip(string* flag, int n) {
for (int i = 0; i < n; i++) {
cout << flag[i] << " ";
}
cout << endl;
}
int main() {
string* flag = new string[10]{ White, Red, Blue, White, Blue, Red, Red, Blue, White, Blue };
PrintGossip(flag, 10);
Gossip(flag, 10);
PrintGossip(flag, 10);
return 0;
}