文章目录
求解n的阶乘
注意:
- cpu代表的是时间方向 可以耗损的时间是无穷的,所以可以无限循环
内存代表的是空间方向 内存是有限的,所以不能无限递归 - 阶乘结果 超出int范围(4字节)怎么办?
n是有符号数时,限制n不能大于12,或者 令溢出时为负数时为-1
当n是无符号数时,不会变成负数,一旦出现进位、借位,CF 就为 1。
递归解法
// O(n) S(n)(执行n次递归语句,开辟n次栈帧)
public static int fac(int n){
if(n<=1) //退出这一层调用 如果无限递归,会 栈溢出(因为每当调用发生时,就要分配新的栈帧)
return 1;
else
return fac(n-1)*n;
}
非递归解法
// O(n) S(1)
public static int fun(int n){
int sum=1;//0的阶乘是1
for(int i=1;i<=n;i++){//如果发生死循环,耗损cpu时间资源,其他程序会运行慢一点
sum*=i;
}
return sum;
}
求解 Fibonacci 数列
题目:
无穷数列 1,1,2,3,5,8,13,21,34,55,…,称为 Fibonacci 数列,计算第 n 位数列
递归解法
代码
/*
序号:0 1 2 3 4 5 6
Fibonacci 斐波那契数列:1 1 2 3 5 8 13
*/
// O(2^n)(总结点数) S(n)(树的最大高度个栈帧,然后就销毁,回退,再往下递推)
public static int Fib(int n){
if(n<=1){
return 1;
}else{
return Fib(n-1)+Fib(n-2);//调用形式像二叉树,层层往下计算
}
}
复杂度分析
O(f(n)):为算法的渐进时间复杂度,简称时间复杂度。
o(小o):表示小于,是最坏时间复杂度
递归调用图
优化时间复杂度
public static int optimize_Fib(int n,int first,int second){
if(n<=1) {
return second;
}else{
return optimize_Fib(n-1,second,first+second);//消除了重复项的计算,利用first+second暂存,类似于下面的for循环实现
}
}
public static int optimize_Fib(int n){
int first=1,second=1;
return optimize_Fib(n,first,second);//单分支递归
}
非递归解法
/*
O(n) S(1)
* */
public static int Fibonacci(int n){
if(n<=1){
return 1;
}else{
int first=1,second=1,sum=1;
//i等于0 1时,不进入循环,直接输出sum
for(int i=2;i<=n;i++){
sum=first+second;
first=second;
second=sum;
}
return sum;
}
}
二分查询
步骤
首先,假设表中元素是按升序排列,
将表中间位置记录的关键字与查找关键字比较,
如果两者相等,则查找成功;
否则利用中间位置记录将表分成前、后两个子表,
如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,
否则进一步查找后一子表。
重复以上过程,
直到找到满足条件的记录,使查找成功,
或直到子表不存在为止,此时查找不成功。
思考
- 有重复值时,怎么找到最右边元素的下标
- 如果数组中间没有有序
- 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。
如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
递归实现
/*
如果数组是从小到大有序的,使用递归和非递归实现二分查询。
* */
public static int binarySearch(int[] array,int tar,int high,int low){
int pos=-1;
if(low<=high){//记住有个等于
int mid=( (high-low)>>1 ) +low;//加法优先级高
if(array[mid]==tar){
while(mid<high && array[mid+1]==tar) ++mid;//找最右边的(注意:<high,才能保证mid+1不越界)
pos= mid;
}else if(tar<array[mid]){
pos= binarySearch(array,tar,low,mid-1);
}else if(tar>array[mid]){
pos= binarySearch(array,tar,mid+1,high);
}
}
return pos;
}
非递归实现
// 常见命名 arr pos index target left right
public static int binSearch(int[] array,int num){
int low=0;
int high=array.length-1;//length不带括号,因为是属性
int pos=-1;
//记住有个等于号,指向同一个数据也要查
while (low<=high) {
int mid=(high-low)/2+low;//比(low+high)/2好在 防止超范围
if (array[mid] > num){
high=mid-1;
}else if (array[mid] < num) {
low=mid+1;
}else{
// 如果有重复值,怎么找到最左边元素的下标???????
while(mid>low && array[mid-1]==num)
--mid;//0号元素值也是的时候,减减会越界;最小为low+1时,--一下也不会越界
// // 如果有重复值,怎么找到最右边元素的下标???????
// while(mid<high && array[mid+1]==num)
// ++mid;//0号元素值也是的时候,减减会越界;最小为low+1时,--一下也不会越界
pos=mid;
// 如果数组中间没有有序???????????????
break;
}
}
return pos;
}
全排列
分析
![](https://img-blog.csdnimg.cn/20210518194102700.png)
![](https://img-blog.csdnimg.cn/20210518194130745.png)
代码
Perm
private static void Perm(int[] arr, int k, int m) {
//k m是要排序字母的起点和终点 位置重合,即只剩一个元素
if(k==m){
for(int i=0;i<=m;i++){
System.out.print(arr[i]+" ");
}
System.out.println();
//执行到此,此层perm已结束,回退
}else{
for(int j=k;j<=m;j++){
Swap_Arr(arr,j,k);//固定一个
Perm(arr,k+1,m);//规模减小
Swap_Arr(arr,j,k);//处理完一个首字母的全排列后,回退,保证原数组仍为1 2 3 不然输出结果发现 1 2 3重复
}
}
}
//j位置是要固定的元素,应该放到最前面 K是要排列元素的起点位置下标
private static void Swap_Arr(int[] arr, int j, int k) {
if(k!=j){
int tmp=arr[j];
arr[j]=arr[k];
arr[k]=tmp;
}
}
Main
int[] arr={1,2,3};
Perm(arr,0,arr.length-1);
子集问题
分析
代码
get_Subset
//找所有子集
private static void get_Subset(int[] arr,int[] brr,int i,int n){
if(i>=n){
boolean zeroFlag=true;
for(int j=0;j<n;j++){//即0.。。length-1 有选择的输出
if(brr[j]==1){
System.out.print(arr[j]+" ");
zeroFlag=false;
}
}
if(zeroFlag==true)
System.out.print("#");
System.out.println();
}else{
//两个get_Subset,像二叉树
brr[i]=1;//1表示走左边
get_Subset(arr,brr,i+1,n);//左子树
brr[i]=0;
get_Subset(arr,brr,i+1,n);//右子树
}
}
Main
//子集
int[] arr={1,2,3};//数组
int[] brr={0,0,0};//打印标识
get_Subset(arr,brr,0,arr.length);//输出标识 对应 length层的树的叶节点的路径,注意wps图
输出结果
1 2 3
1 2
1 3
1
2 3
2
3
#
青蛙跳台阶问题
题目1:
一只青蛙一次可以跳上1级台阶,也可以跳上2级。
求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
思路
本质是斐波那契数列
假设n个台阶,一共有f(n)个方案,则 f(n)=f(n-1)+f(n-2)
即一次跳一个时,跳剩下台阶的所有可能性 与 一次跳两个时 剩下的所有可能性
递归
public static int Fog(int n) {
//特例
if (n == 0)
return 0;
if (n == 1)
return 1;
if (n == 2)
return 2;
return Fog(n - 1) + Fog(n - 2);//双分支递归,调用形式像二叉树,层层往下计算,会有重复计算
}
非递归
public static int NiceFog(int n) {
//特例
if (n == 0)
return 0;
if (n == 1)
return 1;
if (n == 2)
return 2;
int n1=1;
int n2=2;
int count=2;
/*
F(n-2) F(n-1) F(n) F(n+1)
n1 n2 ?
n1 n2 ?
*/
while (count++ <= n){
int tmp=n1;
n1=n2;
n2=tmp+n2;
}
return n2;
}
题目2:
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上 n 级。
求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
思路
FogN_N
public int FogN_N(int n) {
if(n==0){
return 0;
}else if (n == 1) {
return 1;
} else {
return 2 * FogN_N(n - 1);
}
}
题目3
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上m级。
求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
思路
n<=m时, 1 2 3 ... n ...m 等价于上面的FogN_N,可以一次跳1、2、、、、n级到第n级台阶
n>m时: 1 2 3 ... m ...n
列多项式,适当的展开,然后替换:
f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-m)
f(n-1) = f(n-2) + f(n-3) + ... + f(n-m) + f(n-m-1)
化简得:f(n) = 2f(n-1) - f(n-m-1)
FogM_N
public int FogM_N(int n,int m) {
//当大于m的时候是上面的公式
if(n > m){
return 2*FogM_N(n-1, m)-FogM_N(n-1-m, m);
}
//当小于等于m的时候就是和n级的相同了
if(n==0){
return 0;
}else if (n == 1) {
return 1;
} else {
return 2 * FogM_N(n - 1,n);
}
}