算法-分治算法

分治算法概念

分治即分而治之。一个问题规模过大不容易直接解决,就可以划分成许多小问题,如果小问题不容易求解,那么可以再划分成规模更小的问题,直到规模小到很容易解决为止,解决这些小问题,再将小问题的解合并成大问题的解。这就是分治算法的基本思想。

至于小问题的规模到底划分多大,这是没有规定的,依实际情况而定。小问题的规模可以是相等的,也可以是不相等的。可以分成简单的2个小问题,当然也可以分成多个小问题。

分治算法常用的实现方法是递归。因为分治就是将大问题不断划分成小问题,递归的解决小问题,再合并小问题的解就可以得到问题的解。

递归

递归,就是在函数内部调用本函数自身。形式如下

void foo()

{

     //...

     foo();   //递归

     //...

}

经典问题

(1)二分搜索

public class Main{
    /**
     * 在数组a中查找tag,返回其坐标
     * @param a
     * @param tag
     * @return
     */
    public static int binarySearch(int[] a,int tag){
        if(a == null || a.length == 0){
            return -1;
        }
        int low = 0,hight = a.length-1,mid = 0;
        while(low<=hight){
            mid = (low+hight)/2;
            // 正好是中间的元素
            if(tag == a[mid]){
                return mid;
            }else if(tag<a[mid]){      
                hight = mid-1;
            }else{
                low = mid+1;
            }
        }
        return -1;
    }

    public static void main(String[] args){
        int[] arr = new int[]{0,1,2,3,4,5,6,7,8,9};
        System.out.println(binarySearch(arr,22));
    }
}

(2)大整数乘法

(3)Strassen矩阵乘法

(4)棋盘覆盖

一、问题描述
在这里插入图片描述在这里插入图片描述

二、过程详解

1、棋盘如下图,其中有一特殊方格:16*16
在这里插入图片描述

2、第一个分割结果:88
在这里插入图片描述
3、第二次分割结果:4
4
在这里插入图片描述
4、第三次分割结果:22
在这里插入图片描述
5、第四次分割结果:1
1
在这里插入图片描述

6、第一次分割后子棋盘的覆盖效果
在这里插入图片描述

三、代码实现

public class Main {
    // 定义棋盘的大小:2^k,需要的骨牌数是:(4^k-1)/3
    private static int BOARD_SIZE = 8;
    // 定义一个二维数组用来模拟棋盘
    private static int[][] board = new int[BOARD_SIZE][BOARD_SIZE];
    // 定义一个全局变量,用来记录骨牌的编号
    private static int tile = 0;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("棋盘的大小为:" + BOARD_SIZE);
        System.out.println("请输入特殊方格所在的行号:");
        int dr = scanner.nextInt();
        System.out.println("请输入特殊方格所在的列号:");
        int dc = scanner.nextInt();
        scanner.close();
        // 行号和列号与二位数组的下标相差 1
        chessBoard(0, 0, dr - 1, dc - 1, BOARD_SIZE);
        System.out.println("特殊方块在第 " + dr + " 行第 " + dc + "列,覆盖后的棋盘:");
        // 输出棋盘
        printBoard();
    }

    /**
     * 覆盖棋盘:tr、tc、dt、dc从0开始
     * @param tr:棋盘左上角方格的行号
     * @param tc:棋盘左上角方格的列号
     * @param dr:特殊方格所在的行号
     * @param dc:特殊方格所在的列号
     * @param size:当前棋盘的大小
     */
    private static void chessBoard(int tr, int tc, int dr, int dc, int size) {
      if(size == 1){
          return;
      }
      int t = tile++;
      // 分割棋盘
        int s = size / 2;
        // 1、如果特殊方格在棋盘左上角
        if(dr < tr+s && dc < tc+s){
            // 对左上角的棋盘进行覆盖
            chessBoard(tr,tc,dr,dc,s);
        }else{
            // 如果特殊方格不在左上角的子棋盘中,用 t 号骨牌覆盖该子棋盘的右下角作为特殊方格
            board[tr+s-1][tc+s-1] = t;
            // 覆盖其余方格
            chessBoard(tr,tc,tr+s-1,tc+s-1,s);
        }

        // 2、如果特殊方格在棋盘左下角
        if(dr > tr+s-1 && dc < tc+s){
            // 对左下角的棋盘进行覆盖
            chessBoard(tr+s,tc,dr,dc,s);
        }else{
            // 如果特殊方格不在左下角的子棋盘中,用 t 号骨牌覆盖该子棋盘的右上角作为特殊方格
            board[tr+s][tc+s-1] = t;
            // 覆盖其余方格
            chessBoard(tr+s,tc,tr+s,tc+s-1,s);
        }

        // 3、如果特殊方格在棋盘右上角
        if(dr < tr+s && dc > tc + s -1){
            // 对右上角的棋盘进行覆盖
            chessBoard(tr,tc+s,dr,dc,s);
        }else{
            // 如果特殊方格不在右上角的子棋盘中,用 t 号骨牌覆盖该子棋盘的左下角作为特殊方格
            board[tr+s-1][tc+s] = t;
            // 覆盖其余方格
            chessBoard(tr,tc+s,tr+s-1,tc+s,s);
        }

        // 4、如果特殊方格在棋盘右下角
        if(dr > tr+s-1 && dc > tc+s-1){
            // 对右下角的棋盘进行覆盖
            chessBoard(tr+s,tc+s,dr,dc,s);
        }else{
            // 如果特殊方格不在右下角的子棋盘中,用 t 号骨牌覆盖该子棋盘的左上角作为特殊方格
            board[tr+s][tc+s] = t;
            // 覆盖其余方格
            chessBoard(tr+s,tc+s,tr+s,tc+s,s);
        }
    }

    // 输出棋盘
    private static void printBoard() {
        for (int i = 0; i < BOARD_SIZE; i++) {
            for (int j = 0; j < BOARD_SIZE; j++) {
                System.out.print(board[i][j] + "\t");
            }
            System.out.println("\n");
        }
    }
}


(5)合并排序

(6)快速排序

(7)线性时间选择

(8)最接近点对问题

(9)循环赛日程表

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值