算法的开章,递归是实现其他高级算法如深度优先、分治等的基础;

碰到一个题目就找它的重复性,重复性有最近的重复性(根据重复性怎么构造怎么分解-->分治、回溯等办法,本质就是递归),或者最优重复性(即动态规划)。

本质上就是找它的重复性。

找重复性以及分解问题,最后组合每个子问题的结果。

分治算法(divide and conquer)的核心思想,分而治之 ,即将原问题划 分成 n 个规模较小,并且结构与原问题相似的子问题,递归地解决这些子问题(庖丁解牛,依次对子问题进行分析),然后再合并其 结果,就得到原问题的解。

分治算法是一种处理问题的思想,递归是一种编程技巧。

分治算法的递归实现中,每一层递归都会涉及这样三个操作:

  • 分解:将原问题分解成一系列子问题;
  • 解决:递归地求解各个子问题,若子问题足够小,则直接求解;
  • 合并:将子问题的结果合并成原问题。

分治算法能解决的问题,一般需要满足下面这几个条件:

  • 原问题与分解成的小问题具有相同的模式;
  • 原问题分解成的子问题可以独立求解,子问题之间没有相关性,这一点是分治算法跟动态规 划的明显区别,等我们讲到动态规划的时候,会详细对比这两种算法;
  • 具有分解终止条件,也就是说,当问题足够小时,可以直接求解;
  • 可以将子问题合并成原问题,而这个合并操作的复杂度不能太高,否则就起不到减小算法总 体复杂度的效果了。

 

  算法-02 | 分治| 回溯

 

例子:给定一个字符串,把它的每个字符都变成大写;

可以写循环,或者递归

算法-02 | 分治| 回溯

 

 如上图做法的好处是可以并行计算,每个子问题是互不相关的,可以在多核CPU中跑。

分治可以解决的问题,它没有所谓的中间结果(重复计算),如果有大量重复计算如递归分治的效率并不高,这时可以有更适合的算法,比如动态规划、 把中间结果先保存下来下次直接使用。

分治的代码模板:分治一般是用递归来处理的

算法-02 | 分治| 回溯

 

 怎么拆分为子问题,怎么merge这些subresult,得到这些子结果怎么合并起来; 中间的这些子结果如何做质量控制和质量保证,下面的人给你个返回结果你咋知道他做的好还是坏。

专注本层,不要下探。

应用:

 

 如何编程求出一组数据的有序对个数或者逆序对个数呢?

最笨的方法是,拿每个数字跟它后面的数字比较,看有几个比它小的。我们把比它小的数字个数 记作 k,通过这样的方式,把每个数字都考察一遍之后,然后对每个数字对应的 k 值求和,最后 得到的总和就是逆序对个数。不过,这样操作的时间复杂度是 O(n^2)。那有没有更加高效的处 理方法呢?
我们用分治算法来试试。我们套用分治的思想来求数组 A 的逆序对个数。我们可以将数组分成 前后两半 A1 和 A2,分别计算 A1 和 A2 的逆序对个数 K1 和 K2,然后再计算 A1 与 A2 之间 的逆序对个数 K3。那数组 A 的逆序对个数就等于 K1+K2+K3。
View Code

代码:

    private int num = 0;
    public int count(int[] array, int n) {
        num = 0;
        mergeSort(array, 0, n - 1);
        return num;
    }

    private void mergeSort(int[] array, int left, int right) {
        if (left >= right) return;
        int mid = (left + right) / 2;
        mergeSort(array, left, mid);
        mergeSort(array, mid+1, right);
        merge(array, left, mid, right);
    }

    private void merge(int[] array, int left, int mid, int right) {
        int i = left, j = mid + 1, k = 0;
        int[] temp = new int[right - left + 1];
        while (i <= mid && j <= right) {
            if (array[i] <= array[j]) {
                temp[k++] = array[i++];
            } else {
                num += (mid - i + 1); //计数逆序对的个数
                temp[k++] = array[j++];
            }
        }
        while (i <= mid) {
            temp[k++] = array[i++];
        }
        while (j <= right) {
            temp[k++] = array[j++];
        }
        for (int p = 0; p < temp.length; p++) {
            array[left + p] = temp[p];
        }
    }
View Code

相关文章:

猜你喜欢
  • 2021-06-26
  • 2021-11-23
  • 2022-12-23
  • 2021-11-23
  • 2021-11-23
相关资源
相似解决方案