算法第二章上机实验报告

 算法第二章上机实验报告

软工1802 20181003080 吴子熙

实验题目一:

7-1 二分查找 (20 分)
 

输入n值(1<=n<=1000)、n个非降序排列的整数以及要查找的数x,使用二分查找算法查找x,输出x所在的下标(0~n-1)及比较次数。若x不存在,输出-1和比较次数。

输入格式:

输入共三行: 第一行是n值; 第二行是n个整数; 第三行是x值。

输出格式:

输出x所在的下标(0~n-1)及比较次数。若x不存在,输出-1和比较次数。

输入样例:

4
1 2 3 4
1

输出样例:

0
2
代码如下:
#include <iostream>
using namespace std;

int binarysearch(int *a, int n, int key, int &count){
int left = 0;
int right = n-1;

while(left <= right){
int mid = (left + right)/2;
count++;
if(key == a[mid]){
return mid;
}
else if(key < a[mid]){
right = mid - 1;
}
else
{
left = mid + 1;
}

}
return -1;
}

int main(){
int n , key;
int count = 0;
int *a = new int [1000];
cin>>n;
for(int i = 0; i < n; i++){
cin>>a[i];}
cin>>key;

int median = binarysearch(a, n, key, count);

cout<<median<<endl;
cout<<count;
}

算法描述:

第一题比较简单,首先说算法思路。

题目要求二分搜索,而且输入数组为非降序,于是可以采用取数组的中值下标的方法,判断a[mid]和目标元素key的大小关系。并将其放入while循环当中,不断地缩小

搜索范围。

如果相等则直接返回count计数器的值即可。

若a[mid]>key,则使得数组的右极限缩减到mid元素之前,即right下标等于mid-1,否则左极限增加到mid元素之后,即left下标等于mid+1。同时count++进行一次查找计数。

选择执行while的条件为

left <= right
则最后一次循环结束之后仍然a[mid]不等于key的,即可结束循环返回-1,元素不存在。
该算法的时间复杂度为O(log(n))
通过T(n) = T(n/2)+O(1)算得。
空间复杂度为O(1)。


实验题目二:
7-2 改写二分搜索算法 (20 分)
 

题目来源:《计算机算法设计与分析》,王晓东

设a[0:n-1]是已排好序的数组,请改写二分搜索算法,使得当x不在数组中时,返回小于x的最大元素位置i和大于x的最小元素位置j。当搜索元素在数组中时,i和j相同,均为x在数组中的位置。

输入格式:

输入有两行:

第一行是n值和x值; 第二行是n个不相同的整数组成的非降序序列,每个整数之间以空格分隔。

输出格式:

输出小于x的最大元素的最大下标i和大于x的最小元素的最小下标j。当搜索元素在数组中时,i和j相同。 提示:若x小于全部数值,则输出:-1 0 若x大于全部数值,则输出:n-1的值 n的值

输入样例:

在这里给出一组输入。例如:

6 5
2 4 6 8 10 12

输出样例:

在这里给出相应的输出。例如:

1 2

代码如下:
#include <iostream>
using namespace std;

int binarysearch1(int *a, int n, int key){
int left = 0;
int right = n-1;
int i = 0, j =0;

while(left <= right){

int mid = (left + right)/2;

if(key == a[mid]){
i = j = mid;
cout<<i<<" "<<j;
return mid;
}
else if(key < a[mid]){
right = mid - 1;
}
else
{
left = mid + 1;
}
i = right;
j = left;

}
cout<<i<<" "<<j;

}

int main(){
int n ,key;
int *a = new int [10000];
cin>>n>>key;
for(int i = 0; i < n; i++){
cin>>a[i];
}
binarysearch1(a, n ,key);


}

算法思路:

本体基于第一题的二分搜索进行修改,核心内容不变,只需添加输出的内容即可。

在最后一次执行while循环的时,条件为left等于right,证明现在数组只剩下一个元素了,所以left和right下标所指向的一定是同一个元素。这个元素和key不相等则证明不存在。

