问题陈述中缺少将 A 的第一个元素与 B 的第一个元素进行比较的能力。
使用链表将允许快速旋转。使用归并排序应该相当快。
使用先到后轮换将 A 和 B 视为队列。将每个队列视为两个队列,一个输入(前)队列和一个输出(后)队列。使用计数来跟踪每个队列的输入和输出部分之间的边界。合并逻辑的每个输入队列都需要剩余运行计数。
对于初始传递,将元素分成两个队列,从 A 获取一个元素,追加到 B,从 A 获取另一个元素,追加到 A。完成后,A 和 B 包含大小为 1 的运行。
从 A 和 B 合并运行,再次在 A 和 B 之间交替合并运行输出,因此当合并过程完成时,队列已准备好进行下一个过程。
最终所有元素都在一个列表中,排序完成。
更新 - 我使用 C++ std::queue 为队列编写了一个测试程序,我用不到 0.4 秒的时间对我的 100 万个元素(32 位模式,32 位整数)进行排序系统(英特尔 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;
}