75.色の分類
赤、白、青を含むn個の要素の配列が与えられた場合、同じ色の要素が隣接し、赤、白、青の順序で配置されるように、それらを所定の位置に並べ替えます。
この質問では、整数0、1、および2を使用して、それぞれ赤、白、および青を表します。
注:
この問題を解決するためにコードベースのソート機能を使用することはできません。
例:
入力:[2,0,2,1,1,0]
出力:[0,0,1,1,2,2]
詳細:直感的な解決策は、カウントとソートに2パススキャンアルゴリズムを使用することです。
まず、要素0、1、2の数を繰り返し計算し、次に0、1、2の順序で現在の配列を書き換えます。
一定のスペースのみを使用するワンパススキャンアルゴリズムを考えられますか?
解決策1:最初にダブルポインター、移動しないときに3番目のポインターを導入する
class Solution {
public:
void sortColors(vector<int>& nums) {
int len = nums.size();
if (len < 2)
return;
int left = 0, right = len-1;
while (left < right) {
if (nums[left] == 0) { //先两边符合条件的数值
++left;
continue;
} else if (nums[right] == 2) {
--right;
continue;
}
if (nums[left] == 2) {
swap(nums[left], nums[right]);
--right;
} else if (nums[right] == 0) {
swap(nums[left], nums[right]);
++left;
} else if (nums[left] == 1 && nums[right] == 1) { //双指针寸步难行
int mid = left+1; //引入第三指针
while (mid < right && nums[mid] == 1) //找中间不符合条件的数值
++mid;
if (mid == right)
return;
if (nums[mid] == 0) {
swap(nums[left], nums[mid]);
++left;
} else {
swap(nums[right], nums[mid]);
--right;
}
}
}
}
};
この方法の考え方は比較的単純で、値は比較的安定しています。最後の2つは常に最後の側にあり、交換されません。そして、必要な場合(間隔の境界が1時)にのみ、3つのポインターを常に維持することなく、3番目の補助ポインターを導入します。操作は比較的簡単で、考え方は比較的明確です。
右側に1があり、左側に1が多く、その間に少し0/2がある場合、アルゴリズムは頻繁に3番目のポインターを導入し、3番目のポインターは左側の境界1から右側に繰り返し移動します。
解決策2:3つのポインターを指示する
class Solution {
public:
void sortColors(vector<int>& nums) {
int len = nums.size();
if (len < 2)
return;
int left = 0, cur = 0, right = len-1;
while (cur <= right) { //需要同步跟新left, 所以有等号
if (nums[cur] == 0) {
if (left != cur)
swap(nums[left], nums[cur]);
++left;
++cur; //从前往后,一步为0可以往下走
} else if (nums[cur] == 2) {
if (right != cur)
swap(nums[right], nums[cur]);
--right;
//++cur; //cur不能往前走,从后换到当前的值不知道是0/1/2
} else {
++cur; //当cur指的数为1时,会落下left指向第一个为1的数
}
}
}
};
このソリューションは常に3つのポインターを維持し、アルゴリズムには安定性がありません。最後の値が条件を満たしていても、curに変更され、最初に出会った2が最後に変更され、値の安定性は次数反転に相当します。
元の配列の最初に2があり、最後の2がほぼ正しい場合、アルゴリズムは最悪です。