1. 程式人生 > >STL左旋轉字串Rotate的深入理解和自我實現

STL左旋轉字串Rotate的深入理解和自我實現

STL對左旋轉字串針對三種不同資料結構進行了不同的實現。

對單向連結串列採用的是同步位移,雙向連結串列是三次翻轉,都很簡單,主要看看針對隨機存取的陣列做的迴圈位移實現。

STL這個版本的原始碼如下:

template <class RandomAccessIterator, class Distance>
void __rotate(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last, Distance*, random_access_iterator_tag)
{
    // gcd是求最大公約數的函式。
    Distance n = __gcd(last - first, middle - first);

    while (n--)
        // 需要執行__rotate_cycle n次。
        __rotate_cycle(first, last, first + n, middle - first, value_type(first));
}

template <class RandomAccessIterator, class Distance, class T>
void __rotate_cycle(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator initial, Distance shift, T*)
{
    T value = *initial;
    RandomAccessIterator ptr1 = initial;
    RandomAccessIterator ptr2 = ptr1 + shift;

    while (ptr2 != initial) {
        *ptr1 = *ptr2;
        ptr1 = ptr2;
        if (last - ptr2 > shift)
            ptr2 += shift;
        else
            ptr2 = first + (shift - (last - ptr2));
    }

    *ptr1 = value;
}

template <class EuclideanRingElement>
EuclideanRingElement __gcd(EuclideanRingElement m, EuclideanRingElement n)
{
    while (n != 0) {
        EuclideanRingElement t = m % n;
        m = n;
        n = t;
    }

    return m;
}
__gcd是求兩個數的最大公約數,也是迴圈位移的遍數。

舉個例子來說明演算法過程,陣列123456789,把123翻轉到右邊,*first=1,*last=9,*middle=4;

要旋轉字串(123)的長度為3,字串長度為9,3和9的最大公約數為3,因此需要翻轉3遍;

第一遍從*(initial+shift)=6開始,6移到3的位置,9移到6的位置,下一個位置是ptr2 = first + (shift - (last - ptr2))=0+(3-(8-8))=3,不滿足ptr2 != initial的條件,退出迴圈,然後*ptr1 = value,即把數字3移動到數字9的位置,從而完成了3,6,9三個數字的位移,下面的2遍迴圈則分別完成2,5,8和1,4,76個數字的位移,最後得到最終結果456789123。

整個演算法過程可用下圖表示:


自我實現C++的int版:

#include <iostream>
using namespace std;
int __gcd(int a, int b)
{
	while (b)
	{
		int tmp = a%b;
		a = b;
		b = tmp;
	}
	return a;
}
void __rotate_cycle(int *data, int first, int last, int initial, int shift)
{
	int val = data[initial];
	int pos1 = initial;
	int pos2 = initial + shift;
	while (pos2 != initial)
	{
		data[pos1] = data[pos2];
		pos1 = pos2;
		pos2 = (pos2 + shift) % (last - first + 1);
	}
	data[pos1] = val;
}
void __rotate(int *data,int first, int middle, int last)
{
	int gcd = __gcd(last - first+1, middle - first);
	cout << "迴圈位移" << gcd << "遍" << endl;
	while (gcd--)
	{
		__rotate_cycle(data, first, last, first + gcd, middle - first);
	}
}
void main()
{
	int data1[10] = {1,2,3,4,5,6,7,8,9,10};
	__rotate(data1, 0, 3, 9);
	for (int i = 0; i < 10; i++)
	{
		cout << data1[i] << " ";
	}
	cout << endl;
	int data2[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	__rotate(data2, 0, 4, 9);
	for (int i = 0; i < 10; i++)
	{
		cout << data2[i] << " ";
	}
	cout << endl;
	int data3[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	__rotate(data3, 0, 5, 9);
	for (int i = 0; i < 10; i++)
	{
		cout << data3[i] << " ";
	}
	cout << endl;
	int data4[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	__rotate(data4, 0, 6, 9);
	for (int i = 0; i < 10; i++)
	{
		cout << data4[i] << " ";
	}
	int c = 0;
	cin >> c;
}

執行結果: