算法leetcode

刷力扣 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)
	最低位的 1:A & (-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);
    }
力扣 LeetCode 会员特权解析:算法刷题必备还是锦上添花? 本文深度解析力扣LeetCode会员的核心价值,重点剖析其企业题库、面试热度与快速判题等特权功能。对于有明确求职目标、需要高效精准准备算法面试的用户而言,会员能提供针对性练习和流畅体验,是提升刷题效率的实用工具。而对于算法初学者,免费题库已足够夯实基础。 阅读详情

相关推荐

力扣 LeetCode 会员特权解析:是否值得为算法刷题投资?

本文深度解析力扣LeetCode会员的核心价值,重点剖析其专属题库、企业高频题库、极速判题与详细题目分析数据等特权。对于正在求职或希望系统提升算法能力的开发者而言,会员能提供显著的效率提升与信息优势,是一项值得考虑的效率投资。

weixin_29214335的博客 551

LeetCode 力扣算法题解汇总,All in One

我刷过的所有 leetcode 算法题解的汇总。 题目的排列顺序是按照先Easy再Medium再Hard排列的,暂时还没有把题目全部整理完成。后序我会把刷过的所有的题目都整理到这个文档里。 如果想看我全部的力扣算法题解,可以看我的题解博客:[fuxuemingzhu.cn](fuxuemingzhu.cn)

负雪明烛 1万+

Ubuntu 20.04 上 Geant4 安装避坑全记录:从源码编译到 B1 示例跑通(含数据包加速下载)

本文详细记录了在Ubuntu 20.04系统上从源码编译安装Geant4的全过程,包括环境准备、依赖安装、CMake参数解析、多线程编译优化以及数据包加速下载等关键步骤。特别针对B1示例的测试与验证提供了完整流程,帮助科研人员和学生避开常见安装陷阱,提升粒子物理模拟效率。

weixin_31220401的博客 312

力扣启蒙 - 开启算法的世界

点击上方 Java学习之道,选择 设为星标 每天18:30点,干货准时奉上! Part1力扣 刷力扣,刷力扣,力扣究竟是什么呢? 力扣(LeetCode)是领扣网络旗下专注于程序员技术成长和企业技术人才服务的品牌。源自美国硅谷,力扣为全球程序员提供了专业的IT技术职业化提升平台,有效帮助程序员实现快速进步和长期成长。 而刷力扣也就是在力扣的题库里刷算法题。 Part2力扣启蒙 下面来引入力扣的启蒙题来感受一下算法题的魅力吧。 1力扣第一题: 两数之和 给定一个整数数组 nums .

Java学习之道 5903

RKNN3588——YOLOv8的PT模型转RKNN模型

1. 首先克隆rknn修改后的ultralytics版本项目到本地主要是修改了源码的ultralytics/nn/modules/head.py和ultralytics/engine/exporter.py两个文件。2. 使用修改后的ultralytics对pt模型进行模型转换,此处的format=rknn代表支持rknn后续的转换,而不是用onnx,一定要注意!!!

张飞飞的博客 7169

VPI与Matlab协同仿真:相干光通信接收端DSP算法全解析

本文详细解析了如何利用VPI与Matlab协同仿真,构建完整的相干光通信接收端DSP算法处理闭环。重点介绍了从VPI导出数据后,在Matlab中实现时钟恢复、偏振解复用和载波相位估计等核心算法的步骤与原理,并阐述了如何通过误码率计算完成仿真验证,为相干光通信系统算法研究与验证提供了高效实用的方法。

ll5678的博客 609

力扣算法练习

官方网址 https://leetcode-cn.com/

赵家乐 1041

RFSOC实战解析(一):深入RF Data Converter的时钟与数据接口设计

本文深入解析RFSOC中RF Data Converter的时钟与数据接口设计,涵盖GHz采样、数据流处理、时钟树同步及AXI-Stream接口实战。通过详细案例和调试技巧,帮助工程师高效配置IP核,优化ADC/DAC性能,解决跨时钟域和信号完整性等关键问题。

weixin_30955617的博客 473

力扣(leetcode)HOT100算法题总结

