两道关于逆序对的题目

Java基础

浏览数:44

2019-10-8

题目一:合并有序数组:给定两个排序后的数组A和B,其中A的末端有足够的缓冲空间容纳B。编写一个方法,将B合并入A并排序。

思路:注意这儿已经说明A的空间足够,那就说明不能再开辟辅助空间。然后合并的话可以采用归并的思想,就能解决这个问题。

代码:

import java.util.Arrays;

public class 合并有序数组 {

    public static void main(String[] args) {
    
        int A[] = new int[12];
        for (int i = 0; i < 7; i++) {
            A[i] = 2*i+1;
        }
        System.out.println("A数组:"+Arrays.toString(A));
        
        int B[] = new int[5];
        for (int i = 0; i < B.length; i++) {
            B[i] = 3*i+1;
        }
        System.out.println("B数组:"+Arrays.toString(B));
        
        int []res = f(A,7,B,5);
        System.out.println("合并后的数组:"+Arrays.toString(res));
   
        
    }

    private static int[] f(int []A,int p, int B[],int r) {
        int current = p+r-1;
        p--;
        r--;
        while(p!=-1&&r!=-1){
            if (A[p]>=B[r]) {
                A[current] = A[p];
                current--;
                p--;
            }else {
                A[current] = B[r];
                current--;
                r--;
            }
        }
        return A;
    }

    
}

结果:

  

题目二:逆序对个数:一个数列,如果左边的数大,右边的数小,则称这两个数为一个逆序对。求出一个数组中有多少个逆序对。

思路:比如数组2,5,1,3,4。那么逆序对就有(2,1)(5,1)(5,3)(5,4)。再写这个的过程中你就会发现一种解法,两次循环遍历数组就能解决问题。时间复杂度为4+3+2+1,时间复杂度为O(N^2)。那么就需要换一种解决方法,这里使用的是归并排序。归并排序的思想就能解决这个问题。可以思考一下怎样把逆序对打印出来?

代码:

import java.util.Arrays;

public class 逆序对 {
    
    static int niXuNum = 0;

    public static void main(String[] args) {
        int arr[] = new int[10];
        for(int i=0;i<10;i++){
            arr[i] = (int) ((Math.random()+1)*10);
        }
        
        
        System.out.println("数组元素:"+Arrays.toString(arr));
        mergeSort(arr, 0, arr.length-1);
//        System.out.println("排序后:"+Arrays.toString(arr));
        
        System.out.println("逆序对个数:"+niXuNum);

    }
    
    /**
     * 思路:将数组分为左右两个数组,递归调用归并进行排序
     *         分别排序完成后,使用辅助的合并函数将两个有序的子数组合并成一个整体有序的数组
     * 时间复杂度:均:O(nlgn),好:O(nlgn),坏:O(nlgn)
     * 空间复杂度:需要开辟辅助空间,该辅助空间可以重用,大小为N
     * 非原址排序
     * 稳定性:所有排序都是归并,在左的永远在左,在右的永远在右,稳定
     */
    
    static int[]helper;
    
    private static void mergeSort(int[] arr, int p, int r) {
        
        if (p<r) {
            int mid = p + ((r-p)>>1);
            mergeSort(arr, p, mid);  // 对左侧排序
            mergeSort(arr, mid+1, r);  // 对右侧排序
            merge(arr,p,mid,r);    // 合并
        }
        
    }

    private static void merge(int[] arr, int p, int mid, int r) {
        
        // 下面这行代码只能在这里使用,不能在主方法使用。
        helper = Arrays.copyOf(arr, arr.length);  //开辟的一个辅助空间  并拷贝数据
        
        int left = p;  // 左侧队伍的头部指针,指向待比较的元素
        int right = mid + 1;   // 右侧队伍的头部指针,指向待比较的元素
        int current = p;       // 原数组的指针,指向待填入数据的位置
        
        while(left<=mid&&right<=r){
            if (helper[left]<=helper[right]) {
                arr[current] = helper[left];
                current++;
                left++;
            }else {   // 右边小
                arr[current] = helper[right];
                current++;
                right++;
                niXuNum += mid-left+1;
            }
        }
        
        // 如果辅助数组右侧的数据没有全部填入原数组  那么不用管 因为原数组数据存在的
        // 那么只需要考虑辅助数组左侧的数据没有全部填入原数组的情况
        if (left<=mid) {
            while(left<=mid){
                arr[current] = helper[left];
                current++;
                left++;
            }
        }
        
    }

}

结果:

  

 

作者:|旧市拾荒|