c++中的数组和归并排序

Arrays and mergeSort in c++?

本文关键字:归并排序 数组 c++      更新时间:2023-10-16
void Merge(int *array, int lo, int mid, int hi) {
int tArray[20];
int loBeg = lo;
int count = lo;
int hiBeg = mid + 1;
while (loBeg <= mid && hiBeg <= hi) {
if (array[loBeg] < array[hiBeg]) {
tArray[count] = array[loBeg];
loBeg++;
count++;
} else {
tArray[count] = array[hiBeg];
hiBeg++;
count++;
}
}
while (loBeg <= mid) {
tArray[count++] = array[loBeg++];
}
while (hiBeg <= hi) {
tArray[count++] = array[hiBeg++];
}
for (int i = 0; i < count; i++) {
array[i] = tArray[i];
}
}
void mergeSort(int *array, int lo, int hi) {
if (lo < hi) {
int mid = (lo + hi) / 2;
mergeSort(array, lo, mid);
mergeSort(array, mid + 1, hi);
Merge(array, lo, mid, hi);
}
}
int main(int argc, const char * argv[]) {
int array[] = {90, 99, 63, 82, 93, 76, 81, 76};
//int temp[8];
mergeSort(array, 0, 7);
for (int i = 0; i < 8; i++) {
std::cout << array[i] << std::endl;
}
return 0;
}

我的问题是关于数组的性质在这个合并排序代码。这段代码只适用于在Merge, O中设置tArray[20];的初始值为20。为什么我不能将初始值设置为hi + 1,在这种情况下是8(与数组相同)?但是,如果我取消注释出temp[8]数组,并通过mergeSortMerge传递它,并将其用作Merge中的tArray(初始大小为8),则它可以工作。

我认为我缺乏理解也是我原来的merge()(见下文)函数也不起作用的原因:

//this was my original merge code which does not work.
void merge(int *array, int lo, int mid, int hi) {
int tempArray[hi + 1];
int loBeg = lo;
int hiBeg = mid + 1;
for (int i = 0; i <= hi; i++) {
if (hiBeg > hi || (array[loBeg] < array[hiBeg] && loBeg <= mid)) {
tempArray[i] = array[loBeg++];
} else {
tempArray[i] = array[hiBeg++];
}
}
for (int i = 0; i <= hi; i++) {
array[i] = tempArray[i];
}
}

所以基本上我想知道为什么在第一个Merge()函数中,我必须将tArray初始大小设置为20而不是与数组相同的大小,除非我从main传递一个临时数组(初始化为与数组相同的大小),然后进一步,为什么我原来的merge()函数不起作用,但我认为这与我缺乏对第一个Merge()函数的理解有关。

当你创建一个像int array[20];这样的数组时,你是在你的程序的堆栈内存中分配它。这个内存是在程序开始之前分配的。

你的问题来了。当您尝试执行int array[hi + 1];

时,您要求它分配在程序启动之前未知的内容量,这会导致错误。在这种情况下,你需要做的是使用动态内存。这些内存在运行时分配并释放。这意味着您可以执行int* array = new int[hi + 1];并且不会导致错误。

整个合并函数将是:

void merge(int *array, int lo, int mid, int hi) {
int* tempArray = new int[hi + 1];
int loBeg = lo;
int hiBeg = mid + 1;
for (int i = 0; i <= hi; i++) {
if (hiBeg > hi || (array[loBeg] < array[hiBeg] && loBeg <= mid)) {
tempArray[i] = array[loBeg++];
} else {
tempArray[i] = array[hiBeg++];
}
}
for (int i = 0; i <= hi; i++) {
array[i] = tempArray[i];
}
delete[] tempArray;
}

我不建议你自己管理动态内存。您应该使用STL:vector<int> tempArray(hi + 1);而不是int* tempArray = new int[hi + 1];。这样,您就不必在末尾加上delete[] tempArray;了。

您的代码中存在多个问题,以下是其中的几个:

