【问题描述】
编写程序,从任意n个数(升序排列)中插入某一个数k,使得数列仍然保持升序排列。
【输入形式】
输入分3行:第一行为n的值,第二行为n个数,第三行为要插入的数k。
【输出形式】
整个数列(n+1个数)
【样例输入1】:
5
1 2 3 4 6
0
【样例输出1】
0 1 2 3 4 6
【样例输入2】
5
1 2 3 4 6
5
【样例输出2】
1 2 3 4 5 6
【样例输入3】
5
1 2 3 4 6
8
【样例输出3】
1 2 3 4 6 8
【样例说明】
在数列1、2、3、4、6中插入0,整个数列变为0、1、2、3、4、6;如果是在数列中插入5,整个数列变为1、2、3、4、5、6;如果插入的是8,则整个数列变为1、2、3、4、6、8
解析
这道题目思路不难,就是注意一些细节就行了:
1、待插入的数字比原数列的任何数都大或比原数列任何数都小(特殊情况优先处理)
2、原数列是升序排序的,可以用二分查找提高检索效率。但二分查找有一定的局限性,所以第一点就要特殊情况优先处理。
3、原数列有若干项的数字是相同的,而待插入的数字也和这些项一样,要注意防止二分查找陷入死循环。例如:
10
1 3 5 7 7 7 7 7 7 9
7
待插入的数字7和原数列的某些连续项相同,要注意二分查找的时候防止陷入死循环。
4、当找到下标之后,要注意是插入左边还是插入右边。我写程序的时候是不断通过吧目标下标的右侧元素往右移动一个单位来实现数组元素的插入,也就是说默认是插入了目标下标的右边。所以要写一个判断,看看目标下标所对应的元素和待插入数字的大小;如果目标下标所对应的元素比待插入的数字大,那么就先将它们交换,再进行下一步右移操作。
例如:
10
10 20 30 40 50 60 70 80 90 100
75
经过二分查找之后,最终待插入的下标mid=8,a[mid]=80,而待插入的数字a[11]=75<80,如果直接进行移动操作:
for(int i=n;i>mid;i--)
swap(a[i],a[i+1]);
那么最终的结果就是
10 20 30 40 50 60 70 80 75 90 100
所以要先进行交换,因为待插入的数字比目标下标对应的元素小。
if(a[n+1]<a[mid]) swap(a[n+1],a[mid]);
那么最终的结果就是
10 20 30 40 50 60 70 75 80 90 100
代码
#include<iostream>
using namespace std;
const int N=10002;
int n,a[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cin>>a[n+1];
int l=1,r=n,mid;
while(l<=r)
{
mid=(l+r)/2;
if(a[n+1]>a[mid]) l=mid+1;
else if(a[n+1]<a[mid]) r=mid-1;
else break;//注意事项的第三点
}
mid=(l+r+1)/2;
if(a[n+1]<a[mid]) swap(a[n+1],a[mid]);//注意事项第四点
for(int i=n;i>mid;i--)
swap(a[i],a[i+1]);
for(int i=1;i<=n+1;i++)
cout<<a[i]<<" ";
}