使用有限的操作尽快对大号列表(100k)进行排序

时间:2016-11-27 00:16:17

标签: c algorithm list sorting

我有两个列表:列表A包含一组随机数,列表B为空。我必须对列表A进行排序。

我可以对这两个列表进行有限的操作,如:

  • 在列表B或A
  • 的开头移动列表A或B的第一个元素
  • 交换列表A或B的两个第一个元素,如32 41 8 9成为41 32 8 9
  • 使列表A或B的最后一个元素成为此列表中的第一个元素(轮换),如32 41 8 9成为9 32 41 8
  • 使列表A或B的第一个元素成为此列表中的最后一个元素(轮换),如32 41 8 9成为41 8 9 32

我已经设置了一个算法来使用列表B作为堆栈和允许的操作集来对列表A进行排序,但是当列表变大(超过1000个元素)并因此不执行时需要时间所有。

我还尝试使用这组操作设置合并排序算法,但是使用10k的数字需要花费太多时间(超过10秒)。

任何人都知道有效的算法可以快速执行此类操作吗?我也使用链表并在C中执行我的程序以优化效率。

1 个答案:

答案 0 :(得分:1)

缺少问题陈述是能够将A的第一个元素与B的第一个元素进行比较。

使用链接列表可以快速旋转。使用合并排序应该相当快。

首先使用旋转将A和B视为队列。将每个队列视为两个队列,一个输入(前)队列和一个输出(后)队列。使用计数来跟踪每个队列的输入和输出部分之间的边界。合并逻辑的每个输入队列都需要剩余的运行计数。

对于初始传递,将元素分成两个队列,从A获取元素,追加到B,从A获取另一个元素,追加到A.一旦完成,则A和B包含大小为1的运行。

合并从A和B运行,再次在A和B之间交替合并运行输出,因此当完成合并传递时,队列已准备好进行下一次传递。

最终所有元素都会在一个列表中结束并完成排序。

update - 我为队列编写了一个使用C ++ std :: queue编写的测试程序,对我的100万个元素(32位模式,32位整数)进行排序需要不到0.4秒的时间系统(英特尔3770k 3.5 ghz)。使用链表的C程序比std :: queue涉及的开销更少,因此速度更快。

示例C ++程序用于概念验证,它使用两个std :: queues,使用front / pop / push来有效地进行旋转。输入结构(qs0,qs1)不需要指针,但它使输入和输出引用保持一致。结构的输出指针(pqso)在两个队列结构(qs0,qs1)之间交替。有两个goto用于分支内部循环的常见“清理”代码,它复制“其他”运行的其余部分并突破内部循环。这个例子是基于一个旧的合并排序程序,因为这是一个“概念证明”,我没有打扰它。

#include <iostream>
#include <queue>
#include <cstdlib>
#include <ctime>

typedef unsigned int uint32_t;

#define min(a, b)  (((a) < (b)) ? (a) : (b))

typedef struct{
    std::queue <uint32_t> *pq;              // ptr to queue
    size_t fcnt;                            // front count
    size_t bcnt;                            // back count
    size_t rcnt;                            // run count
}QS;

