递归
递归设计经验
- 找重复(子问题)
- 找重复中的变化量->参数
- 找参数变化趋势->设计出口
练习策略
- 循环改递归
- 经典递归
- 大量练习,总结规律,掌握套路
- 找到感觉,挑战高难度
递归练习:
1.求n的阶乘
int f1(int n){
if(n==1)
return 1;
return n*f1(n-1);
}
2.打印i到j
void f2(int i,int j){
if(i>j)
return;
cout<<i<<endl;
f2(i+1,j);
}
3.对arr的所有元素求和
int f3(vector<int> arr,int begin){
if(begin==arr.size()-1)
return arr[begin];
return arr[begin]+f3(arr,begin+1);
}
4.斐波那契数列
int fib(int n){
if(n==1||n==2)
return 1;
return fib(n-1)+fib(n-2);
}
5.最大公约数
int gcd(int m,int n){
if(n>m){
int temp=n;
n=m;
m=temp;
}
if(n==0){
return m;
}
return gcd(n,m%n);
}
6.递归形式进行插入排序
void insertSort(vector<int> &arr,int k){
if(k==0){
return;
}
//对前k-1个元素排序
insertSort(arr,k-1);
//把位置k的元素插入到前面的部分
int temp=arr[k];
int index = k-1;
while(index>-1&&temp<arr[index]){
arr[index+1]=arr[index];
index--;
}
arr[index+1]=temp;
}
7.汉诺塔
//将1-N从A移动到B,C作为辅助
// 等价于:
// 1.1-(N-1)从A移动到C,B为辅助
// 2.把N从A移动到B
// 3.1-(N-1)从C移动到B,A为辅助
void Hanio(int N,string from,string to,string help){
if(N==1){
cout<<"move "<<N<<" from "<<from<<" to "<<to<<endl;
return;
}
Hanio(N-1,from,help,to);//先把N-1个盘子移动辅助空间上去
cout<<"move "<<N<<" from "<<from<<" to "<<to<<endl;
Hanio(N-1,help,to,from);//让N-1从辅助空间回到源空间上去
}
8.跨梯子
有N阶梯子,可以1步,2步,3步走,问有几种方式到达顶部。
//考虑到最后可以通过1步、2步、3步到达顶部,因此可以分解为f(n-1)、f(n-2)、f(n-3)种
#include<bits/stdc++.h>
using namespace std;
int f(int n)
{
if(n==0)
return 1;
if(n==1)
return 1;
if(n==2)
return 2;
return f(n-1)+f(n-2)+f(n-3);
}
int main()
{
int n;
cin>>n;
cout<<f(n);
return 0;
}
9.二分查找递归
#include<bits/stdc++.h>
using namespace std;
int binarySearch(int arr[],int low,int high,int key){
if(low>high)
return -1;
int mid=(high+low)>>1;
int midVal=arr[mid];
if(midVal<key)
return binarySearch(arr,mid+1,high,key);
else if(midVal>key)
return binarySearch(arr,low,high-1,key);
else
return mid;
}
int main()
{
int arr[100000];
for(int i=0;i<100000;i++){
arr[i]=i+1;
}
int target = 100000;
cout<<binarySearch(arr,0,100000-1,target);
return 0;
}
10.设计一个高效的求a的n次幂
#include<bits/stdc++.h>
using namespace std;
int mypow(int a,int n){
if(n==0) return 1;
int res=a;
int ex = 1;
//降低时间复杂度
//通过乘2加快接近需要的幂次
while((ex<<1)<=n){
res *= res;
ex<<=1;
}
return res*pow(a,n-ex);
}
int main()
{
int a=2;
int n=15;
cout<<mypow(a,n);
return 0;
}
排序
- 插入排序
从左往右遍历数组,将此时正在遍历的元素插入在其之前合适的位置,索引下标从1开始。
#include<bits/stdc++.h>
using namespace std;
void insertSort(vector<int> &vec){
for(int i=1;i<vec.size();i++){
int temp=vec[i];
int j=i-1;
while(j>-1&&temp<vec[j]){
vec[j+1]=vec[j];
j--;
}
vec[j+1]=temp;
cout<<"第"<<i<<"轮"<<endl;
for(int k=0;k<vec.size();k++){
cout<<vec[k]<<endl;
}
}
}
int main()
{
int arr[]={7,2,1,8,9,10,3,6,4,5};
vector<int> vec(arr,arr+10);
insertSort(vec);
return 0;
}
- 冒泡排序
外层从左往右遍历每个元素,内层从左往右遍历比较,将最大的数交换移动到最右,内层遍历长度随着i逐渐减小。
#include<bits/stdc++.h>
using namespace std;
void bubleSort(vector<int> &vec){
for(int i=0;i<vec.size()-1;i++)
{
for(int j=0;j<vec.size()-i-1;j++){
if(vec[j]>vec[j+1]){
int temp=vec[j];
vec[j]=vec[j+1];
vec[j+1]=temp;
}
}
}
}
int main()
{
int arr[]={7,2,1,8,9,10,3,6,4,5};
vector<int> vec(arr,arr+10);
bubleSort(vec);
for(int i=0;i<vec.size();i++){
cout<<vec[i]<<endl;
}
return 0;
}
- 希尔排序
即插入排序的缩小增量法,通过不断缩小增量提高效率。数组最开始无序时增量大,需要排序的元素个数少,排序快,之后逐渐有序,增量减小,因为已经大部分有序了,所以排序工作依然较快。
#include<bits/stdc++.h>
using namespace std;
void shellSort(vector<int> &vec){
//不断地缩小增量
for(int interval = vec.size()/2;interval>0;interval=interval/2){
for(int i=interval;i<vec.size();i++){
int target = vec[i];
int j=i-interval;
while(j>-1&&target<vec[j]){
vec[j+interval]=vec[j];
j-=interval;
}
vec[j+interval]=target;
}
}
}
int main()
{
int arr[10]={6,1,7,4,10,10,9,3,0,5};
vector<int> vec(arr,arr+10);
shellSort(vec);
for(int i=0;i<vec.size();i++){
cout<<vec[i]<<endl;
}
return 0;
}
二分查找
int binarySearch(int arr[],int low,int high,int key){
while(low<=high){
int mid=(low+high)>>1;
int midVal = arr[mid];
if(midVal<key){
low=mid+1;
}
else if(midVal>key){
high=mid-1;
}else{
return mid;
}
}
return -1;
}
二分查找变种应用:
1.翻转数组的最小值
将一个有序数组的前若干元素翻转拼接到数组最后形成新的数组,查找最小值的索引。如:{1,2,3,4,5},翻转前4个元素变成{5,1,2,3,4}
思路:
因为是有序,可以想到用效率更高的二分查找。
显而易见,最小值总是在最大值的后面,因此通过二分查找,最后的arr[end]即是最小值
注意:当中间值与arr[begin]、arr[end]都相同时,如{0,1,1,1,1}通过翻转变为{1,0,1,1,1}此时算出最小值为1,因此此时需要直接遍历查找出最小值
#include<bits/stdc++.h>
using namespace std;
int search(int arr[])
{
int min=9999;
for(int i=0;i<5;i++)
{
if(arr[i]<min)
min=arr[i];
}
return min;
}
int reserveSearch(int arr[],int len)
{
int begin=0;
int end=len-1;
//考虑特殊情况没有翻转
if(arr[begin]<arr[end])
return arr[begin];
while(begin+1<end){
int mid = (begin+end)>>1;
//如果左值、右值、中值都相等则采用遍历查找
if(arr[mid]==arr[begin]&&arr[mid]==arr[end])
search(arr);
if(arr[mid]>=arr[begin]){
begin=mid;
}else{
end=mid;
}
}
return arr[end];
}
int main()
{
int arr[]={5,1,2,3,4};
cout<<reserveSearch(arr,5);
return 0;
}
2.在有空字符串中的有序字符串数组中查找
给出一个含有空字符串的有序字符串数组,查找指定元素的索引,查找元素不为空字符串。
#include<bits/stdc++.h>
using namespace std;
int indexOf(string arr[],string t,int len){
int begin=0;
int end=len-1;
while(begin<=end){
int indexOfMid=(begin+end)>>1;
while(arr[indexOfMid]==""){
indexOfMid++;
if(indexOfMid>end)
return -1;
}
if(arr[indexOfMid]<t){
begin=indexOfMid+1;
}else if(arr[indexOfMid]>t){
end=indexOfMid-1;
}else{
return indexOfMid;
}
}
return -1;
}
int main()
{
string arr[]={"a","","ac","","ad","b","","ba"};
int res=indexOf(arr,"b",8);
cout<<res<<endl;
return 0;
}