将值插入到标准 c++ 数组的正确索引(升序)中

Inserting values into the correct index(ascending order) of a standard c++ array

本文关键字:索引 升序 插入 标准 数组 c++      更新时间:2023-10-16

我需要将两个值num1 = 50num2 = 80插入到一个按升序排序的数组中。我不能使用动态数组或列表。也没有结构或类。这是针对课堂作业的,所以我必须遵守准则。教授建议我创建一个新数组,newarray并复制我原始numarray中的值,直到我达到一个条件,提示将数字按升序插入它们适合的位置。我将循环设置为运行尽可能多的次数来填满我的newarray,并设置条件以检查numarray中的前一个数字是否小于num以及下一个值是否大于num. 数字将插入到正确的位置,但是,插入新值会覆盖该值。 我以为 newarray[newindex] = num1; newarray[newindex+1] = numarray[newindex];会将插入的值写入当前索引,然后将numarray[newindex]写入索引。

我将附加下面的功能。函数中的所有内容都是自包含的,除了numarray它只是最多 100 的排序值。提前谢谢。也许走一会儿会帮助我弄清楚。


void insertArray(int* numarray) {
int num1 = 50;
int num2 = 80;
int counter = 0;
int newarray[103] = {};
for (int newindex = 0; newindex < 103; newindex++) {
if (numarray[newindex] <= num1 && numarray[newindex+1] > num1) {
newarray[newindex] = num1;
newarray[newindex+1] = numarray[newindex];

}
else if (numarray[newindex] <= num2 && numarray[newindex+1] > num2) {
newarray[newindex] = num2;
newarray[newindex+1] = numarray[newindex];

}
else {
newarray[newindex] = numarray[newindex];
}
cout << newarray[newindex] << endl;
} 

}

int main() {
int numarray[100] = {};
randomgenerator();
read(numarray);
printArray(numarray);
searchArray(numarray);
Delete(numarray);
sortArray(numarray);
insertArray(numarray);
return 0;
}

更新:

为你们输入后,我尝试了建议的函数,它成功插入了两个值。它插入 0 而不是 50,插入 78 而不是 80。我让它插入 50,但我不明白是什么条件导致它插入 78,或者什么是 80。我尝试以与 for 循环相同的格式编写它以插入 50,但它不起作用。

void insertArray(int* numarray) {
int num1 = 50;
int num2 = 80;
int index = 0;
int newarray[102] = {};

for (; index < 100 && numarray[index] < num1; ++index) {
newarray[index] = numarray[index];
}
newarray[index++] = num1;
for (; index < 101 && numarray[index - 1] < num2; ++index) {
newarray[index] = numarray[index - 1];
}

if (index == 102) {
newarray[index++] = num2;
}
for (; index < 102; ++index) {
newarray[index] = numarray[index - 2];
}
for (int i = 0; i <= 101; i++) {
cout << newarray[i] << endl;
}
}

在我看来,这种方法通常是错误的。

新数组应在函数 main 中声明,插入数字后应输出该数组。

该函数不应一次插入两个数字。它应该只插入一个数字,但调用次数与数组中插入新数字的次数一样多。

当目标数组和源数组是同一个数组时,可以调用该函数来插入值。

因此,我建议在下面的演示程序中显示的以下方法。

#include <iostream>
void insert( const int *a1, size_t n, int *a2, int value )
{
const int *p = a1 + n;
a2 += n;
while ( ( p != a1 ) && ( value < *( p - 1 ) ) ) *a2-- = *--p;
*a2-- = value;
while ( p != a1 ) *a2-- = *--p;
}
int main() 
{
int a1[] = { 5, 15, 25, 35, 45, 55, 65, 75, 85, 95 };
const size_t N = sizeof( a1 ) / sizeof( *a1 );
int a2[N + 2];
insert( a1, N, a2, 50 );
for ( size_t i = 0; i < N + 1; i++ )
{
std::cout << a2[i] << ' ';
}
std::cout << 'n';
insert( a2, N + 1, a2, 80 );
for ( size_t i = 0; i < N + 2; i++ )
{
std::cout << a2[i] << ' ';
}
std::cout << 'n';
return 0;
}

程序输出为

5 15 25 35 45 50 55 65 75 85 95 
5 15 25 35 45 50 55 65 75 80 85 95 

至于你的代码,例如这个循环

