赞
踩
分治法
具体操作: 把原问题分成 k 个较小规模的子问题,对这 k 个子问题分别求解。如果子问题不够小,那么把每个子问题再划分为规模更小的问题。这样一直分解下去,直到问题足够小,很容易求出这些小问题的解为止
求解的问题特征:
用分治法建立模型时,解题步骤:
归并排序
具体操作:
计算复杂度: 对 n 个数进行归并排序:(1)需要 log(2)n 趟归并; (2)在每一趟归并中有很多次合并操作,一共需要 O(n) 次比较。 所以计算复杂度是 O(nlog(2)n)
空间复杂度:由于需要一个临时的 b[] 存储结果,所以空间复杂度是 O(n)
逆序对问题
问题描述
分析:
当 k=0 时,就是求原始序列中有多少个逆序对
考察图 6.7 所示的一次合并过程发现,可以利用这个过程记录逆序对。观察到以下现象:
(1)在子序列内部,元素都是有序的,不存在逆序对;逆序对只存在于不同的子序列之间
(2)在合并两个子序列时,如果前一个子序列的元素比后面子序列的元素小,那么不产生逆序对,如图 6.7(a) 所示;如果前一个子序列的元素比后面子序列的元素大,就会产生逆序对,如图 6.7(b) 所示。不过,在一次合并中,产生的逆序对不止一个,例如在图 6.7(b) 中把 34 放到 b[] 中,它与 94、99 产生了两个逆序对。在下面程序中,相关代码是 " cnt+=mid-i+1; "
根据以上观察,只要在归并排序过程中记录逆序对就行了
以上解决了 k=0 时原始序列的问题,现在考虑,当 k!=0 时(即把序列中任意两个相邻数交换不超过 k 次)逆序对最少有多少?注意:不超过 k 次的意思是可以少于 k 次,而不一定要 k 次
在所有相邻数中,只有交换那些逆序的才会影响逆序对的数量。设原始序列有 cnt 个逆序对,讨论以下两种情况:
import java.util.*; public class Main { final int MAXN = 100005; long[] a = new long[MAXN]; long[] b = new long[MAXN]; long cnt; void Merge(int L, int mid, int r){ int i=L, j=mid+1, t=0; while(i<=mid && j<=r){ if(a[i] > a[j]){ b[t++] = a[j++]; cnt += mid-i+1; //记录逆序对的数量,**除去此句,便是完整的纯归并排序** } else b[t++] = a[i++]; } //一个子序列中的数都处理完了,另一个还没有,把剩下的直接复制过来 while (i<=mid) b[t++] = a[i++]; while(j<=r) b[t++] = a[j++]; for(i=0; i<t; i++) //把排好序的 b[] 复制回 a[] a[L+i] = b[i]; return; } void Mergesort(int L, int r){ if(L < r){ int mid = (L+r)/2; //平分成两个子序列 this.Mergesort(L,mid); this.Mergesort(mid+1,r); this.Merge(L,mid,r); //合并 } return; } public static void main(String args[]){ int n; long k; Main m = new Main(); Scanner sc = new Scanner(System.in); while (!sc.hasNext("#")){ //以 # 字符串结束输出 n = sc.nextInt(); k = sc.nextLong(); m.cnt = 0; for(int i=0; i<n; i++) m.a[i] = sc.nextLong(); m.Mergesort(0,n-1); //归并排序 if(m.cnt <= k) System.out.println(0); else System.out.println(m.cnt-k); } } }
快速排序
思路:把序列分成左、右两部分,使得左边所有的数都比右边的数小;递归这个过程,直到不能再分为止
最简单的办法是设定两个临时空间 X,Y 和一个基准数 t;检查序列中所有的元素,比 t 小的放在 X 中,比 t 大的放在 Y 中。但其实可以直接在原序列上操作,不需要临时空间 X、Y
以下介绍一种很容易操作的方法:
以上方法的实现如下:
import java.util.*; public class Main{ final int N = 10010; int[] data = new int[N]; private void swap(int a, int b){ //交换 int temp = data[a]; data[a] = data[b]; data[b] = temp; } private int partition(int left, int right){ //划分成左右两部分,以 i 指向的数为界 int i = left; int temp = data[right]; //把尾数看成基准数 for(int j=left; j<right; j++){ if(data[j] < temp){ //比基准数小的放在左边 swap(i, j); i++; } } swap(i, right); //把基准数放在中间 return i; //返回基准数的位置 } private void quicksort(int left, int right){ if(left < right){ int i = partition(left, right); //划分 quicksort(left, i-1); //分治:i 左边的继续递归划分 quicksort(i+1, right); //分治:i 右边的继续递归划分 } return; } public static void main(String[] args) { Main m = new Main(); Scanner sc = new Scanner(System.in); int n; n = sc.nextInt(); for(int i=0; i<n; i++) m.data[i] = sc.nextInt(); m.quicksort(0,n-1); //快速排序 for(int i=0; i<n; i++) System.out.print(m.data[i]+","); //输出排序后的序列 System.out.print('\n'); System.out.print(m.data[n/2]); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。