【问题标题】:Radix Sort implemented in C++用 C++ 实现的基数排序
【发布时间】:2010-11-19 06:33:00
【问题描述】:

我正在尝试通过创建一个程序来改进我的 C++,该程序将采用 1 到 10^6 之间的大量数字。将在每次传递中存储数字的存储桶是一个节点数组(其中节点是我创建的包含一个值和下一个节点属性的结构)。

根据最低有效值将数字排序到桶中后,我将一个桶的末尾指向另一个桶的开头(这样我就可以快速获取正在存储的数字而不会破坏顺序)。我的代码没有错误(编译或运行时),但是关于如何解决剩余的 6 次迭代(因为我知道数字的范围),我遇到了困难。

我遇到的问题是,最初这些数字是以 int 数组的形式提供给 radixSort 函数的。在排序的第一次迭代之后,数字现在存储在结构数组中。有什么方法可以重新编写我的代码,以便我只有一个用于 7 次迭代的 for 循环,或者我需要一个 for 循环运行一次,而它下面的另一个循环将运行 6 次,然后返回完全排序的列表?

#include <iostream>
#include <math.h>
using namespace std;

struct node
{
    int value;
    node *next; 
};

//The 10 buckets to store the intermediary results of every sort
node *bucket[10];
//This serves as the array of pointers to the front of every linked list
node *ptr[10];
//This serves as the array of pointer to the end of every linked list
node *end[10];
node *linkedpointer;
node *item;
node *temp;

void append(int value, int n)
{
    node *temp; 
    item=new node;
    item->value=value;
    item->next=NULL;
    end[n]=item;
    if(bucket[n]->next==NULL)
    {
        cout << "Bucket " << n << " is empty" <<endl;
        bucket[n]->next=item;
        ptr[n]=item;
    }
    else
    {
        cout << "Bucket " << n << " is not empty" <<endl;
        temp=bucket[n];
        while(temp->next!=NULL){
            temp=temp->next;
        }
        temp->next=item;
    }
}

bool isBucketEmpty(int n){
    if(bucket[n]->next!=NULL)
        return false;
    else
        return true;
}
//print the contents of all buckets in order
void printBucket(){
    temp=bucket[0]->next;
    int i=0;
    while(i<10){
        if(temp==NULL){
            i++;
            temp=bucket[i]->next;                       
        }
        else break;

    }
    linkedpointer=temp;
    while(temp!=NULL){
        cout << temp->value <<endl;
        temp=temp->next;
    }
}

void radixSort(int *list, int length){
    int i,j,k,l;
    int x;
    for(i=0;i<10;i++){
        bucket[i]=new node;
        ptr[i]=new node;
        ptr[i]->next=NULL;
        end[i]=new node;
    }
    linkedpointer=new node;

    //Perform radix sort
    for(i=0;i<1;i++){
        for(j=0;j<length;j++){          
            x=(int)(*(list+j)/pow(10,i))%10;            
            append(*(list+j),x);
            printBucket(x); 
        }//End of insertion loop
        k=0,l=1;

        //Linking loop: Link end of one linked list to the front of another
        for(j=0;j<9;j++){
            if(isBucketEmpty(k))
                k++;
            if(isBucketEmpty(l) && l!=9)
                l++;
            if(!isBucketEmpty(k) && !isBucketEmpty(l)){
                end[k]->next=ptr[l];
                k++;
                if(l!=9) l++;   
            }

        }//End of linking for loop

        cout << "Print results" <<endl;
        printBucket();

        for(j=0;j<10;j++)
            bucket[i]->next=NULL;                       
        cout << "End of iteration" <<endl;
    }//End of radix sort loop
}

int main(){
    int testcases,i,input;
    cin >> testcases;
    int list[testcases];
    int *ptr=&list[0];
    for(i=0;i<testcases;i++){
        cin>>list[i];
    }

    radixSort(ptr,testcases);
    return 0;
}

【问题讨论】:

  • 无意冒犯,但您的代码看起来像是一个很好的例子,如何将简单的事情复杂化;-)

标签: c++ algorithm sorting radix-sort


【解决方案1】:

我认为您的解决方案过于复杂。您可以使用输入中接收的单个数组来实现基数,每个步骤中的桶由一个索引数组表示,这些索引标记输入数组中每个桶的起始索引。

