一、递归
递归的本质是利用系统栈的数据结构,完成遍历,因此任何递归行为都可以改为非递归。
递归的时间复杂度分析:
任何递归的时间复杂度最终都可以使用上面的表达式来表示,其时间复杂度按照公式分析即可。
真题分析
使用递归的方式,判断数组任意范围内的最大值,代码实现如下:
二、归并排序
1.思路分析
2.代码实现
public static void main(String[] args) {
int[] array = new int[]{5, 6, 1, 9, 7, 2, 3, 8, 9};
process(array, 0, array.length - 1);
System.out.println(Arrays.toString(array));
}
private static void process(int[] arr,int L,int R){
// 递归出口
if(arr==null||L==R){
return;
}
// 计算中间的下标
int M = L+((R-L)>>1);
// 左边排好序
process(arr,L,M);
// 右边排好序
process(arr,M+1,R);
// 合并左边和右边,则最终有序
merge(arr,L,M,R);
}
private static void merge(int[] arr,int L,int M,int R){
// 定义一个新数组,存储排好序的数组
int[] temp = new int[R-L+1];
// 定义新数组的下标,从0开始移动
int i = 0;
// 定义两个指针,一个从左边开始移动,一个从右边开始移动
int left = L;
int right = M+1;
while(left<=M&&right<=R){
if(arr[left]<arr[right]){
temp[i] = arr[left++];
}else{
temp[i] = arr[right++];
}
i++;
}
// 最终会只剩下左边或者右边的一部分数据还没有排好序,此时剩余数字都是有序的,直接拼在后面就行
// 如果是左边的一部分数据还没有排好序
while(left<=M){
temp[i++]=arr[left++];
}
// 如果是右边的一部分数据还没有排好序
while(right<=R){
temp[i++]=arr[right++];
}
// 用临时数组替换原数组中对应的元素
for(int j=0;j<temp.length;j++){
arr[L+j]=temp[j];
}
}
3.时间复杂度:
4.非递归方式实现
其实是自己定义了递归的最小边界mergeSize=1,然后逐渐翻倍,至递归的最大边界mergeSize>=N。
5.面试题试炼
答案解析:
三、快速排序
快速排序(Quicksort)是对冒泡排序算法的一种改进。快速排序实现的重点在于数组的拆分,通常我们将数组的最后一个元素定义为比较元素,然后将数组中小于比较元素的数放到左边,将大于比较元素的放到右边,
这样我们就将数组拆分成了左右两部分:小于比较元素的数组;大于比较元素的数组。我们再对这两个数组进行同样的拆分,直到拆分到不能再拆分,数组就自然而然地以升序排列了。
1. 快速排序第一版:
每次都以最后一个元素作为比较元素:
2. 快速排序第二版:
每次排序确定好一批数(即如果存在重复数字的情况,每次将该重复数字的所有数字都确定好位置)的位置。
// 荷兰国旗问题,返回左边界和右边界
private static int[] netherlandsFlag(int[] arr,int L,int R){
// 将数组中小于n的数放在左边,大于n的数放在右边,等于n的数在中间
if(L>R){
return new int[]{-1,-1};
}
if(L==R){
return new int[]{L,R};
}
// 定义左边界
int left = L-1;
// 定义右边界,因为用R来作为比较的数,因此先把R跳过,最后处理
int right = R;
// 定义数组的指针,从L开始右移
int index = L;
// 当index移动至R-1时,停止移动
while(index<right){
// 如果相等,则直接跳过
if(arr[index]==arr[R]){
index++;
// 小于temp的数和left+1位置的数交换位置,同时index移动至下一位
}else if(arr[index]<arr[R]){
swap(arr,index++,++left);
// 大于temp的数和right位置的数交换位置,同时right指针向左移动一位,index指针不变
}else if(arr[index]>arr[R]){
swap(arr,index,--right);
}
}
// 最后将R位置的数和right+1位置的数交换位置
swap(arr,R,right);
// 返回等于R的索引的左边界和右边界
return new int[]{left+1,right};
}
// 交换数组arr中L和R位置的数
private static void swap(int[] arr,int L,int R){
if(L==R||arr[L]==arr[R]){
return;
}
arr[L]=arr[L]^arr[R];
arr[R]=arr[L]^arr[R];
arr[L]=arr[L]^arr[R];
}
3. 快速排序第三版:
每次在范围内随机取一个数作为比较元素
时间复杂度:O(N*logN)