分治递归相关算法

前情提要:分析代码时只会分析代码的主要算法部分。

一.众数问题。

        首先,这个问题的思路是运用分治递归的思想将其划分为多个小问题,我们要输出的是众数num以及众数的个数maxCnt,a传入输入的非递减数组,n为数组的个数,将数组分为前中后三个部分,先计算中间的重复元素个数赋值为s。

 public static void getMaxCnt(int a[], int n, IntHolder num, IntHolder maxCnt){
        int l =0, r = 0;
        int s;
        int mid = n / 2;
        for( l = 0;l < mid;l ++){
            if(a[l] == a[mid])
                break;
        }
        for(r = mid + 1;r < n;r++){
            if(a[r] != a[mid])
                break;
        }
        //计算重数
        s = r - l;
        //...
    }

        接着比较存储的最大众数个数和s,如果s大更新num和maxCnt,如果相等那么比较众数的大小,最后取较小的众数。

if(s > maxCnt.value){
            num.value = a[mid];
            maxCnt.value = s;
        }
        //如果重数一样则比较众数的大小
        if(s == maxCnt.value){
            if(num.value > a[mid]){
                num.value = a[mid];
                maxCnt.value = s;
            }
        }

        最后是递归过程,看看左右两边的个数是否比已经找到的众数个数大,进行递归,如果小则不用比较。

if(l + 1 > maxCnt.value){
            getMaxCnt(a,l+1,num,maxCnt);
        }
        if(n - r > maxCnt.value){
            int [] b = new int[n-r];
            for(int i = 0;i < n-r;i ++){
                b[i] = a[r + i];
            }
            getMaxCnt(b,n-r,num,maxCnt);
        }

注:这里使用IntHolder类是因为一是要在main方法里输出这两个数,其值要进行改变,二是递归过程中要进行更新。

二.半数集问题。

6半数集肯定包含他本身;
1不大于的一半 所以16;
2不大于6的一半,26;1又不大于2,所以126;
3不大于6的一半,36;1又不大于3,所以136...

观察可得,一个数的半数集等于从1+到这个数一半的半数集(奇数的话直接约)。

        同样使用分治递归的方法,n为传入的数字,因为自己本身也算一个所以count = 1;

用一个数组来存储对应位置的数的半数集求6的半数集就是1+2(半数集)+3(半数集)= 6.

public static int banshu(int n){
        int count = 1;
        if(arr[n]>0){
            return arr[n];
        }
        for(int i = 1;i<=n/2;i++){
            count += banshu(i);
        }
        arr[n] = count;
        return count;
    }

三.重复元素排列问题。

        就是排列问题,先是进入一个for循环,通过递归step+1来进行4*3*2的全排列遍历,其中为了控制在递归过程中遍历字符串中每一个字母,使用book进行存储0未遍历,1已遍历,而其中第一个if条件进行了限制将重复的排列去除。

public static void dfs(int step){
        if(step == ans.length){
            writeOutput("C:\\Users\\Administrator\\Desktop\\新建文件夹\\output.txt",ans);
            System.out.println(Arrays.toString(ans));
        }
        for (int i = 0; i < a.length() ; i++) {
            if(i>0&&a.charAt(i)==a.charAt(i-1)&&book[i-1]==0) continue;
            if(book[i] == 0){
                book[i] = 1;
                ans[step] = a.charAt(i);
                dfs(step+1);
                book[i] = 0;
            }
        }
    }
    static String a= "";
    static char []ans = new char [a.length()];
    static int [] book = new int[a.length()];

    public static void main(String[] args) {
        // 读取输入文件
        String n = readInput("C:\\Users\\Administrator\\Desktop\\新建文件夹\\input.txt");
        if (n == null) {
            System.out.println("无法读取输入文件,程序将终止。");
            return;
        }
        a = n;
        ans = new char[a.length()];
        book = new int[a.length()];
        dfs(0);

    }

四.集合划分问题。

        读取input里的一个数组,n为有多少个元素,m为划分为多少个集合。

1.易知特殊情况:算法的实现原理

                m == 1 时,F ( 1, 1 ) = 1;   //只需要分成一份

                n == m 时,F ( n , m ) = 1;  //当元素个数和份数一样时,每一个元素就为一份

2.一般情况:( 将问题简单化 )

                a.将第 i 个元素单独放,其余 n - 1 个元素分成 m - 1 份 : F ( n -1, m - 1 ) ;

                b.将第 i 个元素插入其余的 n - 1 个元素分成的 m 份中,有 m 种插入的位置:

                m * F ( n -1, m );

public static int f(int n,int m){
        if(m == 1 || n == m){
            return 1;
        }
        return f(n-1,m-1) + m*f(n-1,m);
    }
 public static void main(String[] args) {
        Integer []arr  = readInput("C:\\Users\\admin\\Desktop\\新建文件夹\\input.txt");
        if (arr == null) {
            System.out.println("无法读取输入文件,程序将终止。");
            return;
        }
        Integer count = f(arr[0],arr[1]);
        writeOutput("C:\\Users\\admin\\Desktop\\新建文件夹\\output.txt",count);
    }

五.整数因子分解问题。

        该方法使用了动态规划的方法,num读取一个整数,并输出有多少个分解式。

        我们用一个数组dp来存储num之前所有数的因数,再用一个for循环查找哪些数是其因数,最后将其存储在num里的分解式数量累加求和。

    //整数因子分解问题,动态规划
    //主要利用的原理:一个数的分解式个数等于它所有因子(除了自己)的分解式之和
    public static int f(int n){
        int [] dp =new int[n+1];//用来存储每个数有几种分解式
        for(int i=1; i<=n; i++){
            if(i == 1){
                dp[i] = 1 ;//1的分解式有一个
            }else {
                for (int j = 1; j < i; j++) {
                    if (i % j == 0) {
//说明j是i的因子,并且不等于i,
//只有在这时我们才进行++操作,因为一个数的分解式个数等于它所有因子(除了自己)的分解式之和
                        dp[i] += dp[j];
                    }
                }
            }
        }
        return dp[n] ;
    }
 public static void main(String[] args) {
        Integer num = readInput("C:\\Users\\admin\\Desktop\\新建文件夹\\input.txt");
        if (num == null) {
            System.out.println("无法读取输入文件,程序将终止。");
            return;
        }
        int n = 12;
        int m = f(n);
        // 写入输出文件
        writeOutput("C:\\Users\\admin\\Desktop\\新建文件夹\\output.txt",m);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值