算法中级学习2

一、数组问题

获取最多1的行的坐标

/**
 * @Author: 郜宇博
 * @Date: 2021/11/23 14:59
 */
public class MaxCountLine {
    public static void main(String[] args) {
        int[][] ar = {
                {0,1,1,1,1},
                {0,0,0,1,1},
                {0,0,1,1,1},
                {0,0,0,0,0}
        };
        System.out.println(getMamLine(ar));
    }

    /**
     * 获取1最多的行坐标,从右上角开始遍历,如果左边为1,则左移
     * 左边不为1,下移
     * @param arr
     * @return
     */
    public static int getMamLine(int[][] arr){
        List<Integer> list = new ArrayList<>();
        int count = 0;
        int j = arr[0].length;
        int res = 0;
        for (int i =0; i < arr.length; i++){
            //j = arr[0].length-1;
            while (j >=0){
                if (arr[i][j-1] == 1){
                     count++;
                     j--;
                     res = i;
                } else {
                    break;
                }
            }
        }
        return res;
    }
}

蛇形打印

用zigzag的方式打印矩阵,比如如下的矩阵 0 1 2 3 4 5 6 7 8 9 10 11
打印顺序为:0 1 4 8 5 2 3 6 9 10 7 11

package day14;

/**
 * @Author: 郜宇博
 * @Date: 2021/11/27 13:05
 */
public class ArrayPrintSnake {
    public static void main(String[] args) {
        int[][]arr = {
                {0,1,2,3,5},
                {4,5,6,7,3},
                {8,9,10,11,34}
        };
        print(arr);
    }
    /**
     * 蛇形打印
     * 指定两个坐标,打印斜对角线(1,左下打印;2右上)
     * 初始两个坐标都在0位置,index1向下走到头向右走,index2向右走到头向下走,相碰停止。
     * 每次两坐标都移动,移动一次打印一趟,更换一下打印方向。
     */
    public static void print(int[][] arr){
        int index1R=0,index2R = 0;
        int index1C=0,index2C = 0;;
        boolean ifRightUp = true;
        do {
            //右上方向打印
            print(arr,index1R,index1C,index2R,index2C,ifRightUp);
            //1坐标移动(先看列需不需要动)
            index1C = index1R == arr.length-1?index1C+1:index1C;
            index1R = index1R == arr.length-1?index1R:index1R+1;
            //2坐标移动(先看行需不需要动)
            index2R = index2C == arr[0].length-1?index2R+1:index2R;
            index2C = index2C == arr[0].length-1?index2C:index2C+1;
            ifRightUp = !ifRightUp;
        }while ((index1R != index2R) && (index1C != index2C));
        System.out.println(arr[index1R][index1C]);
    }
    private static void print(int [][] arr,int index1R, int index1C, int index2R, int index2C, boolean ifRightUp) {
        //右上方打印
        if (ifRightUp){
            int times = index2C -index1C;
            for (int i = 0; i <= times; i++){
                System.out.print(arr[index1R--][index1C++]+" ");
            }
        }
        else {
            int times = index2C -index1C;
            for (int i = 0; i <= times; i++){
                System.out.print(arr[index2R++][index2C--]+" ");
            }
        }
    }
}

螺旋打印

用螺旋的方式打印矩阵,比如如下的矩阵 0 1 2 3 4 5 6 7 8 9 10 11
打印顺序为:0 1 2 3 7 11 10 9 8 4 5 6

package day14;

/**
 * @Author: 郜宇博
 * @Date: 2021/11/27 14:11
 */
public class ArrayPrintCricle {
    public static void main(String[] args) {
        int[][] matrix = {
                { 1, 2, 3, 4 }, 
                { 5, 6, 7, 8 }, 
                { 9, 10, 11, 12 },
                { 13, 14, 15, 16 } };
        print(matrix);
    }

    /**
     * 一圈一圈打印
     * 然后坐标1向右下方移动
     * 坐标2向左上方移动
     * 错开的时候:index2R < index1R,停止
     * @param arr
     */
    public static void print(int[][]arr){
        int index1R = 0;
        int index1C = 0;
        int index2R = arr.length-1;
        int index2C = arr[0].length-1;
        while (index2R >= index1R){
            printCricle(arr,index1R++,index1C++,index2R--,index2C--);
        }
    }
    private static void printCricle(int[][] arr, int index1R, int index1C, int index2R, int index2C) {
        //如果在同一行
        if (index2R == index1R){
            while (index1C+1 <= index2C){
                System.out.print(arr[index1R][index1C++]+" ");
            }
        }
        //如果在同一列
        if (index1C == index2C){
            while (index1R+1 <= index2R){
                System.out.print(arr[index1R++][index1C]+" ");
            }
        }
        //正常情况
        else {
            int curC = index1C;
            int curR = index1R;
            //向左
            while (index1C != index2C){
                System.out.print(arr[index1R][index1C++]+" ");
            }
            //向下
            while (index1R != index2R){
                System.out.print(arr[index1R++][index1C]+" ");
            }
            //向左
            while (index1C != curC){
                System.out.print(arr[index1R][index1C--]+" ");
            }
            //向上
            while (index1R != curR){
                System.out.print(arr[index1R--][index1C]+" ");
            }
        }
    }
}

