方法2:循环队列,将未访问的数据移动到循环队列末尾
#define MaxCount 1000
void RoundQueue(void)
{
int list[2*MaxCount]; //多分配一倍的空间,用来放循环数据
int i,index = 0,tail,delcount = 0;
for(i=0;i< MaxCount;i++) list[i]=i; //初始化,存放0~999
tail=MaxCount; //list[tail]用来存放不删除的数据
while(delcount < MaxCount) //删除1000个就退出
{
//第三个数据删除,此处有误,因为index循环时重新赋值了,导致((index %3)的错误
if((index %3) == 2)
{
delcount++;
printf(“%d/t”,list[index]);
}
else //前两个数据放到队列末尾,
{
list[tail]=list[index];
tail=(tail+1) % (2*MaxCount);
}
index=(index+1) % (2*MaxCount);//步近
}
return ;
}
// 缺点,不能输出最后一个删除数的原始下标,只能输出其值,因为该数组可能是随机的,下标与值可能不一致
// 此题的考点就在于删除过程中拷贝了数据后如何保存原始位置,否则的话我每次删除一个值后,将后面所有的数前移,然后再删,没有意义了
循环队列改正版
void RoundQueue(void)
{
int list[2*MaxCount]; //多分配一倍的空间,用来放循环数据
int i,index=0,counter=0,tail,delcount=0;
int lastdel = 0;
for(i=0;i < MaxCount;i++) list[i]=i; //初始化,存放0~999
tail=MaxCount; //list[tail]用来存放不删除的数据
while(delcount < MaxCount) //删除1000个就退出
{
counter++;
if(counter==3) //第三个数据删除,单独计数,不用index
{
delcount++;
counter = 0;
lastdel = list[index];
//printf(“%d/t”,list[index]);
}
else //前两个数据放到队列末尾,
{
list[tail]=list[index];
tail=(tail+1)%(2*MaxCount);
}
index=(index+1)%(2*MaxCount);//步近
}
printf(“%d/n”,lastdel);
}
××××××××××××××××××××××××××××××××
循环队列优化版
//#define STEP 2
//#define FST_ROUND_LEFT (MaxCount - MaxCount/(STEP+1))
//#define ARRAYSIZE (MaxCount+MaxCount)
//#define ARRAYSIZE (MaxCount+FST_ROUND_LEFT)
// 在第一轮里未删除的数,应该保存未删除数据的下标
// 以后待删除的数已经都是下标了,应该保存其值,最后删除的数就是其原始数的下标了
void RoundQueueOpt(void)
{
int list[ARRAYSIZE]; //多分配一倍的空间,用来放循环数据,其实多分配//FST_ROUND_LEFT即可了,即 list[FST_ROUND_LEFT+MaxCount],后面的循环值要改下
int i,index=0,counter=0,tail,delcount=0;
int lastdel = 0, firstround = 1;
for(i=0;i<MaxCount;i++)
list[i]=MaxCount - 1 - i; //初始化,存放999~0,此时下标和数是非对应的
tail=MaxCount; //list[tail]用来存放不删除的数据下标或已经保存的下标
while(delcount<MaxCount) //删除1000个就退出
{
counter++;
if(counter==3) //第三个数据删除
{
delcount++;
counter = 0;
if(firstround)
lastdel=index;
else
lastdel=list[index];
//printf("%d/t",list[index]);
}
else //前两个数据放到队列末尾,
{
if(firstround)
list[tail]=index;
else
list[tail]=list[index];
if(firstround)
if(tail - MaxCount == FST_ROUND_LEFT - 1)
{
firstround = 0; // 清除第一轮标志,以后保存值(保存后的下标)
printf("%d/n",FST_ROUND_LEFT);
}
tail=(tail+1)%(ARRAYSIZE);
}
index=(index+1)%(ARRAYSIZE);//步近
}
printf("%d/n",lastdel);
}
把数组当作一个循环队列,就可以循环运算了,关键在于将数据后移的过程中要保存住其初始下标位置,是通过计第一次循环来实现的
每次扫描的数都是有效的,因此此法相比标志数组法效率更高。但申请了额外的数组,因此空间效率较标志数组法低。
方法三:链表
如果先用链表存储数组的话,将数据和下标封装起来,建立链表,利用指针即可达到快速移动数据的目的;将原数组封装成单向循环链表,每隔两个删除一个节点,最后一个删除的节点即是所求,其保存了原始数据及其下标
当然由于此为单独建立了链表,原有的数组数据并未更改,因此得到了最后一个删除数据的下标即可访问原数组得到对应的数据;因此数据成员可以不含data域
此题是考链表的一个典型的例子
struct node
{
int data;
int index;
node* next;
};
//#define null 0
int LinkTest2(void)
{
int arr[SIZE];
for (int i=0;i < SIZE;++i)
arr[i]=SIZE-i; // 实际情况可能是随机的
node* head=new node;
head->data=arr[0];
head->index=0;
head->next=null;
node* p=head;
for(i=1;i < 1000;i++)
{
node* tmp=new node;
tmp->data=arr[i];
tmp->index=i;
tmp->next=null;
head->next=tmp;
head=head->next;
}
head->next=p;
while(p!=p->next) // p指向自己时,循环链表只剩一个元素了
{
p->next->next=p->next->next->next;
p=p->next->next;
}
// 存在内存泄漏
cout << p->index << ’ ‘<< p->data << endl;
return 0;
}
typedef struct Lnode
{
int num;
struct Lnode *next;
}Lnode,*Linklist;
Lnode *Create()
{
Lnode *head,*p1,*p2;
int i;
head=(Linklist)malloc(sizeof(Lnode));
head->next=NULL;
head->num=0; /头节点赋初值/
p2=head;
for(i=1;i<1000;i++)
{
p1=(Linklist)malloc(sizeof(Lnode));
p1->num=i;
p1->next=NULL;
p2->next=p1;
p2=p1;
}
p1->next=head; // 单向循环链表
return head;
}
void Del(Lnode *head)
{
Lnode *n,*m,*q;
int i=0;
m=head;
// m first n second q third should be deled
while(i<1000) // 未删完,当剩下三个以内时,释放q删除后可能出问题
{
n=m->next;
q=n->next; /* the elem which will be deleted */
n->next=q->next;
free(q); // 不释放q没有问题,但存在内存泄漏啊
m=n->next;
i++;
}
printf(“%d/n”,m->num);
}
void LinkTest1(void)
{
Lnode *q;
q=Create();
Del(q);
}