现在的位置: 首页 > 综合 > 正文

Josephu 问题解决策略:数组与链表的融合,十倍速度的差距(百万级数据)

2013年12月09日 ⁄ 综合 ⁄ 共 2136字 ⁄ 字号 评论关闭

 

           Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。

                    一种最通常的想法是弄个数组,将每一个位置的值都初始化为1,然后进行遍历,数到m时,将其置为0。遇到为0的就跳过继续向下,到最后一个就循环回来。直到只剩一个元素为止。

 

           数组的实现方法:

#include <stdio.h>
#include <malloc.h>

#define n 1000000
#define m 4
int main()
{

    int flag, i, j = 0;
    int k=0;
    int list[n];
    int arr[n];
    for (i = 0; i < n; i++){
        arr[i] = 1;
    }
    for (i = 1; i < n; i++)
    {
        flag = 0;
        while (flag < m)
        {
            if (j == n)  j = 0;  //到了末尾就循环
            if (arr[j]){         //当为1时记录经过的个数,0时跳过
                flag++;
            }
            j++;                
        }
        arr[j - 1] = 0;     //删除出队的数据
        list[k] = j;        //将数据存入结果数组中
        k++;
    }
    list[k]=j-1;           //存入最后一个数
    /*
    for(i=0;i<n;i++){
        printf(" %d ",list[i]);
    }
    */
    printf("\n");
}

       当数据为1000000时,Fedora 测试时间为real 0m0.270s  

  

           或者采用循环链表,即数到了就删除一个节点。直到只剩一个节点为止。虽然指针的操作比数组要慢,但是随着程序的循环进行,链表中的元素越来越少,遍历的数据量也就相应的减少了。

   

        链表实现方法:

 

#include<stdio.h>
#include<stdlib.h>

#define N 1000000
#define M 4

typedef struct node{
    int id;
    struct node* next;
}node;
int main(){
    int i=1;
    node *pre=NULL;
    node *head;
    int list[N];
    for(i;i<=N;i++){              //队列的初始化
        node *n=(node*)malloc(sizeof(node));
        n->id=i;
        if(pre){
            pre->next=n;
        }else{
            head=n;               //第一个元素
        }
        pre=n;
    }
    pre->next = head;             //循环队列
    int j=0;
    node *p=head;
    node *q=p;
    while(p->next != p){
        for(i=1;i<M;i++){         //向后数M个
            q=p;             
            p=p->next;
        }
        q->next = p->next;        //删除节点
        list[j]=p->id;             //记录结果
        j++;
        free(p);
        p=q->next;                //找回P的位置
    }
    list[j]=p->id;                //最后一个数据
    /*
    for(j=0;j<N;j++){
        printf(" %d ",list[j]);
    }
    */
    printf("\n");
}

                非常巧的是,这种算法在数据达到100万的时候正好与数组的算法效率相当。Fedora测试耗时real 0m0.276s

 

       这里要介绍的一种优化了的方法,既能达到数组的访问速度,又能像链表一样有选择性的遍历

 

        大体的逻辑是这样的:要想要数组的访问速度,存储结构必然是数组。链表的特性是存储了下一个数据的指针。所以就想到在数组中存储下一个数据的位置。访问时,数组中的内容作为下一个数据的地址。想要修改“链表”的格式,只修改数据内容就行了。这就实现了跳跃有选择的访问。

 

#include<stdio.h>
#include<stdlib.h>

#define N 1000000
#define M 4

int main(){
    int a[N];
    int i,j=0,k=0;
    int list[N];
    for(i=0;i<N;i++){
        a[i]=i+1;          //每一个位置的内容是下一个位置
    }
    a[9]=0;                //最后一个要指回来
    while(j != a[j]){      //位置和内容一致了就是剩下最后一个了
        for(i=1;i<M;i++){
            j=a[j];        //通过访问数组值的方式访问数组
        }
        list[k]=a[j];      //记录数据
        k++;
        a[j]=a[a[j]];      //相当于循环队列中的删除节点
    }
    list[k]=a[j];          //记录最后一个
    /*
    for(i=0;i<N;i++){
        if(list[i] == 0){
            list[i]=N;     //因为是循环的所以最后一个数据为N,而不是地址0
        }
        printf(" %d ",list[i]);
    }
    */

    printf("\n");
}

 

              应用这种数据结构,程序的访问速度就快的多了。当数据为100万时,测试耗时仅:real 0m0.026s  与前两种方法差出几乎十倍!

 

 

 本篇博客出自  阿修罗道,转载请注明出处:http://blog.csdn.net/fansongy/article/details/6882586

 

 

 

 

抱歉!评论已关闭.