数据结构与算法学习笔记(训练营二第一节)---打表技巧和矩阵处理技巧

打表法

1)问题如果返回值不太多,可以用hardcode的方式列出,作为程序的一部分。
2)一个大问题解决时底层频繁使用规模不大的小问题的解,如果小问题的返回值满足条件1),可以把小问题的解列成一张表,作为程序的一部分。
3)打表找规律(本节课重点),有关1)和2)内容欢迎关注后序课程。

打表找规律

1)某个面试题,输入参数类型简单,并且只有一个实际参数。
2)要求的返回值类型也简单,并且只有一个。
3)用暴力方法,把输入参数对应的返回值,打印出来看看,进而优化code。

  • 小虎去买苹果,商店只提供两种类型的塑料袋,每种类型都有任意数量。
    1)能装下6个苹果的袋子
    2)能装下8个苹果的袋子
    小虎可以自由使用两种袋子来装苹果,但是小虎有强迫症,他要求自己使用的袋子数量必须最少,且使用的每个袋子必须装满。
    给定一个正整数N,返回至少使用多少袋子。如果N无法让使用的每个袋子必须装满,返回-1
/**
 * 小虎去买苹果,商店只提供两种类型的塑料袋,每种类型都有任意数量。
 * 1)能装下6个苹果的袋子
 * 2)能装下8个苹果的袋子
 * 小虎可以自由使用两种袋子来装苹果,但是小虎有强迫症,他要求自己使用的袋子数量必须最少,且使用的每个袋子必须装满。
 * 给定一个正整数N,返回至少使用多少袋子。如果N无法让使用的每个袋子必须装满,返回-1
 */
public class BuyApple {

    // 暴力方法求解
    public static int buyApple(int n){
        if((n & 1)!= 0){
            return -1;
        }

        // 从8个袋子开始尝试
        int max = n / 8;
        int min = Integer.MAX_VALUE;
        for(int i = max;i >=0;i--){
            int temp = n - (i * 8);
            if(temp != 0 && (temp % 6) != 0){
                continue;
            }

            min = Math.min(i + (temp/6),min);
        }

        return min == Integer.MAX_VALUE ? -1 : min;
    }

    // 打表
    public static int buyApple2(int n){
        if((n & 1) != 0){
            return -1;
        }

        if(n < 18){
            if(n == 6 || n == 8){
                return 1;
            }
            if(n == 12 || n == 14 || n ==16){
                return 2;
            }
            return -1;
        }

        return (n - 18) / 8 + 3;
    }
    public static void main(String[] args) {
        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "  " +buyApple(i)+ "       "+i + "  " +buyApple2(i));

        }
    }
}
  • 给定一个正整数N,表示有N份青草统一堆放在仓库里
    有一只牛和一只羊,牛先吃,羊后吃,它俩轮流吃草
    不管是牛还是羊,每一轮能吃的草量必须是:
    1,4,16,64…(4的某次方)
    谁最先把草吃完,谁获胜
    假设牛和羊都绝顶聪明,都想赢,都会做出理性的决定
    根据唯一的参数N,返回谁会赢
/**
 * 给定一个正整数N,表示有N份青草统一堆放在仓库里
 * 有一只牛和一只羊,牛先吃,羊后吃,它俩轮流吃草
 * 不管是牛还是羊,每一轮能吃的草量必须是:
 * 1,4,16,64…(4的某次方)
 * 谁最先把草吃完,谁获胜
 * 假设牛和羊都绝顶聪明,都想赢,都会做出理性的决定
 * 根据唯一的参数N,返回谁会赢
 */
public class WhoWin {
    public static String whoWin(int n){
        // 0   1   2    3 4
        // 后  先   hou   先
        if(n < 5){
            return n == 0 || n ==2 ?"后手" : "先手";
        }
        // 枚举先手吃所有次方开始的草
        int index = 1;
       while (index <= n){
           // 先手index这么多的草,后手继续
           // 因为先手本轮吃过了,在接下去的轮就变成了后手,如果返回后手赢
           // 起始是现在的当前的先手赢了。
           String s = whoWin(n - index);
           if( s == "后手"){
               return "先手";
           }
           // 判断index 的4次方后会不会越界
           if(index > n / 4){
               //index * 4 之后越界
               break;
           }
           index = index * 4;
       }
       return "后手";
    }
    public static void main(String[] args) {
        System.out.println(whoWin(2));
    }
}

  • 定义一种数:可以表示成若干(数量>1)连续正数和的数
    比如:
    5 = 2+3,5就是这样的数
    12 = 3+4+5,12就是这样的数
    1不是这样的数,因为要求数量大于1个、连续正数和
    2 = 1 + 1,2也不是,因为等号右边不是连续正数
    给定一个参数N,返回是不是可以表示成若干连续正数和的数
/**
 * 定义一种数:可以表示成若干(数量>1)连续正数和的数
 * 比如:
 * 5 = 2+3,5就是这样的数
 * 12 = 3+4+5,12就是这样的数
 * 1不是这样的数,因为要求数量大于1个、连续正数和
 * 2 = 1 + 1,2也不是,因为等号右边不是连续正数
 * 给定一个参数N,返回是不是可以表示成若干连续正数和的数
 */
public class LianXiZhengShu {
    public static boolean isNum(int n){
        if(n <= 0){
            return false;
        }

        // 暴力遍历

        for (int i = 1; i <=n; i++) {
            int temp = 0;
            int index = i;
            for (int j = i;j <=n;j++){
                temp += j;
                // 个数要大于1
                if((temp == n) && (j - index) >= 1 ){
                    return true;
                }
            }
        }

        return false;
    }