事实上,你甚至可以递归地做到这一点:

// Sort 'size' number of integers starting at 'input' according to the 'digit'th digit
// For the parameter 'digit', 0 denotes the least significant digit and increases as significance does
void radixSort(int* input, int size, int digit)
{
    if (size == 0)
        return;

    int[10] buckets;    // assuming decimal numbers

    // Sort the array in place while keeping track of bucket starting indices.
    // If bucket[i] is meant to be empty (no numbers with i at the specified digit),
    // then let bucket[i+1] = bucket[i]

    for (int i = 0; i < 10; ++i)
    {
        radixSort(input + buckets[i], buckets[i+1] - buckets[i], digit+1);
    }
}

当然buckets[i+1] - buckets[i]i 为9 时会导致缓冲区溢出,但为了便于阅读,我省略了额外的检查;我相信你知道如何处理它。

这样,您只需调用radixSort(testcases, sizeof(testcases) / sizeof(testcases[0]), 0),您的数组就会被排序。

【讨论】:

  • 我不确定,但按照我的理解,您将在每个递归步骤中仅对上一步中的一个桶进行排序。当您从最不重要的一个开始时,这意味着由于递归调用中存储桶的限制,您将让它们从最不重要到最重要排序。我错了吗?
  • 我不完全明白你的问题是什么:上面的算法是深度优先的,因为在第一个递归调用中创建的第二个桶(按最低有效数字排序)只会开始一旦第一个桶被完全排序(直到最高有效位),就被排序。是的,当您从最高有效数字到最低有效数字时,基数排序会起作用。
【解决方案2】:

因为您的值是 0 ... 1,000,000 范围内的整数

您可以创建一个大小为 1,000,001 的 int 数组,然后分两遍完成整个操作

将第二个数组初始化为全零。

通过您的输入数组,并使用该值作为下标 增加第二个数组中的值。

一旦你这样做了,那么第二遍就很容易了。 遍历第二个数组,每个元素都会告诉你多少次 number 出现在原始数组中。使用该信息重新填充 你的输入数组。

【讨论】:

  • 假设您的输入数组大小为 10。您将使用 32MB(假设为 32 位整数)对其进行排序?仅供参考,您所描述的是具有 64 位基数的基数排序。基数排序的挑战之一是选择不占用太多空间的合适基数。 8 位并不罕见,但即使是 16 位基数,辅助存储也需要 2^16*sizeof(int) = 256KB。
  • 我相信这叫做计数排序。
【解决方案3】:

为了通过更好的内存管理来加速该过程,通过对数组进行单次传递,为将转换为索引的计数创建一个矩阵。分配与原始数组相同大小的第二个临时数组,并在两个数组之间进行基数排序,直到对数组进行排序。如果执行了奇数次基数排序,则需要将临时数组最后复制回原始数组。

为了进一步加快这个过程,基数排序使用基数 256 而不是基数 10。这只需要 1 次扫描通过来创建矩阵和 4 次基数排序通过来进行排序。示例代码:

typedef unsigned int uint32_t;

uint32_t * RadixSort(uint32_t * a, size_t count)
{
size_t mIndex[4][256] = {0};            // count / index matrix
uint32_t * b = new uint32_t [COUNT];    // allocate temp array
size_t i,j,m,n;
uint32_t u;
    for(i = 0; i < count; i++){         // generate histograms
        u = a[i];
        for(j = 0; j < 4; j++){
            mIndex[j][(size_t)(u & 0xff)]++;
            u >>= 8;
        }       
    }
    for(j = 0; j < 4; j++){             // convert to indices
        m = 0;
        for(i = 0; i < 256; i++){
            n = mIndex[j][i];
            mIndex[j][i] = m;
            m += n;
        }       
    }
    for(j = 0; j < 4; j++){             // radix sort
        for(i = 0; i < count; i++){     //  sort by current lsb
            u = a[i];
            m = (size_t)(u>>(j<<3))&0xff;
            b[mIndex[j][m]++] = u;
        }
        std::swap(a, b);                //  swap ptrs
    }
    delete[] b;
    return(a);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    • 2010-11-22
    • 2021-03-10
    • 1970-01-01
    • 2016-11-17
    • 1970-01-01
    相关资源
    最近更新 更多