解题思路:该代码使用二分查找算法来定位目标值在数组中的起始和结束位置。通过两次二分查找(分别查找左边界和右边界),确保在 O(log n) 时间复杂度内完成搜索。如果目标值不存在,返回 [-1, -1]。时间复杂度:O(logn) ,其中n为数组的长度。二分查找的时间复杂度为O(logn),一共会执行两次,因此总时间复杂度为O(logn。

bear_309的博客 3647

基于 Python 的 LIF 模型:探索神经元同步与小世界网络

首先,我们定义了一个LIFNeuronself.soma.L = self.soma.diam = 12.6157 # 固定神经元形状self.cm = cm # 电容self.V_rest = V_rest # 静息电位self.V = V_rest # 初始化膜电位在初始化过程中,每个神经元被赋予初始的静息电位,并且其膜电位会在后续的模拟过程中动态变化。

qq_36224726的博客 1423

海康威视4G摄像头直连实战:不用萤石云,手把手教你用ISUP SDK搞定跨网段视频流

本文详细介绍了如何通过海康威视ISUP SDK实现4G摄像头的直连通信,提供了一套零平台费、自主可控的技术方案。文章涵盖技术选型、设备端配置、服务器端开发及高级应用场景,特别适合中小型安防项目,帮助开发者解决跨网段视频流传输难题,降低长期运营成本。

weixin_27076351的博客 467

力扣LeetCode-贪心算法

贪心算法 基本知识 1.思想 基于局部最优的选择逐渐推导出全局最优解 2.一般步骤 将问题分解为若干个子问题; 找出合适的贪心策略; 求解每一个子问题的最优解; 将局部最优解合成为全局最优解; 典型例题 1. LeetCode 376. 摆动序列 题目 如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。 例如, [1, 7, 4, 9

weixin_56005963的博客 2981

UNet改进(24):注意力机制-从基础原理到高级融合策略

本文系统探讨了如何通过引入各类注意力机制增强UNet在医学图像分割中的性能。文章分为基础篇、进阶篇和高级篇三个层级:基础篇介绍了时序注意力、可变形注意力、分层注意力等8种基础机制;进阶篇提出了5种混合注意力设计;高级篇则探讨了全局+局部+多尺度等复杂组合机制。实验表明,这些注意力模块在心脏MRI、肺部感染等任务中能提升2-15%的分割精度。文章还提供了硬件适配建议和完整实现代码,为研究者提供了全面的技术参考。未来方向包括NAS优化、量子注意力等前沿探索,为UNet在医疗影像等领域的应用革新提供了理论指导。

2501_91516851的博客 764

力扣算法1~10题(js)

标题 力扣算法 文章目录标题1、贩卖柠檬水,能否正确找零错误思路一:解法一缺点:耗时太长解法二(大佬解法)2、返回nums中和为target的两个数的下标解法3、两数相加思路与算法解法4、无重复字符的最长子串找到数组中最大值的方法自己的解法:对js方法还不是很属性,很多想不到的地方大佬解法大佬解法二(哈希)未看懂5、寻找两个正序数组的中位数暴力解法二分查找大佬思维 作者:6xiaodi解题步骤解法6、给定一个字符串 `s`,找到 `s` 中最长的回文子串。思路:暴力解法大佬解法(动态规划)动态规划特别说明

m0_45857808的博客 1357

基于STM32的农业蔬菜大棚温湿度采集控制系统(代码+仿真+全部资料)

摘要: 本研究设计了一种基于STM32微控制器的农业蔬菜大棚温湿度智能控制系统。系统通过DHT11传感器实时采集温湿度数据,LCD屏显示当前值及预设阈值,用户可通过按键模块调整阈值范围。当温湿度超出设定阈值时,系统自动控制风扇或加湿器进行调节,确保大棚环境稳定。实验验证表明,该系统具备高精度监测、稳定控制和模块化扩展等优势,有效优化蔬菜生长环境。未来可扩展远程监控等功能,进一步提升智能化水平。 关键词: STM32、温湿度控制、蔬菜大棚、DHT11、自动化调节

3254968421的博客 785

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

力扣刷题篇之排序算法

​本系列是个人力扣刷题汇总,本文是排序算法。刷题顺序按照[力扣刷题攻略] 这个之前写的左神的课程笔记里也有:左程云算法与数据结构代码汇总之排序(Java)-优快云博客 本来想看 按照这个分类一个个解题的,但是好多都不是最优解甚至会超过时间限制,所以要看较为系统一点的排序算法还是看上面那个之前的汇总吧,只是没有希尔排序,

m0_66076989的博客 1342

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

力扣LeetCode初级算法每日一题

从今天开始坚持每天在LeetCode坚持刷书记算法题有兴趣的小伙伴可以和我一起坚持刷题 Java版本 链接:初级算法 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台https://leetcode.cn/leetbook/detail/top-interview-questions-easy/买卖股票的最佳时机 II链接:初级算法 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台https://leetcode.cn/leetbook/detai

weixin_63426509的博客 897

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

力扣 算法面试题汇总 Python

近期在刷力扣官网的这份题,因为我是因为报名了蓝桥杯才刷算法题的,所以我会选择性地写一些题解 (不包括太简单的、太难的、不在我考试范围的) 以下都是我觉得比较有思考价值的题 热身: 1. 只出现一次的数字 读题之后可以找到两个重点:只有一个元素只出现了一次、其余每个元素均出现两次 “相同为0,相异为1”,马上想到异或运算。所有元素都异或在一起,最终的数值就是只出现一次的元素 class Solution(object): def singleNumber(self, n.

荷碧TongZJ 的水文 5388

力扣算法学习

力扣算法 六六 无聊整理看看。

qq_44173650的博客 1415

力扣算法题

力扣排名前200题目1 前50新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 分享下力扣出现概率前200题目 1 前50 1 两数之和 简单 2 两数相加 中等 5 最长回文子串 中等 3 无重复字符的

qq_19375579的博客 756

LeetCode力扣算法总结-go版本

LeetCode力扣算法总结-go版本

weixin_49406295的博客 1830

力扣LeetCode面试150道题及题解整理:全方位提升算法能力

力扣LeetCode面试150道题及题解整理:全方位提升算法能力 【下载地址】力扣LeetCode面试150道题及题解整理 这份资源整理了力扣(LeetCode)面试中常见的150道题目及其详细解答,涵盖数组、链表、树、图、动态规划、贪心算法等多种算法和数据结构的应用。每道题目均附有详细解答,帮助您理解解题思路和方法。无...

gitblog_06781的博客 1100

LeetCode简单算法笔记

1.冒泡排序 对一串数组 [5,2,3,1] 进行从小到大排序 解题1: public int[] sortArray(int[] nums) { // Arrays.sort(nums); // return nums; //冒泡排序 //外圈控制比较次数 i= 1,2,3 // for (int i = 0; i < nums.length-1; i++) ...

qq_44838943的博客 204

【Java力扣算法】Leetcode 169 求众数

题目: 给定一个大小为n的数组,找到其中的众数。众数是指在数组中出现次数大于⌊ n/2 ⌋的元素。 你可以假设数组是非空的,并且给定的数组总是存在众数。 示例1: 输入: [3,2,3] 输出: 3 示例2: 输入: [2,2,1,1,1,2,2] 输出: 2 思路: 解法一:运行时间7ms,时间复杂度O(nlogn) 先排序,判断如...

懂技术的猫 804

力扣LeetCode算法题第2题-两数之和

先讲下思路,我一开始采用了数据形式去进行运算

逼哥很疯狂的博客 315

力扣 (LeetCode) 字节校园 算法与数据结构

Bytedance-campus-59-Leetcode力扣 (LeetCode) ????️ 字节校园 算法与数据结构 ⚡ ???? 如果你有问题1. 两数之和2. 两数相加3. 无重复字符的最长子串4. 寻找两个正序数组的中位数5. 最长回文子串7. 整数反转14. 最长公共前缀15. 三数之和20. 有效的括号21. 合并两个有序链表22. 括号生成23. 合并K个升序链表25. K 个一组翻转链表3...

程序员哆啦A梦,蓝胖子 719
上一篇: 性能调优实战
下一篇: docker和虚拟机对比
big muscle
博客等级 码龄11年 145粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值