再往下执行,得出的mid下标也是指向该元素,再通过与所找元素比较,根据判断结果将left或者right进行最后一次的下标位置移动。

并根据要求的“输出小于x的最大元素的最大下标i和大于x的最小元素的最小下标j”,把right的值给i,left给j,再结束循环,输出的i和j就分别是小于x的最大元素的最大下标和大于x的最小元素的最小下标。

该算法的时间复杂度为O(log(n)),空间复杂度为O(1)
因为算法核心和第一题一样所以复杂度计算也相同。

实验题目三
7-3 两个有序序列的中位数 (20 分)
 

已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列,的中位数指A(N1)/2​​的值,即第⌊个数(A0​​为第1个数)。

输入格式:

输入分三行。第一行给出序列的公共长度N(0<N≤100000),随后每行输入一个序列的信息,即N个非降序排列的整数。数字用空格间隔。

输出格式:

在一行中输出两个输入序列的并集序列的中位数。

输入样例1:

5
1 3 5 7 9
2 3 4 5 6

输出样例1:

4

输入样例2:

6
-100 -10 1 1 1 1
-50 0 2 3 4 5

输出样例2:

1
代码如下
#include<iostream>
using namespace std;
int MidSerch(int a[], int b[], int la, int ra, int lb, int rb) {
int ma = (la + ra) / 2;
int mb = (lb + rb+1) / 2;
if (la == ra&&lb != rb) {
if (a[la] > b[rb])return b[rb];
else return a[la];
}
if (lb == rb&&la != ra) {
if (b[lb] > a[ra])return a[ra];
else return b[lb];
}
if (la == ra&&lb == rb) {
if (a[la] > b[lb])return b[lb];
else return a[la];
}
else
{ if (la == ra - 1 && lb == rb - 1) 
{
ma = (la + ra) / 2;//第一个序列求中位数方法不变
mb = (lb + rb) / 2;//第二个序列变为与第一序列相同的方法求得中位数
//但由于元素个数只有两个,所以括号内可写为:mb = mb - 1;
}
if (a[ma]==b[mb])
{
return a[ma];
}
else if (a[ma] > b[mb]) {
MidSerch(a, b, la, ma ,mb, rb);
}
else{
MidSerch(a, b, ma, ra, lb, mb);
}
}
} 
int main() {
int n;
cin >> n;
int *a = new int[n];
int *b = new int[n];
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
for (int i = 0; i < n; i++)
{
cin >> b[i];
}
cout<<MidSerch(a, b, 0, n - 1, 0, n - 1);
system("pause");
}

本题本人尚未完全清楚,算法思路是否完全严谨,能看出算法思路是首先判断上下两个数组的中位数大小,然后小的取其数组的右侧剩余元素,大的选择其左侧剩余元素,组成一个新数组,继续二分。

感觉未能够完全处理并集之后元素重复该如何处理的问题

该代码若测试如下数据

5
2 2 2 3 5
2 2 7 8 9

结果数值为2,显然是错误的。要解决这个问题还需要再判断条件上进行补充和修复,亦或是需要新的算法?

算法时间复杂度是O(log(n)),空间复杂度:由于该算法并没有动态申请空间,故空间复杂度为O(1)。

 

心得体会:本次实验是针对二分算法的上机实验,在结对编程的互相合作中加深了对二分算法的理解,已经粗略地学会了二分算法的应用。
但是对时间复杂度的要求还是未能满足(第三题),目前我认为依照上述算法虽然能够达成要求的时间复杂度(不重新排序)但是未能普及所有情况。
然后感谢有一个很强悍的队友出谋划策并悉心指导,互相帮助,我受益匪浅,特别是学会了一些代码规范以及巧妙的思路。希望以后能更多的进行结对编程。

 
posted on 2019-09-23 21:16  午自习  阅读(475)  评论(0编辑  收藏  举报