转载
c++:https://blog.csdn.net/qq_38265137/article/details/85628925
习题:https://blog.csdn.net/qq_44391957/article/category/8657987
java:https://blog.csdn.net/qq_38265137/article/details/85628925
目录
02递归和排序
递归的要点
找变化,找重复,找出口,子问题要与原问题有同样的结构。
练习:
求阶乘
打印i--j
数组求和
翻转字符串
1.求最大公约数
辗转相除法, 又名欧几里德算法(Euclidean algorithm),是求最大公约数的一种方法。它的具体做法是:用较大数除以较小数,再用出现的余数(第一余数)去除除数,再用出现的余数(第二余数)去除第一余数,如此反复,直到最后余数是0为止。如果是求两个数的最大公约数,那么最后的除数就是这两个数的最大公约数。(采自百度词条)
假设有两个数x和y,存在一个最大公约数z=(x,y),即x和y都有公因数z,
那么x一定能被z整除,y也一定能被z整除,所以x和y的线性组合mx±ny也一定能被z整除。(m和n可取任意整数)
对于辗转相除法来说,思路就是:
若x>y,设x/y=n余c,
则x能表示成x=ny+c的形式,
将ny移到左边就是x-ny=c,由于一般形式的mx±ny能被z整除,
所以等号左边的x-ny(作为mx±ny的一个特例)就能被z整除,
即x除y的余数c也能被z整除。
原文链接:https://blog.csdn.net/hello_woo/article/details/79293235
/* 辗转相除法求最大公约数 */
#include <iostream>
using namespace std;
/*m是较大数,n是较小数*/ //最大公约数greatest common divisor
int gcd(int m, int n){
if(n == 0){
return m;
}
else{
return gcd(n, m%n);
}
}
int main() {
cout << gcd(36,24) <<endl;
}
性能分析:
m%n < (m/2)
所以 n%(m%n)< (n/2),每两次进行折半计算。
2*lgn,O(lgn)
2.插入法排序递归
/*插入排序改递归*/
#include <iostream>
using namespace std;
void insertSort(int* arr , int Index)
{
if(Index == 0) //递归结束的条件
return ;
insertSort(arr , Index - 1);
int num = arr[Index]; //保留当前的数
int index = Index -1 ;
while(num < arr[index])
{
/*向后移一位*/
arr[index + 1] = arr[index];
index --;
}
/*元素k应在的位置*/
arr[index + 1] = num;
}
int main()
{
int arr[4] = {1,2,3,4};
insertSort(arr , 3);
for(auto i : arr)
cout<< i << endl;
return 0;
}
3.汉诺塔问题
1~N从A移动到B,C作为辅助等价于:
1、1~(N-1)从A移动到C,B为辅助
2、把N从A移动到B
3、1~(N-1)从C移动到B,A为辅助(最终结果都是移动到B上,所以子问题形式与原问题相同)
#include <iostream>
using namespace std;
#define MAX 100
/*汉诺塔*/
void Hanoi(int N, string from, string to, string help){
if(N == 1){
cout << "move " << N << " from " << from << " to " << to << endl;
return;
}
else{
Hanoi(N-1, from, help, to);
cout << "move " << N << " from " << from << " to " << to << endl;
Hanoi(N-1, help, to, from);
}
}
int main() {
Hanoi(5, "A", "B", "C");
}
算法性能分析:
递归关系:T(n) = 2T(b-1) + O(1)
结果:O(2^n)
4.二分查找的递归解法
三个子问题:
左边找
中间比
右边找
/*二分查找*/
int binarysearch(int arr[], int low, int high, int key){
if(low > high)
return -1;//未找到
int mid = low + ( (high - low) >> 1 );
int val = arr[mid];
if(key > val){
binarysearch(arr, mid+1, high, key);
}
else if(key < val){
binarysearch(arr, low, mid-1, key);
}
else{
return 1;//找到
}
}
5.希尔排序
一趟一个增量,每个增量对应于N/inerval组,每组内采用插入排序
/*希尔排序*/
void ShellSort(int arr[], int K){
int interval;//增量
//不断缩小增量
for(interval=K/2; interval>0; interval=interval/2){
//增量为interval的插入排序
int i;//i表示组
for(i=interval; i<K; i++){//遍历每一组
int x = arr[i];
int index = i - interval;
while(index>-1 && x < arr[index]){
/*将比当前元素大的数后挪interval位*/
arr[index + interval] = arr[index];
index -= interval;
}
/*元素k应在的位置*/
arr[index + interval] = x;
}
}
}
测试代码:便于观察规律。
//希尔排序
#include <iostream>
using namespace std;
void shellSort(int *arr, int k)
{
int interval; //增量
//不断缩小增量
for(interval = k/2 ;interval>0; interval = interval /2 )
{
//增量为interval的插入排序
int i; //i表示组
for(i = interval ; i < k; i++) {//遍历一组
int x = arr[i]; //保存当前的数据
int index = i - interval; //保存需要对比元素的下标
while(interval > -1 && x <arr[index] )
{
//将比当前元素大的数后移动interval位
arr[index + interval ] = arr[index] ;
index -= interval;
}
arr[index + interval] = x;
cout << "i组为:"<< i << endl;
for (int i = 0; i < k; i++)
cout << arr[i] << " ";
cout << endl;
}
cout << "增量为:"<< interval << "********"<<endl;
for (int i = 0; i < k; i++)
cout << arr[i] << " ";
cout << endl;
}
}
int main()
{
int arr[10] = { 6, 4, 8, 9, 2, 3, 1, 15 ,18 ,85} ;
shellSort(arr, 10);
return 0;
}
解题实战:
【1】小白上楼梯
小白正在上楼梯,楼梯有n阶台阶,小白一次可以上1阶, 2阶或3阶,实现-个方法,计算小白有多少种走完楼梯的方式。
思路:就是到达最后一个台阶之前,到n-1个台阶的方式,n-2,n-3台阶之和
/*小白上楼梯*/
int smallwhite(int n){//n表示台阶数
if(n==0) return 1; //注意这里的情况传入的是3,对应的就是直接3个台阶,所以就是1
if(n==1) return 1;
if(n==2) return 2;
return smallwhite(n-1)+smallwhite(n-2)+smallwhite(n-3);//这里是之和
}
【2】旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1.
#include <iostream>
using namespace std;
/*旋转数组的最小数字(二分法)*/
int arrmin(int* arr, int begin, int end){
/*最小的那个数一定在无序的一边*/
if(arr[begin] < arr[end]) return arr[begin];//没有旋转的情况
while(begin + 1 < end){ //说明还没有结束
int mid = begin + ( (end - begin) >> 1);
//要么左侧有序,要么右侧有序
//下面这行代码有个bug 假如数组为 1 1 1 0 1 这种数组应该在程序的入口检测一下
//假如左侧和中间相等了 那么就应该用顺序查找最小值了。
if( arr[mid] >= arr[begin]){//右边无序
begin = mid;
}
else{//左边无序
end = mid;
}
}
return arr[end];
}
int main() {
int arr[5] = {3,4,5,1,2};
cout << arrmin(arr,0,4)<< endl;
int arr2[6] = {1,1,1,0,0,1};
cout << arrmin(arr2,0,5)<< endl;
return 0;
}
【3】在有空字符串的有序字符串数组中查找
有个排序后的字符串数组,其中散布着一 些空字符串,编写一个方法,找出给定字符串(肯定不是空字符串)的索引。
#include <iostream>
#include <string>
using namespace std;
/*在有空字符串的有序字符串数组中查找*/
int indexOf(string str[], string p, int length){
int begin = 0; //因为是二分变体,所以先是开始
int end = length - 1; //终点,都写出来
while(begin <= end){
int indexOfmid = begin + ( (end - begin) >> 1 ); //求中间值
while(str[indexOfmid] == ""){ //如果找到了"" 就向右移动一个,直到遇到不是空的
indexOfmid ++;
if(indexOfmid > end) //如果越界了,就返回-1。错误了
return -1;
}
if(str[indexOfmid].compare(p) > 0) //和目标做对比
end = indexOfmid - 1;
else if(str[indexOfmid].compare(p) < 0)
begin = indexOfmid + 1;
else
return indexOfmid;
}
return -1;
}
int main() {
string str[8] = {"a", "", "ac", "", "ad", "b", "", "ba"} ;
cout << indexOf(str, "ad", 8);
}
【4】最长连续递增子序列
(1,9,2,5,7,3,4,6,8,0)中最长的递增子序列为(3,4,6,8)。
思路:设置一前一后两个指针,前指针先不动,后指针向后扫描,如果是递增的则接着扫描,直到扫到第一个非递增元素,记录此时前后指针之间的距离,记为length,并保留前指针的位置信息。然后重置前后指针,使得前指针指向刚才扫描到的那个非递增元素,重复上面工作直到结束,找到length最长的那个前指针位置信息并输出。
#include <iostream>
using namespace std;
int maxSizeInArrr(int *arr , int n )
{
int front = 0 , end = 1;
int length =1 , max_len = 0 , max_index = 0;
int i;
for(i = 0; i < n; ++i)
{
if(end < n && arr[end] > arr[i])
{
end++;
length++;
}else{
if(length > max_len )
{
max_len = length;
max_index = front;
}
front = end;
end++;
length = 1;
}
}
for(i = 0; i < max_len; ++i)
cout << arr[max_index + i] <<" ";
cout << endl;
return max_len;
}
int main()
{
int arr[15] = {7,8,9,4,5,6,7,1,2,3,4,5,6,7,8};
cout<<"maxsize:"<< maxSizeInArrr(arr, 15)<< endl;
return 0;
}
【5】高效求a的n次幂算法
/*快速设计一个高效的求a的n次幂的算法*/
int pow(int a, int n){
if(n == 0) return 1;
int res = a;
int ex = 1;
//能翻
while( (ex << 1) <= n ){
//翻
res = res * res;
//指数*2
ex <<= 1;
}
//不能翻
//差n-ex方没有乘到结果里面
return res * pow(a, n-ex);
}