void Merge(int *array, int lo, int mid, int hi) {
// The size should be hi - lo + 1, not hi + 1
int tArray[hi + 1];
int loBeg = lo;
// count should start at 0, not lo
int count = lo;
int hiBeg = mid + 1;
while (loBeg <= mid && hiBeg <= hi) {
if (array[loBeg] < array[hiBeg]) {
tArray[count] = array[loBeg];
loBeg++;
count++;
} else {
tArray[count] = array[hiBeg];
hiBeg++;
count++;
}
}
while (loBeg <= mid) {
tArray[count++] = array[loBeg++];
}
while (hiBeg <= hi) {
tArray[count++] = array[hiBeg++];
}
for (int i = 0; i < count; i++) {
// it should be array[lo + i], not array[i]
array[i] = tArray[i];
}
}

以下是对这3个错误的解释:

  1. 你不希望每次都有一个大小为hi + 1的数组,当你沿着"合并树"走下去时,你必须合并较小大小的数组,所以你需要使用hi - lo + 1。实际上,在这种情况下使用hi + 1应该不是问题,但是您分配了比实际需要更多的内存。

  2. 您不应该从lo开始,而应该从0开始,否则您的tArray[count]将超出边界。从lo开始,因为你分配了一个非常大的数组(大小为20hi + 1),但它不适用于大小为hi - lo + 1的数组,所以你应该小心。

  3. 也许最大的错误-你总是替换原始数组的第一个count单元…不!您希望替换lohi之间的单元格,而不是0count之间的单元格。

您需要记住,当您调用Merge (array, lo, mid, hi)时,您要将array[lo:mid]array[mid+1:hi]合并到array[lo:hi]中。


这里有一些详细的解释:

让我们从下面的数组[90, 99, 63, 82, 93, 76, 81, 76]开始,在每次调用mergeSort时,您将数组分成两等份:

第一次你得到[90, 99, 63, 82](lo = 0,hi = 3)和[93, 76, 81, 76](lo = 4hi = 7),然后你继续除法,直到你得到大小为18数组。为了简单起见,我将写(array, lo, hi),例如([90], 0, 0)

你应该得到一个点,你有([90], 0, 0),([99], 1, 1),等等…你将合并这些2乘2,所以你(例如)将合并([93], 4, 4)([76], 5, 5)。当您合并这两个数组时,您将获得Merge的调用,如下所示:

Merge (array, 4, 4, 5) // mid is (4 + 5) / 2 = 4

那么,Merge函数中发生了什么?

  1. 您应该注意到,由于您正在合并两个大小为1的数组([93][76]),因此您需要一个大小为2的临时数组。如果您使用tArray[hi + 1],则分配一个大小为hi + 1 = 6的数组,这比您实际需要的数组大得多(这里可能没有太大的区别,但想象一下如果您有一个大小为十亿的原始数组!)。因此,分配一个大小为hi - lo + 1 = 5 - 4 + 1 = 2的数组就足够了。

  2. 你实际上是你的初始数组的45的范围,所以你想在那部分工作,你不想擦除部分01,你已经排序!但是如果在循环中执行array[i] = tArray[i]会发生什么呢?好吧,i0开始,到count(在这种情况下应该是2),所以你要替换array[0]array[1],而不是array[4]array[5]。这可以通过修改array[lo + i]而不是array[i]来修复。

归并排序被认为适用于列表,因为当你合并两个列表时,你可以自由地将每个元素附加到你想要的元素上。

的例子:

Merge(List{3,4},List{5,6});

导致以下操作

List Merge(List a, List b){ //2 instructions even when Lists are long 1000000 elements
List newlist;
newlist.head = a.head;
newlist.tail = b.tail;
a.tail.next = b.head;
b.head.prev = a.tail;
return newList;
}

当你有数组的时候你需要分配一个新的数组大小:

int * Merge( int array* a, int array* b, unsigned int sizea, unsigned int sizeb){
//int tArray[20]; // :/ wrong
int * newarray = new int[sizea+sizeb];
for(unsigned int i=0; i<sizea; i++)
newarray[i]=a[i];
for(unsigned int i=0; i<sizeb; i++)
newarray[i+sizea]=a[i];
delete []a;
delete []b;
return newarray;
}

这顺便使算法更加昂贵。即使你预先分配了一个足够大的数组,算法的成本也会更高,因为你必须在每次合并时复制所有的元素。