void msort2q(std::queue <uint32_t> &q0, std::queue <uint32_t> &q1, size_t n)
{
QS qs0;                                     // queue 0
QS qs1;                                     // queue 1
QS *pqsi0 = &qs0;                           // input 0
QS *pqsi1 = &qs1;                           // input 1
QS *pqso;                                   // output
size_t s;                                   // run size

    if(n < 2)
        return;
    pqsi0->pq = &q0;
    pqsi0->fcnt = n;
    pqsi0->bcnt = 0;
    pqsi1->pq = &q1;
    pqsi1->fcnt = 0;
    pqsi1->bcnt = 0;
    pqso = pqsi1;
    while(1){                               // split q0
        pqso->pq->push(pqsi0->pq->front());
        pqsi0->pq->pop();
        pqso->bcnt++;
        pqsi0->fcnt--;
        if(pqsi0->fcnt == 0)
            break;
        pqso = (pqso == pqsi0) ? pqsi1 : pqsi0;
    }
    pqsi0->fcnt = pqsi0->bcnt;
    pqsi0->bcnt = 0;
    pqsi1->fcnt = pqsi1->bcnt;
    pqsi1->bcnt = 0;
    s = 1;
    while(s < n){                       // merge sort
        while(1){                       // merge a pair of runs
            pqsi0->rcnt = min(s, pqsi0->fcnt);
            pqsi0->fcnt -= pqsi0->rcnt;
            pqsi1->rcnt = min(s, pqsi1->fcnt);
            pqsi1->fcnt -= pqsi1->rcnt;
            if(pqsi0->rcnt == 0)        // if end run 0
                goto copy1;             //   copy rest of 1 and break
            if(pqsi1->rcnt == 0)        // if end run 1
                goto copy0;             //   copy rest of 0 and break
            while(1){                   // merge an element
                if(pqsi0->pq->front() <= pqsi1->pq->front()){   // if q0 <= q1
                    pqso->pq->push(pqsi0->pq->front());         //   move q0
                    pqso->bcnt++;
                    pqsi0->pq->pop();
                    pqsi0->rcnt--;
                    if(pqsi0->rcnt != 0)    // if not end run 0
                        continue;           //   continue back to while
copy1:              do{                     //  else copy rest of run 1 and break
                        pqso->pq->push(pqsi1->pq->front());
                        pqso->bcnt++;
                        pqsi1->pq->pop();
                        pqsi1->rcnt--;
                    }while (pqsi1->rcnt);
                    break;
                } else {                                        // else q1 < q0
                    pqso->pq->push(pqsi1->pq->front());         //   move q1
                    pqso->bcnt++;
                    pqsi1->pq->pop();
                    pqsi1->rcnt--;
                    if(pqsi1->rcnt != 0)    // if not end run 1
                        continue;           //   continue back to while
copy0:              do{                     //  else copy rest of run 0 and break
                        pqso->pq->push(pqsi0->pq->front());
                        pqso->bcnt++;
                        pqsi0->pq->pop();
                        pqsi0->rcnt--;
                    }while (pqsi0->rcnt);
                    break;
                }
            }
            pqso = (pqso == pqsi0) ? pqsi1 : pqsi0;     // setup for next pair
            if(pqsi0->fcnt == 0 && pqsi1->fcnt == 0)    // if end pass break
                break;
        }
        pqsi0->fcnt = pqsi0->bcnt;      // setup for next pass
        pqsi0->bcnt = 0;
        pqsi1->fcnt = pqsi1->bcnt;
        pqsi1->bcnt = 0;
        s *= 2;
    }
    if(pqsi0->fcnt == 0)                // swap if needed
        std::swap(q0, q1);
}

#define NUMELEM (1024*1024)
int main()
{
clock_t dwTimeStart;                    // clock values
clock_t dwTimeStop;
uint32_t r;
size_t i;

    std::queue <uint32_t> q0;
    std::queue <uint32_t> q1;

//      generate data

    for(i = 0; i < NUMELEM; i++ )
    {
        r  = (((uint32_t)((rand()>>4) & 0xff))<< 0);
        r += (((uint32_t)((rand()>>4) & 0xff))<< 8);
        r += (((uint32_t)((rand()>>4) & 0xff))<<16);
        r += (((uint32_t)((rand()>>4) & 0xff))<<24);
        q0.push(r) ;
    }

//      sort data
    dwTimeStart = clock();
    msort2q(q0, q1, NUMELEM);
    dwTimeStop = clock();
    std::cout << "Number of ticks " << (dwTimeStop-dwTimeStart) << std::endl;

//      check data
    while(1){
        r = q0.front();
        q0.pop();
        if(q0.size() == 0)
            break;
        if(r > q0.front()){
            std::cout << "error" << std::endl;
            break;
        }
    }
    return 0;
}