2. 递归、查找、排序(上)
2.1 递归 (1-4切蛋糕思维,5-6找递推公式,7-8划不开,换方法)
递归:自身调用自身。
关键点:找重复,找变化,找边界。
2.1.1 求一个数的阶乘
找重复,找变化,找边界。
public class 求一个数的阶乘 {
public static void main(String[] args) {
int n = 4;
System.out.println(f(n));
}
public static int f(int n) {
if(n == 1) {
return 1;
}else {
int x = n*f(n-1);
return x;
}
}
}
输出:24
2.1.2 打印i~j
找重复,找变化,找边界。
public class 打印i_j {
public static void main(String[] args) {
f(1, 10);
}
public static void f(int i,int j) {
if(i > j) {
return;
}
System.out.print(i+“ ”);
f(i+1, j);
}
}
输出:1 2 3 4 5 6 7 8 9 10
2.1.3 数组求和
找重复,找变化,找边界。
public static int f2(int[] arr,int begin) {
if(begin == arr.length-1) {
return arr[begin];
}
return arr[begin]+f2(arr, begin+1);
}
输出:15
2.1.4 字符串反转
找重复,找变化,找边界。
//字符串反转
public static String reverse(String s,int end) {
if(end == 0) {
return ""+s.charAt(0);
}else {
return s.charAt(end) + reverse(s, end-1);
}
}
2.1.5 斐波那契数列
用数字表示为: 1 1 2 3 5 8 13 21 …
用公式表示为:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \label{eq1} f(…
public class 斐波那契数列 {
public static void main(String[] args) {
System.out.println(fib(6));
}
public static int fib(int n) {
if(n == 1||n == 2) {
return 1;
}else {
return fib(n-1) + fib(n-2);
}
}
}
递归原则:先纵后横
2.1.6 最大公约数
最大公约数递推公式
f
(
m
,
n
)
=
f
(
n
,
m
%
n
)
f(m,n)=f(n,m \% n )
f(m,n)=f(n,m%n)
public class 求最大公约数 {
public static void main(String[] args) {
System.out.println(gcd(10, 5));
}
public static int gcd(int m,int n) {
if(n == 0) {
return m;
}else {
return gcd(n, m%n);
}
}
}
2.1.7 插入排序改为递归
对 0-倒数第一个数 数组排序
等价于:
对数组 0-倒数第二个元素 部分排序,然后把最后一个元素插入到最后到这个有序的部分中
import java.util.Arrays;
public class 插入排序 {
public static void main(String[] args) {
int[] arr = {5,4,3,2,1};
insertSort(arr, 4);
System.out.println(Arrays.toString(arr));
}
//k:前k个元素
public static void insertSort(int[] arr,int k) {
if(k == 0) {
return;
}
//对前k-1个元素排序
insertSort(arr, k-1);
//把位置k的元素插入到前面的部分
int x = arr[k];
int index = k-1;
while(index > -1 && x < arr[index]) {
arr[index+1] = arr[index];
index--;
}
arr[index+1] = x;
}
}
小节:
找重复:
1、找到划分方法
2、找到递推公式进行等价转换
都是父问题转化为求解子问题
找变化的量:
变化的量通常要作为参数
找出口:
2.1.8 汉诺塔
将 1~n 从A移动到B,C作为辅助。
将 1~n-1:移动到C
n:移动到B(看成空的)
A:是空的,B看成空的,C是1~n-1;
public class 汉诺塔 {
public static void main(String[] args) {
printHanoTower(3, "A", "B", "C");
}
/**
*
* @param N 初始的N个从小到大的盘子,N是大编号
* @param from 原始柱子
* @param to 辅助柱子
* @param help 目标柱子
*/
public static void printHanoTower(int N, String from, String to, String help) {
if (N == 1) {
System.out.println("move" + N + "from" + from + "to" + to);
return;
} else {
// 把N-1个盘子挪到辅助空间
printHanoTower(N - 1, from,help, to);
// N可以顺利到达target
System.out.println("move" + N + "from" + from + "to" + to);
// 让N-1从辅助空间回到原空间
printHanoTower(N - 1, help, to, from);
}
}
}
2.2 查找
2.2.1 二分查找的递归解法
找中值,然后去一半的范围搜索。
剪枝法。
等价于三个子问题:
左边找(递归)
中间比
右边找(递归)
注意:左查找和右查找之选其一
public class 二分查找的递归解法 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
int key = binarySearch(arr, 0, 4, 3);
System.out.println(key);
}
public static int binarySearch(int[] arr, int low, int height, int k) {
if (low > height) {
return -1;
}
int mid = low + ((height-low)>>1);
int midVal = arr[mid];
if(midVal < k) {
return binarySearch(arr, mid+1, height, k);
}else if(midVal > k) {
return binarySearch(arr, low, height-1, k);
}else {
return mid;
}
}
}
2.3 排序
2.3.1 希尔排序
插入排序的改进,缩小增量排序。
9 8 7 6 5 4 3 2 1 以增量为4进行分组得:
9 5 1
8 4
7 3
6 2
每组进行插入排序得到:
1 5 9
4 8
3 7
2 6
变为:1 4 3 2 5 8 7 6 9
一趟一个增量,用增量来分组,组内排序,增量不断减小。
增量变为2。。。
增量变为1。。。
import java.util.Arrays;
public class 希尔排序 {
public static void main(String[] args) {
int arr[] = { 4, 3, 2, 1, 0 };
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void shellSort(int[] arr) {
// 用来定义增量(不断缩小)
for (int interval = arr.length / 2; interval > 0; interval /= 2) {
// //增量为1的插入排序
// for (int i = 1; i < arr.length; i++) {
// int target = arr[i];
// int j = i-1;
// //往后移位
// while(j>-1&&target < arr[j]) {
// arr[j+1] = arr[j];
// j--;
// }
// //插入这个元素
// arr[j+1] = target;
// }
// 增量为interval的插入排序
for (int i = interval; i < arr.length; i = i + interval) {
int target = arr[i];
int j = i - interval;
// 往后移位
while (j > -1 && target < arr[j]) {
arr[j + interval] = arr[j];
j -= interval;
}
// 插入这个元素
arr[j + interval] = target;
}
}
}
}
2.3.2 冒泡排序
public static int[] f1(int[] arr) {
// 冒泡排序时间复杂度N^2
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
2.3.3 选择排序
public static int[] f2(int[] arr) {
/// 选择排序时间复杂度为N^2
for (int i = 0; i < arr.length; i++) {
int min = arr[i];
for (int j = i; j < arr.length; j++) {
if (min > arr[j]) {
min = arr[j];
arr[j] = arr[i];
arr[i] = min;
}
}
}
return arr;
}
2.3.4 插入排序
public static int[] f3(int[] arr) {
long start = System.currentTimeMillis();
// 插入排序时间复杂度N^2
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0; j--) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
} else {
break;
}
}
}
long end = System.currentTimeMillis();
System.out.println("插入排序时间"+(end-start));
return arr;
}
2.4 利用“O表示法”评估时间复杂度
2.4.1 如何评估算法性能 O表示法
f(n) = O(1) :
阶数为常数。 例:f(n) = 3;
f(n) = O(n):
阶数的最高次幂为1次幂 例:f(n) = 2n+1;
f(n) = O(n^2):
阶数的最高次幂为2次幂 例:f(n) = 2n^2+3n+4;
f(n) = O(log2n):
例:
int count = 1;
while(count<n){
count = count * 2;
}
2.4.2 常见函数的时间复杂度计算
算法复杂度 1s可处理的规模
n 100000000
n^2 10000
n^3 500
log2n 2^(100000000)
2^n 27
复杂度 所需时间
lgn 1s
n 100000000s
n^2 10^16s
n^3
2^n
2.4.3 不同查找方法的时间复杂度
顺序查找:O(n)
二分查找:O(lgn)
2.4.4 基本排序算法的时间复杂度
冒泡排序,插入排序,选择排序: O(n^2)
Arrays.Sort() : O(nlgn)
2.4.5 三种递归算法的时间复杂度
汉诺塔:O(2^n)
斐波那契数列:O(2^n)
最大公约数:O(2lgn)
2.4.6 排序性能分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m2JUqTAc-1643114452602)(F:\soft\Typora\蓝桥杯笔记\蓝桥杯笔记.assets\IMG_0314.PNG)]
2.5 题解实战
2.5.1 小白上楼梯(递归)
题目:小白正在上楼梯,楼梯下有n阶台阶,小白一次可以上1阶,2阶或者3阶,实现一个方法,计算小白有多少种走完楼梯的方式。
思路:(找重复,找变化,找边界)
import java.util.Scanner;
public class 小白上楼梯 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int res = f(n);
System.out.println(res);
}
public static 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);
}
}
2.5.2 旋转数字的最小数字(改造二分法)
题目:把一个数组的最开始的若干元素搬到数组的末尾,称为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。如{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,改数组的最小值为1.
public class 旋转数字的最小二分法 {
public static void main(String[] args) {
// 34512
// 23451
// 51234
int[] arr = {3,4,5,1,2};
System.out.println(f(arr));
}
public static int f(int[] arr) {
int begin = 0;
int end = arr.length - 1;
if (arr[begin] < arr[end]) {
return arr[begin];
}
while (begin + 1 < end) {
int mid = begin + ((end - begin) >> 1);
// 左侧有序
if (arr[mid] >= arr[begin]) {
begin = mid;
} else {
end = mid;
}
}
return arr[end];
}
}
2.5.3 在有空字符串的有序字符串数组中查找
题目:有个排序后的字符串数组,其中散布这一些空字符串,编写一个方法,找出指定的字符串(肯定不是空字符串)的索引。
public class 在有空字符串的有序字符串中查找 {
public static void main(String[] args) {
String[] arr = { "a", "", "ac", "", "ad", "b", "", "ba" };
System.out.println(f(arr, "b"));
}
public static int f(String[] arr, String b) {
int begin = 0;
int end = arr.length - 1;
while (begin <= end) {
int indexOfMid = begin + ((end - begin) >> 1);
while (arr[indexOfMid].equals("")) {
indexOfMid++;
if (indexOfMid > end) {
return -1;
}
}
if (arr[indexOfMid].compareTo(b) > 0) {
end = indexOfMid - 1;
} else if (arr[indexOfMid].compareTo(b) < 0) {
begin = indexOfMid + 1;
} else {
return indexOfMid;
}
}
return -1;
}
}
2.5.4 求最长连续增长的子序列(部分有序)
题目:{1,9,2,5,7,3,4,6,8,0}中最长递增的子序列为{3,4,6,8}
import java.util.Arrays;
public class 最长连续增长的子序列 {
public static void main(String[] args) {
/ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
int[] arr = { 1, 9, 2, 5, 7, 3, 4, 6, 8, 0 };
System.out.println(Arrays.toString(f(arr)));
}
public static int[] f(int[] arr) {
int begin = 0;
int count = 0;
int max = 0;
for (int i = 0; i < arr.length-1; i++) {
if (arr[i + 1] >= arr[i]) {
count++;
if(count > max) {
max = count;
begin = i-max+1;
}
}else {
count = 0;
}
}
System.out.println(max+"->"+begin);
int[] arr2 = new int[max+1];
for (int i = 0; i <= max; i++) {
arr2[i] = arr[begin+i];
}
return arr2;
}
}
2.5.5 设计一个高效的求a的n次幂的方法
public class 设计一个高效的求n次幂的方法 {
public static void main(String[] args) {
int a = 2;
int n=15;
System.out.println(pow1(a, n));
}
//时间复杂度O(n)
public static int pow0(int a,int n) {
int res = 1;
for (int i = 0; i < n; i++) {
res = res * a;
}
return res;
}
//时间复杂度O(lgn)
public static int pow1(int a,int n) {
if(n == 0) {
return 1;
}
int res = a;
int ex = 1;
while((ex<<1)<=n) {
res*=res;
ex<<=1;
}
return res*pow1(a, n-ex);
}
}