STL在rotate上的优化是极尽其所能的。分别对前向访问,双向访问,随机访问的数据结构实现了三个版本的rotate。下面是自己按照对三种算法的理解,自己进行的实现。实现中我尽力避免使用C++的特性,从而以后可以在纯C的代码中使用。
下面是我使用到的数据结构,单向链表与双向链表,用于实现算法和验证算法的正确性:
//单链表节点 typedef struct Node* Link; struct Node{ int value; Link next; Link forward(Link& i) { i=i->next; return i; } void swap(Link& i) { int temp=i->value; i->value=this->value; this->value=temp; } }; typedef struct BiNode* BiLink; struct BiNode{ int value; BiLink next; BiLink prev; BiLink forward(BiLink& i) { i=i->next; return i; } BiLink backward(BiLink& i) { i=i->prev; return i; } void swap(BiLink& i) { int temp=i->value; i->value=this->value; this->value=temp; } };
版本一:forward iterator,即类单向链表上的rotate
//forward iterator,即类单向链表 void forwardRotate(Link head,Link middle) { Link i=middle; while(true) { head->swap(i); head->forward(head); i->forward(i); if(head==middle) { if(i==NULL) return ; //如果前后两指针同时到达末尾,则结束 middle=i; //如果前者先到达末尾,则将i作为middle,继续rotate } else if(i==NULL) i=middle; //如果后者先到达末尾,则将middle作为i,继续rotate } }
另附上注释的图像版以帮助理解:
版本二:bidirection iterator,即类双向链表上的rotate
这个版本的算法很容易理解,即是分段进行反转,之后对左右数据进行反转,代码如下:
void reverse(BiLink first,BiLink last) { while(first!=last &&first!=last->backward(last)) { last->swap(first); first->forward(first); } } void bidirectionRotate(BiLink first,BiLink middle,BiLink last) { reverse(first,middle); reverse(middle,last); reverse(first,last); }
版本三:random access iterator,即类数组上的rotate
该版本的效率无疑是最高的,当然算法因为涉及到有关群论的知识所以有点难以理解。其理论支持详见:数组循环移位问题
自己实现版本的代码如下:
//求最大公约数 int gcd(int m,int n) { int t; while(n!=0) { t=m%n; m=n; n=t; } return m; } //循环移位 void rotate_cycle(int array[],int len,int initial,int shift) { int value=array[initial]; int first=initial; int next=initial+shift; while(next!=initial) { array[first]=array[next]; first=next; next=(next+shift)%len; } array[first]=value; } void randomRotate(int array[],int middle,int len) { int n=gcd(len,middle); while(n--) rotate_cycle(array,len,n,middle); }
最后附上我自己的测试代码:
int main() { int len=20; srand(time(0)); //单向访问链表的rotate Link head=new Node; head->value=25; cout<<head->value<<"\t"; Link p=head; Link middle; for(int i=0;i<len;i++) { Link next=new Node; p->next=next; next->value=rand()%200; cout<<next->value<<"\t"; if(i==len/4*3) middle=p; p=p->next; } cout<<endl; p->next=NULL; forwardRotate(head,middle); p=head; while(p!=NULL) { cout<<p->value<<"\t"; p=p->next; } cout<<endl; //双向链表的rotate BiLink bihead=new BiNode; bihead->value=25; cout<<bihead->value<<"\t"; BiLink bip=bihead; BiLink bimiddle; for(int i=0;i<len;i++) { BiLink binext=new BiNode; bip->next=binext; binext->prev=bip; binext->value=rand()%200; cout<<binext->value<<"\t"; if(i==len/4) bimiddle=bip; bip=bip->next; } cout<<endl; BiNode end; bip->next=&end; end.prev=bip; bidirectionRotate(bihead,bimiddle,&end); bip=bihead; while(bip!=&end) { cout<<bip->value<<"\t"; bip=bip->next; } cout<<endl; int array[len]; for(int i=0;i<len;i++) { array[i]=rand()%200; cout<<array[i]<<"\t"; } cout<<endl; randomRotate(array,len/3,len); for(int i=0;i<len;i++) cout<<array[i]<<"\t"; cout<<endl; return 0; }
OK,大致如此了。三个版本的rotate,极至的优化手段,这也许就是STL的魅力所在吧。
本文作者 : cyningsun
本文地址 : https://www.cyningsun.com/04-25-2011/stl-rotate.html
版权声明 :本博客所有文章除特别声明外,均采用 CC BY-NC-ND 3.0 CN 许可协议。转载请注明出处!