我有两个列表:列表A包含一组随机数,列表B为空。我必须对列表A进行排序。
我可以对这两个列表进行有限的操作,如:
32 41 8 9
成为41 32 8 9
32 41 8 9
成为9 32 41 8
32 41 8 9
成为41 8 9 32
我已经设置了一个算法来使用列表B作为堆栈和允许的操作集来对列表A进行排序,但是当列表变大(超过1000个元素)并因此不执行时需要时间所有。
我还尝试使用这组操作设置合并排序算法,但是使用10k的数字需要花费太多时间(超过10秒)。
任何人都知道有效的算法可以快速执行此类操作吗?我也使用链表并在C中执行我的程序以优化效率。
答案 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;
}