一、什么是递归?
自身不断地循环自身。
最简单的递归:
计算阶乘n! = 1 * 2 * 3 * 4 * …… * (n-1) * n
循环实现:
public static int factorial(int n) {int factorial = 1;for (int i = 1; i <= n; i++) {factorial *= i;}return factorial;}
递归实现:
public static int factorial(int n) {//#1. 当 n = 1 时,递归结束if (n == 1) {return 1;}//#2. 把 factorial(n - 1) 的结果和 n 相乘,剩下的交给 factorial(n - 1) 来解决。return n * factorial(n - 1);}
二、如何实现递归?
1.基准条件(结束条件,没有这个条件代码将出现死循环)
2.递归公式(递归公式总是描述自己和下一个递归函数直接的递进关系)
练习题1.
答案:
package com.youkeda;public class Recursive {public static int fibonacci(int n) {if (n == 0 || n == 1) {return n;}return fibonacci(n - 1) + fibonacci(n - 2);}public static void main(String[] args) {System.out.println("fibonacci(1) = " + fibonacci(1));System.out.println("fibonacci(3) = " + fibonacci(3));System.out.println("fibonacci(10) = " + fibonacci(10));}}
练习题2

答案:
package com.youkeda;import java.util.Arrays;public class Recursive {// 查找应该插入的索引位置public static int searchIndex(int[] array, int left, int right, int aim) {// 循环查找节点位置if (left < right) {int middle = (left + right) / 2;int value = array[middle];if (value < aim) {// left = middle + 1;return searchIndex(array, middle + 1, right, aim);} else {// right = middle - 1;return searchIndex(array, left, right-1, aim);}}// #1. 如果最终元素仍然大于目标元素,则将索引位置往左边移动一个if (array[left] > aim) {return left - 1;}// 否则就是当前位置return left;}// 插入排序public static void insertSort(int[] array) {// 从倒数第二位开始,遍历到底0位,遍历 N-1 次for (int i = array.length - 2; i >= 0; i--) {// 存储当前抽离的元素int temp = array[i];//获取应该插入的索引位置int index = searchIndex(array, i + 1, array.length - 1, temp);// 根据插入的索引位置,进行数组的移动和插入int j = i + 1;//对数组进行移动while (j <= index) {array[j - 1] = array[j];j++;}//给数组末尾赋值array[j - 1] = temp;}}public static void main(String[] args) {int[] array = {9, 2, 4, 7, 5, 3};// Arrays.toString 可以方便打印数组内容System.out.println(Arrays.toString(array));insertSort(array);System.out.println(Arrays.toString(array));}}
三、分而治之思想-归并排序







// 归并排序,返回排好序的数组public static int[] mergeSort(int[] array) {// 为了方便查看结果,我们将每个数组进行打印System.out.println(Arrays.toString(array));if (array.length == 1) {return array;}int middle = array.length / 2;// #1. 处理 0 到 middle 左侧数组部分int[] left = mergeSort(subArray(array, 0, middle));// #2. 处理 middle 到 array.length 右侧数组部分int[] right = mergeSort(subArray(array, middle, array.length));// TODO处理合并问题return array;}
大家重点理解上面#1 #2注释的代码,理解创建子数组,然后递归分治的逻辑(我们暂时忽略合并排序的逻辑)。




代码实现:
package com.youkeda;import java.util.Arrays;public class Sort {// 归并排序public static int[] mergeSort(int[] array) {// 为了方便查看结果,我们将每个数组进行打印if (array.length == 1) {return array;}int middle = array.length / 2;// 处理 0 到 middle 左侧数组部分int[] left = mergeSort(subArray(array, 0, middle));// 处理 middle 到 array.length 右侧数组部分int[] right = mergeSort(subArray(array, middle, array.length));// TODO处理合并问题int l = 0;int r = 0;int index = 0;// 依次比较左右两个数组while (l < left.length && r < right.length) {array[index] = Math.min(left[l], right[r]);index++;if (left[l] < right[r]) {l++;} else {r++;}}// 右侧数组已经遍历完成,左侧有剩余if (l < left.length) {for(int i = l; i < left.length; i++){array[index] = left[i];index++;}}// 左侧数组已经遍历完成,右侧有剩余if(r < right.length){for(int i = r; i < right.length; i++){array[index] = right[i];index++;}}return array;}// 拷贝原数组的部分内容,从 left 到 rightpublic static int[] subArray(int[] source, int left, int right) {// 创建一个新数组int[] result = new int[right - left];// 依次赋值进去for (int i = left; i < right; i++) {result[i - left] = source[i];}return result;}public static void main(String[] args) {int[] array = {9, 2, 4, 7, 5, 3};// Arrays.toString 可以方便打印数组内容System.out.println("raw: " + Arrays.toString(array));int[] result = mergeSort(array);System.out.println("result: " + Arrays.toString(result));}}
四、分而治之思想-快速排序(重要)
快速排序使用分治法将序列分成两个较大和较小的子序列,然后递归的排序两个子序列。
- 挑选基准值:从数列中挑出一个元素,称为“基准”(pivot),
- 分割:所有比基准值小的元素放在基准值的左边,所有比基准值大的元素放在基准值的右边,分割成两个子序列
- 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。













// 快速排序public static void quickSort(int[] array) {// 调用快速排序的核心,传入left,rightquickSortCore(array, 0, array.length - 1);}// 快速排序的核心,同样也是递归函数public static void quickSortCore(int[] array, int left, int right) {// 递归基准条件,left >= right 即表示数组只有1个或者0个元素。if (left >= right) {return;}// 根据轴分区int pivotIndex = partition(array, left, right);// 递归调用左侧和右侧数组分区quickSortCore(array, left, pivotIndex - 1);quickSortCore(array, pivotIndex + 1, right);}// 对数组进行分区,并返回当前轴所在的位置public static int partition(int[] array, int left, int right) {}

答案:
package com.youkeda;import java.util.Arrays;public class QuickSort {// 快速排序public static void quickSort(int[] array) {// 调用快速排序的核心,传入left,rightquickSortCore(array, 0, array.length - 1);}// 快速排序的核心,同样也是递归函数public static void quickSortCore(int[] array, int left, int right) {// 递归基准条件,left >= right 即表示数组只有1个或者0个元素。if (left >= right) {return;}// 根据轴分区int pivotIndex = partition(array, left, right);// 递归调用左侧和右侧数组分区quickSortCore(array, left, pivotIndex - 1);quickSortCore(array, pivotIndex + 1, right);}// 对数组进行分区,并返回当前轴所在的位置public static int partition(int[] array, int left, int right) {int pivot = array[right];int leftIndex = left;int rightIndex = right - 1;while (true) {// 左指针移动while (array[leftIndex] <= pivot && leftIndex < right) {leftIndex++;}// 右指针移动while (array[rightIndex] >= pivot && rightIndex > 0) {rightIndex--;}if (leftIndex >= rightIndex) {break;} else {swap(array, leftIndex, rightIndex);}}swap(array, leftIndex, right);return leftIndex;}public static void swap(int[] array, int index1, int index2) {int temp = array[index1];array[index1] = array[index2];array[index2] = temp;}public static void main(String[] args) {int[] array = {9, 2, 4, 7, 5, 3};// Arrays.toString 可以方便打印数组内容System.out.println("raw: " + Arrays.toString(array));quickSort(array);System.out.println("result: " + Arrays.toString(array));}}
五、快速排序应用-快速选择




答案:
package com.youkeda;import java.util.Arrays;public class QuickSort {// 快速选择,返回选中的元素public static int quickFind(int[] array, int aim) {// 调用快速选择的核心,传入left,rightreturn quickFindCore(array, aim, 0, array.length - 1);}// 快速选择的核心,同样也是递归函数public static int quickFindCore(int[] array, int aim, int left, int right) {// 递归基准条件,left >= right 即表示数组只有1个或者0个元素,返回当前的元素if (left >= right) {return array[left];}// 根据轴分区int pivotIndex = partition(array, left, right);// 根据 aim 确定继续递归的方向if (pivotIndex > aim) {return quickFindCore(array, aim, left, pivotIndex - 1);} else if (pivotIndex < aim) {return quickFindCore(array, aim, pivotIndex + 1, right);} else {return array[pivotIndex];}}// 对数组进行分区,并返回当前轴所在的位置public static int partition(int[] array, int left, int right) {int pivot = array[right];int leftIndex = left;int rightIndex = right - 1;while (true) {// 左指针移动while (array[leftIndex] < pivot && leftIndex < right) {leftIndex++;}// 右指针移动while (array[rightIndex] > pivot && rightIndex > 0) {rightIndex--;}if (leftIndex >= rightIndex) {break;} else {swap(array, leftIndex, rightIndex);}}swap(array, leftIndex, right);return leftIndex;}public static void swap(int[] array, int index1, int index2) {int temp = array[index1];array[index1] = array[index2];array[index2] = temp;}public static void main(String[] args) {int[] array = {72, 77, 48, 17, 71, 2, 25, 97, 82, 5, 2, 18, 15, 57, 7, 48, 93, 47, 38, 74, 18, 93, 98, 41, 54, 4, 47, 4, 63, 76};System.out.println("raw: " + Arrays.toString(array));// 目标是倒数第 6 个元素int result = quickFind(array, array.length - 6);System.out.println("result: " + result);}}
