• 算法设计与分析:分治算法


    2-1 众数问题

    问题描述

    给定含有n个元素的多重集合S,每个元素在S中出现的次数称为该元素的重数。多重集S中重数最大的元素称为众数。例如,S={1,2,2,2,3,5}。多重集S的众数是2,其重数为3。

    编程任务:对于给定的由n个自然数组成的多重集S,采用分治算法编程计算S的众数及其重数。

    算法描述
    1. 对数组进行排序(例如利用快速排序,时间复杂度为\(O(NlogN)\)
    2. 求出中位数的统计信息
    3. 将数组进行划分,对子数组分别进行递归进行步骤2的求解
    关键代码
    class Solution {
        int mode;/* 众数 */
        int val;/* 重数 */
    
        public void solve(int[] a, int l, int r) {
            /* 左闭右闭区间 */
            if (l > r) {
                //区间为空
                return;
            }
            int mid = l + (r - l) / 2;
            int i = mid, j = mid;
            //找到左侧第一个不等于a[mid]的位置
            while (i >= 0 && a[i] == a[mid]) {
                i--;
            }
            //找到右侧第一个不等于a[mid]的位置
            while (a[j] == a[mid] && j <= a.length - 1) {
                j++;
            }
            //j-i-1为中位数的重数
            if (j - i - 1 > val) {
                val = j - i - 1;
                mode = a[mid];
            }
            //左侧可能找到结果
            if (i - l + 1 > val) {
                solve(a, l, i);
            }
            //右侧可能找到结果
            if (r - j + 1 > val) {
                solve(a, j, r);
            }
        }
    }
    
    结果分析

    空间复杂度\(O(logN)\)

    排序的时间复杂度\(O(NlogN)\),单个子问题的时间复杂度\(O(N)\),子问题的个数为\(O(logN)\)

    总的时间复杂度为\(O(NlogN)\)

    2-4 半数单集问题

    问题描述

    给定一个自然数n,由n开始可以依次产生半数集set(n)中的数如下:
    (1) n ∈set(n);
    (2) 在n的左边加上一个自然数,但该自然数不能超过最近添加的数的一半;
    (3) 按此规则进行处理,直到不能再添加自然数为止。
    注意,半数单集不是多重集,集合中已经有的元素不再添加到集合中。

    算法描述

    \(f(n)\) = 半数集\(set(n)\)中元素的个数

    在这里插入图片描述

    由图可知:\(f(6) = 1 + f(1) + f(2) + f(3)\)

    归纳得出:

    \[f(n) =1 + \sum\limits_{i=1}^{\frac{n}{2}}f(i) \]

    并且由此也可以发现,递归过程中存在大量的重叠子问题,可以用记忆化的方式进行优化。

    同时题目要求的是半数单集(半数单集不是多重集,集合中已经有的元素不再添加到集合中

    在计算中,可能产生重复的元素是两位数

    一个两位数\(x\)重复产生的条件是,在1位数\(y=x \% 10\)的半数集中已经产生了\(x\),因此有\(x/10<= y/2\)

    关键代码
    class Solution {
        int[] memo;
    
        public Solution() {
            /* 记忆化存储 */
            memo = new int[1000];
        }
    
        public int halfSingleSet(int n) {
            /* 如果f(n)之前算过,就不必再重复计算 */
            if (memo[n] > 0) {
                return memo[n];
            }
            int sum = 1;
            for (int i = 1; i <= n / 2; i++) {
                sum += halfSingleSet(i);
                /* 重复计算的情况 */
                if (i > 10 && i / 10 <= ((i % 10) / 2)) {
                    sum -= halfSingleSet(i / 10);
                }
            }
            memo[n] = sum;
            return sum;
        }
    }
    
    结果分析

    对于每个\(n\)\(f(n)\)仅会被计算一次,每次计算的时间复杂度是\(O(N)\)

    所以总体的时间复杂度是\(O(N^2)\)

    2-7 集合划分问题

    问题描述

    \(n\) 个元素的集合{1,2,…, \(n\) }可以划分为若干个非空子集。

    例如,当 \(n\) = 4 时,集合{1,2, 3,4}可以划分为 15 个不同的非空子集如下:
    {{1},{2},{3},{4}},
    {{1,2},{3},{4}},
    {{1,3},{2},{4}},
    {{1,4},{2},{3}},
    {{2,3},{1},{4}},
    {{2,4},{1},{3}},
    {{3,4},{1},{2}},
    {{1,2},{3,4}},
    {{1,3},{2,4}},
    {{1,4},{2,3}},
    {{1,2,3},{4}},
    {{1,2,4},{3}},
    {{1,3,4},{2}},
    {{2,3,4},{1}},
    {{1,2,3,4}}

    给定正整数 n ,计算出 n 个元素的集合{1,2,…, n }可以划分为多少个不同的非空子集

    算法描述

    观察\(n\) = 4非空子集的划分,所有的划分中非空子集的个数为1,2,3, … , n

    \(f(n,m)\)表示n个元素的集合{1,2,…,\(n\)}可以划分为多少个不同的由m个非空子集组成的集合

    针对于第\(n\)个元素,有两种选择:

    1. \(n\)归入第\(m\)个非空子集
    2. \(n\)单个元素直接作为第\(m\)个非空子集

    即有递推关系式:

    \[f(n,m) =m*f(n-1,m)+f(n-1,m-1) \]

    \[f(n,n)=1 \]

    \[f(n,1)=1 \]

    而总体的非空子集划分的个数结果为

    \[res = \sum\limits_{m=1}^{n}f(n,m) \]

    关键代码
    class Solution {
        /* n个元素划分为由m个非空子集组成的集合的划分方法数 */
        public int divSet(int n, int m) {
            if (m == n || m == 1) {
                return 1;
            }
            return m * divSet(n - 1, m) + divSet(n - 1, m - 1);
        }
    }
    
    
    结果分析

    递归树规模是指数增长的,子问题个数是\(O(2^N)\)的,而每个子问题都在\(O(1)\)时间内完成

    所以总的时间复杂度为\(O(2^N)\)

    友情,恋爱,推理,因为有当初的约定才努力不懈。
  • 相关阅读:
    20.Docker Swarm集群
    Phalcon下命令行应用(command line applications)
    Mysql 命令备忘
    PHPExcel+phalcon+yii批量导入
    Yii 之分页 + bootstrap
    PHPMailer+phalcon中使用
    Phalcon之分页
    虚拟机中ip和host设置问题
    php RSS订阅功能
    ubuntu/linux下设置永久路由
  • 原文地址:https://www.cnblogs.com/ziyanblog/p/15562228.html
Copyright © 2020-2023  润新知