二、机器人打包问题

有n个打包机器从左到右一字排开,上方有一个自动装置会抓取一批放物品到每个打 包机上,放到每个机器上的这些物品数量有多有少,由于物品数量不相同,需要工人 将每个机器上的物品进行移动从而到达物品数量相等才能打包。每个物品重量太大、 每次只能搬一个物品进行移动,为了省力,只在相邻的机器上移动。请计算在搬动最 小轮数的前提下,使每个机器上的物品数量相等。如果不能使每个机器上的物品相同, 返回-1。 例如[1,0,5]表示有3个机器,每个机器上分别有1、0、5个物品,经过这些轮后: 第一轮:1 0 <- 5 =>
1 1 4 第二轮:1 <-1<- 4 =>
2 1 <- 3 => 2 2 2 移动了3轮,每个机器上的物品相等,所以返回3
例如[2,2,3]表示有3个机器,每个机器上分别有2、2、3个物品,
这些物品不管怎么移动,都不能使三个机器上物品数量相等,返回-1

package day14;

/**
 * @Author: 郜宇博
 * @Date: 2021/11/27 12:40
 */
public class PackingMachine {
    public static void main(String[] args) {
        System.out.println(MinOp2(new int[]{20,20, 5,5,2,8}));
    }
    public static int MinOp2(int[] arr){
        if (arr == null || arr.length ==0){
            return -1;
        }
        int size = arr.length;
        int sum = 0;
        int avg = 0;
        for (int j : arr) {
            sum += j;
        }
        if (sum % size != 0){
            return -1;
        }
        avg = sum/size;
        //左侧需要给出多少件,负数表示需要多少
        int leftGive = 0;
        int rightGive = 0;
        //左侧的累加和
        int leftSum = 0;
        int minOp = Integer.MIN_VALUE;
        for (int i = 0; i < size;i++){
            leftGive = leftSum - avg*i;
            rightGive = sum -leftSum-arr[i]- (avg *(size - i - 1));
            //如果左右都不足,那么需要中间的一次一次给两边,操作数为和
            if(leftGive < 0 && rightGive < 0){
                //解决最痛的步骤,其他步骤就解决了,因此选择操作数做多的那个一个
                minOp = Math.max(minOp,Math.abs(leftGive)+Math.abs(rightGive));
            }
            else {
                //其他情况,只需要所需数或要给数多那个步骤(可以中间边给两边,两边边给中间;也可以两边一起给中间)
                minOp = Math.max(minOp,Math.max(Math.abs(leftGive),Math.abs(rightGive)));
            }
            //更新左边和
            leftSum += arr[i];
        }
        return minOp;
    }
}

三、栈转队列

package day14;

import java.util.Stack;

/**
 * @Author: 郜宇博
 * @Date: 2021/11/27 15:42
 */
public class StackToQueue {
    public static void main(String[] args) {
        MyQueue myQueue = new MyQueue();
        myQueue.add(1);
        myQueue.add(2);
        myQueue.add(3);
        System.out.println(myQueue.poll());
        myQueue.add(4);
        System.out.println(myQueue.poll());
    }
    static class MyQueue{
        private Stack<Integer> pushStack = new Stack<>();
        private Stack<Integer> popStack = new Stack<>();

        public void add(Integer num){
            pushStack.add(num);
            //加入弹出栈中
            toPopStack();
        }
        private void toPopStack() {
            if ( !popStack.isEmpty()){
                return;
            }
            while (!pushStack.isEmpty()){
                popStack.add(pushStack.pop());
            }
        }
        public int poll(){
            return popStack.pop();
        }
    }
}

四、队列转栈

package day14;

import java.util.LinkedList;
import java.util.Queue;

/**
 * @Author: 郜宇博
 * @Date: 2021/11/27 15:52
 */
public class QueueToStack {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        myStack.add(1);
        myStack.add(2);
        System.out.println(myStack.pop());
        myStack.add(3);
        System.out.println(myStack.pop());
        System.out.println(myStack.pop());
        //System.out.println(myStack.pop());
    }

    static class MyStack {
        private Queue<Integer> queue1 = new LinkedList<>();
        private Queue<Integer> queue2 = new LinkedList<>();

        public void add(Integer num) {
            queue1.add(num);
        }
        public int pop() {
            if (queue1.isEmpty()){
                throw  new RuntimeException("空");
            }
            while (queue1.size() > 1) {
                queue2.add(queue1.poll());
            }
            int res = queue1.poll();
            swap();
            return res;
        }
        private void swap() {
            Queue<Integer> temp = queue2;
            queue2 = queue1;
            queue1 = temp;
        }

    }
}

五、容器盛水

给定一个数组arr,已知其中所有的值都是非负的,将这个数组看作一个容器, 请返回容器能装多少水 比如,arr = {3,1,2,5,2,4},根据值画出的直方图就是容器形状,该容 器可以装下5格水
再比如,arr = {4,5,1,3,2},该容器可以装下2格水

/**
 * @Author: 郜宇博
 * @Date: 2021/11/27 16:06
 */
public class ContainWater {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++){
            int[] ints = randomArray(10);
            if (getContain2(ints) != getContain1(ints)){
                System.out.println("no");
            }
        }
    }
    /**
     * 预处理法获取各个位置左右的最大值
     * @param arr
     * @return
     */
    public static int getContain1(int[] arr){
        //预处理获取各左右最大高度
        int[] leftMax = new int[arr.length];
        leftMax[0] = -1;
        int[] rigthtMax = new int[arr.length];
        rigthtMax[arr.length-1] = -1;
        for (int i = 1; i < leftMax.length; i++){
            leftMax[i] = Math.max(arr[i-1],leftMax[i-1]);
        }
        for (int i = rigthtMax.length-2; i >=0; i--){
            rigthtMax[i] = Math.max(rigthtMax[i+1],arr[i+1]);
        }
        //获取每个位置积水
        int res = 0;
        int part = 0;
        for (int i = 1; i < arr.length-1;i++){
            part = Math.max((Math.min(leftMax[i], rigthtMax[i])) - arr[i], 0);
            res+=part;
        }
        return res;
    }
    /**
     * 双指针法
     * @param arr
     * @return
     */
    public static int getContain2(int[] arr){
        int leftBorder = 1;
        int rightBorder = arr.length-2;
        int leftMax = arr[0];
        int rightMax = arr[arr.length-1];
        int res = 0;
        while (leftBorder <= rightBorder ){
            //左边界移动
            if (leftMax <= rightMax){
                res += Math.max(leftMax - arr[leftBorder],0);
                leftMax = Math.max(arr[leftBorder++],leftMax);
            }
            //右边界移动
            else {
                res += Math.max(rightMax - arr[rightBorder],0);
                rightMax = Math.max(arr[rightBorder--],rightMax);
            }
        }
        return res;
    }
    public static int[] randomArray(int num){
        int[] ints = new int[num];
        for (int i = 0; i < num; i++){
            ints[i] = new Random().nextInt(10);
        }
        return ints;
    }

}

六、旋转词字符串

如果一个字符串为str,把字符串str前面任意的部分挪到后面形成的字符串叫 作str的旋转词。比如str="12345",str的旋转词有"12345"、"23451"、 "34512"、"45123"和"51234"。给定两个字符串a和b,请判断a和b是否互为旋转
词。 比如: a="cdab",b="abcd",返回true。 a="1ab2",b="ab12",返回false。
a="2ab1",b="ab12",返回true。

/**
 * @Author: 郜宇博
 * @Date: 2021/11/27 18:26
 */
public class ChildString {
    public static void main(String[] args) {
        System.out.println(isChild("2ab1", "ab12"));
    }
    /**
     * 将str1拼为str1+str1,如果新的字符串有子串str2,则互为旋转词
     * @param str1
     * @param str2
     * @return
     */
    public static boolean isChild(String str1,String str2){
        char[] char1 = (str1+str1).toCharArray();
        char[] char2 = str2.toCharArray();
        return KMP(char1,char2);
    }
    public static boolean KMP(char[] char1, char[] char2) {
        int[] next = getNext(char2);
        int cur1 = 0;
        int cur2 = 0;
        while (cur1 < char1.length && cur2 < char2.length){
            if (char1[cur1] == char2[cur2]){
                cur1++;
                cur2++;
            }
            else {
                if (next[cur2] == -1){
                    cur1++;
                }
                else {
                    cur2 = next[cur2];
                }
            }
        }
        return cur2 == char2.length;
    }
    public static int[] getNext(char[] chars){
        if (chars.length == 1){
            return new int[]{-1};
        }
        int[] next = new int[chars.length - 1];
        next[0] = -1;
        next[1] = 0;
        int cn = 0;
        for (int i =2; i < next.length; i++){
            cn = next[i-1];
            if (chars[i-1] == chars[cn]){
                next[i++] = ++cn;
            }
            else {
                if (cn == 0){
                    next[i++] = 0;
                }
                else {
                    cn = next[cn];
                }
            }
        }
        return next;
    }
}
posted @ 2021-12-02 14:08  橡皮筋儿  阅读(93)  评论(0编辑  收藏  举报