C语言基础编程题(约瑟夫环:隔二删除)

删数(约瑟夫环):每隔两个数循环删除数组元素,到末尾时循环至开头继续,直至最后一个元素,返
回其数组下标。
//分析:本题的关键是如何删除,暴力求解法,一个个删除,并将自后的元素全部往前移,时间复杂度
高;另一种是遍历过程跳过需要删除的数;另一个关键是如何在移动过程中保持数组的原始下标,因为
数据本身可能是无序的,下标和数据可能没有关系,并且还可能重复。

性能分析:循环队列法由于每次扫描的数都是未删除的,时间整体效率高;标志数组法只是将此数组标
记为删除了,但遍历时仍然要扫描;链表法要建立链,时间效率低。

方法:
数组方法一的优化版本,程序结构易懂清晰。
数组标志法:将高位置1遍历完后清除,效率最高
循环队列法,创新性。


-------------------------------------------------
方法一:访问原始数组,置删除标志
数组方法一:
 

const int size = 1000;
void ArrayTest1(void)
{
    int arr[size];
    int currentSize = size;//指示当前数组中还剩余的有效元素的个数,为1时表示删除完毕
    int count =0; //间隔计数
    for(int k =0;k

------------------------------------------------------------------------

数组方法一优化:

宏定义意义明确,更改方便,是良好的编程习惯,要在笔试面试中展现这种特点

删除数据时保存了其位置和实际的数据值,无需最后一次扫描

三层判断条件功能清晰:总数,是否删除,是否达到间隔值

//数组方法一优化
//利用宏定义,删除数据时保存位置,无需最后一次扫描
#define SIZE 1000
#define STEP 2
#define DELFLAG (SIZE+1)

void ArrayTest1Opt(void)
{
    int arr[SIZE];
    int currentSize = SIZE;//指示当前数组中还剩余的有效元素的个数,为0时表示删除完毕;
    int count = 0;
    int i=0;
    int lastdelindex = 0;//用于保存每次删除的值得位置,不用留到最后一个然后遍历
    int lastdelvalue = 0;//用来保存每次删除值,不用留最后一个然后遍历

    for(int k=0;k

 -------------------------------------------------------------------------------

数组方法二(标记清零):

对于只读数组,也可以置标志,访问完毕后能够恢复初始序列即可

当前存放的数是正数,且有一定的范围,可是利用其最高位做为已经删除标志,最后一个删除的数其最高位为0,下标可知,实际数据可知。所以数据删除完后,清除最高位的删除标志,即可恢复原始序列。

//标记数组法二:对于只读数组,也可以置标志,访问完毕后能够恢复初始序列即可
//当前存放的数是正数,且有一定的范围,可以利用其最高位作为已经删除标志,最后一个删除的数其最高位为0,下标可知,实际数据可知。
//所以数据删除完毕后,清除最高位的删除标志,即可恢复原始序列。
#define delFLAG (1<<((sizeof(int)*8)-1))
void ArrayTest2(void)
{
    int arr[SIZE];
    int currentSize = SIZE;//指示当前数组中还剩余的有效元素个数
    int count = 0;//间隔计数
    int lastdel = 0;
    int i=0;

    for(int k=0;k=0)//当前数大于等于零,未删除,已经删除则看下一个
        {//按照间隔计数,达到间隔时删除数据
            if(count++ == STEP)
            {
                lastdel = i;
                arr[i] = delFLAG;//将此数最高位置1,此时为负数,表示已经删除
                currentSize--;//有效元素减一
                count = 0;//记数位归零
            }
        }
        i = (i+1)%SIZE;
    }
    printf("The result is :%d",lastdel);
    ///清除最高位的1,恢复原始数组
    for(int s=0;s

---------------------------------------------------------------------------

循环队列法: 循环队列,将未访问的数据移动到循环队列末尾 

//循环队列,将未访问的数据移动到队列末尾
#define MaxCount 1000

void RoundQueue(void)
{
    int list[2*MaxCount];//多分配一倍空间,用来存放循环数据
    int i,index=0,count++,tail,delcount=0;
    int lastdel = 0;
    
    for(i=0;i

-------------------------------------------------

链表法:如果先用链表存储数组的话,将数据和下标封装起来,建立链表,利用指针即可达到快速移动数据的目的;将原数组封装成单向循环链表,每隔两个删除一个节点,最后一个删除的节点即是所求,其保存了原始数据及其下标
当然由于此为单独建立了链表,原有的数组数据并未更改,因此得到了最后一个删除数据的下标即可访问原数组得到对应的数据;因此数据成员可以不含data域
此题是考链表的一个典型的例子
 

//链表法
struct node
{
    int data;
    int index;
    node* next;  
};
#define null 0
int LinkTest(void)
{
    int arr[SIZE];
    for(int i=0;idata=arr[0];
    head->index=0;
    head->next=null;
    
    node *p =head;
    for(int j=1;j<1000;j++)
    {
        node tmp = (node*)malloc(sizeof(node));
        tmp->data=arr[j];
        tmp->index=j;
        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;
    }
    printf("index is :%d",p->index);
    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(int 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;
    
    while(i<1000)
    {
        n=m->next;
        q=n->next;//需删除的元素
        n->next=q->next;
        ree(q);
        m=n->next;
        i++
    }
    printf("%d\n",m->num);
}

 

参考鸣谢:https://blog.csdn.net/sailor_8318/article/details/1800812

你可能感兴趣的:(C语言基础)