循环链表(约瑟夫问题)C_LinkedList
// // C_LinkedList.hpp // test1 // 循环链表 // Created by Zy on 2020/3/28. // Copyright © 2020 Jovan. All rights reserved. // #ifndef C_LinkedList_hpp #define C_LinkedList_hpp #define OK 1 #define ERROR 0 #define OVERFLOW -1 #include <memory.h> #include <stdlib.h> #include <stdio.h> #include <time.h> typedef int Status; typedef int ElemType; typedef struct node{ ElemType data = 0; int index = 0; struct node *next = NULL; struct node *bf = NULL; }C_LinkedList; //初始化、构建实例->插入,删除,长度,读取—>清空循环链表 C_LinkedList * InitList(); void CreateList(C_LinkedList *);//尾插入构建list,实验中用的随机数构建也是用的尾插入法 int GetLength(C_LinkedList *); C_LinkedList *Locate(C_LinkedList *, ElemType );//查找元素位置f,返回一个指针 C_LinkedList *GetAt(C_LinkedList *,int);//查找,move为正代表向正向移动,否则向反向移动,为0不移动;返回移动后的指针。 Status InsertElem(C_LinkedList *, ElemType);//按照指针位置插入元素(如果参数是头指针,则是首插入,可以通过p=p->next改变插入位置) Status InsertElemAt(C_LinkedList *, int, ElemType);//按序插入,距离该点d处插入值。如果d为0表示替换该点 C_LinkedList *DeleteElem(C_LinkedList *,ElemType *);//删除指针位置的结点,并得到该点的值x,返回该指针所对应的下一个结点的指针 Status DeleteElemAt(C_LinkedList *, int ,ElemType *);//按序删除 void Erase(C_LinkedList *); void PrintList(C_LinkedList *); Status IsEmpty(C_LinkedList *); #endif /* C_LinkedList_hpp */
// // C_LinkedList.cpp // test1 // 循环链表 // Created by Zy on 2020/3/28. // Copyright © 2020 Jovan. All rights reserved. // #include "C_LinkedList.hpp" C_LinkedList * InitList(){ C_LinkedList *L; L = (C_LinkedList *)malloc(sizeof(C_LinkedList)); L->next = L; L->bf = L; return L; } //构建空的循环链表 void CreateList(C_LinkedList * L){ C_LinkedList *rear = L,*p; int i,d,n,x; printf("输入循环链表的初始长度n:"); scanf("%d",&n); printf("输入随机数范围d(代表密码范围在[-d,d]之间):"); scanf("%d",&d); srand((int)time(0)); for(i=0;i<n;i++) { x = rand()%(2*d+1)-d; if(!x) x++; //不允许密码为0,要么正要么负。但是这样会导致出现1的概率增大,但是随着d的增大,概率会变小,所以尽量让d取大一点 if(i==0) {rear->data = x;rear->index=1;} else { p = (C_LinkedList *)malloc(sizeof(C_LinkedList)); p->data = x; p->index = rear->index+1; p->next = rear->next; p->bf = rear; rear->next = p; p->next->bf = p; rear=p; } } } Status IsEmpty(C_LinkedList *L){ return L==L->next&&L->index==0; } C_LinkedList *DeleteElem(C_LinkedList *L,ElemType *x){ if(L->index==0) {printf("空的循环链表!\n");return NULL;} else if(L->next == L){*x = L->data;printf("%d ",L->index);L->index = 0;return L;} else { C_LinkedList *p = L->next; L->next->bf = L->bf; L->bf->next = L->next; *x = L->data; printf("%d ",L->index); free(L); if(*x>0) return p; else return p->bf; //必须用返回值更新L,不然L指针的值没有得到修改;或者用C++引用来实现函数内修改 } } C_LinkedList *GetAt(C_LinkedList *L,int move){ int i; C_LinkedList *p = L; if(move>0) for(i=0;i<move;i++) p = p->next; else if(move<0) for(i=0;i>move;i--) p = p->bf; return p; } void PrintList(C_LinkedList *L){ if(!L||L->index==0) {printf("链表为空,无法打印。\n");return;} C_LinkedList *p = L; printf("%d ",p->data); p = p->next; while(p!=L){ printf("%d ",p->data); p = p->next; } printf("\n"); } int GetLength(C_LinkedList *L){ C_LinkedList *p = L->next; int len = 1; while(p!=L){p = p->next;len++;} return len; } C_LinkedList *Locate(C_LinkedList *L, ElemType x){ C_LinkedList *p = L; if(p->data == x) return L; p = p->next; while(p!=L&&p->data!=x){p = p->next;} if(p==L) return NULL; else return p; } Status InsertElem(C_LinkedList *L, ElemType x){ if(L==NULL) return ERROR; if(L->index==0){L->data = x;L->index = 1;return OK;} C_LinkedList *p = (C_LinkedList *)malloc(sizeof(C_LinkedList)); p->data = x; p->index = L->index+1; p->next = L->next; p->next->bf = p; p->bf = L; L->next = p; return OK; } Status InsertElemAt(C_LinkedList *L, int d, ElemType x){ C_LinkedList *p = L; if(!L) return ERROR; if(!d) {L->data = x;return OK;} d = d>0?d-1:d; p = GetAt(p, d); return InsertElem(p, x); } Status DeleteElemAt(C_LinkedList *L, int d,ElemType *x){ C_LinkedList *p = L; if(!L) return ERROR; p = GetAt(p, d); DeleteElem(p, x); return OK; } void Erase(C_LinkedList * L){ C_LinkedList *p = L->next, *temp; while(p!=L){temp = p->next; free(p); p=temp;} free(p); L->index = 0; }
测试:
#include <stdio.h> #include <stdlib.h> #include "C_LinkedList.hpp" int main() { int m; C_LinkedList *L = InitList(); CreateList(L); printf("第一次:"); PrintList(L); printf("输入初始报数上限值:"); scanf("%d",&m);//m如果为正数从1开始报,如果m为负数,从-1开始报,正负只代表报数方向 int next_move = m-1; while(!IsEmpty(L)){ L = GetAt(L, next_move); L = DeleteElem(L, &next_move);//L处删除后L自动更新到删除点后一位处,next_move保存删除点的密码值 next_move =next_move>0?next_move-1:next_move+1; } Erase(L); printf("\n"); PrintList(L); }
输入循环链表的初始长度n:7
输入随机数范围d(代表密码范围在[-d,d]之间):10
第一次:-6 -10 -3 1 -6 10 10
输入初始报数上限值:6
6 3 7 2 1 4 5
链表为空,无法打印。
Program ended with exit code: 0
——————————————————————
优化:根据循环链表长度可以通过取余来减少每次移动次数:即修改GetAt函数,和设置全局变量size
修改后代码为:
// // C_LinkedList.hpp // test1 // 循环链表 // Created by Zy on 2020/3/28. // Copyright © 2020 Jovan. All rights reserved. // #ifndef C_LinkedList_hpp #define C_LinkedList_hpp #define OK 1 #define ERROR 0 #define OVERFLOW -1 #include <memory.h> #include <stdlib.h> #include <stdio.h> #include <time.h> typedef int Status; typedef int ElemType; typedef struct node{ ElemType data = 0; int index = 0; struct node *next = NULL; struct node *bf = NULL; }C_LinkedList; extern int size; //初始化、构建实例->插入,删除,长度,读取—>清空循环链表 C_LinkedList * InitList(); void CreateList(C_LinkedList *);//尾插入构建list,实验中用的随机数构建也是用的尾插入法 int GetLength(C_LinkedList *); C_LinkedList *Locate(C_LinkedList *, ElemType );//查找元素位置f,返回一个指针 C_LinkedList *GetAt(C_LinkedList *,int);//查找,move为正代表向正向移动,否则向反向移动,为0不移动;返回移动后的指针。 Status InsertElem(C_LinkedList *, ElemType);//按照指针位置插入元素(如果参数是头指针,则是首插入,可以通过p=p->next改变插入位置) Status InsertElemAt(C_LinkedList *, int, ElemType);//按序插入,距离该点d处插入值。如果d为0表示替换该点 C_LinkedList *DeleteElem(C_LinkedList *,ElemType *);//删除指针位置的结点,并得到该点的值x,返回该指针所对应的下一个结点的指针 Status DeleteElemAt(C_LinkedList *, int ,ElemType *);//按序删除 void Erase(C_LinkedList *); void PrintList(C_LinkedList *); Status IsEmpty(C_LinkedList *); #endif /* C_LinkedList_hpp */
其中size前面要加extern 表示全局变量声明而非定义,定义在cpp文件中。
// // C_LinkedList.cpp // test1 // 循环链表 // Created by Zy on 2020/3/28. // Copyright © 2020 Jovan. All rights reserved. // #include "C_LinkedList.hpp" int size = 0; C_LinkedList * InitList(){ C_LinkedList *L; L = (C_LinkedList *)malloc(sizeof(C_LinkedList)); L->next = L; L->bf = L; return L; } //构建空的循环链表 void CreateList(C_LinkedList * L){ C_LinkedList *rear = L,*p; int i,d,n,x; printf("输入循环链表的初始长度n:"); scanf("%d",&n); size = n; printf("输入随机数范围d(代表密码范围在[-d,d]之间):"); scanf("%d",&d); srand((int)time(0)); for(i=0;i<n;i++) { x = rand()%(2*d+1)-d; if(!x) x++; //不允许密码为0,要么正要么负。但是这样会导致出现1的概率增大,但是随着d的增大,概率会变小,所以尽量让d取大一点 if(i==0) {rear->data = x;rear->index=1;} else { p = (C_LinkedList *)malloc(sizeof(C_LinkedList)); p->data = x; p->index = rear->index+1; p->next = rear->next; p->bf = rear; rear->next = p; p->next->bf = p; rear=p; } } } Status IsEmpty(C_LinkedList *L){ return L==L->next&&L->index==0; } C_LinkedList *DeleteElem(C_LinkedList *L,ElemType *x){ if(L->index==0) {printf("空的循环链表!\n");return NULL;} else if(L->next == L){*x = L->data;printf("%d ",L->index);L->index = 0;return L;} else { C_LinkedList *p = L->next; L->next->bf = L->bf; L->bf->next = L->next; *x = L->data; printf("%d ",L->index); free(L); size--; if(*x>0) return p; else return p->bf; //必须用返回值更新L,不然L指针的值没有得到修改;或者用C++引用来实现函数内修改 } } C_LinkedList *GetAt(C_LinkedList *L,int move){ int i; move = move%size; C_LinkedList *p = L; if(move>0) for(i=0;i<move;i++) p = p->next; else if(move<0) for(i=0;i>move;i--) p = p->bf; return p; } void PrintList(C_LinkedList *L){ if(!L||L->index==0) {printf("链表为空,无法打印。\n");return;} C_LinkedList *p = L; printf("%d ",p->data); p = p->next; while(p!=L){ printf("%d ",p->data); p = p->next; } printf("\n"); } int GetLength(C_LinkedList *L){ C_LinkedList *p = L->next; int len = 1; while(p!=L){p = p->next;len++;} return len;//也可以用全局变量size } C_LinkedList *Locate(C_LinkedList *L, ElemType x){ C_LinkedList *p = L; if(p->data == x) return L; p = p->next; while(p!=L&&p->data!=x){p = p->next;} if(p==L) return NULL; else return p; } Status InsertElem(C_LinkedList *L, ElemType x){ if(L==NULL) return ERROR; if(L->index==0){L->data = x;L->index = 1;size++;return OK;} C_LinkedList *p = (C_LinkedList *)malloc(sizeof(C_LinkedList)); p->data = x; p->index = L->index+1; p->next = L->next; p->next->bf = p; p->bf = L; L->next = p; size++; return OK; } Status InsertElemAt(C_LinkedList *L, int d, ElemType x){ C_LinkedList *p = L; if(!L) return ERROR; if(!d) {L->data = x;size++;return OK;} d = d>0?d-1:d; p = GetAt(p, d); return InsertElem(p, x); } Status DeleteElemAt(C_LinkedList *L, int d,ElemType *x){ C_LinkedList *p = L; if(!L) return ERROR; p = GetAt(p, d); DeleteElem(p, x); return OK; } void Erase(C_LinkedList * L){ C_LinkedList *p = L->next, *temp; while(p!=L){temp = p->next; free(p); p=temp;} free(p); L->index = 0; size = 0; }
#include <stdio.h> #include <stdlib.h> #include "C_LinkedList.hpp" int main() { int m; C_LinkedList *L = InitList(); CreateList(L); printf("第一次:"); PrintList(L); printf("输入初始报数上限值:"); scanf("%d",&m);//m如果为正数从1开始报,如果m为负数,从-1开始报,正负只代表报数方向 int next_move = m-1; while(!IsEmpty(L)){ L = GetAt(L, next_move); L = DeleteElem(L, &next_move);//L处删除后L自动更新到删除点后一位处,next_move保存删除点的密码值 next_move =next_move>0?next_move-1:next_move+1; } Erase(L); printf("\n"); PrintList(L); printf("size = %d\n",size); }
输入循环链表的初始长度n:7
输入随机数范围d(代表密码范围在[-d,d]之间):5
第一次:-5 5 1 5 1 5 2
输入初始报数上限值:6
6 4 3 5 7 2 1
链表为空,无法打印。
size = 0
Program ended with exit code: 0
通过实验计算时间复杂度===========================================================================================================================
测试运算时间,通过测试每一种规模[n,m] (n<=30), 用10组数据算出运算时间的平均值,控制n不变,改变m;然后控制m不变改变n;m变化区间为[0,2n],步长设为1;
#include <stdio.h> #include <stdlib.h> #include "C_LinkedList.hpp" #include <time.h> #define MAX 100 #include <time.h> void my_delay(int delay_t) { clock_t start_time; //the start time start_time=clock(); while((clock()-start_time)/CLOCKS_PER_SEC <delay_t);//delay_t表示延迟多少ms } typedef struct person{ int code = 0; int isout = 0; }Person; int Inputcode[MAX]={}; double Array_time[MAX]={}; double CList_time[MAX]={}; double Time[2][MAX]={}; double average(double x[],int n){ double sum = 0; for(int i=0;i<n;i++) sum += x[i]; return sum/n; } int main() { clock_t begin,end; double t1,t2; int n,m,d,i,x; int pointer,count,left; C_LinkedList *L = InitList(); Person a[MAX]; memset(a,0,sizeof(a)); // printf("输入循环链表的初始长度n:"); // scanf("%d",&n); n = 20; printf("输入初始报数上限值:"); scanf("%d",&m);//m如果为正数从1开始报,如果m为负数,从-1开始报,正负只代表报数方向 printf("输入随机数范围d(代表密码范围在[-d,d]之间):"); scanf("%d",&d);//范围设置的10 for(d = 10;d<=100;d+=10){ printf("\n>>>>>>>>>>>>>>>>>>n = %d, m = %d ,d = %d >>>>>>>>>>>>>>>>>",n,m,d); printf("start?Y or N:"); getchar();getchar(); for(int k = 1;k <= 10; k++) { my_delay(1); srand((int)time(0)); /*================生成数据==================*/ for(i=1;i<=n;i++){ x = rand()%(2*d+1)-d; Inputcode[i]=x?x:1;//不允许密码为0,要么正要么负。但是这样会导致出现1的概率增大,但是随着d的增大,概率会变小,所以尽量让d取大一点 } printf("\n第%d组数据:(n=%d,m=%d)\n",k,n,m); for(i=1;i<=n;i++) printf("%d ",Inputcode[i]); printf("\n=============================\n"); /*================输入数据===================*/ int j = 5; int temp = m; while(j--){ pointer = count = 0; memset(a, 0, sizeof(a)); for(i = 1;i<=n;i++) a[i].code = Inputcode[i]; left = n; size = n; CreateList(L,Inputcode,n);//用Inputcode分别给循环链表和循环数组赋初值,保证测试数据一致。 //PrintList(L); /*===============测试时间(每组数据测十次取平均值)===================*/ begin = clock(); do{ if(temp>0)pointer++; else pointer--; if(pointer==n+1) pointer=1; else if(pointer==0)pointer=n; if(!a[pointer].isout){ if(temp>0) count++; else count--; } if(count==temp){ count=0; printf("%d ",pointer); a[pointer].isout = 1; temp = a[pointer].code; left--; if(!left) break; if(!(temp%left)) temp = temp>0?-1:1; else if(temp>left) temp = temp%left; else if(temp<-left) temp = temp%left-left; } }while(1); end = clock(); printf("\n"); Array_time[j] = (double)(end-begin)/CLOCKS_PER_SEC; // printf("%lf",Array_time[j]); temp = m; int next_move = temp-1; begin = clock(); while(!IsEmpty(L)){ L = GetAt(L, next_move); L = DeleteElem(L, &next_move);//L处删除后L自动更新到删除点后一位处,next_move保存删除点的密码值 next_move =next_move>0?next_move-1:next_move+1; } end = clock(); printf("\n"); CList_time[j] = (double)(end-begin)/CLOCKS_PER_SEC; } Time[0][k] = average(Array_time, 5); Time[1][k] = average(CList_time, 5); printf("循环数组所花时间:%lfns\n",1000000*Time[0][k]); printf("循环队列所花时间:%lfns\n",1000000*Time[1][k]); } t1 = average(Time[0], 10); t2 = average(Time[1],10); printf("t1 = %lfns;\nt2 = %lfns",1000000*t1,1000000*t2); } }