蓝桥杯算法课《算法最美》笔记——2. 递归、查找、排序(上)

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);
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大果壳Clap

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值