迭代递归和分治

迭代递归和分治

  • 迭代就是自己的输出又成为自己的输入,环形结构。
  • 递归就是自己调用自己,分治法和递归经常一起使用,树形结构。
  • 分治法就是把一个问题分成一些规模较小的子问题,然后分别求解。

递归例子

  • 阶乘函数
(分段函数表达式)
n!=
	1	when n=0
	n(n-1)!	when n>0

(代码表示)
public static int jiecheng(int n){
    
    
	if(n==0) return 1;
	return n*jiecheng(n-1);
}
  • 斐波那契数列
(分段函数表达式)
F(n)=	
	1	when n=0 or 1		
	F(n-1)+F(n-2)	when n>1

(代码表示)
public static int feibonacci(int n){
    
    
	if(n<=1)	return 1;
	return feibonacci(n-1)+feibonacci(n-2);
}
  • Hanoi汉诺塔问题
a b c 3个柱子,把a柱子上面的盘子移动到b柱子上

(代码表示)
public static void hanoi(int n,int a,int b, int c){
    
    
	if(n=1)	nove(a,b);	//直接把一个盘子从a移动到b
	hanoi(n-1,a,c,b);	//把n-1个盘子先移动到c上
	move(a,b);		//把最大的盘子移动到b上
	hanoi(n-1,c,b,a);	//把c上面的n-1个盘子移动到b上
}

迭代和递归的分析方法

  • 分治法一般用递归方程进行分析
    • 代换法(数学归纳)
    • 递归树
    • 主定理(计算机中lgn表示以2为底的对数)
  • 迭代分析
    • 级数求和

分治法的经典问题

二分查找

public static int binarySearch(int[] a, int x, int n){
    
    
// x:需要查找的元素,n:数组a的长度
	int left = 0; int right = n-1;
	
	while(left<=right){
    
    
		int middle = (left+right)/2;
		if(x == a[middle])	return middle;
		if(x > a[middle])	left = middle+1;
		else right = middle-1;
	}
	
	//表示没有找到
	return -1;
}

归并排序(二分归并)

int main(){
    
    
    int a[] = {
    
    1,2,4,5,1,2,5,3};
    int temp[a.length-1];
    mergeSort(a, 0, a.length-1);
}

int mergeSort(int a[], int temp[], int left, int rgiht){
    
    
    if(left < right){
    
    
        // 表示至少2个元素
        
        //中位数
        int middle = (left+right)/2; 
        
        // 对2边子问题进行归并排序
        mergeSort(a, left, middle);	
        mergeSort(a, middle+1, right);
        
        // 合并划分后的元素,并且拷贝回原数组,归并排序需要多余的一个临时数组
        merge_and_copy(a, temp, left, middle, right);
    }
    
}

int merge_and_copy(int a, int temp, int left, int middle, int right){
    
    
    int z = 0;	// 临时数组的索引
    
    // 把a数组划分成2部分 a[left, middle],a[middle+1, right],然后合并
    int x = left;	
    int y = middle + 1;
    
    while(x <= middle && y <= right){
    
    
        //把2部分头一个最小的元素放入temp数组中,索引+1,注意a++和++a,先运算还是先+1。
        if(a[x] < a[y])	temp[z++] = a[x++];	
        else temp[z++] = a[y++];
    }
    
    // 上面的循环结束后表示2部分中的一个数组已经遍历完了,接着查看另一个数组如果有剩余,则全部直接加在temp后面。
    while(x <= middle){
    
    
        temp[z++] = a[x++];
    }
    
    while(y <= right){
    
    
        temp[z++] = a[y++];
    }
    
    // 表示把a[]中从left到right的已经排好序的元素复制回去
    for(int i=0; i <= right-left+1, ++i){
    
    
        a[left+i] = temp[i];
        // 此处应为i有范围限制,所以不能left++
    }
}

快速排序

public static void quickSort(int[] num, int left, int right) {
    
    
		// left and right 表示从num数组的哪段元素排序
		// 如果left等于right,即数组只有一个元素,直接返回
		if(left>=right) {
    
    
			return;
		}
		
		1. // 普通算法:每次都设置最左边的元素为基准值
		int key=num[left];
		
		2. // 随机化算法:先随机挑选一个值作为key,然后把该值和首元素交换位置。
		Random rand = new Random();
		int key_id = rand.nextInt( right-left+1 )+left;
		int key = num[key_id];
		num[key_id] = num[left];
		num[left] = key;
		
		
		// 数组中比key小的放在左边,比key大的放在右边,key值下标为i
		int i = left;
		int j = right;
		while(i<j){
    
    
			
			// j向左移,直到遇到比key小的值
			while(num[j]>=key && i<j){
    
    
				j--;
			}
			
			// i向右移,直到遇到比key大的值
			while(num[i]<=key && i<j){
    
    
				i++;
			}
			
			// 必须判断i和j的大小,然后i和j指向的元素交换
			if(i<j){
    
    
				int temp=num[i];
				num[i]=num[j];
				num[j]=temp;
			}
		}
		// 此时i,j必相等,则此时把j或i的元素(比首元素小)和基准元素交换,然后此时成功分成2部分。
		num[left]=num[i];
		num[i] = key;
		quickSort(num,left,i-1);
		quickSort(num,i+1,right);
  • 最好情况:T(n) = 2T(n/2) + n = O(nlgn)
  • 一般情况:T(n) = T(n/10) + T(9n/10) + n = O(nlgn)
  • 最坏情况:T(n) = 1 + T(n-1) + n = T(n-1) + n = O(n^2)
    • 此时每次partition都划分成1, n-1两部分

    • 所以需要用随机化快排使得期望值最小

  • 上述代码没有把partition分开写,如果要分开需要让partition返回i或者j,这样才能qucikSort(a, left, i) and quickSort(a, i+1, right)

线性时间选择

  • 从一个序列中选择第k大的数
  • 类似快排,不过每次只操作一部分的元素,例如选择第k大的元素,先进行一次快排找出中间值,如果第k大的元素>中间值,那么只用考虑右边的元素。

猜你喜欢

转载自blog.csdn.net/weixin_43891234/article/details/105199311