任务一 二分搜索及变形
(1)在有序数组里面查找是否存在某个元素x, 如果存在, 则返回相应元素所在索引号;如果不存在, 返回-1。分别用递归和非递归的方式实现。
算法描述:
① 将数组分成 l~m-1,m, m+1~r 三部分
② 如果a[m] 为所找值,算法结束;如果所求值小于a[m],将数组l~m-1部分继续操作①;如果所求值大于a[m],将数组m+1~r部分继续操作①。
源码:
#include <bits/stdc++.h>
using namespace std;
int find(int a[], int l, int r, int x){
int m = l + r >> 1;
if(a[m] == x) return m;
if(x < a[m]) find(a, l, m - 1, x);
else find(a, m + 1, r, x);
}
int ffind(int a[], int l, int r, int x){
while(l < r){
int m = (l + r) / 2;
if( a[m] == x){
return m;
}
if( x > a[m] ) l = m + 1;
else r = m - 1;
}
}
int main(){
int a[] = {1, 5, 6, 7, 9, 10, 12, 44};
int x = 12;
int l = 0;
int r = 7;
cout << "递归: " << find(a, l, r, x) << endl;
cout << "非递归: " << ffind(a, l, r, x);
}
(2)在循环有序数组中查找指定元素x。
算法描述
① 将数值分成l~m,m+1~r两部分
② 如果某一部分有序,且所求值在此范围,利用二分查找算法,否则对另一部分执行①
源码
#include <bits/stdc++.h>
using namespace std;
//递归
int Binaryseach (int a[], int l, int r, int x){
int m = l + r >> 1;
if(x == a[m]) return m;
if( x > a[m]) Binaryseach(a, m + 1, r, x);
else Binaryseach(a, l, m - 1, x);
return -1;
}
int find(int a[], int l, int r, int x){
int m = l + r >> 1;
if(a[l] < a[m]){ //左边有序
if(a[l] <= x && x <= a[m]) //若x位于有序区间 ,二分查找
return Binaryseach(a, l, m, x);
else find(a, m + 1, r, x); //否则 继续查找含x的有序区间
}
else{
if(a[m] <= x && x <= a[r])
return Binaryseach(a, m, r, x);
else find(a, l, m - 1, x);
}
}
//非递归
int nBinaryseach(int a[], int l, int r, int x){
while(l < r){
int m = l + r >> 1;
if(a[m] == x) return m;
if(a[m] > x) l = m + 1;
else r = m - 1;
}
}
int nfind(int a[], int l, int r, int x){
while(l < r){
int m = r + l >> 1;
if(a[m] > a[l]){
if(a[l] <= x && x <= a[m])
return nBinaryseach(a, l, m, x);
else l = m + 1;
}
else
if(a[m] <= x && x <= a[r])
return nBinaryseach(a, m, r, x);
else r = m - 1;
}
}
int main(){
int a[] = {9, 12, 16, 18, 20, 41, 100, 1, 4, 6};
int l = 0;
int r = 9;
int x = 41;
cout << "递归 " <<find(a, 0, 9, x) << endl;
cout << "非递归 "<<nfind(a, 0, 9, x) << endl;
return 0;
}
(3)假如集合中的元素有重复, 要找到指定元素x首次出现的位置。
算法描述
①将数组分成l~m,m+1~r两部分
②所求数<=a[m],对l~m执行①,否则对m+1~r执行①
③当r >= l,返回l;
源码
#include<bits/stdc++.h>
using namespace std;
int Binaryseach(int a[], int l, int r, int x){
int m = l + r >> 1;
if(l >= r) return l;
if(x <= a[m]) Binaryseach(a, l, m, x);
else Binaryseach(a, m+1, r, x);
}
int nBinaryseach(int a[], int l, int r, int x){
while(l < r){
int m = l + r >> 1;
if(x <= a[m]) r = m;
else l = m + 1;
}
return l;
}
int main(){
int a[] = {1, 5, 6, 7, 9, 10, 10, 10, 12, 44};
cout << "递归"<<Binaryseach(a, 0, 9, 10) << endl;
cout << "非递归"<<nBinaryseach(a, 0, 9, 10) << endl;
}
(4)在一个有序的数组里, 数据里面元素可能有重复的, 查找指定元素x所在的索引范围。
算法描述
求首位置如(3)
求尾位置:
①将数组分成l~m-1,m~r两部分
②所求数<a[m],对l~-m-1执行①,否则对m~r执行①
源码
③当r >= l,返回l;
源码
#include<bits/stdc++.h>
using namespace std;
//尾位置递归写法
int Binaryseach(int a[], int l, int r, int x){
int m = l + r + 1>> 1;
if(l >= r) return l;
if(x < a[m]) Binaryseach(a, l, m - 1, x);
else Binaryseach(a, m, r, x);
}
//首位置非递归写法
int nBinaryseach(int a[], int l, int r, int x){
while(l < r){
int m = l + r >> 1;
if(x <= a[m]) r = m;
else l = m + 1;
}
return l;
}
int main(){
int a[] = {1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 6, 6, 7, 9, 11, 12};
cout << nBinaryseach(a, 0, 15, 3) << endl;
cout << Binaryseach(a, 0, 15, 3) << endl;
}
(5)利用二分搜索的基本思想, 设计一个算法求n个数的最大值和最小值。
算法描述
①将数组分成l~m,m+1~r两部分
② 若l<r,执行① 否则返回a[l]
③比较两部分返回值 返回较小(大)值
源码
#include<bits/stdc++.h>
using namespace std;
int min(int a[], int l, int r){
//cout << l << " ** " << r << endl;
int m = l + ((r - l) >> 1 );
if(l == r) return a[l];
int m1 = min(a, l, m);
int m2 = min(a, m + 1, r);
//cout << m1 << " " << m2 << endl;
if(m1 > m2) return m2;
else return m1;
}
int max(int a[], int l, int r){
//cout << l << " ** " << r << endl;
int m = l + ((r - l) >> 1 );
if(l == r) return a[l];
int m1 = max(a, l, m);
int m2 = max(a, m + 1, r);
//cout << m1 << " " << m2 << endl;
if(m1 < m2) return m2;
else return m1;
}
int main(){
int a[] = {2, 5, 7, 6, 7, 10, 30};
cout << min(a, 0, 6) << endl;
cout << max(a, 0, 6);
}
任务二 合并排序
(1)数组a中元素为{49, 38, 65, 67, 76, 13, 27, 28}, 用合并排序进行升序排序, 并输出排序结果。分别用递归和非递归的方式实现。
算法描述
①将数组不断分成两个集合
②分别对两个子集进行排序
③把排好序对子集合并成有序集合
源码
#include<bits/stdc++.h>
using namespace std;
void fun(int a[], int l, int m, int r){
int i = l;
int j = m + 1;
int k = 0;
int t[10000];
while(i <= m && j <= r){
if(a[i] < a[j])
t[k++] = a[i++];
else
t[k++] = a[j++];
}
while( i <= m) t[k++] = a[i++];
while( j <= r) t[k++] = a[j++];
i = 0;
while(i < k){
a[l++] = t[i++];
}
}
// 递归
void sort(int a[], int l, int r){
if(l < r){
int m = (l + r) / 2;
sort(a, l, m);
sort(a, m+1, r);
fun(a, l, m, r);
}
}
//非递归
void fsort(int a[], int l, int r){
int s = 1;
while(s <= r){
l = 0;
while(l <= r){
int p = l + s*2 - 1;//
if(p > r) p = r; //防止越界 分成 【l, mid】 【mid + 1 , r】
int m = l + s - 1;//保证m是上次分段的断点 例如 0 1 2 3 4 5 6 7第一次断点是 0 2 4 6 第二次 1 5
if(m > r) m = r;
fun(a, l, m, p);
l = p + 1;
}
s += s;
}
}
void input(int a[], int n){
int i = 0;
while( i < n) cout << a[i ++] << " ";
cout << endl;
}
int main(){
int n = 8;
int a[] = {49,38,65,67,76,13,27,28};
sort(a,0,n - 1);
cout << "递归: " ;
input(a, n);
fsort(a,0,n - 1);
cout << "非递归: " ;
input(a, n);
return 0;
}
(2)数组a中元素为{49, 38, 65, 67, 76, 13, 27, 28}, 用自然合并排序进行升序排序, 并输出排序结果。
算法描述
- 找出数组的有序段,并记录下来
- 对相邻的有序段进行合并排序,直至数组完全有序
源码
#include<bits/stdc++.h>
using namespace std;
int fun1(int a[], int n, int b[]){//找出各有序断点,并存入数组b,返回数组b元素个数
int i = 0;
int j = 1;
while(i < n){
if(a[i] > a[i+1])
b[j++] = i+1;
i++;
}
if(b[j-1] != n)//若最后一个字符为单独一段 作此判断
b[j++] = n;
return j;
}
void merge(int a[], int l, int m, int r){
int c[10] = {0};
int i = l;
int j = m;
int k = 0;
while(i < m && j < r){
if(a[i] < a[j]) c[k++] = a[i++];
else c[k++] = a[j++];
}
while(i < m) c[k++] = a[i++];
while(j < r) c[k++] = a[j++];
i = 0;
while(i < k){
a[l++] = c[i++];
}
}
void nmergesort(int a[], int b[], int m){
if(m == 1) return;
else{
int i = 0;
int s = 1;
while( i + s <= m){
if( m % 2 == 0) i ++;//偶数 说明可以分成奇数段 故先不把第一段加入排序
while(i < m - 1){ //先做循环在做判断 所以新的循环 < m-1
int l = b[i];
i += s;
int c = b[i];
i += s;
int r = b[i];
merge(a, l, c, r);
}
s+=s;
i = 0;
}
if(m % 2 == 0){
merge(a, 0, b[1], b[m-1]);
}
}
}
int main(){
int a[]={49, 38, 65, 67, 76, 13, 27, 28};
int b[10] = {0};
int m = fun1(a, 8, b);
nmergesort(a, b, m);
int i = 0;
while(i < 8)
cout << a[i++] << " " ;
return 0;
}
任务三 快速排序
算法描述
①选择第一个数为基准,将其放在数组中的正确位置,且保证其左边数<=(>=)基准,右边>=(<=)基准
②以基准位置对数组二分,再子集做①操作
③当所有都作为基准后,结束
源码
#include <bits/stdc++.h>
using namespace std;
int fun(int a[], int l, int r){
int b = a[l];
while(l < r){
while(a[r] >= b && l < r)
r--;
a[l] = a[r];
while(a[l] <= b && l < r)
l++;
a[r] = a[l];
}
a[r] = b;
return r;
}
void QuickSort(int a[], int l, int r){
if(l < r){
int m = fun(a, l, r);
QuickSort(a, l, m-1);
QuickSort(a, m+1, r);
}
}
int main(){
int a[6] = {6, 7, 5, 2, 4, 8};
QuickSort(a,0,5);
for(int i = 0; i < 6; i++)
cout << a[i] << " ";
}