for (int newindex = 0; newindex < 103; newindex++) {
if (numarray[newindex] <= num1 && numarray[newindex+1] > num1) {
//...

调用未定义的行为,因为数组numarray没有索引为 100、101 和 102 的元素。

此外,一般来说,它可能发生在源数组没有大于num1num2的元素的情况下。

另一种方法是将插入的值放在单独的数组中,然后在目标数组中合并源数组和插入值的数组。

这是一个演示程序。

#include <iostream>
void insert( const int *a1, size_t n1, const int *a2, size_t n2, int *result )
{
const int *p1 = a1;
const int *p2 = a2;
while ( p1 != a1 + n1 && p2 != a2 + n2 )
{
if ( *p2 < *p1 )
{
*result++ = *p2++;
}
else
{
*result++ = *p1++;
}
}
while ( p1 != a1 + n1 ) *result++ = *p1++;
while ( p2 != a2 + n2 ) *result++ = *p2++;
}
int main() 
{
int a1[] = { 5, 15, 25, 35, 45, 55, 65, 75, 85, 95 };
const size_t N1 = sizeof( a1 ) / sizeof( *a1 );
int a2[] = { 50, 80 };
const size_t N2 = sizeof( a2 ) / sizeof( *a2 );
int result[N1 + N2];

insert( a1, N1, a2, N2, result );
for ( int item : result )
{
std::cout << item << ' ';
}
std::cout << 'n';
return 0;
}

它的输出是

5 15 25 35 45 50 55 65 75 80 85 95

我完全同意莫斯科@Vlad的观点,即您的新数组应该在调用器中声明并作为参数传递给您的插入函数。如果您使用的是 POA(普通旧数组),这是必需的,因为当您的函数返回时,插入函数中声明的新 POA 将不复存在。您始终可以在插入函数中为数组分配存储,并返回指向已分配块的指针,但即便如此,如果您想将插入函数类型作为void,您也可以在调用者中声明它并将其作为参数传递。

有许多方法可以按排序顺序插入到新数组中,对于小数组(例如 1000 个左右的元素)来说,所有方法都可以。但是,由于原始数组已经排序,因此对于较大的数组,可以利用这一事实编写效率提高几个数量级的例程。使用排序数组的有效方法类似于bsearch

在那里,您只需将要插入的值与数组中的中间元素进行比较即可。如果它大于数组的第一个元素并且小于中间元素,则知道将其插入原始数组的前 1/2 中。此时,您可以将数组的整个第二个 1/2 复制到新数组的中间元素 + 1 并重复。

如果该值大于中间元素,则可以将原始数组的整个前 1/2 复制到新数组的开头,然后在数组的后 1/2 中继续搜索。

当您的值小于或等于子数组中的第一个元素时,您已经找到了插入点,并且可以将新数组中的值设置为要插入的值,将子数组的其余部分复制到从下一个索引开始的新数组中。

同样,如果值大于子数组的结束值,则可以将新数组中范围末尾的值插入到该值中,然后将剩余值从原始值复制到新数组中范围的开头。

这样做可以大大减少查找插入点所需的最坏情况迭代次数,并允许您使用更有效的副本来填充插入值上方和下方的新数组的元素。例如,对于一个包含 1,000,000 个元素的数组,如果在最后开始迭代并返回到开头,并且实际的新元素是数组中的第一个元素,则将迭代 1,000,000 次以插入新值并将剩余元素从原始数组复制到新数组。

与上述方法相比,您有效地将原始元素平分为子数组,最坏的情况是最多 20 次迭代以查找插入点,以及 2 次副本以复制插入值下方和上方的元素。

虽然您可以在一个函数中完成所有操作,但它有助于将函数拆分为两个单独的函数。一个用于查找将进行递归调用的新元素的插入位置(例如调用它findinspos根本不需要新数组作为参数),第二个函数(例如调用findinspos然后复制其余元素的insinsorted)

它们可以实现为:

/* returns index where v should be inserted in a for a given 
* start index and an array of nelem elements.
*/
int findinspos (const int *a, int start, int nelem, int v)
{
int mid = (start + nelem) / 2;
if (v <= a[start])                          /* exit conditon ins at start */
return start;
else if (a[nelem - 1] < v)                  /* exit condition ins at end */
return nelem;
else if (v < a[mid])                        /* v in 1st 1/2 subarray */
return findinspos (a, start, mid, v);
else                                        /* v in 2nd 1/2 subarray */
return findinspos (a, mid, nelem, v);
}
/* inserts v in sorted positon within the nelem elements of a with 
* the results stored in b.
*/
void insinsorted (const int *a, int nelem, int v, int *b)
{
int inspos = findinspos (a, 0, nelem, v);   /* get insert positon */
b[inspos] = v;      /* set value at inspos in new array */
if (inspos == 0)    /* if ins at start copy a beginning at next element */
memcpy (b + 1, a, nelem * sizeof *b);
else if (inspos == nelem)   /* if at end, copy a to b */
memcpy (b, a, nelem * sizeof *b);
else {              /* otherwise, copy begin and end of a to b */
memcpy (b, a, inspos * sizeof *b);
memcpy (b + inspos + 1, a + inspos, (nelem - inspos) * sizeof *b);
}
}

一个完整的测试程序(从莫斯科的 Vlad 借用测试数组),并允许您输入任何要插入的数字作为程序的第一个参数(默认值:如果未输入参数,则为4)可以是:

#include <iostream>
#include <cstring>
/* returns index where v should be inserted in a for a given 
* start index and an array of nelem elements.
*/
int findinspos (const int *a, int start, int nelem, int v)
{
int mid = (start + nelem) / 2;
if (v <= a[start])                          /* exit conditon ins at start */
return start;
else if (a[nelem - 1] < v)                  /* exit condition ins at end */
return nelem;
else if (v < a[mid])                        /* v in 1st 1/2 subarray */
return findinspos (a, start, mid, v);
else                                        /* v in 2nd 1/2 subarray */
return findinspos (a, mid, nelem, v);
}
/* inserts v in sorted positon within the nelem elements of a with 
* the results stored in b.
*/
void insinsorted (const int *a, int nelem, int v, int *b)
{
int inspos = findinspos (a, 0, nelem, v);   /* get insert positon */
b[inspos] = v;      /* set value at inspos in new array */
if (inspos == 0)    /* if ins at start copy a beginning at next element */
memcpy (b + 1, a, nelem * sizeof *b);
else if (inspos == nelem)   /* if at end, copy a to b */
memcpy (b, a, nelem * sizeof *b);
else {              /* otherwise, copy begin and end of a to b */
memcpy (b, a, inspos * sizeof *b);
memcpy (b + inspos + 1, a + inspos, (nelem - inspos) * sizeof *b);
}
}
int main (int argc, char **argv) {
int a[] = { 5, 15, 25, 35, 45, 55, 65, 75, 85, 95 },
nelem = sizeof a / sizeof *a,
*b = new int[nelem+1] {},
v = argc > 1 ? strtol (argv[1], NULL, 0) : 4;
for (int i = 0; i < nelem; i++)
std::cout << " " << a[i];
std::cout << 'n';
insinsorted (a, nelem, v, b);
for (int i = 0; i < nelem + 1; i++)
std::cout << " " << b[i];
std::cout << 'n';
delete[] b;
}

(注意:数组b分配了new,如果这超出了您的分配范围,只需将其替换为具有足够存储空间的数组来容纳原始元素和新元素 - 并且不要忘记从末尾删除delete[] b;)

就像我开始的,对于小数组 -- 如何做真的无关紧要,但对于较大的数组,利用原始数组已经排序的事实可以提供数量级的效率提升。

教授建议我应该创建一个新数组 newarray 并从我的原始数字数组中复制值,直到我达到提示将数字插入它们适合升序的条件。

所以让我们这样做。

void copyAndInsert(int* numarray, int size, int* newarray, int num1, int num2)
{
if (num1 > num2) { std::swap(num1, num2); } // ensure we write the smaller number first
int * num1pos = std::lower_bound(numarray, numarray + size, num1); // find where to put num1
int * new1pos = std::copy(numarray, num1pos, newarray); // copy the numbers less than num1
*new1pos++ = num1; // copy num1 and move past it
int * num2pos = std::lower_bound(num1pos, numarray + size, num2); // find where to put num2
int * new2pos = std::copy(num1pos, num2pos, new1pos); // copy the numbers between num1 and num2
*new2pos++ = num2; // copy num2 and move past it
std::copy(num2pos, numarray + size, new2pos); // copy the numbers greater than num2
}
int main() {
int numarray[100] = {};
int newarray[102] = {};
randomgenerator();
read(numarray);
printArray(numarray);
searchArray(numarray);
Delete(numarray);
sortArray(numarray);
copyAndInsert(numarray, 100, newarray, 50, 80);
return 0;
}

如果你不能使用标准标头<algorithm>中的东西,你可以按如下方式实现它们(改编自cpp首选项)

int* lower_bound(int* first, int* last, int value)
{
int count = last - first;
while (count > 0) {
int step = count / 2; 
int* it = first + step;
if (*it < value) {
first = ++it; 
count -= step + 1; 
}
else {
count = step;
}
}
return first;
}
int* copy(int* first, int* last, int* d_first)
{
while (first != last) {
*d_first++ = *first++;
}
return d_first;
}

我建议使用 c++ 标准库中的 std::merge。它将两个排序范围合并为一个排序范围。由于 numarray 已经排序,我们唯一需要做的就是确保我们添加的数字也已排序。

#include<algorithm>
void insertArray(int* numarray) {
int num1 = 50;
int num2 = 80;
std::vector<int> numbersToAdd{num1, num2};
//make sure numbersToAdd is sorted
std::sort(begin(numbersToAdd), end(numbersToAdd));
int newarray[102] = {};
std::merge(numarray, numarray + 100, begin(numbersToAdd), end(numbersToAdd), newarray);
}