c++ 更好的数组移位算法?

h43kikqp  于 12个月前  发布在  其他
关注(0)|答案(4)|浏览(222)

我有一个任务,要求我排序一个基于堆的C风格数组的名称,因为他们正在阅读,而不是阅读他们都,然后排序。这涉及到大量的将数组的内容逐位移动,以允许插入新的名称。我正在使用下面的代码,但它非常慢。我还能做些什么来优化它而不改变存储类型吗?

//the data member
string *_storedNames = new string[4000];

//together boundary and index define the range of elements to the right by one
for(int k = p_boundary - 1;k > index;k--)
   _storedNames[k]=_storedNames[k - 1];

EDIT2:正如Cartroo所建议的,我正试图将memmove用于使用malloc的动态数据。目前,这正确地转移了数据,但在释放过程中再次失败。我是不是漏了什么?

int numberOfStrings = 10, MAX_STRING_SIZE = 32;

char **array = (char **)malloc(numberOfStrings);

for(int i = 0; i < numberOfStrings; i++)
    array[i] = (char *)malloc(MAX_STRING_SIZE);

array[0] = "hello world",array[2] = "sample";   

    //the range of data to move
int index = 1, boundary = 4;
int sizeToMove = (boundary - index) * sizeof(MAX_STRING_SIZE);

memcpy(&array[index + 1], &array[index], sizeToMove);

free(array);
ui7jx7zq

ui7jx7zq1#

为了最小化数组移位的成本,你可以把它变成一个指向string的指针数组:

string **_storedNames = new string*[4000];
  • 现在 * 您可以使用memmove(尽管您可能会发现现在逐个元素的复制已经足够快了)。但是您必须自己管理单个字符串的分配和删除,这有点容易出错。

其他推荐在原始数组上使用memmove的发帖者似乎没有注意到每个数组元素都是string(而不是string*!你不能在这样的类上使用memmovememcpy

0vvn1miw

0vvn1miw2#

如果您希望对方法进行最小的更改,则可以使用memmove()函数,该函数可能比您自己的手动版本更快。你不能像一个评论者建议的那样使用memcpy(),因为内存区域不允许重叠(如果重叠,行为是未定义的)。
在不改变存储类型或算法的情况下,你不能做很多其他事情。但是,如果您更改为使用链表,则操作将变得更加有效,尽管您将执行更多的内存分配。如果分配确实是个问题(除非你在一个有限的嵌入式系统上,否则它可能不是),那么pool allocators或类似的方法可能会有所帮助。

*编辑: 重读你的问题,我猜你实际上并没有使用堆排序,你只是说你的数组是在堆上分配的(即,使用malloc()),而您正在执行简单的insertion sort。在这种情况下,下面的信息对你没有多大用处,尽管你应该意识到插入排序与批量插入和更好的排序算法(例如,Quicksort,可以使用标准库qsort()函数实现)。如果你只需要最低(或最高)的项目,而不是完整的排序顺序,那么堆排序仍然是有用的阅读。

如果你使用的是标准的Heapsort,那么你根本不需要这个操作--元素被附加在数组的末尾,然后“heapify”操作被用来将它们交换到堆中的正确位置。每次交换只需要一个临时变量来交换两个项目-它不需要像代码片段中那样向下拖动任何东西。它确实要求数组中的所有内容都是相同的大小(固定大小的就地字符串,或者更可能的是指针),但您的代码似乎已经假定了这一点(在标准char数组中使用可变长度字符串将是一件非常奇怪的事情)。
请注意,严格地说,堆排序是在二叉树上操作的。因为你正在处理一个数组,我假设你正在使用一个连续数组的实现,其中索引n处的节点的子节点分别存储在索引2n2n+1处。如果不是这种情况,或者你根本没有使用堆排序,你应该更详细地解释你正在尝试做什么,以获得更有帮助的答案。

编辑: 以下是对您以上更新代码的回应。

在释放过程中出现问题的主要原因是,如果您践踏了一些内存-换句话说,您正在复制超出所分配区域大小的内容。这是一件非常糟糕的事情,因为你覆盖了系统用来跟踪你的分配的值,并导致各种各样的问题,这些问题通常会导致你的程序崩溃。
首先,您似乎对内存分配和释放的本质有些混淆。您分配了一个char*数组,它本身就很好。然后为每个字符串分配char数组,这也是可以的。但是,您随后只需调用free()作为初始数组-这是不够的。需要调用free()来匹配对malloc()的每个调用,因此需要释放分配的每个字符串,然后释放初始数组。
其次,您将sizeToMove设置为sizeof(MAX_STRING_SIZE)的倍数,这几乎肯定不是您想要的。这是用于存储MAX_STRING_SIZE常量的变量的大小。你想要的是sizeof(char*)。在某些平台上,这些可能是相同的,在这种情况下,事情仍然可以工作,但不能保证这一点。例如,我希望它能在32位平台上工作(其中intchar*大小相同),但不能在64位平台上工作(它们不是)。
第三,你不能只指定一个字符串常量(例如,"hello world")到一个分配的块-你在这里做的是 * 替换 * 指针。您需要使用类似strncpy()memcpy()的东西将字符串复制到分配的块中。为了方便起见,我建议使用snprintf(),因为strncpy()有一个问题,即它不能保证结果为nul-terminate,但这取决于您。
第四,您仍然使用memcpy()而不是memmove()来 Shuffle 。
最后,我刚刚看到你的评论,你必须使用newdelete。对于这些,没有realloc()的等价物,但如果事先知道一切,这是可以的。看起来你要做的事情是这样的:

bool addItem(const char *item, char *list[], size_t listSize, size_t listMaxSize)
{
    // Check if list is full.
    if (listSize >= listMaxSize) {
        return false;
    }
    // Insert item inside list.
    for (unsigned int i = 0; i < listSize; ++i) {
        if (strcmp(list[i], item) > 0) {
            memmove(list + i + 1, list + i, sizeof(char*) * (listSize - i));
            list[i] = item;
            return true;
        }
    }
    // Append item to list.
    list[listSize] = item;
    return true;
}

我还没有编译和检查,所以要小心一个错误之类的,但希望你能明白。无论你使用malloc()free()还是newdelete,这个函数都应该工作,但是它假设你已经将字符串item复制到了一个分配的缓冲区中,你将保留这个缓冲区,因为它当然只是存储一个指针。

记住,当然你需要在这个函数之外自己更新listSize--这只是为你在数组中的正确位置插入一个元素。如果函数返回true,则将listSize的副本增加1 -如果它返回false,则您没有分配足够的内存,因此没有添加项目。
还要注意,在C和C++中,对于数组list,语法&list[i]list + i是完全等效的-如果您觉得更容易理解,请在memmove()调用中使用第一个。

70gysomp

70gysomp3#

我想你要找的是堆排序:http://en.wikipedia.org/wiki/Heapsort#Pseudocode
数组是实现二叉搜索树的常用方法(即,其中左子节点小于当前节点而右子节点大于当前节点的树)。
堆排序对指定长度的数组进行排序。在你的例子中,由于数组的大小将“在线”增加,你所需要做的就是调用change你传递给堆排序的输入大小(即,将所考虑的元素的数量增加1)。

x7rlezfr

x7rlezfr4#

因为你的数组是排序的,你不能使用任何其他的数据结构,你最好的办法是执行二进制搜索,然后将数组上移一个,以释放插入位置的空间,然后在该位置插入新元素。

相关问题