    public static boolean isNum2(int n){
        if(n < 3){
            return false;
        }
        // 从4开始,只要满足2^x == n 就是false
        // 判断一个数是不是2的某次方,由于2的某次方只有一个最高为的1 ,所以得出一下公式
        // n & (n -1 ) == 0,
        return (n & (n -1 )) != 0;
    }
    
    public static void main(String[] args) {
        for (int i = 1; i < 100; i++) {
            System.out.println(i + " " + isNum(i) + "  "+ isNum2(i));

        }
    }
}

矩阵处理技巧--核心技巧:找到coding上的宏观调度

  • zigzag打印矩阵
// 之字形打印举证
public class ZigZag {

    // 宏观调度
    // 利用两个指针A,B,  A B同时移动,A先水平移动,移到不能再移,就向下移动,
    // B向下移动,移到不能再移,就向右移动,直到AB到达终点。
    public static void zigzag(int[][] matrix){
        if(matrix == null || matrix.length == 0){
            return;
        }
        // 定义A、B两点
        int aR = 0;
        int aC = 0;
        int bR = 0;
        int bC = 0;
        // 终点
        int endR = matrix.length - 1;
        int endC = matrix[0].length - 1;

        // AB移动 一定是同时到达终点,只用判断一个点是否越界就可以了
        // 这里就用A点
        boolean from = true;
        while (aR <= endR && aC <= endC){
            // 没有越界就一直打印
            printLine(matrix,aR,aC,bR,bC,from);
            from = !from;
            // A的移动轨迹
            aR = aC != endC ? aR : ++aR ;
            aC = aC == endC ? endC : ++aC;
            // B的移动轨迹
            bC = bR != endR ? bC:++bC;
            bR = bR != endR ? ++bR:endR;
        }
    }

    // 给点A B 两点和打印方向,打印AB两点的连线
    private static void printLine(int[][] matrix,int aR,int aC,int bR,int bC,boolean from){
        if(from){
            // 从下到上
            while (bR >= aR && bC <= aC){
                System.out.print(matrix[bR--][bC++]);
            }
        }else{
            // 从上到下
            while (aR <= bR && aC >= bC){
                System.out.print(matrix[aR++][aC--]);
            }
        }
    }
    public static void main(String[] args) {
        int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
        zigzag(matrix);


    }
}
  • 转圈打印矩阵
// 转圈打印矩阵
// 先打印外圈环,在打印内圈
public class PrintRing {
    public static void printRing(int[][] matrix){
        if(matrix == null || matrix.length == 0){
            return;
        }
        // 最外城点坐标
        int a = 0;int b = 0;
        int c = matrix.length -1 ;int d = matrix[0].length - 1;

        while (a <= c && b <= d){
            print(matrix,a++,b++,c--,d--);
        }
    }

    // 给定左上角点坐标,右下角点坐标,打印围成的圈。
    private static void print(int[][] matrix,int a,int b,int c,int d){
        if(a == c){
            // 同一条横线上,直接打印
            while (b <= d){
                System.out.print(matrix[a][b++] + " ");
            }
        }else if(b == d){
            // 同一条竖线
            while (a <= c){
                System.out.print(matrix[a++][b]+ " ");
            }
        }else{
            // 一般情况
            // 上横线
            int curR = a;
            int curC = b;
            while (curC < d){
                System.out.print(matrix[curR][curC++]+ " ");
            }
            // 右竖线
            while (curR < c){
                System.out.print(matrix[curR++][curC]+ " ");
            }

            // 下横线
            while (curC > b){
                System.out.print(matrix[curR][curC--]+ " ");
            }

            // 左竖线
            while (curR > a){
                System.out.print(matrix[curR--][curC]+ " ");
            }
        }
    }
    public static void main(String[] args) {
        int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 },
                { 13, 14, 15, 16 } };
        printRing(matrix);

    }
}

  • 原地旋转正方形矩阵
// 原地旋转正方形矩阵 90度
// 先边外圈,然后向里面变
public class YuanDiPrint {

    public static void print(int[][] matrix){
        if(matrix == null || matrix[0].length == 0){
            return;
        }

        int a = 0;
        int b = 0;
        int c = matrix.length - 1;
        int d = matrix[0].length - 1;
        // 由于是正方形判断一遍就行了
        while (a <= c){
            bian(matrix,a++,b++,c--,d--);
        }

    }


    // 变换每一圈中左边的位置
    private static void bian(int[][] matrix,int a,int b,int c,int d){
        // 每一圈需要变换的个数,从0开始
        int num = d - b;
        for (int i = 0; i < num; i++) {
            // 每一组中的坐标变换
            int tmp = matrix[a][b + i];
            matrix[a][b + i] = matrix[c - i][b];
            matrix[c - i][b] = matrix[c][d - i];
            matrix[c][d - i] = matrix[a + i][d];
            matrix[a + i][d] = tmp;
        }
    }
    public static void printMatrix(int[][] matrix) {
        for (int i = 0; i != matrix.length; i++) {
            for (int j = 0; j != matrix[0].length; j++) {
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println();
        }
    }
    public static void main(String[] args) {
        int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 16 } };
        printMatrix(matrix);
        print(matrix);
        System.out.println("=========");
        printMatrix(matrix);

    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容