算法03-分治法

原创 2018年04月16日 17:58:15

算法03-分治法

一、定义

分治法可以通俗的解释为:把一片领土分解,分解为若干块小部分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后让他们彼此异化。

分治法的精髓:

  • 分–将问题分解为规模更小的子问题;
  • 治–将这些规模更小的子问题逐个击破;
  • 合–将已解决的子问题合并,最终得出“母”问题的解;

分治法的应用:二分查找、快速排序、归并排序、循环赛日程表等。

二、二分查找

1、基本思想

分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。

查找过程:

首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

2、代码实现

/**
 * @param arr   被查找的数组
 * @param start 查找开始的位置
 * @param end   查找结束的位置(不包含)
 * @param data  要查找的数据
 * @return 查找数据在数组中的下标
 */
public static <E extends Comparable<E>> int binarySearch(E[] arr, int start, int end, E data) {
    if (Tool.isEmpty(arr)) {
        return -1;
    }
    int low = start;
    // 包左不包右
    int high = end - 1;

    while (low < high) {
        int mid = (low + high) >>> 1;
        if (arr[mid].compareTo(data) == 0) {
            return mid;
        } else if (arr[mid].compareTo(data) > 0) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return -low;// 返回最后查找的位置
}

三、快速排序

1、基本思想

快速排序(Quicksort)是对冒泡排序的一种改进。

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

快速排序:适用于数据量大的顺序表
为什么不适合链式表?

  1. 快速排序的过程,主要是查找比较数据,修改少;
  2. 快速排序是从两端查找比较,如果是单链表,性能会更差。

2、代码实现

/**
 * @param arr:需要排序的数组
 * @param start:开始排序的位置
 * @param end:结束排序的位置(包含)
 */
public static <E extends Comparable<E>> void quicksort(E[] arr, int start, int end) {
    if (Tool.isEmpty(arr) || start >= end) {
        return;
    }

    int low = start;
    int high = end;
    //基准值
    E data = arr[start];
    //从两端开始,与基准值比较,小于基准值就放在左边,大于基准值就放在右边
    while (low < high) {
        //先找比基准值小的数据
        while (low < high && data.compareTo(arr[high]) <= 0) {
            high--;
        }
        //将小的数据放到左边
        arr[low] = arr[high];
        //再找比基准值小的数据
        while (low < high && data.compareTo(arr[low]) >= 0) {
            low++;
        }
        //将大的数据放到右边
        arr[high] = arr[low];
    }
    //low即为基准值的下标
    arr[low] = data;
    //对左边的数据进行快速排序
    quicksort(arr, start, low - 1);
    //对右边的数据进行快速排序
    quicksort(arr, low + 1, end);
}

四、归并排序

1、基本思想

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

归并过程为:比较a[i]和b[j]的大小,若a[i]≤b[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素b[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。

归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。

归并排序适用于数据量大的链表,占用内存比快速排序大。

2、代码实现

/**
 * @param arr:需要排序的数组
 * @param start:开始排序的位置
 * @param end:结束排序的位置(包含)
 */
public static <E extends Comparable<E>> void megreSort(E[] arr, int start, int end) {
    if (arr == null || arr.length == 0) {
        return;
    }

    if (start >= end) {
        return;
    }
    //二进制右移,相当于除以2
    int mid = (start + end) >>> 1;
    //先把左边排好序
    megreSort(arr, start, mid);
    //再把右边排好序
    megreSort(arr, mid + 1, end);

    //创建一个临时数组,用于拷贝数据
    E[] copy = Arrays.copyOf(arr, arr.length);
    int i = start;
    int j = mid + 1;
    //把左右两边合在一起:左边的数可能大于右边
    for (int k = start; k <= end; k++) {
        //将小的数据放到arr中
        if (i <= mid && copy[i].compareTo(copy[j]) < 0) {
            arr[k] = copy[i];
            i++;
            //左边的表中数据放完后,就直接放右表数据
        } else if (i > mid && copy[i].compareTo(copy[j]) < 0) {
            arr[k] = copy[j];
            j++;
        } else if (j <= end && copy[i].compareTo(copy[j]) >= 0) {
            arr[k] = copy[j];
            j++;
            //右边的表中数据放完后,就直接放左表数据
        } else if (j > end && copy[i].compareTo(copy[j]) >= 0) {
            arr[k] = copy[i];
            i++;
        }
    }
}

五、循环赛日程表

1、介绍

循环赛日程表问题是指,设有n=2^k个选手参加比赛,要求设计一个满足一下要求的比赛日程表:

  1. 每个选手必须与其他的n-1个选手个比赛一次;
  2. 每个选手每天只能赛一次 。

解决思路:

  1. 先将选手两两分组,知道每组只有两名选手;
  2. 然后对每组的两名选手安排赛程,先创建一个二维数组A,然后把二名选手按序号放到A的第一行,然后把1号选手复制到A的右下角,把2号选手复制到A的左下角,现在A的排序就是这两名选手的日程表;
  3. 然后把上面的分组再两两合并一次,那么新的分组由之前的两个小分组组成,将这两个小分组当成2个单位,参照步骤2对他们排序,排序结果就是它们的日程表;
  4. 重复步骤2和3,知道全部排好序。

2、代码实现

/**
 * @param k 选手个数的2次幂
 * @return 日程表
 */
public static int[][] cyclingRace(int k) {
    // 选手个数
    int n = 1 << k;
    //日程表
    int[][] result = new int[n][n];
    // 初始化日程表
    for (int j = 0; j < n; ) {
        result[0][j] = ++j;
    }

    //先两两分组,排好序后,再两两合并,继续排序
    for (int i = 0; i < k; i++) {
        int len = 1 << i;
        for (int j = 0; j < n; j += len * 2) {
            // 将二维数组左上部分复制到右下部分
            copyArr(result, 0, j, len, j + len, len);
            // 将二维数组右上部分复制到左下部分
            copyArr(result, 0, j + len, len, j, len);
        }
    }
    return result;
}

/**
 * 拷贝二维数组
 *
 * @param arr:被拷贝的二维数组
 * @param a1:二维数组左上角的横坐标
 * @param a2:二维数组左上角的纵坐标
 * @param b1:二维数组右下起点的横坐标
 * @param b2:二维数组右下起点的纵坐标
 * @param len:拷贝的长度
 */
public static void copyArr(int[][] arr, int a1, int a2, int b1, int b2, int len) {
    for (int i = 0; i < len; i++) {
        for (int j = 0; j < len; j++) {
            arr[b1 + i][b2 + j] = arr[a1 + i][a2 + j];
        }
    }
}

demo已上传gitee,需要的同学可以下载!

上一篇:算法02-递归

下一篇:算法04-棋牌游戏常用排序算法

关闭计算机就这几招

 关闭计算机就这几招注:完全翻译整理自MSDN。非常简单. Vcbear.“系统ShutDown”属于Windows系统的一种基本服务。功能上有“关闭系统”,“注销用户”,“锁定工作站”3中操作。SD...
  • vcbear
  • vcbear
  • 2002-01-11 09:40:00
  • 1522

算法系列—分治法

关键字:相同子问题、递归、合并 一、基本概念:      所谓分治法是指,把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的...
  • daijin888888
  • daijin888888
  • 2016-11-10 14:13:03
  • 451

常用算法设计方法(7)——分治法

七、分治法 1、分治法的基本思想 任何一个可以用计算机求解的问题所需的计算时间都与其规模N有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需...
  • blue225
  • blue225
  • 2006-12-24 04:06:00
  • 1712

算法原理与分析之分治法

一.目录 1.算法基本原理 2.经典问题 二.分治法基本原理     分而治之,先将原问题的规模下降,分解为子问题,此所谓“分”,然后解决子问题,此为“治”。     分治法的基本思想是将一...
  • u012422446
  • u012422446
  • 2016-05-25 23:54:18
  • 3019

分治法算法讲解

1、分治法的基本思想  任何一个可以用计算机求解的问题所需的计算时间都与其规模N有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序 问题,当n=1时,不需任何计...
  • u011225629
  • u011225629
  • 2015-08-15 14:28:26
  • 2283

分治法 算法

  • 2008年03月28日 15:47
  • 33KB
  • 下载

算法 最近点对 分治法

  • 2014年12月12日 17:00
  • 4.92MB
  • 下载

趣学算法系列-分治法

趣学算法系列-分治法声明:本系列为趣学算法一书学习总结内容,在此推荐大家看这本算法书籍作为算法入门, 原作者博客链接,本书暂无免费电子版资源,请大家支持正版,更多实例分析请查看原书内容第三章 分治法...
  • wwlcsdn000
  • wwlcsdn000
  • 2017-11-21 17:48:27
  • 1027

凸包问题之分治法

凸包:按横坐标排序,以最小点与最大点之间的连线为准,在直线一侧找使三角形面积最大的点,此点必在凸包内,以找到点与最大点或最小点继续递归以寻找最大三角形面积寻找凸包点,直至找不到符合条件的点。 实现代...
  • d2457638978
  • d2457638978
  • 2015-12-07 00:11:17
  • 3149

东南大学—算法 分治法

  • 2011年08月25日 16:17
  • 55KB
  • 下载
收藏助手
不良信息举报
您举报文章:算法03-分治法
举报原因:
原因补充:

(最多只允许输入30个字)