练习
2.3-2 重写MERGE
public void merge(int[] A,int p,int q,int r,int[] temp) {// 传入temp数组,避免在运行过程中反复创建数组tempint len_1 = q-p+1,len_2 =r-q;int i=0, j=0;int t=0;while(i<len_1 && j<len_2) {if (A[p+i] < A[q+j+1]) {temp[t++] = A[p+i];i++;}else {temp[t++] = A[q+j+1];j++;}}while( i<len_1) {temp[t++] = A[p+i];i++;}while( j<len_2) {temp[t++] = A[q+j+1];j++;}}public void merge_sort(int[] A,int p,int r,int[] temp) {if (p<r) {int q = (p+r)/2;merge_sort(A,p,q);merge_sort(A,q+1,r);merge(A,p,q,r);}}
2.3-3 证明归并排序的时间为nlogn
证明:
因为T(n) = 2T(n/2) + n = 2 ( 2T(n/2^2) + n/2 ) + n = 2^2 T(n / 2^2) + 2n = … =2^k T(n / 2^k) + kn;
当 n = 2^k时,上式为 n T( 1 ) + n logn;舍去低阶项得T(n) = Theta( nlogn );
2.3-4 给插入排序最坏情况写一个递归式
T(n) = T(n-1) + n;
2.3-5 二分查找最坏时间为什么为logn
二分查找最坏为logn,即每次取中点值都不是,例如[1,2,3,4,5,6,7,8]中查找5或7,则需要进行log8 = 3 次的查找;
2.3-6 插入排序中引入了二分查找,可以将时间减少到nlogn吗?
不可以,因为即使降低了查找插入位置的时间,移动元素的时间依然存在,插入排序的时间依然为Θ(n^2)。
2.3-7 描述一个时间为nlogn的算法,给定n个整数的集合S和另一个数x,以确定S中是否存在两个数的和为x。
首先需要一个排序时间为nlogn的算法,即采用快排的方式进行排序,然后使用双指针进行查找,这样只需要过一遍全部数据,时间为n,代码如下:
// 此代码未经编译运行,仅供复习参考,全记事本手打。public boolean judge(int[] nums ,int target) {quickSort(nums);int i=0,j=nums.length-1;while (i<j && nums[i]+nums[j] != target) {if (nums[i] + nums[j] < target) i++;if (nums[i] + nums[j] > target) j--;}return (nums[i] + nums[j]) == x}public void quickSort(int[] nums) {int len = nums.length;sort(nums,0,len-1);}public void sort(int[] nums,int p,int r) {if (p < r) {int q = partition(nums,p,r);sort(nums,p,q-1);sort(nums,q,r);}}public int partition(int nums,int p,int r) {int x = nums[r];int i = p - 1;for (int j = p;j<r-1;j++) {if (nums[j] < x) {i++;swap(nums,i,j);}}swap(i+1,r);return i+1;}public void swap(int[] nums,int a,int b) {int temp = nums[a];nums[a] = nums[b];nums[b] = temp;}
思考题
2-1
a.插入排序在最坏情况下为n^2,即长度为k的子表所需时间为k*k,然后一共有n/k个子表,所以所需时间为nk。
b.因为已经做了一部分子表的合并,目前树高即为lg(n/k),每层需要n,所以为nlg(n/k).
c.nk+nlg(n/k) = nk + nlgn - nlogk;要使与归并排序有相同的时间nlgn,则k的最大值应为lgn,这时为2n
lgn + nlg(lgn),去掉常数项则为nlgn.
d.在实际应用中,应该选择k为lgn这样在理论上时间相等,但是在机器实际操作中可以使得性能达到最优。(个人意见不一定正确)
2-4 逆序对,对应LeetCode里面的题目,题目链接
代码实现:根据插入排序中得到的规律有,有多少个逆序对就需要进行多少次移动,于是改写归并排序,在时间上时间为nlgn。
class Solution {private int ans = 0;public int reversePairs(int[] nums) {int len = nums.length;int[] temp = new int[len];mergeSort(nums,0,len-1,temp);return ans;}public void mergeSort(int[] nums,int p,int r,int[] temp) {if (p < r) {int q = (p+r)/2;mergeSort(nums,p,q,temp);mergeSort(nums,q+1,r,temp);merge(nums,p,q,r,temp);}}public void merge(int[] nums,int p,int q,int r,int[] temp) {int len1 = q - p + 1;int len2 = r - q;for (int i=p;i<=r;i++) {temp[i-p] = nums[i];}int i = 0;int j = 0;int k = p;for (k=p;k<=r&&i<len1&&j<len2;k++) {if (temp[j+len1] < temp[i]) {ans = ans + len1 - i;// System.out.print(temp[j+len1]+":"+(len1)+" ");nums[k] = temp[j+len1];j++;}else {nums[k] = temp[i];i++;}}if (i!=len1) {while (k<=r) {nums[k++] = temp[i++];}}if (j!=len2) {while (k<=r) {nums[k++] = temp[j+len1];j++;}}}}
