posts - 15,  comments - 6,  views - 13万
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

看完整本书,花心思整理,觉得有帮助别忘了点个

复制代码
   1 package javaTest.javaBase.算法;
   2 
   3 import sun.plugin.javascript.navig.Array;
   4 
   5 import java.math.BigDecimal;
   6 import java.util.*;
   7 import java.util.concurrent.ConcurrentLinkedDeque;
   8 
   9 /**
  10  * <h1>思路总结:<h1/>
  11  *##深度优先搜索DFS##(递归搜索):找到一个单元,对这一个单元进行计算,然后递归辐射到周边单元,并找到退出要求
  12  *##广度优先BFS##(链表):蔓延,遍历该单元时,将下一次需要遍历的数据放到待遍历的数据堆栈中
  13  *##动态规划##:
  14  * 思路一 自底向上思考问题,从一个较小规模的问题开始,通过递推的方式,逐个推导,得到最终问题规模的结果
  15  * 思路二 单元n的结果可以根据上一个单元n-1推导。每个子单元可通过上个子单元推导
  16  * 重点 定义状态和状态转移方程(得到不同规模的问题之间的关系)
  17  *##贪心##:每单元最大化收益计算结果
  18  *##递归的思想##:1). 明确递归终止条件 2). 给出递归终止时的处理办法 3). 提取重复的逻辑,缩小问题规模*。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。经典算法:1.阶乘 2.斐波纳契数列 3.回文字符串的判断
  19  *##单调栈##:是在栈的 先进后出 基础之上额外添加一个特性:从栈顶到栈底的元素是严格递增(or递减)。
  20  *##回溯##:回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
  21  *回溯算法是一种特殊递归,通常有一个全局变量保存满足条件的结果
  22  *##剪枝##(结果筛选):在进行dfs搜索时,如果某结果不符合要求,需要去除,可以叫做剪枝。
  23  *##递归##:从已知问题的结果出发,用迭代表达式逐步推算出问题的开始的条件,即顺推法的逆过程,称为递归。
  24  *##递推##:递推算法是一种用若干步可重复运算来描述复杂问题的方法。递推是序列计算中的一种常用算法。通常是通过计算机前面的一些项来得出序列中的指定象的值。
  25  * 递归与递推区别:相对于递归算法,递推算法免除了数据进出栈的过程,也就是说,不需要函数不断的向边界值靠拢,而直接从边界出发,直到求出函数值。
  26  *##归并排序##是分治思想的典型应用,它包含这样三个步骤:
  27  * 分解: 待排序的区间为 [l, r] ,令 m = (l + r)/2 * ,我们把 [l, r]分成 [l, m] 和 [m + 1, r]
  28  * 解决: 使用归并排序递归地排序两个子序列
  29  * 合并: 把两个已经排好序的子序列 [l, m]和 [m + 1, r]合并起来
  30  * 在待排序序列长度为 11 的时候,递归开始「回升」,因为我们默认长度为 1的序列是排好序的。
  31  *
  32  *
  33  *
  34  *
  35 */
  36 
  37 
  38 /**
  39 * @Param:
  40 * @return:
  41 * @Author: HelloXf
  42 * @Date:
  43 */
  44 public class Leecode剑指offer {
  45 
  46 
  47     /**
  48      * 03. 数组中重复的数字
  49      * [原地置换][哈希]
  50      */
  51     public static int findRepeatNumber(int[] nums) {
  52       /*  HashSet<Integer> hashSet  = new HashSet<>();
  53         for(int i:nums){
  54             if(!hashSet.add(i)){
  55                 return i;
  56             }
  57         }
  58         return -1;*/
  59 
  60         //原地置换
  61         int temp;
  62         for(int i=0;i<nums.length;i++){
  63             while (nums[i]!=i){
  64                 if(nums[i]==nums[nums[i]]){
  65                     return nums[i];
  66                 }
  67                 temp=nums[i];
  68                 nums[i]=nums[temp];
  69                 nums[temp]=temp;
  70             }
  71         }
  72         return -1;
  73     }
  74 
  75 
  76      /**
  77       *  04. 二维数组中的查找
  78       */
  79      public static boolean findNumberIn2DArray(int[][] matrix, int target) {
  80          int x=0,y=0;
  81          if(matrix.length==0){
  82              return false;
  83          }
  84          if(matrix[y].length==0){
  85              return false;
  86          }
  87 
  88          for(  ;x<matrix[y].length;x++){
  89             boolean flag = false;
  90              if(matrix[y][0] > target ){
  91                 return false;
  92              }
  93              if(matrix[y][x]<target&& x<matrix[y].length-1){
  94                  continue;
  95              }
  96 
  97              if( x==matrix[y].length-1&&  matrix[y][matrix[y].length-1]<target){
  98                  flag =true;
  99              }
 100              if(matrix[y][x]==target){
 101                  return  true;
 102              }
 103              for(  ;y<matrix.length;y++){
 104                  if(flag){
 105                      if(matrix[y][x]<target){
 106                          continue;
 107                      }else if(matrix[y][x]==target){
 108                          return  true;
 109                      }else {
 110                          return false;
 111                      }
 112                  }else{
 113                      if(matrix[y][x-1]<target){
 114                          continue;
 115                      }else   if(matrix[y][x-1]==target){
 116                          return  true;
 117                      }else {
 118                          return false;
 119                      }
 120                  }
 121 
 122              }
 123          }
 124         return false;
 125 
 126      }
 127     /**
 128      *  05 请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
 129      */
 130     public static  String replaceSpace(String s) {
 131         return s.replaceAll(" ","%20");
 132     }
 133 
 134     public static class ListNode {
 135         int val;
 136         ListNode next;
 137         ListNode(int x) {
 138             val = x;
 139         }
 140     }
 141 
 142     /**
 143      * 06. 从尾到头打印链表
 144      * [数据结构]
 145      */
 146     public static int[] reversePrint(ListNode head) {
 147         Stack<ListNode> stack = new Stack<ListNode>();
 148         ListNode temp = head;
 149         while (temp != null) {
 150             stack.push(temp);
 151             temp = temp.next;
 152         }
 153         int size = stack.size();
 154         int[] print = new int[size];
 155         for (int i = 0; i < size; i++) {
 156             print[i] = stack.pop().val;
 157         }
 158         return print;
 159 
 160     }
 161      /**
 162       * 剑指 Offer 09. 用两个栈实现队列
 163       * 用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
 164       */
 165      /*static class CQueue {
 166          private Queue<Integer>  ss;
 167          public CQueue() {
 168             this.ss =new ConcurrentLinkedDeque<Integer>();
 169          }
 170          public void appendTail(int value) {
 171              ss.add(value);
 172          }
 173          public int deleteHead() {
 174              if(ss!=null&&ss.size()>0){
 175                  int s= ss.peek();
 176                  ss.remove();
 177                  return s;
 178              }else{
 179                  return -1;
 180              }
 181          }
 182      }*/
 183 
 184      /**
 185       * 剑指 Offer 09. 用两个栈实现队列
 186       * [数据结构]
 187       **/
 188     static class CQueue {
 189          Stack<Integer> stack1;
 190          Stack<Integer> stack2;
 191 
 192          public CQueue() {
 193              stack1 = new Stack<Integer>();
 194              stack2 = new Stack<Integer>();
 195          }
 196 
 197          public void appendTail(int value) {
 198              stack1.push(value);
 199          }
 200 
 201          public int deleteHead() {
 202              // 如果第二个栈为空
 203              if (stack2.isEmpty()) {
 204                  while (!stack1.isEmpty()) {
 205                      stack2.push(stack1.pop());
 206                  }
 207              }
 208              if (stack2.isEmpty()) {
 209                  return -1;
 210              } else {
 211                  int deleteItem = stack2.pop();
 212                  return deleteItem;
 213              }
 214          }
 215     }
 216 
 217 
 218     /**
 219      *  10-1 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))
 220      * 斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
 221      * 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
 222      */
 223     public static int fib(int n) {
 224         int before =1 ,after =1;
 225         int result=0;
 226         if(n==0){
 227             return 0;
 228         }
 229         if(n<=2){
 230             return 1;
 231         }
 232         for(int i=3;i<=n;i++){
 233             result = (before+after)% 1000000007;
 234             before = after;
 235             after  = result;
 236 
 237         }
 238         return result;
 239     }
 240 
 241     /**
 242      *  10-2 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
 243      * 斐波那契额数组
 244      */
 245         public static int numWays1(int n) {
 246         int a = 1, b = 1, sum;
 247         for(int i = 0; i < n; i++){
 248             sum = (a + b) % 1000000007;
 249             a = b;
 250             b = sum;
 251         }
 252         return a;
 253     }
 254 
 255     /**
 256      * 10-2 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
 257      * [排列组合]
 258      */
 259     public static int numWays2(int n) {
 260         int max2 = n/2;
 261         int result=0;
 262         if(n<=1){
 263             return 1;
 264         }
 265         // i为2的个数
 266          for (int count2 =0;count2<=max2;count2++){
 267              int re = 0;
 268              // y为1的个数
 269              int count1= n-2*count2;
 270              //总步数
 271             int   count = count2+count1;
 272             if(count2==0||count1==0){
 273                 re++;
 274             }else {
 275                 re=1;
 276                 //count个 1 2 拍序结果  其中2的个数count2,1的个数count1  计算公式C(count2,count)
 277                 BigDecimal  fenzi =BigDecimal.ONE;
 278                 BigDecimal fengmu = BigDecimal.ONE;
 279                 int countRun = count2>count1?count1:count2;
 280                 for (int x = countRun; x >0; x--) {
 281                     fengmu=fengmu.multiply(BigDecimal.valueOf(x));
 282                 }
 283                 for (int x = 0; x < countRun; x++) {
 284                     fenzi = fenzi .multiply (BigDecimal.valueOf(count - x));
 285                 }
 286                 re =  fenzi.divide(fengmu).intValue() % 1000000007  ;
 287             }
 288             System.out.println("当2的数量为"+count2+",1的数量为:"+count1+"。结果有:"+re);
 289              result = (result +re)% 1000000007 ;
 290          }
 291         return result ;
 292     }
 293 
 294     /**
 295      *  11. 旋转数组的最小数字
 296      *  把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。 
 297      * [二分法][SR]
 298      */
 299     public static int minArray(int[] numbers) {
 300         int low = 0;
 301         int high = numbers.length - 1;
 302         while (low < high) {
 303             int pivot = low + (high - low) / 2;
 304             if (numbers[pivot] < numbers[high]) {
 305                 high = pivot;
 306             } else if (numbers[pivot] > numbers[high]) {
 307                 low = pivot + 1;
 308             } else {
 309                 high -= 1;
 310             }
 311         }
 312         return numbers[low];
 313 
 314     }
 315 
 316 
 317     /**
 318      * 12. 矩阵中的路径
 319      * 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
 320      * 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
 321      * [深度优先搜索(DFS)+ 剪枝][递归][SSR]
 322      */
 323     public static boolean exist(char[][] board, String word) {
 324         char[] words = word.toCharArray();
 325         for(int i = 0; i < board.length; i++) {
 326             for(int j = 0; j < board[0].length; j++) {
 327                 if(dfs(board, words, i, j, 0)) {
 328                     return true;
 329                 }
 330             }
 331         }
 332         return false;
 333     }
 334 
 335     static boolean  dfs(char[][] board, char[] word, int i, int j, int k) {
 336         if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]){
 337             return false;
 338         }
 339         if(k == word.length - 1){
 340             return true;
 341         }
 342         board[i][j] = '\0';
 343         boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
 344                 dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
 345         //回溯 遍历失败赋值回去重新遍历
 346         board[i][j] = word[k];
 347         return res;
 348     }
 349 
 350     /**
 351      * 12. 矩阵中的路径
 352      * 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
 353      * 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
 354      * [深度优先搜索(DFS)+ 回溯][递归][SSR]
 355      */
 356     public static boolean exist1(char[][] board, String word) {
 357         if (board == null || board.length == 0 || board[0].length == 0) {
 358             return false;
 359         }
 360 
 361         char[] chars = word.toCharArray();
 362         boolean[][] visited = new boolean[board.length][board[0].length];
 363         for (int i = 0; i < board.length; i++) {
 364             for (int j = 0; j < board[0].length; j++) {
 365                 // 从 (0, 0) 点开始进行 dfs 操作,不断地去找,
 366                 // 如果以 (0, 0) 点没有对应的路径的话,那么就从 (0, 1) 点开始去找
 367                 if (dfs(board, chars, visited, i, j, 0)) {
 368                     return true;
 369                 }
 370             }
 371         }
 372         return false;
 373     }
 374 
 375     private static boolean dfs(char[][] board, char[] chars, boolean[][] visited, int i, int j, int start) {
 376         if (i < 0 || i >= board.length || j < 0 || j >= board[0].length
 377                 || chars[start] != board[i][j] || visited[i][j]) {
 378             return false;
 379         }
 380         if (start == chars.length - 1) {
 381             return true;
 382         }
 383         visited[i][j] = true;
 384         boolean ans = false;
 385         ans = dfs(board, chars, visited, i + 1, j, start + 1)
 386                 || dfs(board, chars, visited, i - 1, j, start + 1)
 387                 || dfs(board, chars, visited, i, j + 1, start + 1)
 388                 || dfs(board, chars, visited, i, j - 1, start + 1);
 389         visited[i][j] = false;
 390         return ans;
 391     }
 392 
 393 
 394 
 395     /**
 396      * 13. 机器人的运动范围
 397      * 地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
 398      *[广度优先搜索BFS][SSR]
 399      */
 400     public static   int movingCount1(int m, int n, int k) {
 401         if (k == 0) {
 402             return 1;
 403         }
 404         Queue<int[]> queue = new LinkedList<int[]>();
 405         // 向右和向下的方向数组
 406         int[] dx = {0, 1};
 407         int[] dy = {1, 0};
 408         boolean[][] vis = new boolean[m][n];
 409         queue.offer(new int[]{0, 0});
 410         vis[0][0] = true;
 411         int ans = 1;
 412         while (!queue.isEmpty()) {
 413             int[] cell = queue.poll();
 414             int x = cell[0], y = cell[1];
 415             for (int i = 0; i < 2; ++i) {
 416                 int tx = dx[i] + x;
 417                 int ty = dy[i] + y;
 418                 if (tx < 0 || tx >= m || ty < 0 || ty >= n || vis[tx][ty] || get(tx) + get(ty) > k) {
 419                     continue;
 420                 }
 421                 queue.offer(new int[]{tx, ty});
 422                 vis[tx][ty] = true;
 423                 ans++;
 424             }
 425         }
 426         return ans;
 427     }
 428 
 429     /**
 430      * 13. 机器人的运动范围
 431      * 地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
 432      *[递推]
 433      */
 434     public static int movingCount(int m, int n, int k) {
 435             if (k == 0) {
 436                 return 1;
 437             }
 438             boolean[][] vis = new boolean[m][n];
 439             int ans = 1;
 440             vis[0][0] = true;
 441             for (int i = 0; i < m; ++i) {
 442                 for (int j = 0; j < n; ++j) {
 443                     if ((i == 0 && j == 0) || get(i) + get(j) > k) {
 444                         continue;
 445                     }
 446                     // 边界判断
 447                     if (i - 1 >= 0) {
 448                         vis[i][j] |= vis[i - 1][j];
 449                     }
 450                     if (j - 1 >= 0) {
 451                         vis[i][j] |= vis[i][j - 1];
 452                     }
 453                     ans += vis[i][j] ? 1 : 0;
 454                 }
 455             }
 456             return ans;
 457         }
 458 
 459         private static int get(int x) {
 460             int res = 0;
 461             while (x != 0) {
 462                 res += x % 10;
 463                 x /= 10;
 464             }
 465             return res;
 466         }
 467 
 468     /**
 469      * 13. 机器人的运动范围
 470      * 地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
 471      *[深度优先][递归搜索][SSR]
 472      */
 473     int m, n, k;
 474     boolean[][] visited;
 475     public int movingCount3(int m, int n, int k) {
 476         this.m = m; this.n = n; this.k = k;
 477         this.visited = new boolean[m][n];
 478         return dfs(0, 0, 0, 0);
 479     }
 480     public int dfs(int i, int j, int si, int sj) {
 481         if(i >= m || j >= n || k < si + sj || visited[i][j]){
 482             return 0;
 483         }
 484         visited[i][j] = true;
 485         return 1 + dfs(i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj) + dfs(i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8);
 486     }
 487 
 488     /**
 489      * 13. 机器人的运动范围
 490      * 地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
 491      *[深度优先][递归搜索][SSR]
 492      */
 493 
 494     public int movingCount4(int m, int n, int k) {
 495         this.m = m; this.n = n; this.k = k;
 496         this.visited = new boolean[m][n];
 497         return dfs2(0, 0, k);
 498     }
 499     public int dfs2(int i, int j, int k) {
 500         if(i >= m || j >= n ||get(i) + get(j) > k || visited[i][j]){
 501             return 0;
 502         }
 503         visited[i][j] = true;
 504         return 1 + dfs2(i + 1, j, k) + dfs2(i, j + 1, k);
 505     }
 506 
 507 
 508     /**
 509      * 剑指 Offer 14- I. 剪绳子
 510      * 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
 511      *推论一: 将绳子 以相等的长度等分为多段 ,得到的乘积最大。
 512      * 推论二: 尽可能将绳子以长度 33 等分为多段时,乘积最大。
 513      * 推论二: 若切分方案合理,绳子段切分的越多,乘积越大。
 514      * 推论三: 为使乘积最大,只有长度为 22 和 33 的绳子不应再切分,且 33 比 22 更优 (详情见下表) 。
 515      * [数学推导]
 516      */
 517     public static int cuttingRope(int n) {
 518         if(n <= 3) {return n - 1;}
 519         int a = n / 3, b = n % 3;
 520         if(b == 0) {return (int)Math.pow(3, a);}
 521         if(b == 1) {return (int)Math.pow(3, a - 1) * 4;}
 522         return (int)Math.pow(3, a) * 2;
 523     }
 524 
 525     /**
 526      * 剑指 Offer 14- I. 剪绳子
 527      * 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
 528      * [动态规划][SSR]
 529      * 思路一:动态规划
 530      * 我们想要求长度为n的绳子剪掉后的最大乘积,可以从前面比n小的绳子转移而来
 531      * 用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积,也就是dp[i]表示长度为i的绳子剪成m段后的最大乘积,初始化dp[2] = 1
 532      * 我们先把绳子剪掉第一段(长度为j),如果只剪掉长度为1,对最后的乘积无任何增益,所以从长度为2开始剪
 533      * 剪了第一段后,剩下(i - j)长度可以剪也可以不剪。如果不剪的话长度乘积即为j * (i - j);如果剪的话长度乘积即为j * dp[i - j]。取两者最大值max(j * (i - j), j * dp[i - j])
 534      * 第一段长度j可以取的区间为[2,i),对所有j不同的情况取最大值,因此最终dp[i]的转移方程为
 535      * dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]))
 536      * 最后返回dp[n]即可
 537     */
 538      public static int cuttingRope2(int n) {
 539          int[] dp = new int[n + 1];
 540          dp[2] = 1;
 541          for(int i = 3; i < n + 1; i++){//长度为i时,求最大乘机放入dp
 542              for(int j = 2; j < i; j++){
 543                  dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
 544                  System.out.println( Arrays.toString(dp));
 545              }
 546          }
 547          return dp[n];
 548     }
 549 
 550 
 551 
 552     /**
 553      *     思路二:贪心
 554      *     核心思路是:尽可能把绳子分成长度为3的小段,这样乘积最大
 555      */
 556     public static int cuttingRope3(int n) {
 557         if(n < 4){
 558             return n - 1;
 559         }
 560         int res = 1;
 561         while(n > 4){
 562             res *= 3;
 563             n -= 3;
 564         }
 565         return res * n;
 566     }
 567 
 568     /**
 569      * 剑指 Offer 15. 二进制中1的个数
 570      * 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为 汉明重量).)。
 571      * [位运算][与][移位][二进制]
 572      */
 573     public static int hammingWeight(int n) {
 574         int res = 0;
 575         while(n != 0) {
 576             res += n & 1;
 577             n >>>= 1;
 578         }
 579         return res;
 580     }
 581     public static int hammingWeight2(int n) {
 582         int res = 0;
 583         while(n != 0) {
 584             res++;
 585             n &= n - 1;
 586         }
 587         return res;
 588     }
 589 
 590     /**
 591      * 剑指 Offer 16. 数值的整数次方
 592      * 实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
 593      *[位运算][二进制]快速幂解析(二进制角度)
 594      */
 595     public static double myPow(double x, int n) {
 596         if(x == 0) {return 0;}
 597         long b = n;
 598         double res = 1.0;
 599         if(b < 0) {
 600             x = 1 / x;
 601             b = -b;
 602         }
 603         while(b > 0) {
 604             if((b & 1) == 1) {
 605                 res *= x;
 606             }
 607             x *= x;
 608             b >>= 1;
 609             System.out.println("res:"+res+"-----------x:"+x+"------b:"+b);
 610         }
 611         return res;
 612     }
 613 
 614     /**
 615      *剑指 Offer 17. 打印从1到最大的n位数
 616      * 输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
 617      */
 618     public static int[] printNumbers(int n) {
 619         int end = (int)Math.pow(10, n) - 1;
 620         int[] res = new int[end];
 621         for(int i = 0; i < end; i++)
 622         {  res[i] = i + 1;}
 623         return res;
 624     }
 625     /**
 626      *剑指 Offer 17. 打印从1到最大的n位数
 627      * 递归生成全排列[递归][SSR]
 628      */
 629     static   StringBuilder res;
 630    static   int count = 0, n1=0;
 631     static  char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
 632     public static String printNumbers1(int n) {
 633         n1 = n;
 634         res = new StringBuilder(); // 数字字符串集
 635         num = new char[n]; // 定义长度为 n 的字符列表
 636         dfs(0); // 开启全排列递归
 637         res.deleteCharAt(res.length() - 1); // 删除最后多余的逗号
 638         return res.toString(); // 转化为字符串并返回
 639     }
 640     static void  dfs(int x) {
 641         if(x == n1) { // 终止条件:已固定完所有位
 642             res.append(String.valueOf(num) + ","); // 拼接 num 并添加至 res 尾部,使用逗号隔开
 643             return;
 644         }
 645         for(char i : loop) { // 遍历 ‘0‘ - ’9‘
 646             num[x] = i; // 固定第 x 位为 i
 647             dfs(x + 1); // 开启固定第 x + 1 位
 648         }
 649     }
 650     /**
 651      * 递归生成全排列
 652      */
 653     static  int nine = 0, start;
 654     public static String printNumbers2(int n) {
 655         n1 = n;
 656         res = new StringBuilder();
 657         num = new char[n];
 658         start = n - 1;
 659         dfs1(0);
 660         res.deleteCharAt(res.length() - 1);
 661         return res.toString();
 662     }
 663     static void dfs1(int x) {
 664         if(x == n1) {
 665             String s = String.valueOf(num).substring(start);
 666             if(!s.equals("0")){ res.append(s + ",");}
 667             if(n1 - start == nine){ start--;}
 668             return;
 669         }
 670         for(char i : loop) {
 671             if(i == '9') {nine++;}
 672             num[x] = i;
 673             dfs(x + 1);
 674         }
 675         nine--;
 676     }
 677 
 678 
 679     /**
 680      * 剑指 Offer 18. 删除链表的节点
 681      *给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
 682      * 返回删除后的链表的头节点。
 683      */
 684     public static ListNode deleteNode(ListNode head, int val) {
 685         if(head.val == val) {return head.next;}
 686         ListNode pre = head, cur = head.next;
 687         while(cur != null && cur.val != val) {
 688             pre = cur;
 689             cur = cur.next;
 690         }
 691         if(cur != null) {pre.next = cur.next;}
 692         return head;
 693 
 694     }
 695 
 696 
 697 
 698     /**
 699      *  剑指 Offer 20. 表示数值的字符串
 700      *     请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
 701      * [自动机][SSR]
 702      */
 703     public static boolean isNumber(String s) {
 704 
 705         Map<State, Map<CharType, State>> transfer = new HashMap<State, Map<CharType, State>>();
 706         Map<CharType, State> initialMap = new HashMap<CharType, State>() {{
 707             put(CharType.CHAR_SPACE, State.STATE_INITIAL);
 708             put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
 709             put(CharType.CHAR_POINT, State.STATE_POINT_WITHOUT_INT);
 710             put(CharType.CHAR_SIGN, State.STATE_INT_SIGN);
 711         }};
 712         transfer.put(State.STATE_INITIAL, initialMap);
 713         Map<CharType, State> intSignMap = new HashMap<CharType, State>() {{
 714             put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
 715             put(CharType.CHAR_POINT, State.STATE_POINT_WITHOUT_INT);
 716         }};
 717         transfer.put(State.STATE_INT_SIGN, intSignMap);
 718         Map<CharType, State> integerMap = new HashMap<CharType, State>() {{
 719             put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
 720             put(CharType.CHAR_EXP, State.STATE_EXP);
 721             put(CharType.CHAR_POINT, State.STATE_POINT);
 722             put(CharType.CHAR_SPACE, State.STATE_END);
 723         }};
 724         transfer.put(State.STATE_INTEGER, integerMap);
 725         Map<CharType, State> pointMap = new HashMap<CharType, State>() {{
 726             put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
 727             put(CharType.CHAR_EXP, State.STATE_EXP);
 728             put(CharType.CHAR_SPACE, State.STATE_END);
 729         }};
 730         transfer.put(State.STATE_POINT, pointMap);
 731         Map<CharType, State> pointWithoutIntMap = new HashMap<CharType, State>() {{
 732             put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
 733         }};
 734         transfer.put(State.STATE_POINT_WITHOUT_INT, pointWithoutIntMap);
 735         Map<CharType, State> fractionMap = new HashMap<CharType, State>() {{
 736             put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
 737             put(CharType.CHAR_EXP, State.STATE_EXP);
 738             put(CharType.CHAR_SPACE, State.STATE_END);
 739         }};
 740         transfer.put(State.STATE_FRACTION, fractionMap);
 741         Map<CharType, State> expMap = new HashMap<CharType, State>() {{
 742             put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
 743             put(CharType.CHAR_SIGN, State.STATE_EXP_SIGN);
 744         }};
 745         transfer.put(State.STATE_EXP, expMap);
 746         Map<CharType, State> expSignMap = new HashMap<CharType, State>() {{
 747             put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
 748         }};
 749         transfer.put(State.STATE_EXP_SIGN, expSignMap);
 750         Map<CharType, State> expNumberMap = new HashMap<CharType, State>() {{
 751             put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
 752             put(CharType.CHAR_SPACE, State.STATE_END);
 753         }};
 754         transfer.put(State.STATE_EXP_NUMBER, expNumberMap);
 755         Map<CharType, State> endMap = new HashMap<CharType, State>() {{
 756             put(CharType.CHAR_SPACE, State.STATE_END);
 757         }};
 758         transfer.put(State.STATE_END, endMap);
 759 
 760         int length = s.length();
 761         State state = State.STATE_INITIAL;
 762 
 763         for (int i = 0; i < length; i++) {
 764             CharType type = toCharType(s.charAt(i));
 765             if (!transfer.get(state).containsKey(type)) {
 766                 return false;
 767             } else {
 768                 state = transfer.get(state).get(type);
 769             }
 770         }
 771         return state == State.STATE_INTEGER || state == State.STATE_POINT || state == State.STATE_FRACTION || state == State.STATE_EXP_NUMBER || state == State.STATE_END;
 772     }
 773 
 774     public static CharType toCharType(char ch) {
 775         if (ch >= '0' && ch <= '9') {
 776             return CharType.CHAR_NUMBER;
 777         } else if (ch == 'e' || ch == 'E') {
 778             return CharType.CHAR_EXP;
 779         } else if (ch == '.') {
 780             return CharType.CHAR_POINT;
 781         } else if (ch == '+' || ch == '-') {
 782             return CharType.CHAR_SIGN;
 783         } else if (ch == ' ') {
 784             return CharType.CHAR_SPACE;
 785         } else {
 786             return CharType.CHAR_ILLEGAL;
 787         }
 788     }
 789 
 790     enum State {
 791         STATE_INITIAL,
 792         STATE_INT_SIGN,
 793         STATE_INTEGER,
 794         STATE_POINT,
 795         STATE_POINT_WITHOUT_INT,
 796         STATE_FRACTION,
 797         STATE_EXP,
 798         STATE_EXP_SIGN,
 799         STATE_EXP_NUMBER,
 800         STATE_END
 801     }
 802 
 803     enum CharType {
 804         CHAR_NUMBER,
 805         CHAR_EXP,
 806         CHAR_POINT,
 807         CHAR_SIGN,
 808         CHAR_SPACE,
 809         CHAR_ILLEGAL
 810     }
 811 
 812     /**
 813      * 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
 814      * 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
 815      */
 816     public static int[] exchange(int[] nums) {
 817         int n = nums.length;
 818         int[] res = new int[n];
 819         int index = 0;
 820         for(int num : nums){
 821             if(num % 2 == 1){
 822                 res[index++] = num;
 823             }
 824         }
 825         for(int num : nums){
 826             if(num % 2 == 0){
 827                 res[index++] = num;
 828             }
 829         }
 830         return res;
 831     }
 832 
 833     /**
 834      * 头尾双指针[SR]
 835      */
 836     public int[] exchange1(int[] nums) {
 837         int left = 0, right = nums.length - 1;
 838         while(left <= right){
 839             while(left <= right && nums[left] % 2 == 1)
 840             {left++;}
 841             while(left <= right && nums[right] % 2 == 0)
 842             {right--;}
 843             if(left > right)
 844             {        break;}
 845             int tmp = nums[left];
 846             nums[left] = nums[right];
 847             nums[right] = tmp;
 848         }
 849         return nums;
 850     }
 851 
 852     /**
 853      *剑指 Offer 22. 链表中倒数第k个节点
 854      * 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
 855      */
 856     public static ListNode getKthFromEnd(ListNode head, int k) {
 857         HashMap<Integer,ListNode>   headMap = new HashMap<Integer, ListNode>();
 858         ListNode  cur  = head;
 859         Integer key = 1;
 860         while (cur !=null){
 861             headMap.put(key,cur);
 862             key++;
 863             cur = cur.next;
 864         }
 865         return headMap.get(key-k);
 866     }
 867 
 868     /**
 869      *剑指 Offer 22. 链表中倒数第k个节点
 870      * 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
 871      *[双指针]
 872      */
 873     public static ListNode getKthFromEnd1(ListNode head, int k) {
 874         // 初始化都指向链表头
 875         ListNode cur = head;
 876         ListNode target = head;
 877         // cur指针先前进 k-1 次
 878         for (int i = 0; i < k - 1; i++) {
 879             cur = cur.next;
 880         }
 881         // 当 cur 指针指向链表尾时,target 指针指向倒数第 k 个节点
 882         while (cur.next != null) {
 883             cur = cur.next;
 884             target = target.next;
 885         }
 886         return target;
 887     }
 888 
 889 
 890     /**
 891      * 剑指 Offer 24. 反转链表
 892      * 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
 893      *[数据结构][链表][SSR]
 894      */
 895     public static ListNode reverseList(ListNode head){
 896         ListNode prev = null;
 897         ListNode curr = head;
 898         while (curr != null) {
 899             //记录下一个等待指针遍历的位置
 900             ListNode next = curr.next;
 901             //将指针遍历到的节点拼接到 新的结果链表的前面
 902             curr.next = prev;
 903             //存放新的结果链表
 904             prev = curr;
 905             //指针后移
 906             curr = next;
 907         }
 908         return prev;
 909     }
 910 
 911     /**
 912      * [递归][SSR]
 913      */
 914     public static ListNode reverseList2(ListNode head) {
 915         if (head == null || head.next == null) {
 916             return head;
 917         }
 918         ListNode newHead = reverseList2(head.next);
 919         //赋值的过程都是先放入方法栈中,最后head为倒数第一个时开始赋值
 920         head.next.next = head;
 921         //防止链表循环
 922         head.next = null;
 923         return newHead;
 924     }
 925 
 926     /**
 927      *  剑指 Offer 25. 合并两个排序的链表
 928      * 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
 929      * [数据结构][链表][迭代]
 930      */
 931     public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
 932         ListNode dum = new ListNode(0), cur = dum;
 933         while(l1 != null && l2 != null) {
 934             if(l1.val < l2.val) {
 935                 cur.next = l1;
 936                 l1 = l1.next;
 937             }
 938             else {
 939                 cur.next = l2;
 940                 l2 = l2.next;
 941             }
 942             cur = cur.next;
 943         }
 944         cur.next = l1 != null ? l1 : l2;
 945         return dum.next;
 946     }
 947 
 948     /**
 949      *  剑指 Offer 25. 合并两个排序的链表
 950      * 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
 951      *[递归][SSR]
 952      */
 953     public static ListNode mergeTwoLists2(ListNode l1, ListNode l2) {
 954         if(l1 == null || l2 == null){
 955             return l1 != null ? l1 : l2;
 956         }
 957         if(l1.val <= l2.val){
 958             l1.next = mergeTwoLists(l1.next, l2);
 959             return l1;
 960         }else{
 961             l2.next = mergeTwoLists(l1, l2.next);
 962             return l2;
 963         }
 964     }
 965 
 966     static class TreeNode {
 967           int val;
 968           TreeNode left;
 969           TreeNode right;
 970           TreeNode(int x) { val = x; }
 971       }
 972 
 973     /**
 974      * 剑指 Offer 26. 树的子结构
 975      * 输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
 976      * B是A的子结构, 即 A中有出现和B相同的结构和节点值。
 977      *[对称性递归][二叉树]
 978      */
 979     public static boolean isSubStructure(TreeNode A, TreeNode B) {
 980         return (A != null && B != null) && (recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B));
 981     }
 982    static boolean recur(TreeNode A, TreeNode B) {
 983         if(B == null) {return true;}
 984         if(A == null || A.val != B.val) {return false;}
 985         return recur(A.left, B.left) && recur(A.right, B.right);
 986     }
 987 
 988     /**
 989      * 剑指 Offer 27. 二叉树的镜像
 990      * 请完成一个函数,输入一个二叉树,该函数输出它的镜像。
 991      * [递归][二叉树]
 992      */
 993     public static TreeNode mirrorTree(TreeNode root) {
 994         if (root == null) {
 995             return null;
 996         }
 997         TreeNode left = mirrorTree(root.left);
 998         TreeNode right = mirrorTree(root.right);
 999         root.left = right;
1000         root.right = left;
1001         return root;
1002     }
1003 
1004     /**
1005      * 剑指 Offer 28. 对称的二叉树
1006      * 请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
1007      * [递归][二叉树]
1008      */
1009     public static   boolean isSymmetric(TreeNode root) {
1010         return root == null ? true : recur1(root.left, root.right);
1011     }
1012 
1013     static  boolean recur1(TreeNode L, TreeNode R) {
1014         if (L == null && R == null) {
1015             return true;
1016         }
1017         if (L == null || R == null || L.val != R.val) {
1018             return false;
1019         }
1020         return recur1(L.left, R.right) && recur(L.right, R.left);
1021     }
1022 
1023 
1024 
1025     /**
1026      * 剑指 Offer 29. 顺时针打印矩阵
1027      * 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
1028      */
1029     public static int[] spiralOrder(int[][] matrix) {
1030         if(matrix.length == 0) {return new int[0];}
1031         int l = 0, //游标
1032                 r = matrix[0].length - 1, //数组列数
1033                 t = 0, //游标
1034                 b = matrix.length - 1, //数组排数
1035                 x = 0;//返回数值下标
1036         int[] res = new int[(r + 1) * (b + 1)];//返回值
1037         while(true) {
1038             for(int i = l; i <= r; i++) {res[x++] = matrix[t][i]; }// 从左到右
1039             if(++t > b) {break;}//防止重复遍历排
1040             for(int i = t; i <= b; i++) {res[x++] = matrix[i][r];} // top to bottom.
1041             if(l > --r) {break;}//防止重复遍历列
1042             for(int i = r; i >= l; i--) {res[x++] = matrix[b][i];} // right to left.
1043             if(t > --b) {break;}//防止重复遍历排
1044             for(int i = b; i >= t; i--) {res[x++] = matrix[i][l];} // bottom to top.
1045             if(++l > r) {break;}//防止重复遍历列
1046         }
1047         return res;
1048 
1049     }
1050 
1051     /**
1052      * 按层遍历
1053      * [思维]
1054      */
1055     public int[] spiralOrder2(int[][] matrix) {
1056         if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
1057             return new int[0];
1058         }
1059         int rows = matrix.length, columns = matrix[0].length;
1060         int[] order = new int[rows * columns];
1061         int index = 0;
1062         int left = 0, right = columns - 1, top = 0, bottom = rows - 1;
1063         while (left <= right && top <= bottom) {
1064             for (int column = left; column <= right; column++) {
1065                 order[index++] = matrix[top][column];
1066             }
1067             for (int row = top + 1; row <= bottom; row++) {
1068                 order[index++] = matrix[row][right];
1069             }
1070             if (left < right && top < bottom) {
1071                 for (int column = right - 1; column > left; column--) {
1072                     order[index++] = matrix[bottom][column];
1073                 }
1074                 for (int row = bottom; row > top; row--) {
1075                     order[index++] = matrix[row][left];
1076                 }
1077             }
1078             left++;
1079             right--;
1080             top++;
1081             bottom--;
1082         }
1083         return order;
1084     }
1085 
1086     /**
1087      * 剑指 Offer 30. 包含min函数的栈
1088      * 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
1089      */
1090     class MinStack {
1091         Stack<Integer>  stack = new Stack<>();
1092         Stack<Integer>  source = new Stack<>();
1093         /** initialize your data structure here. */
1094         public MinStack() {
1095             stack = new Stack<>();
1096             source = new Stack<>();
1097         }
1098         public void push(int x) {
1099             this.source.push(x);
1100             if(stack.empty() || stack.peek()>x){
1101                 stack.push(x);
1102             }
1103         }
1104         public void pop() {
1105             if(this.source.pop().equals( stack.peek())){
1106                 stack.pop();
1107             }
1108 
1109         }
1110         public int top() {
1111             return source.peek();
1112         }
1113         public int min() {
1114             return stack.peek();
1115         }
1116     }
1117 
1118 
1119     /**
1120      * 剑指 Offer 31. 栈的压入、弹出序列
1121      * 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
1122      *
1123      */
1124     public static boolean validateStackSequences(int[] pushed, int[] popped) {
1125         Stack<Integer> stack = new Stack<>();
1126         int i = 0;
1127         for(int num : pushed) {
1128             stack.push(num); // num 入栈
1129             while(!stack.isEmpty() && stack.peek() == popped[i]) { // 循环判断与出栈
1130                 stack.pop();
1131                 i++;
1132             }
1133         }
1134         return stack.isEmpty();
1135 
1136     }
1137 
1138     /**
1139      * 剑指 Offer 32 - I. 从上到下打印二叉树
1140      * 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
1141      * [BFS][广度优先搜索]
1142      */
1143     public int[] levelOrder(TreeNode root) {
1144         if(root == null){ return new int[0];}
1145         Queue<TreeNode> queue = new LinkedList<TreeNode>(){{ add(root); }};
1146         ArrayList<Integer> ans = new ArrayList<>();
1147         while(!queue.isEmpty()) {
1148             TreeNode node = queue.poll();
1149             ans.add(node.val);
1150             if(node.left != null) {queue.add(node.left);}
1151             if(node.right != null) {queue.add(node.right);}
1152         }
1153         int[] res = new int[ans.size()];
1154         for(int i = 0; i < ans.size(); i++)
1155         { res[i] = ans.get(i);}
1156         return res;
1157     }
1158 
1159     /**
1160      * 剑指 Offer 33 - I. 从上到下打印二叉树2
1161      * 从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
1162      *
1163      */
1164     public List<List<Integer>> levelOrder2(TreeNode root) {
1165         LinkedList<TreeNode> queue = new LinkedList<>();
1166         List<List<Integer>> res = new ArrayList<>();
1167         if(root != null) {queue.add(root);}
1168         while(!queue.isEmpty()) {
1169             List<Integer> tmp = new ArrayList<>();
1170             for(int i = queue.size(); i > 0; i--) {
1171                 TreeNode node = queue.poll();
1172                 tmp.add(node.val);
1173                 if(node.left != null) {queue.add(node.left);}
1174                 if(node.right != null) {queue.add(node.right);}
1175             }
1176             res.add(tmp);
1177         }
1178         return res;
1179     }
1180     /**
1181      * 剑指 Offer 32 - III. 从上到下打印二叉树 III
1182      *请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
1183      *[队列][数据结构][SR][二叉树]
1184      */
1185     public List<List<Integer>> levelOrder3(TreeNode root) {
1186         Queue<TreeNode> queue = new LinkedList<>();
1187         List<List<Integer>> res = new ArrayList<>();
1188         if(root != null) {queue.add(root);}
1189         while(!queue.isEmpty()) {
1190             LinkedList<Integer> tmp = new LinkedList<>();
1191             for(int i = queue.size(); i > 0; i--) {
1192                 TreeNode node = queue.poll();
1193                 if(res.size() % 2 == 0) {tmp.addLast(node.val); }// 偶数层 -> 队列头部
1194                 else {tmp.addFirst(node.val);} // 奇数层 -> 队列尾部
1195                 if(node.left != null) {queue.add(node.left);}
1196                 if(node.right != null) {queue.add(node.right);}
1197             }
1198             res.add(tmp);
1199         }
1200         return res;
1201     }
1202 
1203     /**
1204      * 剑指 Offer 33. 二叉搜索树的后序遍历序列
1205      * 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。
1206      * 假设输入的数组的任意两个数字都互不相同。
1207      *
1208      * 解析:
1209      * 根据二叉搜索树的定义,可以通过递归,判断所有子树的 正确性 (即其后序遍历是否满足二叉搜索树的定义) ,若所有子树都正确,则此序列为二叉搜索树的后序遍历。
1210      *
1211      * 终止条件: 当 i \geq ji≥j ,说明此子树节点数量 \leq 1≤1 ,无需判别正确性,因此直接返回 truetrue ;
1212      * 递推工作:
1213      * 划分左右子树: 遍历后序遍历的 [i, j][i,j] 区间元素,寻找 第一个大于根节点 的节点,索引记为 mm 。此时,可划分出左子树区间 [i,m-1][i,m−1] 、右子树区间 [m, j - 1][m,j−1] 、根节点索引 jj 。
1214      * 判断是否为二叉搜索树:
1215      * 左子树区间 [i, m - 1][i,m−1] 内的所有节点都应 << postorder[j]postorder[j] 。而第 1.划分左右子树 步骤已经保证左子树区间的正确性,因此只需要判断右子树区间即可。
1216      * 右子树区间 [m, j-1][m,j−1] 内的所有节点都应 >> postorder[j]postorder[j] 。实现方式为遍历,当遇到 \leq postorder[j]≤postorder[j] 的节点则跳出;则可通过 p = jp=j 判断是否为二叉搜索树。
1217      * 返回值: 所有子树都需正确才可判定正确,因此使用 与逻辑符 \&\&&& 连接。
1218      * p = jp=j : 判断 此树 是否正确。
1219      * recur(i, m - 1)recur(i,m−1) : 判断 此树的左子树 是否正确。
1220      * recur(m, j - 1)recur(m,j−1) : 判断 此树的右子树 是否正确。
1221      *
1222      * [递归分治][二叉树][SSR]
1223      */
1224     public boolean verifyPostorder(int[] postorder) {
1225         return verifyPostorderrecur(postorder, 0, postorder.length - 1);
1226     }
1227     boolean verifyPostorderrecur(int[] postorder, int i, int j) {
1228         if(i >= j) {return true;}
1229         int p = i;
1230         while(postorder[p] < postorder[j]) {p++;}
1231         int m = p;
1232         while(postorder[p] > postorder[j]) {p++;}
1233         return p == j && verifyPostorderrecur(postorder, i, m - 1) && verifyPostorderrecur(postorder, m, j - 1);
1234     }
1235 
1236     /**
1237      * 剑指 Offer 33. 二叉搜索树的后序遍历序列
1238      * 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。
1239      * 假设输入的数组的任意两个数字都互不相同。
1240      * [单调栈][SSR]
1241      */
1242     public boolean verifyPostorder2(int[] postorder) {
1243         Stack<Integer> stack = new Stack<>();
1244         int root = Integer.MAX_VALUE;
1245         for (int i = postorder.length - 1; i >= 0; i--) {
1246             if (postorder[i] > root){ return false;}
1247             while (!stack.isEmpty() && stack.peek() > postorder[i])
1248             {
1249                 root = stack.pop();
1250             }
1251             stack.add(postorder[i]);
1252         }
1253         return true;
1254     }
1255 
1256 
1257     List<List<Integer>> ret = new LinkedList<List<Integer>>();
1258     Deque<Integer> path = new LinkedList<Integer>();
1259 
1260     /**
1261      * 剑指 Offer 34. 二叉树中和为某一值的路径
1262      * 输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
1263      * [DFS][深度优先搜索][SSR][二叉树]
1264      */
1265     public List<List<Integer>> pathSum(TreeNode root, int target) {
1266         pathSumdfs(root, target);
1267         return ret;
1268     }
1269 
1270     public void pathSumdfs(TreeNode root, int target) {
1271         if (root == null) {
1272             return;
1273         }
1274         path.offerLast(root.val);
1275         target -= root.val;
1276         if (root.left == null && root.right == null && target == 0) {
1277             ret.add(new LinkedList<Integer>(path));
1278         }
1279         pathSumdfs(root.left, target);
1280         pathSumdfs(root.right, target);
1281         path.pollLast();
1282     }
1283     //存放子节点对应父节点的映射
1284     Map<TreeNode, TreeNode> map = new HashMap<TreeNode, TreeNode>();
1285 
1286     /**
1287      * 剑指 Offer 34. 二叉树中和为某一值的路径
1288      * [广度优先搜索][BFS][SSR][二叉树]
1289      */
1290     public List<List<Integer>> pathSum2(TreeNode root, int target) {
1291         if (root == null) {
1292             return ret;
1293         }
1294 
1295         //分别存放即将遍历的每个节点以及走到该节点时的合计值
1296         Queue<TreeNode> queueNode = new LinkedList<TreeNode>();
1297         Queue<Integer> queueSum = new LinkedList<Integer>();
1298         queueNode.offer(root);
1299         queueSum.offer(0);
1300 
1301         while (!queueNode.isEmpty()) {
1302             TreeNode node = queueNode.poll();
1303             int rec = queueSum.poll() + node.val;
1304 
1305             if (node.left == null && node.right == null) {
1306                 if (rec == target) {
1307                     getPath(node);
1308                 }
1309             } else {
1310                 if (node.left != null) {
1311                     map.put(node.left, node);
1312                     queueNode.offer(node.left);
1313                     queueSum.offer(rec);
1314                 }
1315                 if (node.right != null) {
1316                     map.put(node.right, node);
1317                     queueNode.offer(node.right);
1318                     queueSum.offer(rec);
1319                 }
1320             }
1321         }
1322 
1323         return ret;
1324     }
1325 
1326     public void getPath(TreeNode node) {
1327         List<Integer> temp = new LinkedList<Integer>();
1328         while (node != null) {
1329             temp.add(node.val);
1330             node = map.get(node);
1331         }
1332         Collections.reverse(temp);
1333         ret.add(new LinkedList<Integer>(temp));
1334     }
1335 
1336     //复杂链表
1337     class Node {
1338         int val;
1339         Node next;
1340         Node random;
1341 
1342         public Node(int val) {
1343             this.val = val;
1344             this.next = null;
1345             this.random = null;
1346         }
1347     }
1348 
1349     /**
1350      *  剑指 Offer 35. 复杂链表的复制
1351      *     请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
1352      *思路及算法
1353      *
1354      * 本题要求我们对一个特殊的链表进行深拷贝。如果是普通链表,我们可以直接按照遍历的顺序创建链表节点。而本题中因为随机指针的存在,当我们拷贝节点时,「当前节点的随机指针指向的节点」可能还没创建,因此我们需要变换思路。一个可行方案是,我们利用回溯的方式,让每个节点的拷贝操作相互独立。对于当前节点,我们首先要进行拷贝,然后我们进行「当前节点的后继节点」和「当前节点的随机指针指向的节点」拷贝,拷贝完成后将创建的新节点的指针返回,即可完成当前节点的两指针的赋值。
1355      *
1356      * 具体地,我们用哈希表记录每一个节点对应新节点的创建情况。遍历该链表的过程中,我们检查「当前节点的后继节点」和「当前节点的随机指针指向的节点」的创建情况。如果这两个节点中的任何一个节点的新节点没有被创建,我们都立刻递归地进行创建。当我们拷贝完成,回溯到当前层时,我们即可完成当前节点的指针赋值。注意一个节点可能被多个其他节点指向,因此我们可能递归地多次尝试拷贝某个节点,为了防止重复拷贝,我们需要首先检查当前节点是否被拷贝过,如果已经拷贝过,我们可以直接从哈希表中取出拷贝后的节点的指针并返回即可。
1357      *
1358      * 在实际代码中,我们需要特别判断给定节点为空节点的情况。
1359      *[递归][回溯][SSR][链表]
1360      */
1361     Map<Node, Node> cachedNode = new HashMap<Node, Node>();
1362     public Node copyRandomList(Node head) {
1363         if (head == null) {
1364             return null;
1365         }
1366         if (!cachedNode.containsKey(head)) {
1367             Node headNew = new Node(head.val);
1368             cachedNode.put(head, headNew);
1369             headNew.next = copyRandomList(head.next);
1370             headNew.random = copyRandomList(head.random);
1371         }
1372         return cachedNode.get(head);
1373 
1374     }
1375     //[迭代] + 节点拆分
1376     public Node copyRandomList2(Node head) {
1377         if (head == null) {
1378             return null;
1379         }
1380         for (Node node = head; node != null; node = node.next.next) {
1381             Node nodeNew = new Node(node.val);
1382             nodeNew.next = node.next;
1383             node.next = nodeNew;
1384         }
1385         for (Node node = head; node != null; node = node.next.next) {
1386             Node nodeNew = node.next;
1387             nodeNew.random = (node.random != null) ? node.random.next : null;
1388         }
1389         Node headNew = head.next;
1390         for (Node node = head; node != null; node = node.next) {
1391             Node nodeNew = node.next;
1392             node.next = node.next.next;
1393             nodeNew.next = (nodeNew.next != null) ? nodeNew.next.next : null;
1394         }
1395         return headNew;
1396     }
1397     class NodeList {
1398         public int val;
1399         public NodeList left;
1400         public NodeList right;
1401 
1402         public NodeList() {}
1403 
1404         public NodeList(int _val) {
1405             val = _val;
1406         }
1407 
1408         public NodeList(int _val,NodeList _left,NodeList _right) {
1409             val = _val;
1410             left = _left;
1411             right = _right;
1412         }
1413     };
1414     // 打印中序遍历
1415     void dfs中序遍历(NodeList root) {
1416         if(root == null) {return;}
1417         dfs中序遍历(root.left); //
1418         System.out.println(root.val); //
1419         dfs中序遍历(root.right); //
1420     }
1421 
1422     NodeList pre, head;
1423 
1424     /**剑指 Offer 36. 二叉搜索树与双向链表
1425      *输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
1426      * [递归][二叉树][链表][中序遍历][深度优先搜索算法][DFS]
1427      */
1428     public NodeList treeToDoublyList(NodeList root) {
1429         if(root == null) {return null;}
1430         dfstreeToDoublyList(root);
1431         head.left = pre;
1432         pre.right = head;
1433         return head;
1434     }
1435     void dfstreeToDoublyList(NodeList cur) {
1436         if(cur == null) {return;}
1437         dfstreeToDoublyList(cur.left);
1438         if(pre != null) {pre.right = cur;}
1439         else{ head = cur;}
1440         cur.left = pre;
1441         pre = cur;
1442         dfstreeToDoublyList(cur.right);
1443     }
1444 
1445 
1446     /**
1447      * 剑指 Offer 37. 序列化二叉树
1448      * 请实现两个函数,分别用来序列化和反序列化二叉树。
1449      *
1450      *     你需要设计一个算法来实现二叉树的序列化与反序列化。
1451      *     这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串
1452      *     并且将这个字符串反序列化为原始的树结构。
1453      *     [SSR][序列化][二叉树]
1454      */
1455     // Encodes a tree to a single string.
1456     public static String serialize(TreeNode root) {
1457         return rserialize(root, "");
1458     }
1459 
1460     public static TreeNode deserialize(String data) {
1461         String[] dataArray = data.split(",");
1462         List<String> dataList = new LinkedList<String>(Arrays.asList(dataArray));
1463         return rdeserialize(dataList);
1464     }
1465 
1466     public static String rserialize(TreeNode root, String str) {
1467         if (root == null) {
1468             str += "None,";
1469         } else {
1470             str += root.val + ",";
1471             str = rserialize(root.left, str);
1472             str = rserialize(root.right, str);
1473         }
1474         return str;
1475     }
1476 
1477     public static TreeNode rdeserialize(List<String> dataList) {
1478         if (dataList.get(0).equals("None")) {
1479             dataList.remove(0);
1480             return null;
1481         }
1482 
1483         TreeNode root = new TreeNode(Integer.valueOf(dataList.get(0)));
1484         dataList.remove(0);
1485         root.left = rdeserialize(dataList);
1486         root.right = rdeserialize(dataList);
1487 
1488         return root;
1489     }
1490 
1491     /**
1492      * 案例
1493      * [回溯][SSR]
1494      */
1495     static class Permutations {
1496 
1497         //题目描述:Given a collection of distinct integers, return all possible permutations.(给定一组不同的整数,返回其所有的可能组合)
1498         public List<List<Integer>> permute(int[] nums) {
1499             //一个全局变量,用于保存所有集合
1500             List<List<Integer>> list = new ArrayList<>();
1501             //传入三个参数,没有附加参数
1502             backtrack(list, new ArrayList<>(), nums);
1503             return list;
1504         }
1505 
1506         private void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] nums) {
1507             //一个终结条件,也就是满足条件的时候
1508             if (tempList.size() == nums.length) {
1509                 //全局变量添加一个满足条件的集合
1510                 list.add(new ArrayList<>(tempList));
1511             } else {
1512                 for (int i = 0; i < nums.length; i++) {
1513                     if (tempList.contains(nums[i])) {
1514                         continue;
1515                     }
1516                     //如果tempList没有包含nums[i]才添加
1517                     tempList.add(nums[i]);
1518                     //递归调用,此时的tempList一直在变化,直到满足终结条件才结束
1519                     backtrack(list, tempList, nums);
1520                     System.out.println("tempList的内容:" + tempList + "-------" + "i的值:" + i);
1521                     //它移除tempList最后一个元素的作用就是返回上一次调用时的数据,也就是希望返回之前的节点再去重新搜索满足条件。这样才能实现回溯
1522                     tempList.remove(tempList.size() - 1);
1523                 }
1524             }
1525         }
1526     }
1527     /**
1528      *   剑指 Offer 38. 字符串的排列
1529      *     输入一个字符串,打印出该字符串中字符的所有排列。
1530      *[递归][剪枝](类似与剪枝,因为不是在DFS中)[SSR]
1531      */
1532     static class Solution38_1 {
1533         //为了让递归函数添加结果方便,定义到函数之外,这样无需带到递归函数的参数列表中
1534         List<String> list = new ArrayList<>();
1535         //同;但是其赋值依赖c,定义声明分开
1536         char[] c;
1537         public String[] permutation(String s) {
1538             c = s.toCharArray();
1539             //从第一层开始递归
1540             dfs(0);
1541             //将字符串数组ArrayList转化为String类型数组
1542             return list.toArray(new String[list.size()]);
1543         }
1544 
1545         private void dfs(int x) {
1546             //当递归函数到达第三层,就返回,因为此时第二第三个位置已经发生了交换
1547             if (x == c.length - 1) {
1548                 //将字符数组转换为字符串
1549                 list.add(String.valueOf(c));
1550                 return;
1551             }
1552             //为了防止同一层递归出现重复元素
1553             HashSet<Character> set = new HashSet<>();
1554             //这里就很巧妙了,第一层可以是a,b,c那么就有三种情况,这里i = x,正巧dfs(0),正好i = 0开始
1555             // 当第二层只有两种情况,dfs(1)i = 1开始
1556             for (int i = x; i < c.length; i++){
1557                 //发生剪枝,当包含这个元素的时候,直接跳过
1558                 if (set.contains(c[i])){
1559                     continue;
1560                 }
1561                 set.add(c[i]);
1562                 //交换元素,这里很是巧妙,当在第二层dfs(1),x = 1,那么i = 1或者 2, 不是交换1和1,要就是交换1和2
1563                 swap(i,x);
1564                 //进入下一层递归
1565                 dfs(x + 1);
1566                 //返回时交换回来,这样保证到达第1层的时候,一直都是abc。这里捋顺一下,开始一直都是abc,那么第一位置总共就3个交换
1567                 //分别是a与a交换,这个就相当于 x = 0, i = 0;
1568                 //     a与b交换            x = 0, i = 1;
1569                 //     a与c交换            x = 0, i = 2;
1570                 //就相当于上图中开始的三条路径
1571                 //第一个元素固定后,每个引出两条路径,
1572                 //     b与b交换            x = 1, i = 1;
1573                 //     b与c交换            x = 1, i = 2;
1574                 //所以,结合上图,在每条路径上标注上i的值,就会非常容易好理解了
1575                 swap(i,x);
1576             }
1577         }
1578 
1579         private void swap(int i, int x) {
1580             char temp = c[i];
1581             c[i] = c[x];
1582             c[x] = temp;
1583         }
1584     }
1585 
1586     /**
1587      *    剑指 Offer 38. 字符串的排列
1588      *    输入一个字符串,打印出该字符串中字符的所有排列。
1589      *   [递归][剪枝][回溯][SSR][代表性]
1590      */
1591     static   class Solution38_2 {
1592         //结果集
1593         List<String> rec;
1594         //防重复
1595         boolean[] vis;
1596 
1597         public String[] permutation(String s) {
1598             //字符串长度
1599             int n = s.length();
1600             rec = new ArrayList<String>();
1601             vis = new boolean[n];
1602             //字符
1603             char[] arr = s.toCharArray();
1604             //字符排序
1605             Arrays.sort(arr);
1606             StringBuffer perm = new StringBuffer();
1607             backtrack(arr, 0, n, perm);
1608             int size = rec.size();
1609             String[] recArr = new String[size];
1610             for (int i = 0; i < size; i++) {
1611                 recArr[i] = rec.get(i);
1612             }
1613             return recArr;
1614         }
1615 //思路及解法
1616 //
1617 //我们将这个问题看作有 nn 个排列成一行的空位,我们需要从左往右依次填入题目给定的 n 个字符,每个字符只能使用一次。首先可以想到穷举的算法,即从左往右每一个空位都依次尝试填入一个字符,看是否能填完这 n 个空位,编程实现时,我们可以用「回溯法」来模拟这个过程。
1618 //定义递归函数backtrack(i,perm) 表示当前排列为perm,下一个待填入的空位是第 i 个空位(下标从 00 开始)。那么该递归函数分为两个情况:
1619 //如果 i=n,说明我们已经填完了 n 个空位,找到了一个可行的解,我们将 perm 放入答案数组中,递归结束。
1620 //如果 i<n,此时需要考虑第 i 个空位填哪个字符。根据题目要求我们肯定不能填已经填过的字符,因此很容易想到的一个处理手段是我们定义一个标记数组 vis 来标记已经填过的字符,那么在填第 i 个字符的时候我们遍历题目给定的 n 个字符,如果这个字符没有被标记过,我们就尝试填入,并将其标记,继续尝试填下一个空位,即调用函数 backtrack(i+1,perm)。回溯时,我们需要要撤销该空位填的字符以及对该字符的标记,并继续向当前空位尝试填入其他没被标记过的字符。
1621 
1622         public void backtrack(char[] arr, int i, int n, StringBuffer perm) {
1623             if (i == n) {
1624                 rec.add(perm.toString());
1625                 return;
1626             }
1627             for (int j = 0; j < n; j++) {
1628                 //保证在填每一个空位的时候重复字符只会被填入一次。具体地,我们首先对原字符串排序,保证相同的字符都相邻,在递归函数中,我们限制每次填入的字符一定是这个字符所在重复字符集合中「从左往右第一个未被填入的字符」
1629                 // 保证没有浏览过
1630                 if (vis[j] || (j > 0 && !vis[j - 1] && arr[j - 1] == arr[j])) {
1631                     continue;
1632                 }
1633                 vis[j] = true;
1634                 perm.append(arr[j]);
1635                 backtrack(arr, i + 1, n, perm);
1636                 perm.deleteCharAt(perm.length() - 1);
1637                 vis[j] = false;
1638             }
1639         }
1640     }
1641 
1642     /**
1643      *   剑指 Offer 38. 字符串的排列
1644      *     输入一个字符串,打印出该字符串中字符的所有排列。
1645      *对于一个长度为 n 的字符串(假设字符互不重复),其排列方案数共有:
1646      * n×(n−1)×(n−2)…×2×1
1647      *重复排列方案与剪枝:
1648      *
1649      * 当字符串存在重复字符时,排列方案中也存在重复的排列方案。为排除重复方案,需在固定某位字符时,保证 “每种字符只在此位固定一次” ,即遇到重复字符时不交换,直接跳过。从 DFS 角度看,此操作称为 “剪枝” 。
1650      *[下一个排列]
1651      */
1652     static  class Solution38_3 {
1653         public String[] permutation(String s) {
1654             List<String> ret = new ArrayList<String>();
1655             char[] arr = s.toCharArray();
1656             Arrays.sort(arr);
1657             do {
1658                 ret.add(new String(arr));
1659             } while (nextPermutation(arr));
1660             int size = ret.size();
1661             String[] retArr = new String[size];
1662             for (int i = 0; i < size; i++) {
1663                 retArr[i] = ret.get(i);
1664             }
1665             return retArr;
1666         }
1667 
1668         public boolean nextPermutation(char[] arr) {
1669             int i = arr.length - 2;
1670             while (i >= 0 && arr[i] >= arr[i + 1]) {
1671                 i--;
1672             }
1673             if (i < 0) {
1674                 return false;
1675             }
1676             int j = arr.length - 1;
1677             while (j >= 0 && arr[i] >= arr[j]) {
1678                 j--;
1679             }
1680             swap(arr, i, j);
1681             reverse(arr, i + 1);
1682             return true;
1683         }
1684 
1685         public void swap(char[] arr, int i, int j) {
1686             char temp = arr[i];
1687             arr[i] = arr[j];
1688             arr[j] = temp;
1689         }
1690 
1691         public void reverse(char[] arr, int start) {
1692             int left = start, right = arr.length - 1;
1693             while (left < right) {
1694                 swap(arr, left, right);
1695                 left++;
1696                 right--;
1697             }
1698         }
1699     }
1700 
1701 
1702     /**
1703      * 剑指 Offer 39. 数组中出现次数超过一半的数字
1704      *     数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
1705      *     [HASH]
1706      */
1707     public static int majorityElement(int[] nums) {
1708         HashMap<Integer,Integer>  count  = new HashMap<>();
1709         int lengthHalf =  Integer.valueOf(nums.length/2)  ;
1710         if(lengthHalf==0){
1711             return nums[0];
1712         }
1713 
1714         for(int s :nums){
1715             if(count.get(s)==null){
1716                 count.put(s,1);
1717             }else{
1718                 if(count.get(s)+1>lengthHalf){
1719                     return s;
1720                 }
1721                 count.put(s,count.get(s)+1);
1722             }
1723         }
1724         return 0;
1725     }
1726 
1727     /**
1728      * 剑指 Offer 39. 数组中出现次数超过一半的数字
1729      * [排序]
1730      */
1731     public static int majorityElement2(int[] nums) {
1732         Arrays.sort(nums);
1733         return nums[nums.length / 2];
1734     }
1735 
1736     /**
1737      * 剑指 Offer 39. 数组中出现次数超过一半的数字
1738      * [分治][SSR]
1739      * 我们使用经典的分治算法递归求解,直到所有的子问题都是长度为 1 的数组。
1740      * 长度为 1 的子数组中唯一的数显然是众数,直接返回即可。
1741      * 如果回溯后某区间的长度大于 1,我们必须将左右子区间的值合并。
1742      * 如果它们的众数相同,那么显然这一段区间的众数是它们相同的值。
1743      * 否则,我们需要比较两个众数在整个区间内出现的次数来决定该区间的众数。
1744      *
1745      */
1746    static class Solution39 {
1747         private int countInRange(int[] nums, int num, int lo, int hi) {
1748             int count = 0;
1749             for (int i = lo; i <= hi; i++) {
1750                 if (nums[i] == num) {
1751                     count++;
1752                 }
1753             }
1754             return count;
1755         }
1756 
1757         private int majorityElementRec(int[] nums, int lo, int hi) {
1758             // base case; the only element in an array of size 1 is the majority
1759             // element.
1760             if (lo == hi) {
1761                 return nums[lo];
1762             }
1763 
1764             // recurse on left and right halves of this slice.
1765             int mid = (hi - lo) / 2 + lo;
1766             int left = majorityElementRec(nums, lo, mid);
1767             int right = majorityElementRec(nums, mid + 1, hi);
1768 
1769             // if the two halves agree on the majority element, return it.
1770             if (left == right) {
1771                 return left;
1772             }
1773 
1774             // otherwise, count each element and return the "winner".
1775             int leftCount = countInRange(nums, left, lo, hi);
1776             int rightCount = countInRange(nums, right, lo, hi);
1777 
1778             return leftCount > rightCount ? left : right;
1779         }
1780 
1781         public int majorityElement(int[] nums) {
1782             return majorityElementRec(nums, 0, nums.length - 1);
1783         }
1784     }
1785 
1786     /**
1787      * Boyer-Moore 投票算法[投票]
1788      */
1789     public static int majorityElement5(int[] nums) {
1790         int count = 0;
1791         Integer candidate = null;
1792 
1793         for (int num : nums) {
1794             if (count == 0) {
1795                 candidate = num;
1796             }
1797             count += (num == candidate) ? 1 : -1;
1798         }
1799 
1800         return candidate;
1801     }
1802 
1803 
1804     /**
1805      *  剑指 Offer 40. 最小的k个数
1806      *     输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
1807      *     [排序]
1808      */
1809     public static int[] getLeastNumbers(int[] arr, int k) {
1810         int[] vec = new int[k];
1811         Arrays.sort(arr);
1812         for (int i = 0; i < k; ++i) {
1813             vec[i] = arr[i];
1814         }
1815         return vec;
1816     }
1817 
1818     /**
1819      *  剑指 Offer 40. 最小的k个数
1820      * [数据结构][堆]
1821      */
1822     class Solution40_2 {
1823         public int[] getLeastNumbers(int[] arr, int k) {
1824             int[] vec = new int[k];
1825             if (k == 0) { // 排除 0 的情况
1826                 return vec;
1827             }
1828             PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>() {
1829                 @Override
1830                 public int compare(Integer num1, Integer num2) {
1831                     return num2 - num1;
1832                 }
1833             });
1834             for (int i = 0; i < k; ++i) {
1835                 queue.offer(arr[i]);
1836             }
1837             for (int i = k; i < arr.length; ++i) {
1838                 if (queue.peek() > arr[i]) {
1839                     queue.poll();
1840                     queue.offer(arr[i]);
1841                 }
1842             }
1843             for (int i = 0; i < k; ++i) {
1844                 vec[i] = queue.poll();
1845             }
1846             return vec;
1847         }
1848     }
1849 
1850     /**
1851      *  剑指 Offer 40. 最小的k个数
1852      * [递归][快排思想]
1853      */
1854     class Solution40 {
1855         public int[] getLeastNumbers(int[] arr, int k) {
1856             randomizedSelected(arr, 0, arr.length - 1, k);
1857             int[] vec = new int[k];
1858             for (int i = 0; i < k; ++i) {
1859                 vec[i] = arr[i];
1860             }
1861             return vec;
1862         }
1863 
1864         private void randomizedSelected(int[] arr, int l, int r, int k) {
1865             if (l >= r) {
1866                 return;
1867             }
1868             int pos = randomizedPartition(arr, l, r);
1869             int num = pos - l + 1;
1870             if (k == num) {
1871                 return;
1872             } else if (k < num) {
1873                 randomizedSelected(arr, l, pos - 1, k);
1874             } else {
1875                 randomizedSelected(arr, pos + 1, r, k - num);
1876             }
1877         }
1878 
1879         // 基于随机的划分
1880         private int randomizedPartition(int[] nums, int l, int r) {
1881             int i = new Random().nextInt(r - l + 1) + l;
1882             swap(nums, r, i);
1883             return partition(nums, l, r);
1884         }
1885 
1886         private int partition(int[] nums, int l, int r) {
1887             int pivot = nums[r];
1888             int i = l - 1;
1889             for (int j = l; j <= r - 1; ++j) {
1890                 if (nums[j] <= pivot) {
1891                     i = i + 1;
1892                     swap(nums, i, j);
1893                 }
1894             }
1895             swap(nums, i + 1, r);
1896             return i + 1;
1897         }
1898 
1899         private void swap(int[] nums, int i, int j) {
1900             int temp = nums[i];
1901             nums[i] = nums[j];
1902             nums[j] = temp;
1903         }
1904     }
1905 
1906 
1907     /**
1908      * 剑指 Offer 41. 数据流中的中位数
1909      * 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
1910      * 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
1911      * [栈][根堆][顶堆]
1912      */
1913     static class MedianFinder {
1914         /** initialize your data structure here. */
1915         Queue<Integer> A, B;
1916         public MedianFinder() {
1917             A = new PriorityQueue<>(); // 小顶堆,保存较大的一半
1918             B = new PriorityQueue<>((x, y) -> (y - x)); // 大顶堆,保存较小的一半
1919         }
1920 //从数据流中添加一个整数到数据结构中。
1921         public void addNum(int num) {
1922             if(A.size() != B.size()) {
1923                 A.add(num);
1924                 B.add(A.poll());
1925             } else {
1926                 B.add(num);
1927                 A.add(B.poll());
1928             }
1929         }
1930 // 返回目前所有元素的中位数。
1931     public double findMedian() {
1932     return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;
1933 }
1934 
1935     }
1936 
1937     /**
1938      * 剑指 Offer 42. 连续子数组的最大和
1939      * 输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
1940      * 要求时间复杂度为O(n)。
1941      * [动态规划][SSR]
1942      */
1943     public int maxSubArray(int[] nums) {
1944         int res = nums[0];
1945         for(int i = 1; i < nums.length; i++) {
1946             nums[i] += Math.max(nums[i - 1], 0);
1947             res = Math.max(res, nums[i]);
1948         }
1949         return res;
1950     }
1951 
1952     /**
1953      * 剑指 Offer 42. 连续子数组的最大和
1954      * 输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
1955      * 要求时间复杂度为O(n)。
1956      * [分治][线段树][SSR]
1957      */
1958     class Solution {
1959         public class Status {
1960             public int lSum, rSum, mSum, iSum;
1961 
1962             public Status(int lSum, int rSum, int mSum, int iSum) {
1963                 this.lSum = lSum;
1964                 this.rSum = rSum;
1965                 this.mSum = mSum;
1966                 this.iSum = iSum;
1967             }
1968         }
1969 
1970         public int maxSubArray(int[] nums) {
1971             return getInfo(nums, 0, nums.length - 1).mSum;
1972         }
1973 
1974         public Status getInfo(int[] a, int l, int r) {
1975             if (l == r) {
1976                 return new Status(a[l], a[l], a[l], a[l]);
1977             }
1978             int m = (l + r) >> 1;
1979             Status lSub = getInfo(a, l, m);
1980             Status rSub = getInfo(a, m + 1, r);
1981             return pushUp(lSub, rSub);
1982         }
1983 
1984         public Status pushUp(Status l, Status r) {
1985             int iSum = l.iSum + r.iSum;
1986             int lSum = Math.max(l.lSum, l.iSum + r.lSum);
1987             int rSum = Math.max(r.rSum, r.iSum + l.rSum);
1988             int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
1989             return new Status(lSum, rSum, mSum, iSum);
1990         }
1991     }
1992 
1993     /**
1994      * 剑指 Offer 43. 1~n 整数中 1 出现的次数
1995      *     输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
1996      *     例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
1997      *     枚举每一数位上 11 的个数
1998      *
1999      */
2000     public static int countDigitOne(int n) {
2001         // mulk 表示 10^k
2002         // 在下面的代码中,可以发现 k 并没有被直接使用到(都是使用 10^k)
2003         // 但为了让代码看起来更加直观,这里保留了 k
2004         long mulk = 1;
2005         int ans = 0;
2006         for (int k = 0; n >= mulk; ++k) {
2007             ans += (n / (mulk * 10)) * mulk + Math.min(Math.max(n % (mulk * 10) - mulk + 1, 0), mulk);
2008             mulk *= 10;
2009         }
2010         return ans;
2011     }
2012 
2013 
2014 
2015 
2016     /**
2017      *剑指 Offer 44. 数字序列中某一位的数字
2018      *     数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。
2019      *     请写一个函数,求任意第n位对应的数字。
2020      *[迭代]
2021      */
2022     public static int findNthDigit(int n) {
2023             int digit = 1;
2024             long start = 1;
2025             long count = 9;
2026             while (n > count) { // 1.
2027                 n -= count;
2028                 digit += 1;
2029                 start *= 10;
2030                 count = digit * start * 9;
2031             }
2032             long num = start + (n - 1) / digit; // 2.
2033             return Long.toString(num).charAt((n - 1) % digit) - '0'; // 3.
2034         }
2035 
2036 
2037     /**
2038      *  剑指 Offer 45. 把数组排成最小的数
2039      *     输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
2040      *
2041      */
2042     public String minNumber(int[] nums) {
2043         String[] strs = new String[nums.length];
2044         for(int i = 0; i < nums.length; i++)
2045         { strs[i] = String.valueOf(nums[i]);}
2046         Arrays.sort(strs, (x, y) -> (x + y).compareTo(y + x));
2047         StringBuilder res = new StringBuilder();
2048         for(String s : strs)
2049         {  res.append(s);}
2050         return res.toString();
2051     }
2052     /**
2053      *  剑指 Offer 45. 把数组排成最小的数
2054      *     输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
2055      *[快速排序][SSR]
2056      */
2057     class Solution45 {
2058         public String minNumber(int[] nums) {
2059             String[] strs = new String[nums.length];
2060             for(int i = 0; i < nums.length; i++)
2061                 strs[i] = String.valueOf(nums[i]);
2062             quickSort(strs, 0, strs.length - 1);
2063             StringBuilder res = new StringBuilder();
2064             for(String s : strs)
2065                 res.append(s);
2066             return res.toString();
2067         }
2068         void quickSort(String[] strs, int l, int r) {
2069             if(l >= r) {return;}
2070             int i = l, j = r;
2071             String tmp = strs[i];
2072             while(i < j) {
2073                 while((strs[j] + strs[l]).compareTo(strs[l] + strs[j]) >= 0 && i < j) {j--;}
2074                 while((strs[i] + strs[l]).compareTo(strs[l] + strs[i]) <= 0 && i < j) {i++;}
2075                 tmp = strs[i];
2076                 strs[i] = strs[j];
2077                 strs[j] = tmp;
2078             }
2079             strs[i] = strs[l];
2080             strs[l] = tmp;
2081             quickSort(strs, l, i - 1);
2082             quickSort(strs, i + 1, r);
2083         }
2084     }
2085 
2086     /**
2087      * 标准快排算法
2088      * 既快又省空间
2089      * [快速排序][SSR]
2090      */
2091     public class QuickSort {
2092         public   void quickSort(int[] arr,int low,int high){
2093             int i,j,temp,t;
2094             if(low>high){
2095                 return;
2096             }
2097             i=low;
2098             j=high;
2099             //temp就是基准位
2100             temp = arr[low];
2101 
2102             while (i<j) {
2103                 //先看右边,依次往左递减
2104                 while (temp<=arr[j]&&i<j) {
2105                     j--;
2106                 }
2107                 //再看左边,依次往右递增
2108                 while (temp>=arr[i]&&i<j) {
2109                     i++;
2110                 }
2111                 //如果满足条件则交换
2112                 if (i<j) {
2113                     t = arr[j];
2114                     arr[j] = arr[i];
2115                     arr[i] = t;
2116                 }
2117 
2118             }
2119             //最后将基准为与i和j相等位置的数字交换
2120             arr[low] = arr[i];
2121             arr[i] = temp;
2122             //递归调用左半数组
2123             quickSort(arr, low, j-1);
2124             //递归调用右半数组
2125             quickSort(arr, j+1, high);
2126         }
2127 
2128     }
2129 
2130     /**
2131      * 剑指 Offer 46. 把数字翻译成字符串
2132      * 给定一个数字,我们按照如下规则把它翻译为字符串:
2133      * 0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。
2134      * 一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
2135      * [动态规划][递推][递归][SSR]
2136      *
2137      *我们可以归纳出翻译的规则,字符串的第 i 位置:
2138      * 可以单独作为一位来翻译
2139      * 如果第 i - 1位和第 i 位组成的数字在 10到 25 之间,可以把这两位连起来翻译
2140      * 到这里,我们发现它和「198. 打家劫舍」非常相似。我们可以用 f(i) 表示以第 i 位结尾的前缀串翻译的方案数,考虑第 ii 位单独翻译和与前一位连接起来再翻译对 f(i) 的贡献。单独翻译对 f(i) 的贡献为 f(i - 1);如果第 i - 1 位存在,并且第 i - 1位和第 i位形成的数字 xx 满足 10≤x≤25,那么就可以把第 i - 1位和第 i 位连起来一起翻译,对 f(i)的贡献为 f(i - 2),否则为 0。我们可以列出这样的动态规划转移方程:
2141      * f(i)=f(i−1)+f(i−2)[i−1≥0,10≤x≤25]
2142      * 边界条件是 f(-1) = 0f(−1)=0,f(0) = 1f(0)=1。方程中 [c] 的意思是 cc 为真的时候 [c] = 1[c]=1,否则 [c] = 0[c]=0。
2143      * 有了这个方程我们不难给出一个时间复杂度为 O(n) ,空间复杂度为 O(n) 的实现。考虑优化空间复杂度:这里的 f(i)f(i) 只和它的前两项 f(i - 1)  和 f(i - 2)  相关,我们可以运用「滚动数组」思想把 ff 数组压缩成三个变量,这样空间复杂度就变成了 O(1)。
2144      *
2145      *  类似青蛙跳台
2146      *      * [动态规划][SSR]
2147      */
2148     public static int translateNum46_动态规划版1(int num) {
2149         String s = String.valueOf(num);
2150         //a为dp[n-1],b为 dp[n-2]
2151         int a = 1, b = 1;
2152         for(int i = 2; i <= s.length(); i++) {
2153             String tmp = s.substring(i - 2, i);
2154             int c = tmp.compareTo("10") >= 0 && tmp.compareTo("25") <= 0 ? a + b : a;
2155             b = a;
2156             a = c;
2157         }
2158         return a;
2159     }
2160 
2161     /**
2162      *存储过程的解法,易理解
2163      */
2164     public int translateNum46动态规划版2(int num) {
2165         String s = String.valueOf(num);
2166         int[] dp = new int[s.length()+1];
2167         dp[0] = 1;
2168         dp[1] = 1;
2169         for(int i = 2; i <= s.length(); i ++){
2170             String temp = s.substring(i-2, i);
2171             if(temp.compareTo("10") >= 0 && temp.compareTo("25") <= 0)
2172             {dp[i] = dp[i-1] + dp[i-2];}
2173             else
2174             { dp[i] = dp[i-1];}
2175         }
2176         return dp[s.length()];
2177     }
2178 
2179     public static int translateNum46(int num) {
2180         if(num < 10) {return 1;}
2181         if(num <= 25) {return 2;}
2182 
2183         int digit = 1;
2184         while(num / digit >= 10){
2185             digit *= 10;
2186         }
2187 
2188         int high = num / digit;
2189         int low = num - high * digit;
2190         int high2 = num / (digit/10);
2191         int low2 = num - high2 * (digit/10);
2192 
2193         return ( high2 > 25 ) ? translateNum46(low) : translateNum46(low2) + translateNum46(low);
2194     }
2195 
2196 
2197 
2198     /**
2199      * 剑指 Offer 47. 礼物的最大价值
2200      *     在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。
2201      *     你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。
2202      *     给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
2203      *     [SSR][求最优解][动态规划]
2204      */
2205     public int maxValue47(int[][] grid) {
2206         int row = grid.length;
2207         int column = grid[0].length;
2208         //dp[i][j]表示从grid[0][0]到grid[i - 1][j - 1]时的最大价值
2209         int[][] dp = new int[row + 1][column + 1];
2210         for (int i = 1; i <= row; i++) {
2211             for (int j = 1; j <= column; j++) {
2212                 dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + grid[i - 1][j - 1];
2213             }
2214         }
2215         return dp[row][column];
2216     }
2217 
2218     /**
2219      * DIY的方法,leecode解题超时,由于是全部遍历,其实不用,用int[][] gridMax 右下角存储每个"子数组"当前最大值结果。   [递归][DFS]
2220      */
2221     static class Solution47 {
2222         static int xl,y1,result=0;
2223         public static int maxValue(int[][] grid) {
2224            xl =grid.length;
2225            y1=grid[0].length;
2226             result= 0;
2227             maxV(grid,0,0,grid[0][0]);
2228             return result;
2229         }
2230 
2231         public static void maxV(int[][] grid,int x,int y,int cunt ){
2232             if(x==xl-1||y==y1-1){
2233                 result = cunt+grid[x][y]>result?cunt+grid[x][y]:result;
2234                return;
2235             }
2236             maxV(grid,x+1,y,grid[x][y]+cunt);
2237             maxV(grid,x,y+1,grid[x][y]+cunt);
2238 
2239         }
2240     }
2241 
2242     /**
2243      * 优化后
2244      */
2245     private static class Solution47_2 {
2246         static int xl,y1;
2247         static int[][] result;
2248         public static int maxValue(int[][] grid) {
2249             xl =grid.length;
2250             y1=grid[0].length;
2251             result= new int[xl][y1];
2252             result[0][0]=0;
2253             maxV(grid,0,0);
2254             return result[xl-1][y1-1];
2255         }
2256 
2257         public static void maxV(int[][] grid,int x,int y ){
2258             if(x==xl||y==y1){
2259                 return ;
2260             }
2261             int yy= y==0?0: result[x][y-1];
2262             int xx= x==0?0: result[x-1][y];
2263             result[x][y] =xx+grid[x][y]>yy+grid[x][y]?xx+grid[x][y]:yy+grid[x][y];
2264             maxV(grid,x+1,y);
2265             maxV(grid,x,y+1);
2266         }
2267     }
2268 
2269     private static class Solution47_3 {
2270         //用来存储该节点到右下角的最大价值
2271         static int[][] store;
2272         static int row, column;
2273 
2274         public static int maxValue(int[][] grid) {
2275             row = grid.length;
2276             column = grid[0].length;
2277             store = new int[row][column];
2278             return helper(grid, 0, 0);
2279         }
2280 
2281         public static int helper(int[][] grid, int i, int j) {
2282             int right = 0, down = 0;
2283             if (i == row - 1 && j == column - 1)
2284             { return grid[i][j];}
2285             //当前节点越界
2286             if (i < 0 || j < 0 || i >= row || j >= column)
2287             { return 0;}
2288             //右边没有被访问
2289             if (j + 1 < column && store[i][j + 1] == 0) {
2290                 right = helper(grid, i, j + 1);
2291                 store[i][j + 1] = right;
2292             }
2293             //右边已经被访问了,取存储的值
2294             if (j + 1 < column && store[i][j + 1] > 0) {
2295                 right = store[i][j + 1];
2296             }
2297             //下边没有被访问了
2298             if (i + 1 < row && store[i + 1][j] == 0) {
2299                 down = helper(grid, i + 1, j);
2300                 store[i + 1][j] = down;
2301             }
2302             //下边已经被访问了,取存储的值
2303             if (i + 1 < row && store[i + 1][j] == 1) {
2304                 down = store[i + 1][j];
2305             }
2306             //该节点到右下角的最大价值
2307             return Math.max(right, down) + grid[i][j];
2308         }
2309     }
2310 
2311 
2312 
2313 
2314     /**
2315      *  剑指 Offer 48. 最长不含重复字符的子字符串
2316      *     请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
2317      *     [动态规划]
2318      */
2319     public static int lengthOfLongestSubstring(String s) {
2320         Map<Character, Integer> dic = new HashMap<>();
2321         int res = 0, tmp = 0;
2322         for(int j = 0; j < s.length(); j++) {
2323             int i = dic.getOrDefault(s.charAt(j), -1); // 获取索引 i
2324             dic.put(s.charAt(j), j); // 更新哈希表
2325             tmp = tmp < j - i ? tmp + 1 : j - i; // dp[j - 1] -> dp[j]
2326             res = Math.max(res, tmp); // max(dp[j - 1], dp[j])
2327         }
2328         return res;
2329     }
2330 
2331     /**
2332      *  剑指 Offer 48. 最长不含重复字符的子字符串
2333      *     请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
2334      *     [双指针]
2335      */
2336     public static int lengthOfLongestSubstring2(String s) {
2337         Map<Character, Integer> dic = new HashMap<>();
2338         int i = -1, res = 0;
2339         for(int j = 0; j < s.length(); j++) {
2340             if(dic.containsKey(s.charAt(j)))
2341                 {i = Math.max(i, dic.get(s.charAt(j))); }// 更新左指针 i
2342             dic.put(s.charAt(j), j); // 哈希表记录
2343             res = Math.max(res, j - i); // 更新结果
2344         }
2345         return res;
2346     }
2347 
2348 
2349 
2350     /**
2351      *  剑指 Offer 49. 丑数
2352      *     我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
2353      *[动态规划][SSR]
2354      */
2355     public int nthUglyNumber(int n) {
2356         int[] dp = new int[n + 1];
2357         dp[1] = 1;
2358         int p2 = 1, p3 = 1, p5 = 1;
2359         for (int i = 2; i <= n; i++) {
2360             int num2 = dp[p2] * 2, num3 = dp[p3] * 3, num5 = dp[p5] * 5;
2361             dp[i] = Math.min(Math.min(num2, num3), num5);
2362             if (dp[i] == num2) {
2363                 p2++;
2364             }
2365             if (dp[i] == num3) {
2366                 p3++;
2367             }
2368             if (dp[i] == num5) {
2369                 p5++;
2370             }
2371         }
2372         return dp[n];
2373     }
2374 
2375 
2376 
2377 
2378 
2379     /**
2380      * 剑指 Offer 50. 第一个只出现一次的字符
2381      *     在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
2382      */
2383     public char firstUniqChar(String s) {
2384         Integer l = s.length();
2385         String re=" ";
2386         for(Integer i=0;i<l;i++){
2387             String nowS= s.substring(i,i+1);
2388             String leftS=s.substring(0,i)+s.substring(i+1,l);
2389             if(!leftS.contains(nowS)){
2390                 return nowS.toCharArray()[0];
2391             }
2392         }
2393         return re.toCharArray()[0];
2394     }
2395 
2396     public char firstUniqChar2(String s) {
2397         HashMap<Character, Boolean> dic = new HashMap<>();
2398         char[] sc = s.toCharArray();
2399         for(char c : sc)
2400         { dic.put(c, !dic.containsKey(c));}
2401         for(char c : sc)
2402         { if(dic.get(c)) {return c;}}
2403         return ' ';
2404     }
2405 
2406 
2407     /**
2408      *     剑指 Offer 51. 数组中的逆序对
2409      *     在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
2410      *     输入一个数组,求出这个数组中的逆序对的总数。
2411      *[自创超时]
2412      */
2413     public static int reversePairs(int[] nums) {
2414         int res = 0;
2415         for(int x=0;x<nums.length;x++){
2416             for(int y=x+1;y<nums.length;y++){
2417                 if(nums[x]>nums[y]){
2418                     sysout(nums[x]+"-"+nums[y]);
2419                     res++;
2420                 }
2421             }
2422         }
2423         return res;
2424     }
2425 
2426 
2427 
2428     /**
2429      *  [归并排序][SSR][分治][递归]
2430      */
2431     public class Solution51 {
2432         public int reversePairs(int[] nums) {
2433             int len = nums.length;
2434 
2435             if (len < 2) {
2436                 return 0;
2437             }
2438 
2439             int[] copy = new int[len];
2440             for (int i = 0; i < len; i++) {
2441                 copy[i] = nums[i];
2442             }
2443 
2444             int[] temp = new int[len];
2445             return reversePairs(copy, 0, len - 1, temp);
2446         }
2447 
2448         private int reversePairs(int[] nums, int left, int right, int[] temp) {
2449             if (left == right) {
2450                 return 0;
2451             }
2452 
2453             int mid = left + (right - left) / 2;
2454             int leftPairs = reversePairs(nums, left, mid, temp);
2455             int rightPairs = reversePairs(nums, mid + 1, right, temp);
2456 
2457             if (nums[mid] <= nums[mid + 1]) {
2458                 return leftPairs + rightPairs;
2459             }
2460 
2461             int crossPairs = mergeAndCount(nums, left, mid, right, temp);
2462             return leftPairs + rightPairs + crossPairs;
2463         }
2464 
2465         private int mergeAndCount(int[] nums, int left, int mid, int right, int[] temp) {
2466             for (int i = left; i <= right; i++) {
2467                 temp[i] = nums[i];
2468             }
2469 
2470             int i = left;
2471             int j = mid + 1;
2472 
2473             int count = 0;
2474             for (int k = left; k <= right; k++) {
2475 
2476                 if (i == mid + 1) {
2477                     nums[k] = temp[j];
2478                     j++;
2479                 } else if (j == right + 1) {
2480                     nums[k] = temp[i];
2481                     i++;
2482                 } else if (temp[i] <= temp[j]) {
2483                     nums[k] = temp[i];
2484                     i++;
2485                 } else {
2486                     nums[k] = temp[j];
2487                     j++;
2488                     count += (mid - i + 1);
2489                 }
2490             }
2491             return count;
2492         }
2493     }
2494 
2495 
2496     /**
2497      * 离散化树状数组
2498      * [离散化][树状数组]
2499      * 「树状数组」是一种可以动态维护序列前缀和的数据结构,它的功能是:
2500      *
2501      * 单点更新 update(i, v): 把序列 i 位置的数加上一个值 v,这题 v = 1
2502      * 区间查询 query(i): 查询序列 [1⋯i] 区间的区间和,即 i 位置的前缀和
2503      * 修改和查询的时间代价都是  O(logn),其中 nn 为需要维护前缀和的序列的长度。
2504      */
2505     class Solution51_2 {
2506         public int reversePairs(int[] nums) {
2507             int n = nums.length;
2508             int[] tmp = new int[n];
2509             System.arraycopy(nums, 0, tmp, 0, n);
2510             // 离散化
2511             Arrays.sort(tmp);
2512             for (int i = 0; i < n; ++i) {
2513                 nums[i] = Arrays.binarySearch(tmp, nums[i]) + 1;
2514             }
2515             // 树状数组统计逆序对
2516             BIT bit = new BIT(n);
2517             int ans = 0;
2518             for (int i = n - 1; i >= 0; --i) {
2519                 ans += bit.query(nums[i] - 1);
2520                 bit.update(nums[i]);
2521             }
2522             return ans;
2523         }
2524     }
2525 
2526     class BIT {
2527         private int[] tree;
2528         private int n;
2529 
2530         public BIT(int n) {
2531             this.n = n;
2532             this.tree = new int[n + 1];
2533         }
2534 
2535         public   int lowbit(int x) {
2536             return x & (-x);
2537         }
2538 
2539         public int query(int x) {
2540             int ret = 0;
2541             while (x != 0) {
2542                 ret += tree[x];
2543                 x -= lowbit(x);
2544             }
2545             return ret;
2546         }
2547 
2548         public void update(int x) {
2549             while (x <= n) {
2550                 ++tree[x];
2551                 x += lowbit(x);
2552             }
2553         }
2554     }
2555 
2556 
2557     /**
2558      * 剑指 Offer 52. 两个链表的第一个公共节点
2559      */
2560     public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
2561         ListNode PA = headA;
2562         ListNode PB = headB;
2563         while (PA != PB) {
2564             PA = PA == null ? headB : PA.next;
2565             PB = PB == null ? headA : PB.next;
2566         }
2567         return PA;
2568     }
2569 
2570 
2571 
2572     /**
2573      * 剑指 Offer 53 - I. 在排序数组中查找数字 I
2574      */
2575     public int search(int[] nums, int target) {
2576         // 搜索右边界 right
2577         int i = 0, j = nums.length - 1;
2578         while(i <= j) {
2579             int m = (i + j) / 2;
2580             if(nums[m] <= target) {i = m + 1;}
2581             else {j = m - 1;}
2582         }
2583         int right = i;
2584         // 若数组中无 target ,则提前返回
2585         if(j >= 0 && nums[j] != target) {return 0;}
2586         // 搜索左边界 right
2587         i = 0; j = nums.length - 1;
2588         while(i <= j) {
2589             int m = (i + j) / 2;
2590             if(nums[m] < target) {
2591                 i = m + 1;
2592             }else {
2593                 j = m - 1;
2594             }
2595         }
2596         int left = j;
2597         return right - left - 1;
2598     }
2599 
2600     /**
2601      * 剑指 Offer 53 - I. 在排序数组中查找数字 I
2602      * 二分法
2603      */
2604     public int search2(int[] nums, int target) {
2605         return helper(nums, target) - helper(nums, target - 1);
2606     }
2607     int helper(int[] nums, int tar) {
2608         int i = 0, j = nums.length - 1;
2609         while(i <= j) {
2610             int m = (i + j) / 2;
2611             if(nums[m] <= tar) {i = m + 1;}
2612             else {j = m - 1;}
2613         }
2614         return i;
2615     }
2616 
2617     /**
2618      * 剑指 Offer 53 - II. 0~n-1中缺失的数字
2619      * 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。
2620      * 在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
2621      * [二分][SR]
2622      */
2623     public int missingNumber(int[] nums) {
2624         int i = 0, j = nums.length - 1;
2625         while(i <= j) {
2626             int m = (i + j) / 2;
2627             if(nums[m] == m) {i = m + 1;}
2628             else {j = m - 1;}
2629         }
2630         return i;
2631     }
2632 
2633 
2634     /**
2635      * 剑指 Offer 54. 二叉搜索树的第k大节点
2636      * [中序遍历][SSR]
2637      */
2638     class Solution54 {
2639         int res, k;
2640         public int kthLargest(TreeNode root, int k) {
2641             this.k = k;
2642             dfs(root);
2643             return res;
2644         }
2645         void dfs(TreeNode root) {
2646             if(root == null) {return;}
2647             dfs(root.right);
2648             if(k == 0) {return;}
2649             if(--k == 0) {res = root.val;}
2650             dfs(root.left);
2651         }
2652     }
2653 
2654     /**
2655      *  输入一棵二叉树的根节点,求该树的深度。
2656      *     从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
2657      *     后序遍历(DFS)[SSR]
2658      */
2659     public int maxDepth1(TreeNode root) {
2660         if(root == null) {return 0;}
2661         return Math.max(maxDepth1(root.left), maxDepth1(root.right)) + 1;
2662     }
2663 
2664 
2665     /**
2666      *   层序遍历(BFS)[SSR]
2667      */
2668     public int maxDepth2(TreeNode root) {
2669         if(root == null) {return 0;}
2670         List<TreeNode> queue = new LinkedList<TreeNode>() {{ add(root); }}, tmp;
2671         int res = 0;
2672         while(!queue.isEmpty()) {
2673             tmp = new LinkedList<>();
2674             for(TreeNode node : queue) {
2675                 if(node.left != null) {tmp.add(node.left);}
2676                 if(node.right != null) {tmp.add(node.right);}
2677             }
2678             queue = tmp;
2679             res++;
2680         }
2681         return res;
2682     }
2683 
2684 
2685     /**
2686      * 剑指 Offer 55 - II. 平衡二叉树
2687      *     输入一棵二叉树的根节点,判断该树是不是平衡二叉树。
2688      *     如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
2689      *     后序遍历 + 剪枝 (从底至顶)[SSR]
2690      */
2691     static class Solution55 {
2692         public boolean isBalanced(TreeNode root) {
2693             return recur(root) != -1;
2694         }
2695 
2696         private int recur(TreeNode root) {
2697             if (root == null) {return 0;}
2698             int left = recur(root.left);
2699             if(left == -1) {return -1;}
2700             int right = recur(root.right);
2701             if(right == -1) {return -1;}
2702             return Math.abs(left - right) < 2 ? Math.max(left, right) + 1 : -1;
2703         }
2704 
2705     }
2706 
2707 
2708 
2709     /**
2710      *  先序遍历 + 判断深度 (从顶至底)[SSR]
2711      */
2712     class Solution55_2 {
2713         public boolean isBalanced(TreeNode root) {
2714             if (root == null) {return true;}
2715             return Math.abs(depth(root.left) - depth(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
2716         }
2717 
2718         private int depth(TreeNode root) {
2719             if (root == null) {return 0;}
2720             return Math.max(depth(root.left), depth(root.right)) + 1;
2721         }
2722     }
2723 
2724     /**
2725      *  剑指 Offer 56 - I. 数组中数字出现的次数
2726      *     一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。
2727      *     请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
2728      *     [分组异或][位运算]
2729      */
2730     public int[] singleNumbers(int[] nums) {
2731             int ret = 0;
2732             for (int n : nums) {
2733                 ret ^= n;
2734             }
2735             int div = 1;
2736             while ((div & ret) == 0) {
2737                 div <<= 1;
2738             }
2739             int a = 0, b = 0;
2740             for (int n : nums) {
2741                 if ((div & n) != 0) {
2742                     a ^= n;
2743                 } else {
2744                     b ^= n;
2745                 }
2746             }
2747             return new int[]{a, b};
2748         }
2749 
2750 
2751 
2752     /**
2753      * 有限状态自动机[位运算]
2754      */
2755     public int singleNumber(int[] nums) {
2756         int ones = 0, twos = 0;
2757         for(int num : nums){
2758             ones = ones ^ num & ~twos;
2759             twos = twos ^ num & ~ones;
2760         }
2761         return ones;
2762     }
2763 
2764 
2765     /**
2766      * 剑指 Offer 56 - II. 数组中数字出现的次数 II
2767      * 在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
2768      *  遍历统计[位运算]
2769      */
2770     public int singleNumber2(int[] nums) {
2771         int[] counts = new int[32];
2772         for(int num : nums) {
2773             for(int j = 0; j < 32; j++) {
2774                 counts[j] += num & 1;
2775                 num >>>= 1;
2776             }
2777         }
2778         int res = 0, m = 3;
2779         for(int i = 0; i < 32; i++) {
2780             res <<= 1;
2781             res |= counts[31 - i] % m;
2782         }
2783         return res;
2784     }
2785 
2786 
2787 
2788 
2789 
2790     /**
2791      *  剑指 Offer 57. 和为s的两个数字
2792      *  输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。
2793      *     如果有多对数字的和等于s,则输出任意一对即可。
2794      *     [双指针]
2795      */
2796     public int[] twoSum(int[] nums, int target) {
2797         int i = 0, j = nums.length - 1;
2798         while(i < j) {
2799             int s = nums[i] + nums[j];
2800             if(s < target) {i++;}
2801             else if(s > target) {j--;}
2802             else {return new int[] { nums[i], nums[j] };}
2803         }
2804         return new int[0];
2805     }
2806 
2807 
2808 
2809     /**
2810      * 剑指 Offer 57 - II. 和为s的连续正数序列
2811      *  输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
2812      *     序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
2813      *     [滑动窗口][SSR]
2814      */
2815     public int[][] findContinuousSequence(int target) {
2816         int left = 1; // 滑动窗口的左边界
2817         int right = 2; // 滑动窗口的右边界
2818         int sum = left + right; // 滑动窗口中数字的和
2819         List<int[]> res = new ArrayList<>();
2820         //窗口的左边是窗口内的最小数字,只能小于等于target / 2,
2821         //因为题中要求的是至少含有两个数
2822         while (left <= target / 2) {
2823             if (sum < target) {
2824                 //如果窗口内的值比较小,右边界继续向右移动,
2825                 //来扩大窗口
2826                 sum += ++right;
2827             } else if (sum > target) {
2828                 //如果窗口内的值比较大,左边边界往右移动,
2829                 //缩小窗口
2830                 sum -= left++;
2831             } else {
2832                 //如果窗口内的值正好等于target,就把窗口内的值记录
2833                 //下来,然后窗口的左边和右边同时往右移一步
2834                 int[] arr = new int[right - left + 1];
2835                 for (int k = left; k <= right; k++) {
2836                     arr[k - left] = k;
2837                 }
2838                 res.add(arr);
2839                 //左边和右边同时往右移一位
2840                 sum -= left++;
2841                 sum += ++right;
2842             }
2843         }
2844         //把结果转化为数组
2845         return res.toArray(new int[res.size()][]);
2846     }
2847 
2848 
2849     /**
2850      * 数学公式:S=n*a+n*(n-1)/2
2851      */
2852     public int[][] findContinuousSequence2(int target) {
2853         List<int[]> res = new ArrayList<>();
2854         int n = 2;
2855         //死循环
2856         while (true) {
2857             int total = target - n * (n - 1) / 2;
2858             //当分子小于等于0的时候,退出循环
2859             if (total <= 0)
2860             { break;}
2861             //如果首项是正整数,满足条件
2862             if (total % n == 0) {
2863                 int[] arr = new int[n];
2864                 //找出首项的值
2865                 int startValue = total / n;
2866                 for (int k = 0; k < n; k++) {
2867                     arr[k] = startValue + k;
2868                 }
2869                 res.add(arr);
2870             }
2871             //继续找
2872             n++;
2873         }
2874         //反转,比如当target等于9的时候,结果是
2875         //[[4,5],[2,3,4]],但题中要求的是不同
2876         // 序列按照首个数字从小到大排列,所以这里反转一下
2877         Collections.reverse(res);
2878         //把list转化为数组
2879         return res.toArray(new int[res.size()][]);
2880     }
2881 
2882     /**
2883      * 数学公式2:
2884      * 假如target是两个连续数字的和,那么这个序列的首项就是(target-1)/2。
2885      * 假如target是三个连续数字的和,那么这个序列的首项就是(target-1-2)/3。
2886      * 假如target是四个连续数字的和,那么这个序列的首项就是(target-1-2-3)/4。
2887      */
2888     public int[][] findContinuousSequence3(int target) {
2889         List<int[]> res = new ArrayList<>();
2890         //因为至少是两个数,所以target先减1
2891         target--;
2892         for (int n = 2; target > 0; n++) {
2893             //找到了一组满足条件的序列
2894             if (target % n == 0) {
2895                 int[] arr = new int[n];
2896                 //找出首项的值
2897                 int startValue = target / n;
2898                 for (int k = 0; k < n; k++) {
2899                     arr[k] = startValue + k;
2900                 }
2901                 res.add(arr);
2902             }
2903             target -= n;
2904         }
2905         Collections.reverse(res);
2906         //把list转化为数组
2907         return res.toArray(new int[res.size()][]);
2908     }
2909 
2910 
2911     /**
2912      * 字符串反转
2913      */
2914     public static String reverseWords(String s) {
2915         /* char[] ss=s.toCharArray();
2916          int leng = ss.length-1;
2917         for(int i=leng;i>=leng/2;i--){
2918             char st=  ss[i];
2919             ss[i]=ss[leng-i];
2920             ss[leng-i]=st;
2921         }
2922         StringBuffer str5 = new StringBuffer();
2923         for (char s1 : ss) {
2924             str5.append(s1);
2925         }
2926         return  str5.toString() ;*/
2927       return  new StringBuffer(s).reverse().toString();
2928     }
2929 
2930     /**
2931      * 剑指 Offer 58 - I. 翻转单词顺序
2932      *     输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
2933      *     为简单起见,标点符号和普通字母一样处理。
2934      *     例如输入字符串"I am a student. ",则输出"student. a am I"。
2935      *     [双指针]
2936      */
2937     public String reverseWords2(String s) {
2938         s = s.trim(); // 删除首尾空格
2939         int j = s.length() - 1, i = j;
2940         StringBuilder res = new StringBuilder();
2941         while(i >= 0) {
2942             while(i >= 0 && s.charAt(i) != ' ') {i--;} // 搜索首个空格
2943             res.append(s.substring(i + 1, j + 1) + " "); // 添加单词
2944             while(i >= 0 && s.charAt(i) == ' ') {i--;} // 跳过单词间空格
2945             j = i; // j 指向下个单词的尾字符
2946         }
2947         return res.toString().trim(); // 转化为字符串并返回
2948     }
2949 
2950     /**
2951      * 分割 + 倒序
2952      */
2953     public String reverseWords3(String s) {
2954         String[] strs = s.trim().split(" "); // 删除首尾空格,分割字符串
2955         StringBuilder res = new StringBuilder();
2956         for(int i = strs.length - 1; i >= 0; i--) { // 倒序遍历单词列表
2957             if(strs[i].equals("")) {continue;} // 遇到空单词则跳过
2958             res.append(strs[i] + " "); // 将单词拼接至 StringBuilder
2959         }
2960         return res.toString().trim(); // 转化为字符串,删除尾部空格,并返回
2961     }
2962 
2963     /**
2964      * 剑指 Offer 58 - II. 左旋转字符串
2965      * 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
2966      * 字符串切片
2967      */
2968     public String reverseLeftWords(String s, int n) {
2969         return s.substring(n, s.length()) + s.substring(0, n);
2970     }
2971 
2972     public String reverseLeftWords2(String s, int n) {
2973         StringBuilder res = new StringBuilder();
2974         for(int i = n; i < s.length(); i++)
2975         {res.append(s.charAt(i));}
2976         for(int i = 0; i < n; i++)
2977         {  res.append(s.charAt(i));}
2978         return res.toString();
2979     }
2980 
2981 
2982     /**
2983      *  字符串遍历拼接
2984      */
2985     public String reverseLeftWords3(String s, int n) {
2986         String res = "";
2987         for(int i = n; i < n + s.length(); i++)
2988             {  res += s.charAt(i % s.length());}
2989         return res;
2990     }
2991 
2992     /**
2993      * 剑指 Offer 59 - I. 滑动窗口的最大值
2994      * 给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
2995      * [双端队列][单调栈][滑动窗口]
2996      */
2997     class Solution59 {
2998         public int[] maxSlidingWindow(int[] nums, int k) {
2999             if(nums.length == 0 || k == 0) {return new int[0];}
3000             Deque<Integer> deque = new LinkedList<>();
3001             int[] res = new int[nums.length - k + 1];
3002             for(int j = 0, i = 1 - k; j < nums.length; i++, j++) {
3003                 // 删除 deque 中对应的 nums[i-1]
3004                 if(i > 0 && deque.peekFirst() == nums[i - 1])
3005                 { deque.removeFirst();}
3006                 // 保持 deque 递减
3007                 while(!deque.isEmpty() && deque.peekLast() < nums[j])
3008                 {   deque.removeLast();}
3009                 deque.addLast(nums[j]);
3010                 // 记录窗口最大值
3011                 if(i >= 0)
3012                 {  res[i] = deque.peekFirst();}
3013             }
3014             return res;
3015         }
3016     }
3017 
3018     /**
3019      *  剑指 Offer 59 - II. 队列的最大值
3020      *     请定义一个队列并实现函数 max_value 得到队列里的最大值,
3021      *     要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
3022      *     若队列为空,pop_front 和 max_value 需要返回 -1
3023      *     [双栈]
3024      */
3025     class MaxQueue {
3026         Queue<Integer> q;
3027         Deque<Integer> d;
3028 
3029         public MaxQueue() {
3030             q = new LinkedList<Integer>();
3031             d = new LinkedList<Integer>();
3032         }
3033 
3034         public int max_value() {
3035             if (d.isEmpty()) {
3036                 return -1;
3037             }
3038             return d.peekFirst();
3039         }
3040 
3041         public void push_back(int value) {
3042             while (!d.isEmpty() && d.peekLast() < value) {
3043                 d.pollLast();
3044             }
3045             d.offerLast(value);
3046             q.offer(value);
3047         }
3048 
3049         public int pop_front() {
3050             if (q.isEmpty()) {
3051                 return -1;
3052             }
3053             int ans = q.poll();
3054             if (ans == d.peekFirst()) {
3055                 d.pollFirst();
3056             }
3057             return ans;
3058         }
3059     }
3060 
3061     /**
3062      * 剑指 Offer 60. n个骰子的点数
3063      *     把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
3064      *     [动态规划][SSR]
3065      *     由于新增骰子的点数只可能为 11 至 66 ,因此概率 f(n - 1, x)仅与 f(n, x + 1) ,
3066      *     f(n, x + 2), ... , f(n, x + 6) 相关。
3067      *     因而,遍历 f(n - 1) 中各点数和的概率,并将其相加至 f(n)
3068      *     中所有相关项,即可完成 f(n - 1)至 f(n)  的递推。
3069      */
3070     class Solution60 {
3071         public double[] dicesProbability(int n) {
3072             double[] dp = new double[6];
3073             Arrays.fill(dp, 1.0 / 6.0);
3074             for (int i = 2; i <= n; i++) {
3075                 double[] tmp = new double[5 * i + 1];
3076                 for (int j = 0; j < dp.length; j++) {
3077                     for (int k = 0; k < 6; k++) {
3078                         tmp[j + k] += dp[j] / 6.0;
3079                     }
3080                 }
3081                 dp = tmp;
3082             }
3083             return dp;
3084         }
3085     }
3086 
3087     /**
3088      * 剑指 Offer 61. 扑克牌中的顺子
3089      * 从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
3090      */
3091     class Solution61 {
3092         public boolean isStraight(int[] nums) {
3093             Set<Integer> repeat = new HashSet<>();
3094             int max = 0, min = 14;
3095             for(int num : nums) {
3096                 if(num == 0) continue; // 跳过大小王
3097                 max = Math.max(max, num); // 最大牌
3098                 min = Math.min(min, num); // 最小牌
3099                 if(repeat.contains(num)) return false; // 若有重复,提前返回 false
3100                 repeat.add(num); // 添加此牌至 Set
3101             }
3102             return max - min < 5; // 最大牌 - 最小牌 < 5 则可构成顺子
3103         }
3104     }
3105 
3106     /**
3107      * 剑指 Offer 64. 求1+2+…+n
3108      * 求 1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
3109      */
3110     class Solution64 {
3111         public int sumNums(int n) {
3112             boolean flag = n > 0 && (n += sumNums(n - 1)) > 0;
3113             return n;
3114         }
3115     }
3116 
3117     /**
3118      * 剑指 Offer 65. 不用加减乘除做加法
3119      * 写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
3120      *本题考察对位运算的灵活使用,即使用位运算实现加法。
3121      * 设两数字的二进制形式 a, b,其求和 s = a + b,a(i) 代表 a 的二进制第 i 位,则分为以下四种情况:
3122      * 无进位和 与 异或运算 规律相同,进位 和 与运算 规律相同(并需左移一位)。
3123      * 因此,无进位和 n 与进位 c 的计算公式如下;
3124      */
3125     class Solution65 {
3126         public int add(int a, int b) {
3127             while(b != 0) { // 当进位为 0 时跳出
3128                 int c = (a & b) << 1;  // c = 进位
3129                 a ^= b; // a = 非进位和
3130                 b = c; // b = 进位
3131             }
3132             return a;
3133         }
3134     }
3135 
3136     /**
3137      * 剑指 Offer 66. 构建乘积数组
3138      * 给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],
3139      * 其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积,
3140      * 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。
3141      *
3142      */
3143     class Solution66 {
3144         public int[] constructArr(int[] a) {
3145             int len = a.length;
3146             if(len == 0) return new int[0];
3147             int[] b = new int[len];
3148             b[0] = 1;
3149             int tmp = 1;
3150             for(int i = 1; i < len; i++) {
3151                 b[i] = b[i - 1] * a[i - 1];
3152             }
3153             for(int i = len - 2; i >= 0; i--) {
3154                 tmp *= a[i + 1];
3155                 b[i] *= tmp;
3156             }
3157             return b;
3158         }
3159     }
3160 
3161     /**
3162      * 剑指 Offer 67. 把字符串转换成整数
3163      */
3164     class Solution67 {
3165         public int strToInt(String str) {
3166             int res = 0, bndry = Integer.MAX_VALUE / 10;
3167             int i = 0, sign = 1, length = str.length();
3168             if(length == 0) return 0;
3169             while(str.charAt(i) == ' ')
3170                 if(++i == length) return 0;
3171             if(str.charAt(i) == '-') sign = -1;
3172             if(str.charAt(i) == '-' || str.charAt(i) == '+') i++;
3173             for(int j = i; j < length; j++) {
3174                 if(str.charAt(j) < '0' || str.charAt(j) > '9') break;
3175                 if(res > bndry || res == bndry && str.charAt(j) > '7')
3176                     return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
3177                 res = res * 10 + (str.charAt(j) - '0');
3178             }
3179             return sign * res;
3180         }
3181     }
3182 
3183 
3184     /**
3185      * 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
3186      */
3187     class Solution68 {
3188         public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
3189             while(root != null) {
3190                 if(root.val < p.val && root.val < q.val) // p,q 都在 root 的右子树中
3191                     root = root.right; // 遍历至右子节点
3192                 else if(root.val > p.val && root.val > q.val) // p,q 都在 root 的左子树中
3193                     root = root.left; // 遍历至左子节点
3194                 else break;
3195             }
3196             return root;
3197         }
3198     }
3199 
3200     /**
3201      * d
3202      */
3203     class Solution682 {
3204         public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
3205             if(root == null || root == p || root == q) return root;
3206             TreeNode left = lowestCommonAncestor(root.left, p, q);
3207             TreeNode right = lowestCommonAncestor(root.right, p, q);
3208             if(left == null) return right;
3209             if(right == null) return left;
3210             return root;
3211         }
3212     }
3213 
3214 
3215     public static void main(String[] arg){
3216         sysout(reverseWords("I am a student. "));
3217        //sysout(lengthOfLongestSubstring("abcabcbb") );
3218         //reversePairs(new int[]{7,5,6,4});
3219        //sysout(lengthOfLongestSubstring("dvdf") );
3220         /*int[][] matrix =  {{1,   4,  7, 11, 15}
3221                 , {2,   5,  8, 12, 19}
3222                 , {3,   6,  9, 16, 22}
3223                 , {10, 13, 14, 17, 24}
3224                 , {18, 21, 23, 26, 30}};*/
3225         /*int[][] matrix =  {{3,8,6,0,5,9,9,6,3,4,0,5,7,3,9,3},
3226                             {0,9,2,5,5,4,9,1,4,6,9,5,6,7,3,2},
3227                             {8,2,2,3,3,3,1,6,9,1,1,6,6,2,1,9},
3228                             {1,3,6,9,9,5,0,3,4,9,1,0,9,6,2,7},
3229                             {8,6,2,2,1,3,0,0,7,2,7,5,4,8,4,8},
3230                             {4,1,9,5,8,9,9,2,0,2,5,1,8,7,0,9},
3231                             {6,2,1,7,8,1,8,5,5,7,0,2,5,7,2,1},{8,1,7,6,2,8,1,2,2,6,4,0,5,4,1,3},{9,2,1,7,6,1,4,3,8,6,5,5,3,9,7,3},{0,6,0,2,4,3,7,6,1,3,8,6,9,0,0,8},{4,3,7,2,4,3,6,4,0,3,9,5,3,6,9,3},{2,1,8,8,4,5,6,5,8,7,3,7,7,5,8,3},{0,7,6,6,1,2,0,3,5,0,8,0,8,7,4,3},{0,4,3,4,9,0,1,9,7,7,8,6,4,6,9,5},{6,5,1,9,9,2,2,7,4,2,7,2,2,3,7,2},{7,1,9,6,1,2,7,0,9,6,6,4,4,5,1,0},{3,4,9,2,8,3,1,2,6,9,7,0,2,4,2,0},{5,1,8,8,4,6,8,5,2,4,1,6,2,2,9,7}};
3232        */ //sysout( Solution47_2.maxValue(matrix));
3233         // sysout( Solution47.maxValue(matrix));
3234       /*  sysout(translateNum46_动态规划版1(1234120193));
3235         sysout(translateNum46(1234120193));*/
3236        // countDigitOne(120);
3237 
3238    /*    MedianFinder obj = new MedianFinder();
3239        obj.addNum(1);
3240        obj.addNum(2);
3241         sysout(obj.findMedian());
3242         obj.addNum(3);
3243         sysout(obj.findMedian());*/
3244 
3245        /* System.out.println( Arrays.toString ((new Solution38_1()).permutation("fba")));
3246         System.out.println( Arrays.toString ((new Solution38_2()).permutation("fba")));
3247         System.out.println( Arrays.toString ((new Solution38_3()).permutation("fba")));*/
3248       //  System.out.println( majorityElement( new int[]{1}));
3249 
3250       /*  PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>() {
3251             @Override
3252             public int compare(Integer num1, Integer num2) {
3253                 return num2 - num1;
3254             }
3255         });
3256         queue.add(1);
3257         queue.add(13);
3258         queue.add(2);
3259         sysout(queue.poll()); sysout(queue.poll()); sysout(queue.poll());
3260         queue.offer(1);
3261         queue.offer(13);
3262         queue.offer(2);
3263         sysout(queue.poll());sysout( queue.poll());sysout( queue.poll());*/
3264 
3265         /*ListNode node = new ListNode(1);
3266         node.next = new ListNode(2);
3267         node.next.next = new ListNode(3);*/
3268 
3269    /*     TreeNode  oo= deserialize("1,2,None,None,3,4,None,None,5,None,None,");
3270         System.out.println(serialize(deserialize("1,2,None,None,3,4,None,None,5,None,None,")));
3271         System.out.println(oo);*/
3272 
3273        /* int[] nums={1,2,3};
3274         (new Permutations()).permute(nums);*/
3275 
3276        // reverseList2(node);
3277        // System.out.println(printNumbers2(3));
3278        // myPow(2,3);
3279         //cuttingRope2(20);
3280         //System.out.println(Arrays.toString(exchange(new int[]{1,2,3,4,5,6})));
3281         //cuttingRope2(20);
3282 
3283          //System.out.println(movingCount(  20,   30,   20));
3284          //System.out.println(movingCount1(  15,   15,   3));
3285 
3286          /*char[][] board = new char[][]{"ABCE".toCharArray(), "DECS".toCharArray(), "AEEE".toCharArray()};
3287          System.out.println(exist( board, "ABCCED"));
3288          System.out.println(exist1( board, "ABCCED"));*/
3289 
3290         // System.out.println(minArray(new int[]{3,4,5,1,2}));
3291 
3292          /*System.out.println(numWays1(51));
3293          System.out.println(numWays2(51));*/
3294         //System.out.println(fib(6));
3295 
3296        /*  CQueue obj = new CQueue();
3297          obj.appendTail(1);
3298          int param_2 = obj.deleteHead();*/
3299 
3300         /* int[][] matrix =  {{1,   4,  7, 11, 15}
3301                  , {2,   5,  8, 12, 19}
3302                  , {3,   6,  9, 16, 22}
3303                  , {10, 13, 14, 17, 24}
3304                  , {18, 21, 23, 26, 30}};
3305          int target = 16;
3306          System.out.println(findNumberIn2DArray(matrix,  target));*/
3307 
3308       /*   int nums[] = {2, 3, 1, 0, 2, 5, 3};
3309         System.out.println(findRepeatNumber(nums)); */
3310 
3311 //add测试
3312          //结论:add测试一样
3313         /* LinkedList<Integer> linkedList = new LinkedList<>();
3314          Stack<Integer> stack = new Stack<>();
3315 
3316          linkedList.add(1);
3317          linkedList.add(2);
3318          linkedList.add(3);
3319 
3320          stack.add(1);
3321          stack.add(2);
3322          stack.add(3);
3323          System.out.println(linkedList);
3324          System.out.println(stack);
3325 
3326          //push测试
3327          //结论:测试不一样,linkedlist的push是addFirst;stack是在数组尾部addElement
3328          linkedList.push(4);
3329          linkedList.push(5);
3330          linkedList.push(6);
3331 
3332          stack.push(4);
3333          stack.push(5);
3334          stack.push(6);
3335          stack.pop();
3336          stack.pop();
3337          linkedList.pop();
3338          linkedList.pop();
3339          System.out.println(linkedList);
3340          System.out.println(stack);*/
3341      }
3342 
3343     public static void sysout(Object dd){
3344         System.out.println(dd);
3345     }
3346 
3347  }
复制代码

 

posted on   HelloXF_jeff  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示