一个更好的阵列移位算法?

时间:2013-03-31 21:57:21

标签: c++ arrays malloc free memmove

我有一个赋值,要求我在读取它们时对基于堆的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);

4 个答案:

答案 0 :(得分:1)

如果您在对方法进行微小更改后,可以使用memmove()功能,这可能比您自己的手动版本更快。根据一位评论者的建议,您不能使用memcpy(),因为内存区域不允许重叠(如果这样做,则行为未定义)。

在不更改存储类型或算法的情况下,您无法做很多其他事情。但是,如果您更改为使用链接列表,那么操作将变得更加高效,尽管您将进行更多内存分配。如果分配真的是一个问题(除非你在有限的嵌入式系统上,它可能不是),那么pool allocators或类似的方法可能会有所帮助。

编辑: 重新阅读您的问题,我猜测您实际上并未使用Heapsort,您只是意味着您的数组已在堆上分配(即使用malloc())并且您正在做一个简单的insertion sort。在这种情况下,下面的信息对您来说并没有多大用处,尽管您应该知道插入排序与批量插入相比效率非常低,然后是更好的排序算法(例如Quicksort,您可以使用标准库qsort()函数实现)。如果你只需要最低(或最高)的项目而不是完整的排序顺序,那么Heapsort仍然是有用的阅读。

如果您使用的是标准Heapsort,那么您根本不需要此操作 - 项目会附加到数组的末尾,然后是&#34; heapify&#34 ; operation用于将它们交换到堆中的正确位置。每个交换只需要一个临时变量来交换两个项目 - 它不需要像在代码片段中那样随机删除任何内容。它确实要求数组中的所有内容都是相同的大小(固定大小的就地字符串,或者更可能是一个指针),但是你的代码似乎已经假设(并且在标准中使用可变长度字符串{{ 1}}数组将是一件非常奇怪的事情。)

请注意,严格来说,Heapsort在二叉树上运行。由于您正在处理数组,我假设您正在使用实现,其中使用连续数组,其中索引char处的节点的子节点存储在标记n和{{ 1}}分别。如果情况并非如此,或者您根本没有使用Heapsort,则应该更详细地解释您为获得更有帮助的答案而尝试做些什么。

编辑: 以下内容是为了回应您上面的更新代码。

在重新分配过程中看到问题的主要原因是你是否踩踏了一些内存 - 换句话说,你要复制的东西超出你分配区域的大小。当您覆盖系统用于跟踪分配的值并导致通常会导致程序崩溃的各种问题时,这是一件非常糟糕的事情。

首先,你似乎对内存分配和释放的性质有些轻微的混淆。你分配了一个2n的数组,它本身很好。然后为每个字符串分配2n+1的数组,这也没关系。但是,您只需为初始数组调用char* - 这还不够。需要调用char才能匹配每个free()的调用,因此您需要释放分配的每个字符串,然后释放初始数组。

其次,您将free()设置为malloc()的倍数,这几乎肯定不是您想要的。这是用于存储sizeToMove常量的变量的大小。相反,你想要sizeof(MAX_STRING_SIZE)。在某些平台上,这些可能是相同的,在这种情况下,事情仍然有效,但并不能保证。例如,我希望它能够在32位平台上工作(其中MAX_STRING_SIZEsizeof(char*)的大小相同),但不能在64位平台上工作(他们会在这些平台上工作)。不是)。

第三,你不能只为一个已分配的块分配一个字符串常量(例如int) - 你在这里做的是替换指针。您需要使用char*"hello world"之类的内容将字符串复制到已分配的块中。为方便起见,我建议使用strncpy(),因为memcpy()存在问题,即它不能保证终止结果,但这取决于您。

第四,您仍然使用snprintf()而不是strncpy()来随机播放项目。

最后,我刚刚看到您的评论,您必须使用memcpy()memmove()。对于这些,没有等效的new,但如果事先知道所有事情,那就没问题了。看起来你正在尝试做的事情是这样的:

delete

我还没有编译和检查过,所以要注意一个一个错误之类的东西,但希望你能得到这个想法。无论您使用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(),此功能都应该有效,但它假定您已将字符串new复制到你将保留一个分配的缓冲区,因为它当然只存储一个指针。

请记住,当然您需要在此功能之外更新delete - 这只是为您插入一个项目到数组中的正确位置。如果函数返回item,则将listSize的副本增加1 - 如果它返回true,那么您没有分配足够的内存,因此您的项目未被添加。

另请注意,在C和C ++中,对于数组listSize,语法falselist完全等效 - 在&list[i]调用中使用第一个而不是list + i调用你会发现它更容易理解。

答案 1 :(得分:0)

我认为您正在寻找的是heapsort:http://en.wikipedia.org/wiki/Heapsort#Pseudocode

数组是实现二叉搜索树的常用方法(即,左子节点小于当前节点且右子节点大于当前节点的树)。

Heapsort对指定长度的数组进行排序。在你的情况下,由于数组的大小将增加“在线”,你需要做的就是调用你传递给heapsort的输入大小(即增加被认为是1的元素数量)。

答案 2 :(得分:0)

由于您的数组已排序且您无法使用任何其他数据结构,您最好的选择是执行二进制搜索,然后将数组向上移动一个位置以插入位置的空闲空间然后插入新元素在那个位置。

答案 3 :(得分:0)

为了最小化移动数组的成本,你可以使它成为一个指向字符串的指针数组:

string **_storedNames = new string*[4000];

现在您可以使用memmove(尽管您现在可能会发现逐个元素的副本足够快)。但是你必须自己管理各个字符串的分配和删除,这有点容易出错。

其他建议在原始数组上使用memmove的海报似乎没有注意到每个数组元素都是string(不是string*!)。您不能在类似的类上使用memmovememcpy