繁体   English   中英

数组和C ++中的mergeSort?

[英]Arrays and mergeSort in c++?

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;
}

我的问题是关于此合并排序代码期间数组的性质。 该代码仅在设置为tArray[20]; 将初始值设置为20。为什么不能将初始值设置为hi + 1 (在这种情况下为8 (与数组相同)? 但是,如果我取消注释temp[8]数组,并通过mergeSortMerge传递它,并在Merge tArray其用作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; 在末尾。

合并排序被认为可用于列表,因为当您合并2个列表时,您可以将每个元素自由地附加到所需的元素上。

例:

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;
}

附带地,这使算法更加昂贵。 即使在您已经预分配了足够大的数组的情况下,算法的成本也更高,因为您必须在每次合并时复制所有元素。

您的代码中有多个问题,其中有几个:

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]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, 93, 76, 81, 76] ,在对mergeSort每次调用中,您将数组分为两个相等的部分:

第一次获得[90, 99, 63, 82]lo = 0hi = 3 )和[93, 76, 81, 76]lo = 4hi = 7 ),并且不断除法直到得到8个大小为1数组。 为了简单起见([90], 0, 0)我将编写(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 + 1 = 6 ,这比您实际需要的要大得多(在这里可能不会有很大的不同,但是请想象您是否有原始大小为10亿的数组!)。 因此,分配大小为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]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM