算法leetcode

0. 代码

Deque<Integer> stack = new LinkedList<>();  stack.push(); stack.pop()
队列   Queue<Integer> queue = new LinkedList<>();  queue.add(3); queue.remove()
双端队列 Deque<Integer> deque = new LinkedList<>();  队列相关 add,remove,栈相关:addFirst,removeLast
List<Integer> list = new ArrayList<>();  //添加元素到list的指定位置
list.add(0,5);

小根堆(默认) PriorityQueue<Integer> pq = new PriorityQueue<>();  pq.add(1); pq.remove()
大根堆        PriorityQueue<Integer> pq = new PriorityQueue<>((v1,v2)-> v2-v1);
        

        HashSet<Integer> hashSet = new HashSet<>(); //hash表
        TreeSet<Integer> treeSet = new TreeSet<>(); //有序表
comparator方法 (a,b)->{return a-b;}  //升序
若只有一行可以改为 (a,b)-> a-b   //升序

二进制String转十进制
Integer.parseInt("1111",2)
十进制转二进制
Integer.toBinaryString(7)


集合翻转
        Collections.reverse(result);

位运算
	判断是否是 2 的幂 :A & (A - 1) == 0
	最低位的 1 变为 0 :n &= (n - 1)
	最低位的 1A & (-A)


list转数组
list.toArray();
二维
list.toArray(new int[0][]);

         Math.ceil(3.5);//天花板
        Math.floor(3.5);//地板

 	
     

1. 队列

电话号码的字母组合

在这里插入图片描述
思路:先入队列,再逐个出队列,出队列的元素和新的字符相加再进入队列,直到遍历结束

   public static List<String> letterCombinations(String digits) {
        Map<Integer,String[]> map = new HashMap<>();
        map.put(2,new String[]{"a","b","c"});
        map.put(3,new String[]{"d","e","f"});
        map.put(4,new String[]{"g","h","i"});
        map.put(5,new String[]{"j","k","l"});
        map.put(6,new String[]{"m","n","o"});
        map.put(7,new String[]{"p","q","r","s"});
        map.put(8,new String[]{"t","u","v"});
        map.put(9,new String[]{"w","x","y","z"});
        Queue<String> queue =new LinkedList<>();
        for(int i = 0;i<digits.length();i++){
            if(queue.isEmpty()){
                String[] tmp = map.get(Integer.valueOf(String.valueOf(digits.charAt(i))));
                for(int j = 0;j<tmp.length;j++){
                    queue.add(tmp[j]);
                }
            }else {
                int m = queue.size();
                String[] tmp = map.get(Integer.valueOf(String.valueOf(digits.charAt(i))));
                for(int j = 0;j<m;j++){
                    String a = queue.poll();
                    for(int k = 0;k<tmp.length;k++){
                        queue.add(a+tmp[k]);
                    }
                }
            }
        }
        List<String> result = Lists.newArrayList();
        while (!queue.isEmpty()){
            result.add(queue.poll());
        }
        return result;

    }

层次遍历

 public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> result = new ArrayList<>();
        if(root == null){
            return result;
        }
        queue.add(root);
        while (!queue.isEmpty()){
            int num = queue.size();
            List<Integer> tmp = new ArrayList<>();
            for(int i = 0;i<num;i++){
                TreeNode node = queue.remove();
                if(null != node.left)
                queue.add(node.left);
                if(null != node.right)
                    queue.add(node.right);
                tmp.add(node.val);
            }
            result.add(tmp);
        }
        return result;
    }

2. 栈

括号匹配

 public static boolean isValid(String s) {
        Stack<String> stack = new Stack<>();
        for (int i = 0;i<s.length();i++){
            String tmp = String.valueOf(s.charAt(i));
            if(tmp.equals("(") || tmp.equals("{") || tmp.equals("[")){
                stack.push(tmp);
            }else if(tmp.equals(")")){
                if(stack.isEmpty() || !stack.pop().equals("(")){
                    return false;
                }
            }else if(tmp.equals("}")){
                if(stack.isEmpty() || !stack.pop().equals("{")){
                    return false;
                }
            }else if(tmp.equals("]")){
                if(stack.isEmpty() || !stack.pop().equals("[")){
                    return false;
                }
            }

        }
        if(stack.isEmpty()){
            return true;
        }
        return false;
    }

最长匹配括号

思路,栈中存储括号的下标,若为(,则入栈,否则出栈,判断栈是否为空,若为空,说明没有以其匹配的左括号,放入下标(最后一个没有被匹配右括号的下标);否则,和栈顶元素计算最大长度;

public static int maxl(String s){
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        int max = 0;
        for (int i = 0;i<s.length();i++){
            if(s.charAt(i) == '('){
                stack.push(i);
            }else {
                stack.pop();
                if(stack.isEmpty()){
                    stack.push(i);
                }else {
                    max = Math.max(max,i-stack.peek());
                }
            }
        }
        return max;
    }

linux路径

单调栈

数组左右两遍比他大的(接雨水)

        //3 2 1 4 1 5
        //i入栈时确定 i左边比他大的(栈顶)
        //i出栈 确定 i右边比他大的,要加入的
   
    public static int[][] getMax(int arr[]) {
        int n = arr.length;
        int[][] result = new int[n][2];
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        result[0][0] = -1;
        result[n-1][1] = -1;
        for(int i = 1;i< n;i++){
            if(!stack.isEmpty() && arr[stack.peek()]<=arr[i]){//出战
                while (!stack.isEmpty() && arr[stack.peek()]<=arr[i]){
                    result[stack.pop()][1] = arr[i];
                }
                    result[i][0] = stack.isEmpty() ? -1 : arr[stack.peek()] ;
                stack.push(i);
            } else { //入栈
                result[i][0] = arr[stack.peek()];
                stack.push(i);
            }

        }
        return result;
    }
    int arr3[] = new int[]{3,2,1,4,1,5};
        int arr4[][] =  getMax(arr3);
for (int i = 0; i < length; i++) {
            while (!stack.isEmpty() && T[i] > T[stack.peek()]) {
                int pre = stack.pop();
                result[pre] = i - pre;
            }
            stack.add(i);

        }

每日温度(减栈)

栈中元素为下标和温度值,温度减少才入栈

去除重复字符(增栈,考虑剩余)

去除重复字符,字典序最小

求最大矩形(84,写,增栈)

3. 队列

往往类似于“请找到满足xx的最x的区间(子串、子数组)的xx”这类问题都可以使用该方法进行解决。
用滑动窗口的双端队列解决

滑动窗口的最大值

    public static int[] maxValue(int arr[],int k){
        Deque<Integer> queue = new LinkedList<Integer>();
        queue.addFirst(0);
        int result[] = new int[arr.length-k+1];
        for(int i = 1;i<arr.length;i++){
            if(arr[i] >= arr[queue.getLast()]) {
                while (!queue.isEmpty() && arr[i] >= arr[queue.getLast()]) {
                    queue.removeLast();
                }

            }
            while(!queue.isEmpty() && i-queue.getFirst() >= k){
                queue.removeFirst();
            }
            queue.addLast(i);
            if(i>=k-1){
                result[i-k+1] = arr[queue.getFirst()];
            }
        }
        return result;
    }
      int arr[] = new int[]{1,3,2,2,1,5};
            int arr2[] = maxValue(arr,3);

3. 链表

在这里插入图片描述
在这里插入图片描述

链表逆序

public static ListNode swapAll(ListNode head) {
        Stack<ListNode> stack = new Stack<>();
        ListNode result = new ListNode(1,null);
        result.next = head;
        ListNode tmp = head;
        while (tmp != null){
            stack.push(tmp);
            tmp = tmp.next;
        }
        tmp = result;
        while (!stack.isEmpty()){
            tmp.next = stack.pop();
            tmp = tmp.next;
        }
        tmp.next = null;
        return result.next;
    }

两两交换链表的节点

申请一个节点指向头节点,一个临时节点做交换

    public static ListNode swapPairs(ListNode head) {
        ListNode dummyHead = new ListNode(1,null);
        dummyHead.next = head;
        ListNode temp = dummyHead;
        while (temp.next != null && temp.next.next != null) {
            ListNode node1 = temp.next;
            ListNode node2 = temp.next.next;
            temp.next = node2;
            node1.next = node2.next;
            node2.next = node1;
            temp = node1;
        }
        return dummyHead.next;
    }

旋转链表

先形成环,再在指定位置断开

4. 数组

旋转图像(数组旋转90度)

先对角线旋转,再水平旋转

螺旋数组

public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> res = new ArrayList<>();
        int count = 0, row = matrix.length, column = matrix[0].length;
        int total = row * column;
        int up = 0, down = row - 1, left = 0, right = column - 1;
        while(count < total){
            for(int i = left; i <= right && count < total; i++){
                res.add(matrix[up][i]);
                count++;
            }
            up++;
            for(int i = up; i <= down && count < total; i++){
                res.add(matrix[i][right]);
                count++;
            }
            right--;
            for(int i = right; i >= left && count < total; i--){
                res.add(matrix[down][i]);
                count++;
            }
            down--;
            for(int i = down; i >= up && count < total; i--){
                res.add(matrix[i][left]);
                count++;
            }
            left++;
        }
        return res;
    }

合并区间

先升序排列,再合并

public static int[][] merge(int[][] intervals) {
        List<int[]> res = new ArrayList<>();
        Arrays.sort(intervals,(a,b)->a[0]-b[0]);
        int[][] result= new int[][]{};
        int left = -1,right = -1;
        for(int i = 0;i<intervals.length;i++){
            if(left == -1 && right == -1){
                left = intervals[i][0];
                right = intervals[i][1];
            }
            if(intervals[i][0]<=right){
                right = Math.max(right,intervals[i][1]);
            }else {
                res.add(new int[]{left,right});
                left = intervals[i][0];
                right = intervals[i][1];
            }
        }
        res.add(new int[]{left,right});
        return res.toArray(new int[0][]);

    }

5. 二维数组

矩阵置0

描述,O(1)空间复杂度,若某一行或某一列有0,则这一行及这一列都置为0
思路,用第一行和第一列作为这一行或者这一列是否有0的标志位,用两个变量记录第一行及第一列本身是否有0

6.有序表

有序表(log(n)treeMap,很多数据结构都能实现,红黑树(约束目的就是长的路径不能是短的2倍),avl树(左右高度差不超过1),sizeBalance树(外甥的节点数量不能超过叔叔),前三个都通过左右旋实现,区别是判断平衡性的规则不一样,跳表等)

大楼轮廓

》大楼的轮廓问题:
1:确定每个点的最大高度,即可构成结果;
2: 确定每个点的最大高度treemap2(坐标,高度),需要遍历点的时候求得当前有哪些高度及高度出现的次数,treemap的getLastKey,treemap1(最大高度,高度的次数)
3:将每个大楼变为两个边(上还是下)的对象,形成的序列排序;即可构成treemap1
在这里插入图片描述

  public static class buildC implements Comparator<Build>{
        @Override
        public int compare(Build o1, Build o2) {
            if(o1.pos == o2.pos){
                return o1.up ? -1 : 1;
            }else {
                return o1.pos - o2.pos;
            }

        }
    }



    public static class Build{
        public int pos;
        public int height;
        public boolean up;
        public Build(int pos ,int height,boolean up){
            this.pos = pos;
            this.height = height;
            this.up = up;
        }
    }



    public static List<List<Integer>> get(int builds[][]){
        List<List<Integer>> results = new ArrayList<List<Integer>>();
        Build[] buildBusi = new Build[builds.length*2];
        for (int i = 0;i<builds.length;i++) {
            buildBusi[2*i] = new Build(builds[i][0],builds[i][2],true);
            buildBusi[2*i+1] = new Build(builds[i][1],builds[i][2],false);
        }
        Arrays.sort(buildBusi,new buildC());
        TreeMap<Integer,Integer> levelmap = new TreeMap<>();//高度,出现次数
        TreeMap<Integer,Integer> maxmap = new TreeMap<>();//位置,最高高度
        for(int i = 0;i<buildBusi.length;i++){
            Build tmp = buildBusi[i];
            if(tmp.up){//先更新高度次数信息
                if(levelmap.containsKey(tmp.height)){
                    levelmap.put(tmp.height,levelmap.get(tmp.height)+1);
                }else {
                    levelmap.put(tmp.height,1);
                }
            }else {
                levelmap.put(tmp.height,levelmap.get(tmp.height)-1);
                if(levelmap.get(tmp.height) == 0){
                    levelmap.remove(tmp.height);
                }
            }
            if(levelmap.isEmpty()){
                maxmap.put(tmp.pos,0);
            }else {
                maxmap.put(tmp.pos,levelmap.lastKey());
            }

        }
        int preH = 0;
        int preindex = 0;
        for (int pos: maxmap.keySet()) {
            int curH = maxmap.get(pos);
            if(curH != preH){
                if(preH == 0){
                    preH = curH;
                    preindex = pos;
                    continue;
                }
                List<Integer > result = new ArrayList<>();
                result.add(preindex);
                result.add(pos);
                result.add(preH);
                results.add(result);
                preindex = pos;
                preH = curH;
            }
            
        }

      return results;
    }

            int  builds[][] = new int[][]{{1,3,3},{2,4,4},{5,6,1}};

System.out.println(get(builds));

找工作

    public static int[] getMaxPay(Job[] jobs,int [] abli){
        int[] result = new int[abli.length];
        Arrays.sort(jobs,new JobComparator());
        TreeMap<Integer,Integer> best = new TreeMap<>();
        best.put(jobs[0].limit,jobs[0].pay);
        Job pre = jobs[0];
        for(int i = 1;i<jobs.length;i++){
            if(jobs[i].limit== pre.limit || jobs[i].pay <= pre.pay){
                continue;
            }
            pre = jobs[i];
            best.put(pre.limit,pre.pay);
        }
        for(int i = 0;i<abli.length;i++){
            result[i] = best.floorKey(abli[i]) != null ? best.get( best.floorKey(abli[i])) : 0;
        }
        return result;
    }

    static class Job{
        public int limit;
        public int pay;

        public Job(int limit,int pay){
            this.limit = limit;
            this.pay = pay;
        }
    }

    public static class JobComparator implements Comparator<Job>{
        public int compare(Job o1,Job o2){
            return o1.limit == o2.limit ? o2.pay - o1.pay : o1.limit - o2.limit;
        }
    }```

        Job [] jobs = new Job[]{new Job(1,2),new Job(3,6),new Job(1,5),new Job(2,4)};
        int b[] = getMaxPay(jobs,new int[]{1,2,3,5});
        System.out.println();

# 4. 回溯
深度优先,递归

## 模板

public static void dfs(){
//终止条件

for(){
//if()剪枝
//操作
dfs();//递归调用
//逆操作,回溯
}
}


## 全排列

要解决重复问题,我们只要设定一个规则,保证在填第
idx
idx 个数的时候重复数字只会被填入一次即可。而在本题解中,我们选择对原数组排序,保证相同的数字都相邻,然后每次填入的数一定是这个数所在重复数集合中「从左往右第一个未被填过的数字」,即如下的判断条件:

```java
 if(i>0 && nums[i] == nums[i-1] && used[i-1] == 0){
                continue;
            }

全排列后的第k个

方法1:既然所有的全排列是从小到大,那么可以对每一位的数字进行定位。例如,假如给定题目为(5,46)。固定第一位数,后面4位的全排列数为24,math.ceil(46/24)=2,即处于第1位数的第二个循环中,即第一位数为2.同理,对于固定第二位数,math.ceil((46-24)/6)=4,即处于第2位数的第四个循环中(此时列表移除了已确定的数字2),即第2位数为5.同理,可依次推理出最后结果为“25341”.总复杂度为O(n).

方法二:全排列+剪枝

n皇后,8皇后

原理同上,只是判断重复时,斜着的和竖着的通过set判断

括号生成

/**
     * @param curStr 当前递归得到的结果
     * @param left   左括号已经用了几个
     * @param right  右括号已经用了几个
     * @param n      左括号、右括号一共得用几个
     * @param res    结果集
     */
    private void dfs(String curStr, int left, int right, int n, List<String> res) {
        if (left == n && right == n) {
            res.add(curStr);
            return;
        }

        // 剪枝
        if (left < right) {
            return;
        }

        if (left < n) {
            dfs(curStr + "(", left + 1, right, n, res);
        }
        if (right < n) {
            dfs(curStr + ")", left, right + 1, n, res);
        }
    }
}

ip生成,电话号码

5. 递归(树的深度优先)

先序中虚得到后续

    public static String getPost(String pre,String in){
        return getPost(pre,in,0,pre.length() -1,0,pre.length()-1);
    }

    public static String getPost(String pre,String in,int preL,int preR,int inL,int inR){
        if(preL == preR){
            return String.valueOf(pre.charAt(preL));
        }
        if(preL > preR){
            return "";
        }
        char chara = pre.charAt(preL);
        int inIndex = in.indexOf(chara);
        int leftLength = inIndex - inL;
        int rightLength = preR - preL - leftLength;
        String left = getPost(pre,in,preL+1,preL+1+leftLength-1,inL,inL+leftLength-1);
        String right = getPost(pre,in,preR-rightLength+1, preR,inR-rightLength+1 , inR);
        return left+right+chara;
    }
    public static String getPost(String pre,String in){
        return getPost(pre,in,0,pre.length() -1,0,pre.length()-1);
    }

    public static String getPost(String pre,String in,int preL,int preR,int inL,int inR){
        if(preL == preR){
            return String.valueOf(pre.charAt(preL));
        }
        if(preL > preR){
            return "";
        }
        char chara = pre.charAt(preL);
        int inIndex = in.indexOf(chara);
        int leftLength = inIndex - inL;
        int rightLength = preR - preL - leftLength;
        String left = getPost(pre,in,preL+1,preL+1+leftLength-1,inL,inL+leftLength-1);
        String right = getPost(pre,in,preR-rightLength+1, preR,inR-rightLength+1 , inR);
        return left+right+chara;
    }

数学公式的结果

包含括号的需要递归调用不包含括号的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//不包含括号的
System.out.println(result("1+2*12+4"));

public static int result(String str){
    if(null == str){
        throw new RuntimeException("");
    }
    Stack<String> stack = new Stack<>();
    String input = "";
    for(int i = 0;i<str.length();i++){
        if(str.charAt(i) == '+' || str.charAt(i) == '-' || str.charAt(i) == '*' || str.charAt(i) == '/'){
            if(!stack.isEmpty() && (stack.peek().equals("*") || stack.peek().equals("/"))){
                String fuhao = stack.pop();
                stack.push(String.valueOf(comp(stack.pop(),input,fuhao)));
            }else {
                stack.push(input);
            }
            stack.push(String.valueOf(str.charAt(i)));
            input = "";
        }else {
            input += str.charAt(i);
        }
    }
    stack.push(input);
    String result = stack.pop();
    while (!stack.isEmpty()){
        String fuhao = stack.pop();
        result = String.valueOf(comp(stack.pop(),result,fuhao));
    }
    return Integer.valueOf(result);
}

public static int comp(String a,String b,String fuhao){
    if(fuhao.equals("*")){
        return Integer.valueOf(a)*Integer.valueOf(b);
    }else if(fuhao.equals("/")){
        return Integer.valueOf(a)/Integer.valueOf(b);
    }else if(fuhao.equals("+")){
        return Integer.valueOf(a)+Integer.valueOf(b);
    }else if(fuhao.equals("-")){
        return Integer.valueOf(a)/Integer.valueOf(b);
    }
    return -1;
}

求完全二叉树的节点个数

先求深度,然后分为,右侧的最左节点是否深度一样 ?(左子树满二叉+1+递归调用右子树):(右慢二叉+1+递归调用左子树)

    public static int get(Node node){
        if(null == node){
            return 0;
        }
        if(node.left == null && node.right == null){
            return 1;
        }
        int deep = 1;
        Node tmp = node;
        while (tmp.left != null){
            deep++;
            tmp = tmp.left;
        }
        int rightDeep = 1;
        tmp = node.right;
        while (tmp != null){
            rightDeep++;
            tmp = tmp.left;
        }

        if(deep == rightDeep){
            return 1+get(node.right)+(int)Math.pow(2,deep-1) -1;
        } else {
            return 1+get(node.left)+(int)Math.pow(2,deep-2) -1;
        }
    }
        Node node7 = new Node(7,null,null);
        Node node6 = new Node(6,null,null);
        Node node5 = new Node(5,null,null);
        Node node4 = new Node(4,null,null);
        Node node3 = new Node(3,node6,node7);
        Node node2 = new Node(2,node4,node5);
        Node node1 = new Node(1,node2,node3);
        System.out.println(get(node1));

根据先序中序得到后序

    public static void set(int [] pre,int [] mid,int [] pos,int preL,int preR,int midL,int midR,int posL,int posR){
        if(pre == null || mid == null){
            return ;
        }
        if(preL > preR){
            return;
        }
        if(pre[preL] == mid[midL]){
            pos[posL] = pre[preL];
        }
        pos[posR] = pre[preL];
        int inPos = midL;//3
        for(int i =midL;i<=midR;i++){
            if(mid[i] == pre[preL]){
                inPos = i;
                break;
            }
        }
       // inPos - midL 左边的长度 3
       // midR - inPos 右边的长度 2
        //midL+midR+1整体长度
        set(pre,mid,pos,preL+1,preL+ inPos-midL,midL,inPos-1,posL,posL+inPos-midL-1);
        set(pre,mid,pos,preL +inPos - midL +1,preR,inPos+1,midR,posL-midL+inPos,posR-1);
//        Node head = pre;
        return ;
    }
    //124536  425163 452631

            int pre[] = new int[]{1,2,4,5,3,6};
            int mid[] = new int[]{4,2,5,1,6,3};
            int pos[] = new int[6];
            set(pre,mid,pos,0,pre.length-1,0,pre.length-1,0,pre.length-1);
            System.out.println();

二叉树最大路径(根到叶子)和

    public static int maxRoute(Node head){
        if(head == null){
            return 0;
        }
        if(head.left == null && head.right == null){
            return head.value;
        }
        int left = maxRoute(head.left);
        int right = maxRoute(head.right);

        return Math.max(left,right) + head.value;
    }

数字转字母的方式数

在这里插入图片描述

    //考虑str的第k位,前面形成的数量是times
    public static int calcu(String str,int k,int times){
        if(k >= str.length() - 1){
            return times;
        }
        if(str.charAt(k) == '0'){
            return 0;
        }
        if(str.charAt(k) - '1' >= 2){//3等等
            return calcu(str,k+1,times);
        }
        else if(str.charAt(k) - '1' == 1){//为2
            if(k+1 <=str.length() && str.charAt(k+1) - '1' <= 5){
                return calcu(str,k+1,times) + calcu(str,k+2,times);
            }else {
                return calcu(str,k+1,times);
            }
        } else {//为1
            return calcu(str,k+1,times) + calcu(str,k+2,times);
        }


    }

    public static int calcu2(String str,int index){
        if(index >= str.length() - 1){
            return 1;
        }
        if(str.charAt(index) == '0'){
            return 0;
        }
        int res = calcu2(str,index+1);
        if(index + 1 == str.length()){
            return res;
        }
        if((str.charAt(index)-'0')*10 + (str.charAt(index+1)-'0') <=26){
            res +=  calcu2(str,index+2);
        }
        return res;
    }

    public static int calcu3(String str){
        int deep[] = new int[str.length()+1];
        deep[str.length()] = 1;
        deep[str.length()-1] = str.charAt(str.length()-1) == '0' ? 0 : 1;
        for(int i = str.length() - 2;i>=0;i--){
            if(str.charAt(i) == '0'){
                deep[i] =  0;
            } else {
                deep[i] =  deep[i+1];
                if((str.charAt(i)-'0')*10 + (str.charAt(i+1)-'0') <=26){
                    deep[i] = deep[i] + deep[i+2];
                }
            }
        }
        return deep[0];
    }
 System.out.println(calcu("127221",0,1));
        System.out.println(calcu2("127221",0));

逆序栈

    //逆序栈
    public static void reverseStack(Stack<Integer> stack){
        if(stack.isEmpty()){
            return ;
        }
        int i = f(stack);
        reverseStack(stack);
        stack.push(i);

    }

    //删除并返回栈底元素,其他不变
    public static int f(Stack<Integer> stack){
        int result = stack.pop();
        if(stack.isEmpty()){
            return result;
        }
        int last = f(stack);
        stack.push(result);
        return last;
    }
   Stack<Integer> stack2 = new Stack<Integer>();
        stack2.push(1);
        stack2.push(2);
        stack2.push(3);
        stack2.push(4);
        stack2.push(5);
        reverseStack(stack2);

取数

    /**
     * 取数
     * @param arr
     * @param start
     * @param end
     * @return
     */
    public static int first(int arr[],int start,int end){
        if(start >= end){
            return arr[start];
        }
        return Math.max(arr[start] +second(arr,start+1,end), arr[end] +second(arr,start,end-1));
    }

    public static int second(int arr[],int start,int end){
        if(start >= end){
            return 0;
        }
        return Math.min(first(arr,start+1,end),first(arr,start,end-1));
    }
    int arr[] = new int[]{8,3,2,9,7,};
        System.out.println(first(arr,0,arr.length-1));

字符串全排列

  public static void printAll(String str){
        char[] chs = str.toCharArray();
        List<String> results = new ArrayList<>();
        process(chs,0,results);
        System.out.println();

    }

    /**
     * 123
     * 到str中i位置的要处理,i之前的数据固定
     * @param str
     * @param i
     */
    public static void process(char[] str,int i,List<String> results){
       if(i == str.length){
           results.add(String.valueOf(str));
           return;
       }
       boolean visited[] = new boolean[26];
       for(int j = i;j<str.length;j++){
           if(!visited[str[j]- 'a']){
               visited[str[j] - 'a'] = true;
           swap(str,i,j);
           process(str,i+1,results);
           swap(str,i,j);}
       }
    }

    public static void swap(char[] str,int i,int j){
        char tmp = str[i];
        str[i] = str[j];
        str[j] = tmp;
    }
        printAll("abca");

字符串的子序列

    public static void print(String str,int k,String route){
        if(k == str.length()){
            System.out.println(route);
        } else {
           String have = route + str.charAt(k);
            print(str,k+1,have);
           String nohave = route ;
            print(str,k+1,nohave);
        }
    }
        print("123",0,"");

汉诺塔步骤打印

n层汉诺塔问题:大的不能压小的,打印步骤; (2的n次方)-1

   public static void hannuota(int n){

        move(n,"左","中","右");
    }

    public static void move(int n,String from ,String other,String to){
        if(n == 1){
            System.out.println("move 1 from" + from +"to" + to);
        }else {
            move(n-1,from,to,other);
            System.out.println("move" +n+  "from" + from +"to" + to);
            move(n-1,other,from,to);
        }

    }
 hannuota(3);

验证二插搜索树

public boolean isValidBST(TreeNode root) {

        return isValidBST(root,Long.MIN_VALUE,Long.MAX_VALUE);

    }

    public boolean isValidBST(TreeNode root,long low,long high){
        if(root == null)
            return true;
        if(root.val <=low || root.val>= high){
            return false;
        }
        return isValidBST(root.left,low,root.val) && isValidBST(root.right,root.val,high);
    }

相同的树

有序数组转为二叉搜索

public static TreeNode sortedArrayToBST(int[] nums) {
        TreeNode result = sortedArrayToBST(nums,0, nums.length-1);
        return result;
    }

    public static TreeNode sortedArrayToBST(int[] nums,int left,int right){
        if(left == right){
            return new TreeNode(nums[left]);
        }else if(left > right){
            return null;
        }
        int mid = (right+left)/2;
        return  new TreeNode(nums[mid],sortedArrayToBST(nums,left,mid-1),sortedArrayToBST(nums,mid+1,right));
    }

二叉树最大路径和

1:求最大贡献值(节点值+左右子树最大贡献值)
2:求最大路径(节点值+左右子树最大贡献值(如果值为正的话))

 public static int maxPathSum(TreeNode root) {

       List<Integer> max = new ArrayList<>();
       max.add(Integer.MIN_VALUE);
        maxPathSum(root,max);
        return max.get(0);
    }

    public   static int maxPathSum(TreeNode root,List<Integer> max){
        if(root == null){
            return 0;
        }
        if(root.left == null && root.right == null){
            max.set(0,Math.max(max.get(0),root.val));
            return root.val;
        }
        int left = maxPathSum(root.left,max);
        int right = maxPathSum(root.right,max);
        int tmp = root.val;
        if(left >0){
            tmp += left;
        }
        if(right>0){
            tmp+=right;
        }
        max.set(0,Math.max(max.get(0),tmp));
        return root.val+Math.max(left,right);
    }

6. 树

morris遍历:若没有左节点,到右节点;若有左节点,左节点的最右.right == cur(遍历过) ? 最右.right = null然后遍历右节点 :最右.right指向cur然后指向左节点

在这里插入图片描述

二叉树中最大搜索二叉树的节点个数

  /**

     *    //返回最大搜索二叉树的节点数量及当前节点值,是否以当前节点为头
     */
    public static Info max(Node node){
        if(null == node){
            return null;
        }
        Info result = new Info();
        result.value = node.value;
        result.count = 1;
        if(null == node.left && null == node.right){
            result.ifThis = true;
            return result;
        }

        Info left  = max(node.left);
        if(null != left){
            if(left.ifThis && left.value < node.value){
                result.ifThis = true;
                result.count += left.count;
            } else {
                result.count = left.count > result.count ? left.count : result.count;
            }
        }else {
            result.ifThis = true;
        }
        Info right  = max(node.right);
        if(null != right){
            if(right.ifThis && right.value > node.value){
                if(result.ifThis){
                    result.count += right.count;
                } else {
                    if(right.value + 1 > result.count){
                        result.ifThis = true;
                        result.count = right.count + 1;
                    }
                }
            }else {
                if(right.count > result.count){
                    result.ifThis = false;
                    result.count =  right.count;
                }

            }
        }
        return result;
    }

    public static class Info{
       public int count; //数量
       public int value; //节点值
       public boolean ifThis = false; //是否以此节点为头
    }
//    /**  2
//      3    2
//     1  4 6 8
//      2
            Node node2 = new Node(2,null,null);
        Node node1 = new Node(1,null,node2);
        Node node4 = new Node(4,null,null);
        Node node3 = new Node(3,node1,node4);
        Node node6 = new Node(6,null,null);
        Node node8 = new Node(8,null,null);
        Node node7 = new Node(2,node6,node8);
        Node node5 = new Node(2,node3,node7);
            Info info = max(node5);

搜索二叉树转为有序的双向链表

 //返回以node为头的搜索二叉树的头尾节点
    public static Result change(Node node){
        if(null == node){
            return null;
        }
        Result result = new Result();
        ListNode2 mid = new ListNode2(node.value);
        Result left = change(node.left);
        if(null != left){
            result.small = left.small;

            left.large.next = mid;
            mid.pre = left.large;
        }else {
            result.small = mid;
        }
        Result right = change(node.right);
        if(null != right){
            result.large = right.large;
            right.small.pre = mid;
            mid.next = right.small;
        }else {
            result.large = mid;
        }
        return result;


    }


public static class Result{
    ListNode2 small;
    ListNode2 large;
}
    /**  5
      3    7
     1  4 6 8
      2
     * @param args
     */
            Node node2 = new Node(2,null,null);
            Node node1 = new Node(1,null,node2);
            Node node4 = new Node(4,null,null);
            Node node3 = new Node(3,node1,node4);
            Node node6 = new Node(6,null,null);
            Node node8 = new Node(8,null,null);
            Node node7 = new Node(7,node6,node8);
            Node node5 = new Node(5,node3,node7);

            Result result = change(node5);

最大快乐值


//    最大快乐值
//    当前go + 下一个not,   当前not + 下一级 max(go, not)
    //返回 当前go和not 的最大happy值
    public static Info maxHappy(Node2 node){
        if(node == null){
            return new Info(0,0);
        }
        if(null == node.nexts || node.nexts.size() == 0){
            return new Info(node.happy, node.not);
        }
        int curH = node.happy;
        int curNot = node.not;
        for (Node2 tmp:node.nexts){
            Info info = maxHappy(tmp);
            curH += info.not;
            curNot = curNot + Math.max(info.not ,info.go);
        }

        return new Info(curH,curNot);
    }


       static class Info{
        public int go;
        public int not;

        public Info(int go,int not){
            this.go = go;
            this.not = not;
        }

    }
       Node2 node2 = new Node2(100,2);
        Node2 node3 = new Node2(1,2);
        Node2 node4 = new Node2(10,2);
        node2.add(node3);
        node2.add(node4);

        Node2 node5 = new Node2(9,2);
        Node2 node6 = new Node2(5,2);
        Node2 node7 = new Node2(2,2);
        node3.add(node5);
        node3.add(node6);
        node3.add(node7);

        Info result =  maxHappy(node2);

二叉树最大距离

    //若考虑头 ,左高 + 右高 +1
    //若不考虑头 ,max(左,右)
    public static Info maxLength(Node head){
        if(null == head){
            return new Info(0,0);
        }
        Info left = maxLength(head.left);
        Info right = maxLength(head.right);

        int leftHeight = left.height;
        int leftValue = left.value;

        int rightHeight = right.height ;
        int rightV = right.value;

        int v1 = leftHeight+rightHeight + 1;//考虑头
        int v2 = Math.max(v1,Math.max(leftValue,rightV)); //value
        int height = Math.max(leftHeight,rightHeight) + 1; //高度

        return new Info(height,v2) ;
    }
  static class Info{
       public int height;
       public int value;

       Info(int height,int value){
           this.height = height;
           this.value = value;
       }
    }
  Node node7 =new Node(7,null,null);
            Node node6 =new Node(6,null,null);
            Node node5 =new Node(5,null,null);
            Node node4 =new Node(4,null,null);
            Node node3 =new Node(3,node6,node7);
            Node node2 =new Node(2,node4,node5);
            Node node1 =new Node(1,node2,node3);
//            morrisPre(node1);
//            morrisIn(node1);
//            morrisafter(node1);
            Info info = maxLength(node1);```

## 深度用递归
时间复杂度o(n*n!)

```java
 public  static void dfs(){
 //终止条件
 //
 //操作
 dfs();//递归调用
 //逆操作,回溯
}

广度用队列

中序遍历

递归

  public static void recoverTree(TreeNode root) {
        List<TreeNode> res = new ArrayList<>();
        recoverTree( root,res);
        }
 private static void recoverTree(TreeNode root, List<TreeNode> res) {
        if(root == null){
            return;
        }
        recoverTree(root.left,res);
        res.add(root);
        recoverTree(root.right,res);
    }

恢复二插搜索树

用中序遍历(递增)找到错误位置的数据的下标,然后交换

public static void recoverTree(TreeNode root) {
        List<TreeNode> res = new ArrayList<>();
        recoverTree( root,res);
        TreeNode a = null,b=null;
        for(int i = 1;i<res.size();i++){
            if(res.get(i).val<res.get(i-1).val){
                a = res.get(i);
                if(b == null){
                    b = res.get(i-1);
                }
            }
        }
    }

    private static void recoverTree(TreeNode root, List<TreeNode> res) {
        if(root == null){
            return;
        }
        recoverTree(root.left,res);
        res.add(root);
        recoverTree(root.right,res);
    }

常数级别的可以用莫里斯遍历

二叉树展开为链表

先中序遍历,再展开

 public static void flatten(TreeNode root) {
        if(null == root){
            return;
        }
        List<TreeNode> list = new ArrayList<>();
        flatten(root,list);
        TreeNode result = root;
       for(int i = 0;i<list.size();i++){
           TreeNode tmp = list.get(i);
           result.left = null;
           result.right = tmp;
           result = result.right;
       }

       result.right = null;

    }
    public static void flatten(TreeNode root,List<TreeNode> result){
        if(root == null){
            return;
        }
        result.add(root);
        flatten(root.left,result);
        flatten(root.right,result);
    }

前缀树(next节点用map)

子数组最大异或和

           System.out.println(max(new int[]{11,1,15,10,13,4}));

    public static int max(int arr[]){
        int sum = 0;
        Node head = new Node();
        int max = Integer.MIN_VALUE;
        addNum(0,head);
        for(int i = 0;i<arr.length;i++){
            sum ^= arr[i];
            max = Math.max(max,maxXor(head,sum));
            addNum(arr[i],head);
        }
       return max;
    }

    public static void addNum(int num,Node head){
        Node node = head;
        for(int bit = 31;bit >=0;bit--){
            int path = num>>bit & 1;
            node.nexts[path] = node.nexts[path] == null ? new Node() : node.nexts[path];
            node = node.nexts[path];
        }
    }

    public static int maxXor(Node head,int num){
        int result = 0;
        Node node = head;
        for(int bit = 31;bit >=0;bit--){
            int path = (num>>bit & 1);//理论最好
            int best = bit == 31 ? path : path^1;
            int real = node.nexts[best] != null ? best : (best ^ 1);//实际
            result |=  (path^real)<<bit;
            node=node.nexts[real];
        }
        return result;
    }

    public static class Node{
        public Node[] nexts = new Node[2];
    }

目录打印成树形结构

构造前缀树,然后深度优先打印
在这里插入图片描述

    public static void print(Node3 head,String pre){
        if(pre != ""){
            System.out.println(pre+head.value);
        }
        pre = pre + "--";
        for (String key: head.nexts.keySet()) {
            print(head.nexts.get(key),pre);
        }
    }



        public static Node3 print(String [] arrs){
            Node3 head = new Node3("head",new TreeMap<>());
            for (String str: arrs) {
                Node3 cur = head;
                String[] nodes =  str.split(",");
                for (String nodeValue: nodes) {
                    if( cur.nexts.containsKey(nodeValue)){
                        cur = cur.nexts.get(nodeValue);

                    } else {
                        Node3 newNode = new Node3(nodeValue,new TreeMap<>());
                        cur.nexts.put(nodeValue,newNode);
                        cur = newNode;
                    }
                }
            }
            return head;

        }
public class Node3 {
    public String value;
    public TreeMap<String,Node3> nexts;

    public Node3(String value,TreeMap<String,Node3> nexts) {
        this.value = value;
        this.nexts = nexts;
    }
    public Node3(){

    }

}

            Node3 head =  print(new String[]{"a,b,c","a,d,e","f"});
            print(head,"");

7. 并查集

并查集:多链表,集合中每个元素指向上一个元素,只有一个元素时指向自己(集合快速合并,一个元素的最上面的指针指向另一个集合的顶部元素,判断两个元素是否在同一个集合)优化:沿着元素往上找,将指针指向顶部元素

========================

算法

1. 排序

在这里插入图片描述
一般用快排,复杂度常数项低,有空间限制用堆排序,需要稳定用归并
注:快排空间o(logn)因为递归调用

选择排序(逐渐找到最小的,严格o(n2),不稳定)

在这里插入图片描述

冒泡(两两比较,找到最大的放最后,严格o(n2),稳定)

在这里插入图片描述

插入(逐渐做到0-i有序,最差o(n2),稳定)

在这里插入图片描述

归并排序(先排左右两测,再归并,o(n*logn),稳定)

在这里插入图片描述
在这里插入图片描述

求小和and逆序对

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

快排(小于放左边,递归,不稳定,写,奇数放左边,偶数右边的题目 )

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

大小根堆(o(n*logn),不稳定)

堆排序过程:先构建大根堆,然后一个个删除堆顶,放到最后,结束后就排序完成
在这里插入图片描述

在这里插入图片描述

是完全二叉树,通过下标计算来比较调整树

插入数据
在这里插入图片描述

删除头节点
删除最大值后,最后一个数据放到头节点,然后和左右孩子较大的比较,输了交换
在这里插入图片描述

给定一个数组构造大根堆(下面两个方法都可以)
在这里插入图片描述

几乎有序的数组排序

在这里插入图片描述
在这里插入图片描述
k=6的话,构造空间大小为7的小根堆
在这里插入图片描述

计数排序(年龄)

基数排序(电话)

先排个位,准备多个桶,数字按照个位放入相应桶中,从桶中出来放回队列(桶中数据是队列,先入先出)

2. 逆序对个数

分两步
1:合并两个有序数组(归并排序)
2:改1,合并时,左边数组的指针移动时,计算逆序对的贡献

https://leetcode.cn/problems/merge-sorted-array/solution/he-bing-liang-ge-you-xu-shu-zu-by-leetco-rrb0/

拓扑排序

有向无环图 转成 线性的排序 就叫 拓扑排序。

课程表

用队列维持入度为0的点,然后相关的入度减1

2. 逆序

  public static void test(int a[],int start,int end){
        while(start < end){
            int tmp = a[start];
            a[start] = a[end];
            a[end] = tmp;
            start++;
            end--;
        }
    }

3.1. 旋转数组(数组右移动k位)

思路:3次逆序

3.2 旋转数组的最小值

思路:二分法(二分法带等号)
区间先递增,后到最小值,再递增

public int findMin(int[] nums) {
        int low = 0;
        int high = nums.length - 1;
        while (low <= high) {
            int pivot = low + (high - low) / 2;
            if (nums[pivot] < nums[high]) {
                high = pivot;
            } else {
                low = pivot + 1;
            }
        }
        return nums[low];
    }

3.3 搜索旋转数组(二分)

3. 动态规划(可以拆分为子问题,递归改dp时,注意dp方程的参数改为i,j)

从左往右尝试

0-1背包

当前位置和前一位置有关,构造位置和剩余容量的dp

          System.out.println(bag(new int[]{8,3,4,3},new int[]{9,3,4,3},3,10));
            System.out.println(bag2(new int[]{8,3,4,3},new int[]{9,3,4,3},10));
    public static int bag2(int w[],int v[],int W){
        int deep[][] = new int[w.length][W+1];
        for(int j = 0;j<=W;j++){
            if(j>=w[0]){
               deep[0][j] = w[0];
            }
        }
        for (int i =1;i<w.length;i++){
            for(int j = 0;j<=W;j++){
                int result = deep[i-1][j];
                //当前放
                if(j>=w[i]){
                    result = Math.max(deep[i-1][j-w[i]]+v[i],result) ;
                }
                deep[i][j] =  result;
            }
        }
        return deep[w.length-1][W];

    }
   * 到0-index位置时,剩余W空间
     * 返回最大价值
    public static int bag(int w[],int v[],int index,int W){
        if(index < 0 ){
            return 0;
        }
        //当前不放
        int result = bag(w,v,index-1,W);
        //当前放
        if(W>=w[index]){
            result = Math.max(bag(w,v,index-1,W-w[index])+v[index],result) ;
        }
        return result;

    }

两个字符串的最长公共子序列(子序列要考虑左上角的,dp不能是必须以什么结尾的)

定义dp【】【】,代表arr1的0-i和arr2的0-j的最长公共子串;分四种情况,是否以i结尾和以j结尾,取max

System.out.println(max("12345","1666231456"));

 public static int max(String str1,String str2){
        if(null == str1 || null == str2){
            return 0;
        }
        int deep[][] = new int[str1.length()][str2.length()];
        deep[0][0] = str1.charAt(0) == str2.charAt(0) ? 1 : 0;
        for(int j =1;j<str2.length();j++){//第一行
            if(deep[0][j-1] == 1){
                deep[0][j] = 1;
                continue;
            }
            if(str1.charAt(0) == str2.charAt(j)){
                deep[0][j] = 1;
            }
        }
        for(int i =1;i<str1.length();i++){//第一列
            if(deep[i-1][0] == 1){
                deep[i][0] = 1;
                continue;
            }
            if(str2.charAt(0) == str1.charAt(i)){
                deep[i][0] = 1;
            }
        }
        for(int i = 1;i<str1.length();i++){
            for(int j = 1;j<str2.length();j++){
               int tmp = deep[i-1][j-1];
               tmp = Math.max(tmp,deep[i][j-1]);
               tmp = Math.max(tmp,deep[i-1][j]);
               if(str1.charAt(i) == str2.charAt(j)){
                   tmp = Math.max(tmp,deep[i-1][j-1]+1);
               }
               deep[i][j] = tmp;
            }
        }
        return deep[str1.length()-1][str2.length()-1];
    }

两个字符串的最长公共子串

定义dp【】【】,代表arr1必须以i结尾和arr2必须以j结尾的最长公共子串,每个位置和左上角的位置有关;可以空间压缩

    public static int max(String str1,String str2){
        if(null == str1 || null == str2){
            return 0;
        }
        int deep[][] = new int[str1.length()][str2.length()];
        int max = 0;
        for(int j =0;j<str2.length();j++){
            if(str1.charAt(0) == str2.charAt(j)){
                deep[0][j] = 1;
            }
        }
        for(int i =0;i<str1.length();i++){
            if(str2.charAt(0) == str1.charAt(i)){
                deep[i][0] = 1;
            }
        }
        for(int i = 1;i<str1.length();i++){
            for(int j = 1;j<str2.length();j++){
                if(str1.charAt(i) == str2.charAt(j)){
                    deep[i][j] = deep[i-1][j-1]+1;
                    max = Math.max(deep[i][j],max);
                }
            }
        }
        return max;
    }

字符串最少多少次切割成多个回文子串

System.out.println(min("134343414"));

 public static int min(String str){
        char chars[] = str.toCharArray();
        int len = chars.length;
        int deep[] = new int[len+1];//i-后面需要最少的
        deep[len-1] = 1;
        deep[len] = 0;
        boolean[][] isHui = isH(chars);
        for(int i = len-2;i>=0;i--){
            deep[i] = len-i;
            for(int j = i;j<len;j++){
                 if(isHui[i][j]){
                    deep[i] = Math.min(deep[i],1+deep[j+1]);
                }
            }
        }
        return deep[0];
    }

    public static boolean[][] isH(char chars[]){
        boolean[][] results = new boolean[chars.length][chars.length];
        for(int i=chars.length-1;i>=0;i--){
            for (int j = i;j<chars.length;j++){
                if(i == j){
                    results[i][j] = true;
                }else {
                    if(chars[i] == chars[j]){
                        if(j-i<3 || results[i+1][j-1]){
                            results[i][j] = true;
                        }
                    }
                }
            }
        }
        return results;

    }

范围上尝试

(str(i,j)上尝试,可能性要考虑是否包含开头和结尾,重点在于思考可能性是否能解决问题)一般子序列有这种问题
遍历顺序:一个递减,一个递增

最长回文子串

方程:
遍历顺序:一个递减,一个递增

System.out.println(max("1234565432"));

    public static int max(String str){
        int len = str.length();
        boolean deep[][] = new boolean[len][len];//以i开头,j结尾是不是
        int max = 1;
        for(int i = len-1;i>=0;i--){
            for(int j = i;j<len;j++){
                if(i==j){
                    deep[i][j] = true;
                }else if(str.charAt(i) == str.charAt(j)){
                    if(j-i<3 || deep[i+1][j-1]){
                        deep[i][j] = true;
                        max = Math.max(max,j-i+1);
                    }
                }
            }
        }
        return max;
    }

字符串str的最长回文子序列(可能性要考虑是否包含开头和结尾,重点在于思考可能性是否能解决问题,一般子序列有这种问题)

            System.out.println(max("1a2b3c4d3e2f1"));

    public static int max(String str){
        int len = str.length();
         int deep[][] = new int[len][len];//以i,j范围内
        for(int i = len-1;i>=0;i--){
            for(int j = i;j<len;j++){
                if(i==j){
                    deep[i][j] = 1;
                }else {
                    int tmp = deep[i+1][j-1];
                    tmp = Math.max(tmp,deep[i+1][j]);
                    tmp = Math.max(tmp,deep[i][j-1]);
                    if(str.charAt(i) == str.charAt(j)){
                        tmp = Math.max(tmp,2+deep[i+1][j-1]);
                    }
                    deep[i][j] = tmp;
                }
            }
        }
        return deep[0][len-1];
    }

蛇走路

在这里插入图片描述

    //有依赖但没有单调性,所以需要遍历
    public static int max(int arr[][]){
        int result = 0;
        for(int i = 0;i<arr.length;i++){
            for(int j = 0;j<arr[0].length;j++){
                Info f = max(arr,i,j);
                result = Math.max(result,Math.max(f.no, f.yes));
            }
        }
        return result;
    }


    public static Info max(int arr[][],int i,int j) {
        if(j==0){
            return new Info(arr[i][j],-arr[i][j]);
        }
        int preNo = -1;
        int preyes = -1;
        if(i>0){
            Info preUp = max(arr,i-1,j-1);
            if(preUp.no >=0){
                preNo =  preUp.no;
            }
            if(preUp.yes >=0){
                preyes =   preUp.yes;
            }
        }
        Info pre = max(arr,i,j-1);
        if(pre.no >=0){
            preNo =  Math.max(pre.no,preNo);
        }
        if(pre.yes >=0){
            preyes =   Math.max(pre.yes,preyes);
        }
        if(i< arr.length-1){
            Info down = max(arr,i+1,j-1);
            if(down.no >=0){
                preNo =  Math.max(down.no,preNo);
            }
            if(down.yes >=0){
                preyes =   Math.max(down.yes,preyes);
            }
        }
        int no = -1;
        int yes = -1;
        if(preNo >=0){
            no = preNo+arr[i][j];
            yes = preNo-arr[i][j];
        }
        if(preyes>=0){
            yes = preyes+arr[i][j];
        }
        return new Info(no,yes);
    }

    public static class Info{
        public int no;
        public int yes;

        public Info(int no,int yes){
            this.no = no;
            this.yes = yes;
        }
    }
    System.out.println(max(new int[][]{{1,-4,10},{3,-2,-1},{2,-1,0},{0,5,-2}}));

数组拆分子数组,需要子数组内的数据异或结果都为0,最多能拆分多少个子数组

在这里插入图片描述
维护dp和异或和结果的最新位置的下标,如果两个位置异或和结果一样,那这两个位置之间异或和为0;dp分两种,
以i结尾的数字不在疑惑和为0的结果中,dp[i] = dp[i-1],
以i结尾的小数字疑惑和=0,dp[i]=1+dp[上一个疑惑和为这个值的下标];
两种情况求max

    public static int max(int arr[]) {
        Map<Integer,Integer> resultMap = new HashMap<>();//异或和结果的最新下标
        int result = 0;
        int[]deep = new int[arr.length];
        deep[0] = arr[0] == 0 ? 1 : 0;
        resultMap.put(arr[0],0);
        for(int i = 1;i<arr.length;i++){
            result = result ^ arr[i];
            //当前数字不在结果里面
            int result1 = deep[i-1];
            if(resultMap.get(result) != null){
                //当前数字在结果里面
                result1 = Math.max(result1,1+deep[resultMap.get(result)]);
            }
            resultMap.put(result,i);
            deep[i] = result1;
        }
        return deep[arr.length-1];
    }
    System.out.println(max(new int[]{3,2,1,0,1,0,2,0,3,2,1,0,4,0}));

字符串1变成字符串2的最小代价

在这里插入图片描述

    public static int minPrice(String str1,String str2,int i ,int j,int add,int del,int cha){
        if(i < 0){
            return j < 0 ? 0 : add*(j+1);
        }
        if(j<0){
            return i<0?0:del*(i+1);
        }
        int min ;
        if(str1.charAt(i) == str2.charAt(j)){
            min = minPrice(str1,str2,i-1,j-1,add,del,cha);
        }else {
            min = minPrice(str1,str2,i-1,j-1,add,del,cha) + cha;
        }
        min = Math.min(min,minPrice(str1,str2,i-1,j,add,del,cha) + del);
        min = Math.min(min,minPrice(str1,str2,i,j-1,add,del,cha) + add);
        return min;
    }
System.out.println(minPrice("bcde","cbed",3,3,1,1,1));

找到字符串没有重复字符的最长子串(从左往右)

假设当前i位置,结果为min(i-1位置推出的最大长度+1,i位置所在字符的上一个位置的长度)
在这里插入图片描述

    public static int maxNoRep(String str){
        if(null == str || str.length() < 1){
            return 0;
        }
        char[] chars = str.toCharArray();
        Map<Character,Integer> map = new HashMap<>();
        int deep[] =new int[chars.length];
        map.put(chars[0],0);
        int max = 1;
        deep[0] = 1;
        for(int i = 1;i<chars.length;i++){
            if(map.containsKey(chars[i])){
                deep[i] = Math.min(deep[i-1]+1,i-map.get(chars[i]));

            }else {
                deep[i]  = deep[i-1]+1;
            }
            map.put(chars[i],i);
            max = Math.max(max,deep[i]);
        }
        return max;
    }
System.out.println(maxNoRep("1234216987"));

最长有效括号长度

    //必须以index结尾的最大长度,遍历一遍得到最大的
         public static int max2(String str){
            int deep[] = new int[str.length()];
            int max = 0;
            deep[0] = 0;
            for(int i = 1;i<str.length();i++){
                if(str.charAt(i)=='('){
                    deep[i] = 0;
                }else {
                    if(str.charAt(i-1) == '('){
                        deep[i] =  i>=2 ? 2 + deep[i-2] : 2;
                        max = deep[i] > max ? deep[i] : max;
                    } else {
                        int pre = deep[i-1] ;
                        if(pre >=0 && i-pre-1 >=0 && str.charAt(i-pre-1) == '('){
                            deep[i] =  pre+2;
                            max = deep[i] > max ? deep[i] : max;
                        }
                    }
                }
            }
            return max;
         }




         //以index结尾的最大长度
        public static int max(String str,int index){
        if(index <=  0){
            return 0;
        }
        if(str.charAt(index) == '('){
            return 0;
        }
        if(str.charAt(index-1) == '('){
            return 2 + max(str,index-2);
        } else {
            int pre =  max(str,index-1) ;
            if(pre >=0 && str.charAt(index-pre-1) == '('){
                return pre+2;
            }
        }
        return 0;
    }
System.out.println(max("((()))()))(",7));
System.out.println(max2("((()))()))("));

n个节点的二叉树的种类

    public static int nums2(int n){
        int deep[] = new int[n+1];
        deep[0] = 1;
        deep[1] = 1;
        deep[2] = 2;
        for(int i=3;i<=n;i++){
            for(int k = 0;k<i;k++){
                int leftnum = deep[k];
                int rightnum = deep[i-k-1];
                deep[i] += leftnum*rightnum;
            }
        }
        return deep[n];
    }

    //
    public static int nums(int n){
        if(n<0){
            return 0;
        }
        if(n<=1){
            return 1;
        }
        if(n==2){
            return 2;
        }
        int total = 0;
        for(int i = 0;i<n;i++){
            int leftnum = nums(i);
            int rightnum = nums(n-i-1);
            total += leftnum*rightnum;
        }
        return total;
    }
           System.out.println(nums(3));
            System.out.println(nums2(3));

mn格子xy位置随机走k步存活概率

    //    mn格子xy位置随机走k步
    //返回存活的概率
    //3/4 3/4 3/4 1 4
    public static double live(int M,int N,int k,int x,int y){
        if(x<0 || y <0 || x >= M || y >=N ){
            return 0.0;
        }
        if(k == 0){
            return 1.0;
        }
        double a = live(M,N,k-1,x-1,y);
        double b = live(M,N,k-1,x+1,y);
        double c = live(M,N,k-1,x,y+1);
        double d = live(M,N,k-1,x,y-1);
        return (a+b+c+d )/4.0;
    }

        System.out.println(live(3,4,2,1,1));

马跳


    //马在9*10格子经过k步从原点跳到xy的方式数
    public static int ways2(int x,int y,int k){
        int deep[][][] = new int[9][10][k+1]; //0-8 0-9 0-k
        deep[0][0][0] = 1;
        for(int rest = 1;rest<=k;rest++){
            for(int i = 0;i<=8;i++){
                for (int j = 0; j <= 9; j++) {
                    deep[i][j][rest] =
                            getValue(deep, i - 1, j - 2, rest - 1)
                                    + getValue(deep, i + 1, j - 2, rest - 1)
                                    + getValue(deep, i - 2, j - 1, rest - 1)
                                    + getValue(deep, i + 2, j - 1, rest - 1)
                                    + getValue(deep, i - 1, j + 2, rest - 1)
                                    + getValue(deep, i + 1, j + 2, rest - 1)
                                    + getValue(deep, i - 2, j + 1, rest - 1)
                                    + getValue(deep, i + 2, j + 1, rest - 1);
                }
            }
        }
        return deep[x][y][k];
    }

    public static int getValue(int deep[][][] ,int x,int y,int k){
        if(x<0 || y < 0 || x > 8 || y > 9) {
            return 0;
        }
        return deep[x][y][k];
    }

    //    马到xy经过k的方法数 9*10的盘子
    public static int ways(int x,int y,int k){
        if(x<0 || y < 0 || x > 8 || y > 9){
            return 0;
        }
        if(k == 0){
            return x == 0 && y == 0 ? 1 : 0;
        }
        return ways(x-1,y-2,k-1)
                +ways(x+1,y-2,k-1)
                +ways(x-2,y-1,k-1)
                +ways(x+2,y-1,k-1)
                +ways(x-1,y+2,k-1)
                +ways(x+1,y+2,k-1)
                +ways(x-2,y+1,k-1)
                +ways(x+2,y+1,k-1);
    }
   System.out.println(ways(2,1,3));
        System.out.println(ways2(2,1,3));

找钱

            int money[] = new int[]{2,5,10,20}; 
        System.out.println(num(money,20,0));
        System.out.println(num2(money,20,0));
    public static int num2(int arr[],int aim,int index){
        int[][] deep = new int[arr.length + 1][aim+1];
        deep[arr.length][0] = 1;
        for(int i = arr.length -1 ;i>=0;i--){
            for(int j =0;j<= aim;j++){
                int result = 0;
                for(int k =0;arr[i]*k<=j;k++){
                    result  += deep[i+1][j - arr[i]*k];
                }
                deep[i][j] =  result;

            }
        }
        return deep[0][aim];

    }


    //方法数 凑够aim,只能用index及之后的数据
    // 返回方法数
    public static int num(int arr[],int aim,int index){
        if(index == arr.length){
            return aim == 0? 1 : 0;
        }
        if (aim < 0) {
            return 0;
        }
        int result = 0;
        for(int i =0;arr[index]*i<=aim;i++){
            result  += num(arr,aim - arr[index]*i,index+1);
        }
        return result;
    }

斜率优化
在这里插入图片描述

正数数组,每个数代表一枚硬币,构成aim的总价值,硬币数最少的方法,不可重复使用


    //还需要凑够rest,已经用了used
    //尝试要注意从左往右或者范围上等,变量规模要减少,方便改递归,这个就不合适
    //返回最小的个数
    public static int minC(int arr[],int rest,int used){
        if(rest == 0){
            return used;
        }
        int coin = Integer.MAX_VALUE;
        for(int i=0;i<arr.length;i++){
            if(arr[i] <= rest){
                coin = Math.min(minC(arr,rest-arr[i],used+1),coin);
            }
        }
        return coin;
    }
    //还需要凑够rest,只使用index之后的货币,还需要rest
    //返回最小coin的个数, 不能凑成返回-1,能凑成返回0
    public static int minC2(int arr[],int index,int rest){
        if(rest < 0){
            return -1;
        }
        if(rest == 0){
            return 0;
        }
        if(index == arr.length){
            return -1;
        }
        int usei =  minC2(arr,index+1,rest-arr[index]) ;
        int notUsei = minC2(arr,index+1,rest);
        if(usei == -1 && notUsei == -1){
            return -1;
        }
        if(usei == -1){
            return notUsei ;
        }else if(notUsei == -1){
            return usei + 1;
        }else {
            return Math.min(usei+1,notUsei);
        }
    }
    public static int minC3(int arr[],int rest) {
        int deep[][] = new int[arr.length+1][rest+1];
        for(int i = 0;i<=rest;i++){
            deep[arr.length][i] = -1;
        }
        deep[arr.length][0] = 0;
        for(int i = arr.length-1;i>=0;i--){//用index后面的钱
            for (int j =0;j<=rest;j++){//需要凑够rest
                int usei = j-arr[i] <0 ? -1 : deep[i+1][j-arr[i]];
                int notUsei = deep[i+1][j];
                if(usei == -1 && notUsei == -1){
                    deep[i][j]  = -1;
                }
                if(usei == -1){
                    deep[i][j] =  notUsei ;
                }else if(notUsei == -1){
                    deep[i][j] =  usei + 1;
                } else {
                    deep[i][j] =  Math.min(usei+1,notUsei);
                }

            }
        }
        return deep[0][rest];
    }
        int arr[] = new int[]{1,2,5,10};
        System.out.println(minC2(arr,0,13));
        System.out.println(minC3(arr,13));

机器人走路

 public static void main(String[] args) {
        int n = 4;
        int rest = 5;
        int[][] cache = new int[n][rest+1];
        for (int i =0;i<n;i++){
            for (int j=0;j<=rest;j++){
                cache[i][j] = -1;
            }
        }
        System.out.println(walk(4,3,0,5));
    }

    //机器人在n,从cur走到end,用rest步的方法数
    public static int walk(int n,int end ,int cur,int rest){
        if(rest == 0 ){
            return  cur == end ? 1 : 0;
        }
        if(cur == 0){
            return walk(n,end,cur+1,rest-1);
        }else if(cur == n - 1){
            return walk(n,end,cur-1,rest-1);
        }else {
            return walk(n,end,cur+1,rest-1) + walk(n,end,cur-1,rest-1);
        }
    }

    public static int walk2(int n,int end ,int cur,int rest,int[][] cache){
        if(cache[cur][rest] != -1){
            return cache[cur][rest];
        }
        if(rest == 0){
            cache[cur][rest] = cur == end ? 1 : 0;
            return  cache[cur][rest];
        }
        if(cur == 0){
            cache[cur][rest] = walk2(n,end,cur+1,rest-1,cache);


        }else if(cur == n - 1){
            cache[cur][rest] = walk2(n,end,cur-1,rest-1,cache);
        }else {
            cache[cur][rest] = walk2(n,end,cur+1,rest-1,cache) + walk2(n,end,cur-1,rest-1,cache);
        }
        return cache[cur][rest];
    }


    //当前在cur,剩余rest的方法数
    public static int walk3(int n,int end ,int cur,int rest){
        int deep[][] = new int[n+1][rest+1];
        deep[end][0] = 1;
        for(int i = 1;i<=rest;i++){//剩余步数增加
            for(int j = 0 ;j<n;j++) {//当前位置
                if (j == 0) {
                    deep[j][i] = deep[j + 1][i - 1];
                } else if (j == n - 1) {
                    deep[j][i] =deep[j - 1][i - 1];
                } else {
                    deep[j][i] = deep[j + 1][i - 1] + deep[j - 1][i - 1];
                }
            }
        }
        return deep[cur][rest];
    }

鸡蛋掉落

在这里插入图片描述

最大最小问题

dp[i] = min(dp[i], dp[i-num]+1)
dp[i] = max(dp[i], dp[i-num]+1)

最大子序列和

如{5,4,-1,7,8}的最长子序列和是23

 dp[i] = Math.max(dp[i-1]+a[i],a[i]);

编辑距离

一个单词变为另一个单词(删除,添加,替换的方式)的最短距离
思路:动态规划,如果

 if(word1.charAt(i-1) == word2.charAt(j-1)){
                    dp[i][j] = dp[i-1][j-1];
                } else {
                    dp[i][j] = 1+Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1]);
                }

数字字符串编码为字母

第一种情况:最后一位使用一个字符,s【i】!=0,dp[i] = dp[i-1]
第二种情况:最后两位拼成字母,s[i-1]!=0 && 10*s[i-1]+s[i] <26,dp[i] = dp[i-2]

不同二插搜索树的个数

 public static int aaa(int n) {
        int dp[] = new int[n + 1];
        dp[0] = 1;
        dp[1] = 1;
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                dp[i] += dp[j-1] * dp[i - j];
            }
        }
        return dp[n];
    }

交错字符串

定义 f(i,j) 表示 s1的前 i 个元素和 s2的前 j 个元素是否能交错组成 s3的前 i+j 个元素

 public static boolean isaa(String s1,String s2,String s3){
        int m = s1.length();
        int n = s2.length();
        if(m+n != s3.length()){
            return false;
        }
        boolean dp[][] = new boolean[m+1][n+1];
        dp[0][0] = true;
        for (int i = 0;i<=m;i++){
            for(int j = 0;j<=n;j++){
                if(i>0){
                    if(s3.charAt(i+j-1) == s1.charAt(i-1) && dp[i-1][j]){
                        dp[i][j] = true;
                    }
                }
                if(j>0){
                    if(s3.charAt(i+j-1) == s2.charAt(j-1) && dp[i][j-1]){
                        dp[i][j] = true;
                    }
                }
            }
        }
        return dp[m][n];
    }

不同的子序列

s和t,s中包含t的个数
dp[i][j]代表s前i个字符中包含t前j个字符的个数

 public static int aa(String s,String t){
        int m = s.length(),n=t.length();
        if(m<n){
            return 0;
        }
//        dp[i][j] s中i位能和t中j位匹配的个数
        int dp[][] = new int[m+1][n+1];
        for(int i = 0;i<=m;i++){
            for(int j = 0;j<=n;j++){
                if(j == 0){
                    dp[i][j] = 1;
                    continue;
                }
                if(i == 0){
                    dp[i][j] = 0;
                    continue;
                }
                if(s.charAt(i-1) == t.charAt(j-1)){
                    dp[i][j] = dp[i-1][j-1]+dp[i-1][j];
                }else {
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[m][n];
    }

三角形最短路径和

dp[i][j]=min(dp[i−1][j−1],dp[i−1][j])+c[i][j]

买卖股票(可以交易多次)

//i天,状态j(0,股票),手里的最大收益
       dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]-prices[i]);
       dp[i][1] = Math.max(dp[i-1][0]+prices[i],dp[i-1][1]);

零钱兑换,数组总和

对于面额为 coin 的硬币,当 coin≤i≤amount 时,如果存在一种硬币组合的金额之和等于i−coin,则在该硬币组合中增加一个面额为 coin 的硬币,即可得到一种金额之和等于 ii 的硬币组合。因此需要遍历coins,对于其中的每一种面额的硬币,更新数组 }dp 中的每个大于或等于该面额的元素的值。

 public int change(int amount, int[] coins) {
        int[] dp = new int[amount + 1];
        dp[0] = 1;
        for (int coin : coins) {
            for (int i = coin; i <= amount; i++) {
                dp[i] += dp[i - coin];
            }
        }
        return dp[amount];
    }

4. 二分法

两个有序数组第k小的数(两个有序数组中位数)

中位数是先求两个第k小的再计算

求第k小,先判断两个数组第k/2的数是不是想等,不想等的话,去掉较小的数组的前部分x个,再从剩下的取k-x小;

    //求第k小的数;从k=1或一个数组为空返回
    public static double mid(int arr1[],int arr2[],int mleft,int nleft,int k){
        int m = arr1.length-mleft;//数组长度
        int n = arr2.length-nleft;
        int mIndex = mleft+  k/2-1; //数组坐标
        int nIndex = nleft+  k-k/2-1;
        if(m == 0){
            return arr2[k-1+nleft];
        }
        if(n == 0){
            return arr1[k-1+mleft];
        }
        if(k==1){
            return arr1[mleft]<arr2[nleft] ? arr1[mleft] : arr2[nleft];
        }
        if(arr1[mIndex] == arr2[nIndex]){
            return arr1[mIndex];
        }
        if(arr1[mIndex] < arr2[nIndex]){//mIndex left抛弃
            return mid(arr1, arr2, mIndex+1, nleft, k-(mIndex-mleft+1));
        }else {
            return mid(arr1, arr2, mleft, nIndex+1, k-nIndex+nleft-1);
        }
    }
      int arr1[] = new int[]{1,3,4,9};
            int arr2[] = new int[]{1,2,3,4,5,6,7,8,9,10};
            System.out.println(mid(arr1, arr2, 0, 0, 7));
            System.out.println(mid(arr1, arr2, 0, 0, 8));

找局部最小

先判断0和n-1位置是否局部最小,都不是的话2分法
在这里插入图片描述

中位返回模板(猜数字,平方根,搜索旋转数组)

while L <= R:
    M = (L + R) // 2
    if nums[M] == T:
        return M
    elif nums[M] < T:
        L = M + 1
    else:
        R = M - 1

空间压缩模板(求峰值)

while L < R:
    M = (L + R) // 2
    if need in s[L:M]:
        R = M
    else:
        L = M + 1

5. 滑动窗口,双指针

正数组组,累计和为k的连续子数组的最长长度

public static int max(int arr[],int k){//2,1,2,1,1,1,6,1
    int max = 0;
    int left = 0;
    int right = 0;
    int total = arr[0];
    int tmp = 0;
    while(left < arr.length && right < arr.length){
        total += tmp;
        if(total == k){
            max = right-left+1 > max ? right-left+1:max;
            tmp = right+1 == arr.length ? 0 : arr[right+1];
            right++;
        }else if(total < k){
            tmp = right+1 == arr.length ? 0 : arr[right+1];
            right++;
        }else {
            tmp =  -arr[left];
            left++;
        }
    }
    return max;
}
System.out.println(max(new int[]{1,2,1,1,1},3));

绳子覆盖的点数

在这里插入图片描述

   public static int maxCover(int arr[],int k){
        if(k == 0){
            return 0;
        }
        int result = 0;
        int left = 0;
        int right = 0;
        for(int i = 1;i<arr.length;i++){
            right = i;
            if(arr[right] - arr[left] < k){
                continue;
            }else if(arr[right] - arr[left] == k){
                result =  right - left +1 > result ? right - left +1: result;
                left++;
            }else {
                result =  right - left > result ? right - left : result;
                left++;
            }
        }
        return result;
    }
          int arr[] = new int[]{1,3,4,5,6,7,10};
        System.out.println(maxCover(arr,3));

无重复字符的最长子串

 public static int lengthOfLongestSubstring(String s) {

        int left = 0;
        int max = 0;
        Map<Character,Integer> map = new HashMap<>();
        for(int i = 0;i<s.length();i++){
            if(map.containsKey(s.charAt(i))){
                left = Math.max(map.get(s.charAt(i))+1,left);
            }
            map.put(s.charAt(i),i);
            max = Math.max(max,i-left+1);
        }
        return max;

    }

盛最多水的容器

在这里插入图片描述

public static int maxArea(int[] height) {
            int left = 0,right = height.length-1;
            int max = 0;
            while (left < right){
                int current = Math.min(height[left],height[right])*(right-left);
                max = Math.max(max,current);
                if(height[left] > height[right]){
                    right--;
                }else {
                    left++;
                }
            }
            return max;
        }

三数之和

  public static List<List<Integer>> threeSum(int[] a) {
        Arrays.sort(a);
        List<List<Integer>> result = new ArrayList<>();
        for(int i = 0;i<a.length;i++){
            if(a[i] > 0){
                break;
            }
            if(i > 0 && a[i] == a[i-1]) continue;
            int l = i+1,r=a.length-1;
            while (l<r){
                if(a[i] + a[l] + a[r] == 0){
                    ArrayList tmp = new ArrayList();
                    tmp.add(a[i]);
                    tmp.add(a[l]);
                    tmp.add(a[r]);
                    result.add(tmp);
                    l++;r--;
                }else if(a[i] + a[l] + a[r] < 0){
                    l++;
                }else {
                    r--;
                }
            }
        }
        return result;
    }

最接近的三数之和(同上)

链表倒数第n个节点(写)

x的平方分

要考虑边界情况,用long类型

是否有环(快慢指针)

6. 前缀和(数组连续,求子数组)

累加和存位置(最长相等01子数组,最长偶数元音子数组)

累加和存目标(连续和为 k 倍 的子数组)

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        int n = nums.length;
        int[] sum = new int[n + 1];
        for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
        Set<Integer> set = new HashSet<>();
        for (int i = 2; i <= n; i++) {
            set.add(sum[i - 2] % k);
            if (set.contains(sum[i] % k)) return true;
        }
        return false;
    }
}

7. 堆

中位数

大根堆和小根堆
两个堆数量不一样,则放入大根堆,再poll大根堆的到小根堆
两个堆数量一样,则放入小根堆,再poll小根堆的到大根堆

  public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length,n=nums2.length;
        Queue<Integer> big = new PriorityQueue<>((a,b)->(b-a));//大,存小的
        Queue<Integer> small = new PriorityQueue<>();

       for(int i = 0;i<m;i++){
           addNum(big,small,nums1[i]);
       }
        for(int i = 0;i<n;i++){
            addNum(big,small,nums2[i]);
        }
        return small.size() != big.size() ? big.peek() : (small.peek() + big.peek())/2.0;

    }

    public static void addNum(Queue<Integer> big,Queue<Integer> small,int num){
        if(big.size() == small.size()) {
            small.add(num);
            big.add(small.poll());
        } else {
            big.add(num);
            small.add(big.poll());
        }


    }

8. 其他

z字形变换

public static String re(String s, int num) {
        List<StringBuilder> sbs = new ArrayList<>(num);
        for (int i = 0; i < num; i++) {
            sbs.add(new StringBuilder());
        }
        int index = 0;
        int cycle = num * 2 - 2;
        int yu = 0;
        for (int i = 0; i < s.length(); i++) {
            yu = i % cycle;
            if(yu == 0){
                index = 0;
            }
            if (yu < num) {
                sbs.get(index).append(s.charAt(i));
                index++;
            } else {
                sbs.get(num-index+1).append(s.charAt(i));
                index++;
            }

        }
        StringBuilder result = new StringBuilder();
        for (StringBuilder temp :
                sbs) {
            result.append(temp);
        }
        return result.toString();


    }

下一个升序排列

arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1]

思路:先从后往前找升序对,index= i;i之后的一定降序;在【i+1,len)之间找到最小的大于num【i】的,然后交换值,然后【i+1,len)之间的升序

public static void nextPermutation(int[] nums) {
        for(int i = nums.length-2 ;i>= 0;i--){
                if(nums[i]<nums[i+1]){
                    for(int j = nums.length-1;j>i;j--){
                        if(nums[j]>nums[i]){
                            int a = nums[i];
                            nums[i] = nums[j];
                            nums[j] = a;
                            sort1(nums,i+1);
                            return;
                        }
                    }
                }
        }
            Arrays.sort(nums);
    }
    public static   void sort1(int[] nums,int i){
        int[] result = new int[nums.length];
        int[] sort = new int[nums.length-i];
        for(int j = i;j<nums.length;j++){
            sort[j-i] = nums[j];
        }
        Arrays.sort(sort);
        for(int j = 0;j<nums.length;j++){
            if(j>=i){
                nums[j] = sort[j-i];
            }
        }
    }

接雨水

对于每一个坐标i,雨水为两边最大高度的较小者减去高度:min(left_max,right_max)-height [i]

买卖股票

遍历一次,记录当前节点之前的最低价格,和最高利润

9. 贪心

坐船问题

》坐船问题,最多两人一条,船限制limit,需要最少多少个船:从小于limit/2的最大位置两个指针,统计出配对的数量a,左边未配对的数量b,右边未配对的c,结果是a+b/2+c(贪心,L先匹配到最右边的,右边移动的位数step,此时L也往左移动step,这部分的配为最优)

    public static int min(int arr[],int limit){
        if(null == arr || arr.length < 1 || limit <1){
            return -1;
        }
        Arrays.sort(arr);
        if(arr[arr.length-1] <= limit/2){
            return (arr.length+1)<<1;
        }
        if(arr[0]>limit/2){
            return arr.length;
        }
        int lessR = arr.length -1;
        while (lessR>limit/2){
            lessR--;
        }
        int L = lessR;
        int R =L+1;
        int lessUnused = 0;
        while (L>=0){
            int solved = 0;
            while (R< arr.length && arr[L]+arr[R]<=limit){
                R++;
                solved++;
            }
            if(solved == 0){
                lessUnused++;
                L--;


            } else {
                L = Math.max(-1,L-solved);
            }
        }
        int lessAll = lessR+1;
        int lessUsed = lessAll - lessUnused;
        int moreUnsolved = arr.length - lessR - 1 - lessUsed;

        return lessUsed + (lessUnused+1)/2 +  moreUnsolved ;


    }
    System.out.println(min(new int[]{1,2,2,3,4,5,5,6,7,8},8));

最小字典(排序即可)

字符串数组拼接成最小的字典序:字符串按照字典序排序后拼接(错,b,ba),应按照a+b < b+a ? a放前面 : b放前面 

在这里插入图片描述

做项目

会议室

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

   /**
     * 只有一个会议室
     * @param meets
     * @return
     */
    public static int meeting(ArrayList<Meet> meets){
//        Meet[] meets
//        Arrays.sort(meets,new MyCompatator());
        meets.sort(new MyCompatator());
        int able = 0;
        int result = 0;
        for (int i = 0;i<meets.size() ;i++){
            if(meets.get(i).start >= able){
                result ++;
                able = meets.get(i).end;
            }
        }
        return result;
    }
 ArrayList<Meet> meets = new ArrayList<>();
        Meet meet1 = new Meet(0,1); meets.add(meet1);
        Meet meet2 = new Meet(1,2);meets.add(meet2);
        Meet meet3 = new Meet(2,5);meets.add(meet3);
        Meet meet4 = new Meet(1,4);meets.add(meet4);
        Meet meet5 = new Meet(7,9);meets.add(meet5);
        System.out.println(meeting(meets));

金条切割

在这里插入图片描述

  public static int minPrice(int arr[]){
        PriorityQueue<Integer> queue = new PriorityQueue();
        for (int i = 0;i<arr.length;i++){
            queue.add(arr[i]);
        }
        int price = 0;
        while(queue.size() > 1){
            int newPrice = queue.poll() + queue.poll();
            price += newPrice;
            queue.add(newPrice);
        }
        return price;
    }
      int[] minPriceA = new int[]{10,30,20};
        System.out.println(minPrice(minPriceA));

整数转罗马数字

思路:先枚举,再从大往小减

    public static String intToRoman(int num) {
        int a[] = new int[]{1000,900,500,400,100,90,50,40,10,9,5,4,1};
        String b[] = new String[]{"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
        StringBuilder sb= new StringBuilder();
        int count = 0;
        while (num != 0){
            while (num - a[count] >= 0){
                sb.append(b[count]);
                num -= a[count];
            }
            count++;
        }
        return sb.toString();
    }

罗马转数字

有公共前缀,替换

  public static int romanToint(String s) {
        s.replaceAll("CM","a");
        s.replaceAll("CD","b");
        s.replaceAll("XC","c");
        s.replaceAll("XL","d");
        s.replaceAll("IX","e");
        s.replaceAll("IV","f");
        StringBuilder stringBuilder = new StringBuilder();
        int result = 0;
        for(int i = 0;i<s.length();i++){
            result += get(String.valueOf(s.charAt(i)));
        }
        return result;
    }

    public static int get(String s){
        switch (s) {
            case "M" : return 1000;
            case "a" : return 900;
            case "D" : return 500;
            case "b" : return 400;
            case "C" : return 100;
            case "c" : return 90;
            case "L" : return 50;
            case "d" : return 40;
            case "X" : return 10;
            case "e" : return 9;
            case "V" : return 5;
            case "f" : return 4;
            case "I" : return 1;
            default: return 0;
        }
    }

10. 数学

整数反转

通过取余能求整数的最后一位
思路:通过对10取余求 最后一位,除法求剩余的数字

    public static int reverse(int x) {
        if (x > Integer.MAX_VALUE || x < Integer.MIN_VALUE)
            return 0;
        int result = 0;
        while (x != 0) {
            int yu = x % 10;
            x = x / 10;
            result = result * 10 + yu;
        }
        return result;
    }

指数运算(pow)

二分法,递归
如x的77次方,可以按照x的38,19,9,4,2,1的顺序

   public static double pow(double x, int n) {
        if(n == 0){
            return 1;
        }
        double a = pow(x,n/2);
        if(n%2 == 0){
            return a*a;
        }else {
            return a*a*x;
        }

    }

两数相除

格雷编码

原有的集合a(n),前面各位前面加0的集合a1(n) + a镜像后前面加1的集合a2(n) = 即为 a(n+1)
其中a1(n) == a(n)

11. 打表

只能吃4的幂这么多(1,4,16)。两个牛先后吃,谁吃最后一份谁赢,打表法

在这里插入图片描述

    //先手
    //是不是赢
    public static boolean win1(int n){
        if(n<5){
            return n == 2  || n == 0 ? false : true;
        }
        int k = 1;
        while(n - k >= 0){//先手拿掉这一部分,作为后手是不是能赢
            if(!win1(n - k)){
                return true;
            }
            k *= 4;
        }
        return false;

    }

    for (int i = 0;i<=100;i++){

                System.out.println(i+" "+win1(i));
            }

6.8袋子

    public static int min(int n){
        if(n<0 || n%2!=0){
            return -1;
        }
        if(n % 8 == 0){
            return n / 8;
        }
        int b6 = -1;
        int b8 = n / 8;
        int rest = n- 8*b8;
        while(b8 >= 0 && rest < 24){//只用6袋子处理,>24的先-24,这个数之前必定算过,需要剪枝
            if(rest % 6 == 0){
                b6 = rest / 6;
                break;
            }
            b8--;
            rest = n - 8*b8;
        }
        return b6 == -1 ? -1 : b6 + b8;

    }
    for (int i = 0;i<=1000;i++){

                System.out.println(i+" "+min(i));
            }

12. 预处理(代码中部分查询频繁,用低的代价初始化查询结果,加快查询)

最大边长都是1的正方形

在这里插入图片描述
n的4次方复杂度,左边顶点n平方,边长n,最内层判断四个边是否为1
预处理两个数组,该点右边有多少个连续的1,下边有多少个连续的1,上面的方法最内层的判断可通过这两个数组快速查询,时间复杂度变为n的三次方

LR

在这里插入图片描述

public static int minColor(String str){
        char [] chars = str.toCharArray();
        int [] ls = new int[chars.length];//i位置及之前L的数量
    int n = chars.length;
        int count = 0;
        for(int i = 0;i<n;i++){
            if(chars[i] == 'L'){
                count++;
            }
            ls[i] = count;
        }
        int min = Integer.MAX_VALUE;
        for(int i = 0;i<n;i++){ //i及左边是L,右边是R
            if(i == 0){//全是R,ls 中L的数量
                min = Math.min(min,ls[n-1]);
            }else if(i == n-1){//全是L ,ls中R数量
                min = Math.min(min,n-ls[i]);
            }else { //i及左边是L,右边是R (i+1) n-i-1 ,
                min = Math.min(min, i+1 -ls[i] + ls[n-1] - ls[i] );
            }
        }
        return min;
}

13. 记录辅助信息

包含负数的数组,累计和<=k的连续子数组的最长长度

维护两个数组,数组1以i开始的累加和最小值,数组2是数组1对应的右坐标;
在这里插入图片描述

    public static int max(int arr[],int k){
        //以i开始往后的最小累加和及下标
        int len = arr.length;
        int minSum[] = new int[len];
        int ends[]  = new int[len];
        minSum[len-1] = arr[len-1];
        ends[len-1] = len-1;
        for (int i = arr.length -2;i>=0;i--) {
            if(minSum[i+1]>0){
                minSum[i] = arr[i];
                ends[i] = i;
            }else {
                minSum[i] = arr[i]+minSum[i+1];
                ends[i] = ends[i+1];
            }
        }
        int sum = 0;
        int end = 0;
        int res = 0;
        for(int i =0;i<arr.length;i++){
            while ( end < len && sum+minSum[end] <= k){
                sum += minSum[end];
                end = ends[end]+1;//end遍历后为不满足的下一个起始
            }
            res = Math.max(res,end-i);
            if(end>i){
                sum -= arr[i];
            }else {
                end=i+1;
            }
        }
        return res;

    }

包含负数的数组,累加和为k的连续子数组的最长长度

map,记录所有累加和及最早出现的位置

    public static int max(int arr[],int k){
        Map<Integer,Integer> map = new HashMap<>();
        int sum = 0;
        int result = 0;
        map.put(0,-1);
        for(int i =0;i<arr.length;i++){
            sum += arr[i];
            if(map.containsKey(sum - k)){
                result = Math.max(result,i - map.get(sum - k) );
            }else {
                map.put(sum,i);
            }
        }
        return result;
    }
    System.out.println(max(new int[]{1,1, 2, 1, -1,1,-1, 2 },3));
System.out.println(max(new int[]{1,1, 2, 1, -1, 2 },5));

接雨水

    public static int water(int arr[]) {
        int n = arr.length;
        int leftMax[] = new int[n];
        int rightMax[] = new int[n];
        leftMax[0] = 0; rightMax[n-1] = 0;
        int max = arr[0];int maxR = arr[n-1];
        for(int i = 1;i<n;i++){
            leftMax[i] = arr[i] < max ? max : 0;
            max = max < arr[i] ? arr[i] : max;
        }
        for(int i = n-1;i>=0;i--){
            rightMax[i] = arr[i] < maxR ? maxR : 0;
            maxR = maxR < arr[i] ? arr[i] : maxR;
        }
        int result = 0;
        for(int i = 1;i<n-1;i++){
            result += (leftMax[i] !=0 && rightMax[i] !=0) ? Math.min(leftMax[i],rightMax[i]) - arr[i]: 0;
        }
        return result;
    }
System.out.println(water(new int[]{4,1,3,5,4,2,6}));

给一个字符串,需要放多少个能构成完成括号字符串

》完整的括号字符串:栈,或者一个变量count,遇到左括号count++,右括号count–,若过程count<0则错误,若结果count!=0则错误
》给一个字符串,需要放多少个能构成完成括号字符串:同上,再加一个ans,若过程中count<0,则令count=0(加一个左括号),同时ans++;遍历结束若count>0,ans+count(补多少个右括号),返回ans
》括号的深度,同上,count的最大值

    public static int howMany(String str) {
        if(null == str || str.length() == 0){
            return 0;
        }
        int result = 0;
        int lefts = 0;
        for (int i = 0;i<str.length();i++){
            if(str.charAt(i) == '('){
                lefts++;
            }else {
                lefts--;
            }
            if(lefts < 0){
                result++;
                lefts = 0;
            }
        }
        return lefts+result;
    }
System.out.println(howMany(")((()))()))((()"));

========================

todo 写

优先级1
37,38,41,43,44,45,55,60,61,63,65,68,76,79,82,83,92,95,105,106,112,113,123,146,10

优先级2,高
77,78,90,93

  • List item

算法实战

布隆过滤器
大数据的:分片(hash),bit数组,1G=10亿字节
搜索热点词:分片,hash计算数量,大根堆
海量数据的中位数:二分,二进制的最高位,分为两个文件,统计个数
短链系统:高进制来缩短,缓存存放映射关系
在线并发用户数:redis
在线并发用户数,redis,zset
红包算法:
线性切割法,一个区间切N-1刀。越早越多
二倍均值法,【0 ~ 剩余金额 / 剩余人数 * 2】中随机,相对均匀

快排

两个方法,第一个,当low《high 时,获取中间的指针,并递归调用左右两边
第二个,返回中间指针,遍历,当遇到比最右边小的时候,交换pointer和当前元素,遍历完成后交换pointer和最右边元素,返回中间的指针pointer

 public static int partition(int[] array, int low, int high) {
        // 取最后一个元素作为中心元素
        int pivot = array[high];
        // 定义指向比中心元素大的指针,首先指向第一个元素
        int pointer = low;
        // 遍历数组中的所有元素,将比中心元素大的放在右边,比中心元素小的放在左边
        for (int i = low; i < high; i++) {
            if (array[i] <= pivot) {
                // 将比中心元素小的元素和指针指向的元素交换位置
                // 如果第一个元素比中心元素小,这里就是自己和自己交换位置,指针和索引都向下一位移动
                // 如果元素比中心元素大,索引向下移动,指针指向这个较大的元素,直到找到比中心元素小的元素,并交换位置,指针向下移动
                int temp = array[i];
                array[i] = array[pointer];
                array[pointer] = temp;
                pointer++;
            }
            System.out.println(Arrays.toString(array));
        }
        // 将中心元素和指针指向的元素交换位置
        int temp = array[pointer ];
        array[pointer] = array[high];
        array[high] = temp;
        return pointer;
    }

    public static void quickSort(int[] array, int low, int high) {
        if (low < high) {
            // 获取划分子数组的位置
            int position = partition(array, low, high);
            // 左子数组递归调用
            quickSort(array, low, position -1);
            // 右子数组递归调用
            quickSort(array, position + 1, high);
        }
    }

归并

分:从中间分开,左右两边循环调用,直至只有一个元素,然后调用第二个方法
和:第二个方法,申请一个数组做合并使用

  //合并
    public static void merge(int[] arr, int L, int M, int R) {
        int[] help = new int[R - L + 1];
        int i = 0;
        int p1 = L;
        int p2 = M + 1;
        while (p1 <= M && p2 <= R)
            help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        while (p1 <= M)
            help[i++] = arr[p1++];
        while (p2 <= R)
            help[i++] = arr[p2++];
        for (i = 0; i < help.length; i++)
            arr[L + i] = help[i];
    }
    //分
    public static void mergeSort(int[] arr, int L, int R) {
        if (L == R)
            return;
        int mid = L + ((R - L) >> 1);
        mergeSort(arr, L, mid);
        mergeSort(arr, mid + 1, R);
        merge(arr, L, mid, R);
    }

LRU

get和set方法,基于linkedHashMap(双向链表 )set时先删除,再判断容量(超过时删除最后一个元素),再put
get时如果存在,需要充值元素位置到第一个

左神

删掉字符串的重复字符,使字典序最小

public static String min(String str){
    if(null == str|| str.length()<1){
        return str;
    }
    char[] map = new char[256];

    for (int i = 0;i<str.length();i++) {
        map[str.charAt(i)]++;
    }
    int minIndex = 0;
    for (int i = 0;i<str.length();i++) {
        minIndex =  str.charAt(i) < str.charAt(minIndex) ? i : minIndex;
        if(--map[str.charAt(i)] == 0){
            break;
        }
    }
    return str.charAt(minIndex) + min(str.replaceAll(String.valueOf(str.charAt(minIndex)),""));
}
System.out.println(min("bbcaakb"));

在这里插入图片描述

过称为选择某一个字符,选到某一段词频为0了,在前面的一段选最小的,删掉string的该字符继续这个操作

一个数组,求如果排序后相邻两个数的最大差值,且时间复杂度0(n)

在这里插入图片描述
在这里插入图片描述

找出长度为n数组中所有未出现的数,每个数字位于1到n之间

由一个点出发,力争让i位置上的数字为i+1,当不满足时不断调整,满足的时候从下一个下标继续开始

        for(int i = 0;i<arr.length;i++){
            int tmp = arr[i];
            if(tmp == i+1){
                continue;
            }
            while(tmp != arr[tmp-1]){
                int tmp2 = arr[tmp-1];
                arr[tmp-1] = tmp;
                tmp = tmp2;
            }
        }
            notShow(new int[]{4,2,1,3,4});

路灯安装

public static int min(String str){
    if(null == str || str.length() <1){
        return 0;
    }
   char[] chars = str.toCharArray();
    int result = 0;
    for(int i =0;i<chars.length;){
        if(chars[i] == '.' ){
            result++;
            if(i+1 < chars.length && chars[i+1] =='.'){//第二个也是。,那第三个不管是。还是x都跳过
                i = i+3;
                continue;
            }
        }
        i++;
    }
    return result;
}
System.out.println(min(".x.x.."));

子数组的最大连续累加和

    public static int max(int arr[]) {
        int result = Integer.MIN_VALUE;
        int preSum = 0;
        for(int i = 0;i<arr.length;i++){
            preSum += arr[i];
            result = Math.max(result,preSum);
            preSum = preSum > 0 ? preSum : 0;
        }
        return result;
    }
System.out.println(max(new int[]{1,2,3,-4,5,-1,2,1} ));

字符串转数字(对超过min的拦截)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

 public static int change(String str){
        if(null == str || str.length()<1){
            throw new RuntimeException("aa");
        }
        char[] chars;
        boolean neg = false;
        if(str.charAt(0) == '-' ){
            neg = true;
            chars = str.substring(1).toCharArray();
        }else {
           chars = str.toCharArray();
        }
        if(!isValid(chars)){
            throw new RuntimeException("bb");
        }
        int mina = Integer.MIN_VALUE / 10;
        int minb = Integer.MIN_VALUE % 10;
        int result = - (chars[0]-'0');
        for(int i = 1;i<chars.length;i++){
            if(result <  mina ||  (result ==  mina && ('0'-chars[i] < minb))){
                throw new RuntimeException("bb");
            }
            result = result*10 - (chars[i]-'0');
        }
        if(!neg && result == Integer.MIN_VALUE){
            throw new RuntimeException("bb");
        }
        return neg ? result : -result;
    }


    public static boolean isValid(char[] chars){
        if(chars[0] <= '0' || chars[0] > '9'){
            return false;
        }
        for(int i = 1;i<chars.length;i++){
            if(chars[i]>'9' || chars[i] <'0'){
                return false;
            }
        }
        return true;
    }
            System.out.println(Integer.MIN_VALUE);

System.out.println(change("2147483649"));

洗衣机均分

思想:就是计算每个位置需要经历多少次传递,其中的最大值
1;求每个位置最小的轮数,分为左右两边多,少共四种情况,两边都少时,为L+R,其他为max(L,R)
2;求每个位置最小的轮数中的最大值

    public static int min(int arr[]) {
        if(arr.length<2){
            return arr[0];
        }
        int length = arr.length;
        int total = 0;
        for(int i = 0;i<length;i++){
            total += arr[i];
        }
        if(total % length != 0){
            return -1;
        }
        int each = total / length;
        int max = Math.abs(arr[0]-each);
        max = Math.max(max,Math.abs(arr[length-1]-each));
        int curTotal = 0;
        for(int i =1;i<length-1;i++){
            curTotal += arr[i-1];//左边有i个
            int leftNeed = each*i;
            int rightTotal = total - curTotal -arr[i];
            int rightNeed = each*(length-i-1);
            if(curTotal < leftNeed && rightNeed>rightTotal){
                max = Math.max(max,leftNeed-curTotal+rightNeed-rightTotal);
            }else {
                max = Math.max(max,Math.max(Math.abs(leftNeed-curTotal),Math.abs(rightNeed-rightTotal)));
            }
        }
        return max;
    }
//            int arr[] = new int[]{1, 0, 5};
            int arr[] = new int[]{0, 2, 0};
            System.out.println(min(arr));

数组最大前缀疑惑和

在这里插入图片描述

前缀数标准代码
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
对数器:
在这里插入图片描述

打爆气球(尝试模型)

每个位置的气球最后被打爆
在这里插入图片描述

在这里插入图片描述

汉诺塔游戏路径

n层汉诺塔最优2的n次方-1步
f(n) = f(n-1) + 1 + f(n-1)

分3步

  • 0 - i-1 从from到other
  • i到to
  • 0 - i-1从other到to

f(n) = f(n-1) + 1 + f(n-1)
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

判断两个字符串是否互为旋变串

在这里插入图片描述
同一级的可以互换位置,新的字符串就是原来的旋变串
范围上尝试
先尝试str1的所有可能的第一刀怎么划分,然后考虑str2的两种情况,旋变或者不旋变
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
dp
在这里插入图片描述
在这里插入图片描述

str1包含str2所有字符的最小长度(滑动窗口)

在这里插入图片描述
在这里插入图片描述

实现LFU(最近最少使用,code难,原理简单,try)

在这里插入图片描述
o(1)时间复杂度
通过词频的双向链表(存key,value对象),双向链表之间也是通过双向链表连接,和key的map,map指向链表对象的地址
在这里插入图片描述

良好加油站(code难)

在这里插入图片描述
建立ill - res的数组
然后四个变量,若前闭后开的变量中没有良好出发点,则没有,若有,如H,则验证剩下的点(沿H逆时针)能到H即可
在这里插入图片描述

二叉搜索树的错误节点

错误节点是中序遍历后,第一次降序的前一个节点和最后一次降序的后一个节点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

最多矩阵重叠

在这里插入图片描述
先处理线段最多重合,线段按照start排序,建立有序表,用于存end字段,每个线段进入时,将有序表中《=start的内容删掉,然后加入自己的end(含义:重合区域必须已start开头的最多有几段),此时有序表的节点个数就是该线段的最多重合
在这里插入图片描述
然后,
矩阵按照下边界排序,逐个将矩阵上边界放入容器中,遍历,容器中比下一个矩阵的下边界底的丢出去,再放入自己的上边界,此时容器中的矩阵的每个左右边界,变成了一维的求最多重合问题,遍历完求出最大
在这里插入图片描述
在这里插入图片描述

数组中两个数据出现了奇数次

在这里插入图片描述

最长有效括号

    public static int maxLength(String str){
        int limit = 0;
        int currL = 0;
        int maxL = 0;
        char[] chara = str.toCharArray();
        for(int i =0;i<chara.length;i++){
            if(chara[i] =='('){
                limit++;
            }else {
                if(limit == 0){
                    currL = 0;
                }else {
                    limit--;
                    currL+=2;
                    maxL = currL > maxL ? currL : maxL;
                }
            }
        }
        return maxL;
    }
   System.out.println(maxLength(")((((())())"));

将栈a中数据调整有序,栈顶最大

    public static void orderStack(Stack<Integer> stack){
        if(stack.isEmpty()){
            return;
        }
        Stack<Integer> help = new Stack<>();
        while (!stack.isEmpty()){
            Integer top = stack.pop();
            while (!help.isEmpty() && help.peek() > top){
                stack.push(help.pop());
            }
            help.push(top);
        }
        while (!help.isEmpty()){
            stack.push(help.pop());
        }
    }
         Stack<Integer> stack = new Stack<>();
            stack.push(5);
            stack.push(1);
            stack.push(3);
            stack.push(2);
            stack.push(4);
            orderStack(stack);
            System.out.println();

不考虑局部移动

矩阵z字型打印

定义两个点开始都在左上角,然后a往右移动,到尽头后往下移动,b往下移动,到尽头后往右移动,打印a到b之间的斜线,分为a到b打印和b到a打印,交替进行

public static void printZ(int arr[][]){
        int ax=0,bx=0,ay=0,by=0;
        boolean reverse = true;
        int step = 0;
        int row = arr.length;
        int col = arr[0].length;
        while (step<=arr.length+arr[0].length){
            print(arr,ax,ay,bx,by,reverse);
            if(ax<row-1){
                ax++;
            }else {
                ay++;
            }
            if(by<col-1){
                by++;
            }else {
                bx++;
            }
            reverse = !reverse;
            step++;
        }

}

public static void print(int arr[][],int ax,int ay,int bx,int by,boolean reverse){
        if(reverse){
            while (ax >= bx){
                System.out.println(arr[ax--][ay++]);
            }
        }else {
            while (ax >= bx){
                System.out.println(arr[bx++][by--]);
            }
        }
}
    //1 2 3 4
    //4 5 6 7
    //7 8 9 1
            int arr[][] = new int[][]{{1,2,3,4},{4,5,6,7},{7,8,9,1}};

            printZ(arr);

二维数组旋转

正方形矩阵旋转90度:有限变量:一层层转变,定义框调整的函数f(),每一层根据4个顶点开始互换,然后点移动继续互换,定义两个左上右下的顶点确定一个正方形

 public static void xuan(int arr[][]) {
        if(null == arr || arr.length < 2){
            return;
        }
        int lx = 0;;
        int rx = arr.length-1;
        while (lx<rx){
            xuan(arr,lx,rx);
            lx++;
            rx--;
        }


    }
    //1234
    //4567
    //7891
    //4567
    public static void xuan(int arr[][],int lx,int rx){
        int ax = lx;
        int ay = lx;//++
        int bx = lx;//++
        int by = rx;
        int cx = rx;
        int cy = rx;//--
        int dx = rx;//--
        int dy = lx;
        while (ay<rx){
            int tmp = arr[ax][ay];
            arr[ax][ay] = arr[dx][dy];
            arr[dx][dy] = arr[cx][cy];
            arr[cx][cy] = arr[bx][by];
            arr[bx][by] = tmp;
            ay++;
            bx++;
            cy--;
            dx--;
        }
    }
            //1234
            //4567
            //7891
            //2345
            int arr[][] = new int[][]{{1,2,3,4},{4,5,6,7},{7,8,9,1},{2,3,4,5}};
            xuan(arr);

螺旋打印

    public static void print(int [][] arr){
        int lx = 0;
        int ly = 0;
        int rx = arr.length-1;
        int ry = arr[0].length-1;
        while (lx <= rx && ly <= ry){
            printSide(arr,lx,ly,rx,ry);
            lx++;ly++;
            rx--;ry--;
        }
    }

    public static void printSide(int [][] arr,int lx,int ly,int rx,int ry){
        if(lx == rx){//同一行
            for(int i=ly;i<=ry;i++){
                System.out.println(arr[lx][i]);
            }
        }else if(ly ==ry){
            for(int i=lx;i<=rx;i++){
                System.out.println(arr[i][ly]);
            }
        } else {
            for(int i =ly;i< ry;i++){
                System.out.println(arr[lx][i]);
            }
            for(int i =lx;i<rx;i++){
                System.out.println(arr[i][ry]);
            }
            for(int i =ry;i> ly;i--){
                System.out.println(arr[rx][i]);
            }
            for(int i = rx;i>lx;i--){
                System.out.println(arr[i][ly]);
            }
        }
    }
            //123456
            //567821
            //678914
            //743139
            //134241
            int arr[][] = new int[][]{{1, 2, 3, 4, 5,6}, {5, 6, 7, 8, 2,1}, {6, 7, 8, 9, 1,4},{7,4,3,1,3,9},{1,3,4,2,4,1}};
            print(arr);

技巧

压缩数组

二维数组的最大累加和(n3次方)

给定一个整形矩阵,找出子矩阵的最大累加和:求第一行,第一行到第2行,一直到第n行;第二行到第n行,两层for循环,判断i,j行内都包含在里面的最大累加和;
循环内部压缩数组,用(子数组的最大连续累加和)方法;

数据交换(异或 )

    int a = 1;
    int b = 4;
     a = a^b;
     b = a^b;
     a = a^b;

向上取整

向上取整:+1再除以2

位运算

位运算:
得到num二进制位:假设32位,则 for(move=31;move》=0;move-- ){ 第i位=num>>move & 1}
相加:a+b= a | b

得到n最右侧的1
int rightone = n & (~n + 1)

>>,>>>区别,对负数有区别
技巧1(+号表示互斥条件的if,else)
    两个有符号数较大的,不能比较:互斥条件用加法相加的形式能实现if,else,可以用+号表示互斥条件的if,else
拿到数最右侧的1:
判断一个数是不是2的幂(二进制只有一个1),拿到最右侧的1,和原数比较是否相等;或者x & (x -1) == 0
判断一个数是不是4的幂:先判断只有一个1,再与上01010101,!= 0,说明是4的幂
不用算术运算符,实现加减乘除:
    加:异或(无进位相加)a,与运算作左移一位的进位信息b;结果 = a+b ;继续运算直至进位信息为0
        相反数 = 取反+1 = ~n + 1
    减:add(a,相反数b)=add(a,add(~b,1))
    乘:模拟二进制的乘法;
    除:a/b,b左移动但不超过a,减去移动后的值,记录移动位数,剪掉剩下的值作为a,继续这个操作;

异或

异或:无进位相加
x ^ x = 0; (题目,一些数,只有一个数出现了奇数次,其他都是偶数次)
x^0 = x;
x ^ y =z,则x = y^z
数组中arr[i到j]的异或和 = arr[0到i-1] ^ arr[0到j]

拿金币问题,数组中所有数异或,结果为0则必败;
在这里插入图片描述

大数据技巧(大事化小(hash分块);位图;范围的区间)

多循环几次,每次只处理这个一个小区间的数
在这里插入图片描述

对数器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

比较器

在这里插入图片描述

分数

a/b,求最大公约数c,然后a/c +“/” + b/c
最大公约数

  public static long max(long m,long n){
        return n == 0 ? m : max(n, m % n);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值