编程合集: https://www.cnblogs.com/jssj/p/12002760.html

前言:不仅仅要实现,更要提升性能,精益求精,用尽量少的时间复杂度和空间复杂度解决问题。

【程序78】
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

  1. /**
  2. * 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
  3. * 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
  4. * 必须原地修改,只允许使用额外常数空间。
  5. * 以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
  6. * 1,2,3 → 1,3,2
  7. * 3,2,1 → 1,2,3
  8. * 1,1,5 → 1,5,1
  9. */
  10. public class Subject78 {
  11.  
  12. public static void main(String[] args) {
  13. int[] arr = new int[]{2,3,1,3,3};
  14. new Subject78().nextPermutation(arr);
  15. for (int i = 0; i < arr.length; i++) {
  16. System.out.print(arr[i]+" ");
  17. }
  18. }
  19.  
  20. /**
  21. * 下一个最大值
  22. * @param nums
  23. */
  24. public void nextPermutation(int[] nums) {
  25. int lengths = nums.length;
  26. int size = -1;
  27. for (int i = lengths-1; i >= 0; i--) {
  28. if(i-1 >= 0 && nums[i-1] < nums[i]){
  29. size = i-1;
  30. break;
  31. }
  32. }
  33. //如果没有最大的值了
  34. if(size == -1){
  35. for (int i = 0 ,j= lengths-1; i <= j ; i++,j--) {
  36. int tmp = 0;
  37. tmp = nums[i];
  38. nums[i] = nums[j];
  39. nums[j] = tmp;
  40. }
  41. }else{ //处理size后边的数据,重新整理成一个最小数组。
  42. //找到比size位置大的数中的最小数。
  43. int tmp = nums[size+1];
  44. int sizeExchange = size+1;
  45. for (int i = size+1; i < lengths ; i++) {
  46. //这里可以优化
  47. if(nums[i] <= tmp && nums[i] > nums[size]){
  48. tmp = nums[i];
  49. sizeExchange = i;
  50. }
  51. }
  52. nums[sizeExchange] = nums[size];
  53. nums[size] = tmp;
  54. //剩余数据重新重小到大排序
  55. for (int i = size+1 ,j= lengths-1; i <= j ; i++,j--) {
  56. int tmp0 = 0;
  57. tmp0 = nums[i];
  58. nums[i] = nums[j];
  59. nums[j] = tmp0;
  60. }
  61. }
  62. }
  63. }

时间复杂度:O(n)

运行结果:

【程序79】
给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。

  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. /**
  5. * 给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。
  6. */
  7. public class Subject79 {
  8.  
  9. public static void main(String[] args) {
  10.  
  11. System.out.println(new Subject79().longestValidParentheses("(()()()(()))))))"));
  12. }
  13.  
  14. public int longestValidParentheses(String s) {
  15. int lengths = s.length();
  16. if(lengths <= 0){
  17. return 0;
  18. }
  19. char[] arr = s.toCharArray();
  20.  
  21. List<Integer> list = new ArrayList<>();
  22.  
  23. /**
  24. * 将不可以匹配的括号留下,并且记录位置。
  25. */
  26. for (int i = 0; i < arr.length; i++) {
  27. if('(' == arr[i]){
  28. list.add(i);
  29. }else{
  30. int size = list.size();
  31. if(')' == arr[i] && list.size() > 0 && '(' == arr[list.get(size-1)]){
  32. list.remove(size-1);
  33. }else{
  34. list.add(i);
  35. }
  36. }
  37. }
  38.  
  39. //获取最大间隔时间
  40. int maxLength = 0;
  41. for (int i = 0; i < list.size() ; i++) {
  42. if( i == 0 ){
  43. maxLength = list.get(i);
  44. }else {
  45. int tmp = list.get(i) - list.get(i-1) -1;
  46. if(tmp > maxLength){
  47. maxLength = tmp;
  48. }
  49. }
  50. }
  51.  
  52. if(list.size() > 0){
  53. int endLength = lengths - list.get(list.size()-1) -1;
  54. if(endLength > maxLength){
  55. maxLength = endLength;
  56. }
  57. } else {
  58. maxLength = lengths;
  59. }
  60.  
  61. return maxLength;
  62. }
  63. }

时间复杂度:O(n)

运行结果:

【程序80】
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组[0,1,2,4,5,6,7]可能变为[4,5,6,7,0,1,2])。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回-1。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是O(logn) 级别。

示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4

示例2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1

  1. /**
  2. * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。
  3. * ( 例如,数组[0,1,2,4,5,6,7]可能变为[4,5,6,7,0,1,2])。
  4. * 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回-1。
  5. * 你可以假设数组中不存在重复的元素。
  6. * 你的算法时间复杂度必须是O(logn) 级别。
  7. *
  8. * 示例 1:
  9. * 输入: nums = [4,5,6,7,0,1,2], target = 0
  10. * 输出: 4
  11. *
  12. * 示例2:
  13. * 输入: nums = [4,5,6,7,0,1,2], target = 3
  14. * 输出: -1
  15. */
  16. public class Subject80 {
  17.  
  18. int [] nums;
  19. int target;
  20.  
  21. public static void main(String[] args) {
  22. int[] nums = new int[]{1};
  23. System.out.println(new Subject80().search(nums,0));
  24. }
  25.  
  26. public int search(int[] nums, int target) {
  27. this.nums = nums;
  28. this.target = target;
  29.  
  30. int n = nums.length;
  31.  
  32. if (n == 0)
  33. return -1;
  34. if (n == 1)
  35. return this.nums[0] == target ? 0 : -1;
  36.  
  37. /**
  38. * 找到旋转节点
  39. */
  40. int rotate_index = find_rotate_index(0, n - 1);
  41.  
  42. // if target is the smallest element
  43. if (nums[rotate_index] == target)
  44. return rotate_index;
  45. // if array is not rotated, search in the entire array
  46. if (rotate_index == 0)
  47. return search(0, n - 1);
  48. if (target < nums[0])
  49. // search in the right side
  50. return search(rotate_index, n - 1);
  51. // search in the left side
  52. return search(0, rotate_index);
  53. }
  54.  
  55. /**
  56. * 找旋转节点
  57. * @param left
  58. * @param right
  59. * @return
  60. */
  61. public int find_rotate_index(int left, int right) {
  62. if (nums[left] < nums[right])
  63. return 0;
  64.  
  65. while (left <= right) {
  66. int pivot = (left + right) / 2;
  67. if (nums[pivot] > nums[pivot + 1])
  68. return pivot + 1;
  69. else {
  70. if (nums[pivot] < nums[left])
  71. right = pivot - 1;
  72. else
  73. left = pivot + 1;
  74. }
  75. }
  76. return 0;
  77. }
  78.  
  79. /**
  80. * Binary search 二分查找法
  81. * @param left
  82. * @param right
  83. * @return
  84. */
  85. public int search(int left, int right) {
  86. while (left <= right) {
  87. int pivot = (left + right) / 2;
  88. if (nums[pivot] == target)
  89. return pivot;
  90. else {
  91. if (target < nums[pivot])
  92. right = pivot - 1;
  93. else
  94. left = pivot + 1;
  95. }
  96. }
  97. return -1;
  98. }
  99. }

时间复杂度:O(logN)

运行结果:

【程序81】
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是O(log n) 级别。
如果数组中不存在目标值,返回[-1, -1]。

  1. /**
  2. * 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
  3. * 你的算法时间复杂度必须是O(log n) 级别。
  4. * 如果数组中不存在目标值,返回[-1, -1]。
  5. */
  6. public class Subject81 {
  7. public static void main(String[] args) {
  8. int[] arr = new int[]{1};
  9. int[] result = new Subject81().searchRange(arr,1);
  10. for (int i = 0; i < result.length; i++) {
  11. System.out.print(result[i]+" ");
  12. }
  13. }
  14.  
  15. /**
  16. * 二分查找法
  17. * @param nums
  18. * @param target
  19. * @return
  20. */
  21. public int[] searchRange(int[] nums, int target) {
  22. int left = 0;
  23. int right = nums.length-1;
  24. int pivot = -1;
  25. boolean flag = false;
  26. while (left <= right) {
  27. pivot = (left + right) / 2;
  28. if (nums[pivot] == target) {
  29. flag = true;
  30. break;
  31. } else {
  32. if (target < nums[pivot])
  33. right = pivot - 1;
  34. else
  35. left = pivot + 1;
  36. }
  37. }
  38. if(!flag){
  39. pivot = -1;
  40. }
  41. if(pivot != -1){
  42. int leftTmp = pivot;
  43. int rightTmp = pivot;
  44. while(leftTmp >= 0){
  45. leftTmp = leftTmp-1;
  46. if(leftTmp < 0 || nums[leftTmp] != target){
  47. break;
  48. }
  49. }
  50. while(rightTmp <= nums.length-1){
  51. rightTmp = rightTmp+1;
  52. if(rightTmp > nums.length-1 ||nums[rightTmp] != target){
  53. break;
  54. }
  55. }
  56. return new int[]{leftTmp+1,rightTmp-1};
  57. }else{
  58. return new int[]{-1,-1};
  59. }
  60. }
  61. }

时间复杂度:O(log2​n)

运行结果:

【程序82】
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

  1. /**
  2. * 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
  3. */
  4. public class Subject82 {
  5.  
  6. public static void main(String[] args) {
  7. int[] arr = new int[]{1,3,4,5,6,7,9,10};
  8. System.out.println(new Subject82().searchInsert(arr,8));
  9. }
  10.  
  11. public int searchInsert(int[] nums, int target) {
  12. if(nums.length < 0){
  13. return 0;
  14. }
  15. int size = this.search(0,nums.length-1,nums,target);
  16. if(nums[size] == target){
  17. return size;
  18. }else{
  19. if(nums[size] > target){
  20. return size;
  21. }else{
  22. return size+1;
  23. }
  24. }
  25. }
  26.  
  27. /**
  28. * Binary search 二分查找法
  29. * @param left
  30. * @param right
  31. * @return
  32. */
  33. public int search(int left, int right,int[] nums, int target) {
  34. while (left <= right) {
  35. int pivot = (left + right) / 2;
  36. if (nums[pivot] == target)
  37. return pivot;
  38. else {
  39. if (target < nums[pivot])
  40. right = pivot - 1;
  41. else
  42. left = pivot + 1;
  43. }
  44. }
  45. if(right <= -1){
  46. return left;
  47. }
  48. if(left >= nums.length){
  49. return right;
  50. }
  51. return left <= right? left :right;
  52. }
  53. }

时间复杂度:O(logn)

运行结果:

【程序83】
判断一个9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
数字1-9在每一行只能出现一次。
数字1-9在每一列只能出现一次。
数字1-9在每一个以粗实线分隔的3x3宫内只能出现一次。

  1. /**
  2. * 判断一个9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
  3. * 数字1-9在每一行只能出现一次。
  4. * 数字1-9在每一列只能出现一次。
  5. * 数字1-9在每一个以粗实线分隔的3x3宫内只能出现一次。
  6. */
  7. public class Subject83 {
  8. public static void main(String[] args) {
  9. char[][] board = new char[][]{
  10. {'.','.','.','.','5','.','.','1','.'},
  11. {'.','4','.','3','.','.','.','.','.'},
  12. {'.','.','.','.','.','3','.','.','1'},
  13. {'8','.','.','.','.','.','.','2','.'},
  14. {'.','.','2','.','7','.','.','.','.'},
  15. {'.','1','5','.','.','.','.','.','.'},
  16. {'.','.','.','.','.','2','.','.','.'},
  17. {'.','2','.','9','.','.','.','.','.'},
  18. {'.','.','4','.','.','.','.','.','.'}};
  19. System.out.println( new Subject83().isValidSudoku(board));
  20. }
  21.  
  22. public boolean isValidSudoku(char[][] board) {
  23. int[] rowCnt = new int[9];
  24. int[] colCnt = new int[9];
  25. int[] boxCnt = new int[9];
  26. for (int i = 0; i < 9; i++) {
  27. for (int j = 0; j < 9; j++) {
  28. if ('.' == board[i][j]) {
  29. continue;
  30. }
  31. //处理成int型
  32. int num = board[i][j] - 48;
  33. // 处理行
  34. if ((rowCnt[i] >> num) % 2 == 1) {
  35. return false;
  36. } else {
  37. rowCnt[i] += 1 << num;
  38. }
  39. // 处理列
  40. if ((colCnt[j] >> num) % 2 == 1) {
  41. return false;
  42. } else {
  43. colCnt[j] += 1 << num;
  44. }
  45. // 处理框
  46. int boxNum = i / 3 * 3 + j / 3;
  47. if ((boxCnt[boxNum] >> num) % 2 == 1) {
  48. return false;
  49. } else {
  50. boxCnt[boxNum] = boxCnt[boxNum] + (1 << num);
  51. }
  52. }
  53. }
  54. return true;
  55. }
  56. }

时间复杂度:O(1)

运行结果:

【程序84】
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字1-9在每一行只能出现一次。
数字1-9在每一列只能出现一次。
数字1-9在每一个以粗实线分隔的3x3宫内只能出现一次。
空白格用'.'表示。

  1. /**
  2. * 编写一个程序,通过已填充的空格来解决数独问题。
  3. * 一个数独的解法需遵循如下规则:
  4. * 数字1-9在每一行只能出现一次。
  5. * 数字1-9在每一列只能出现一次。
  6. * 数字1-9在每一个以粗实线分隔的3x3宫内只能出现一次。
  7. * 空白格用'.'表示。
  8. */
  9. public class Subject84 {
  10. public static void main(String[] args) {
  11. char[][] board = new char[][]{
  12. {'.','.','9','7','4','8','.','.','.'},
  13. {'7','.','.','.','.','.','.','.','.'},
  14. {'.','2','.','1','.','9','.','.','.'},
  15. {'.','.','7','.','.','.','2','4','.'},
  16. {'.','6','4','.','1','.','5','9','.'},
  17. {'.','9','8','.','.','.','3','.','.'},
  18. {'.','.','.','8','.','3','.','2','.'},
  19. {'.','.','.','.','.','.','.','.','6'},
  20. {'.','.','.','2','7','5','9','.','.'}};
  21. new Subject84().solveSudoku(board);
  22. for (int i = 0; i < 9; i++) {
  23. for (int j = 0; j < 9; j++) {
  24. System.out.print(board[i][j]+" ");
  25. }
  26. System.out.println();
  27. }
  28. }
  29.  
  30. // box size
  31. int n = 3;
  32. // row size
  33. int N = n * n;
  34.  
  35. int [][] rows = new int[N][N + 1];
  36. int [][] columns = new int[N][N + 1];
  37. int [][] boxes = new int[N][N + 1];
  38.  
  39. char[][] board;
  40.  
  41. boolean sudokuSolved = false;
  42.  
  43. public boolean couldPlace(int d, int row, int col) {
  44. /*
  45. 检查是否可以在(行,列)单元格中放置数字d
  46. */
  47. int idx = (row / n ) * n + col / n;
  48. return rows[row][d] + columns[col][d] + boxes[idx][d] == 0;
  49. }
  50.  
  51. public void placeNumber(int d, int row, int col) {
  52. /*
  53. 在(行,列)单元格中放置数字d
  54. */
  55. int idx = (row / n ) * n + col / n;
  56.  
  57. rows[row][d]++;
  58. columns[col][d]++;
  59. boxes[idx][d]++;
  60. board[row][col] = (char)(d + '0');
  61. }
  62.  
  63. public void removeNumber(int d, int row, int col) {
  64. /*
  65. 删除一个无法找到解决方案的数字
  66. */
  67. int idx = (row / n ) * n + col / n;
  68. rows[row][d]--;
  69. columns[col][d]--;
  70. boxes[idx][d]--;
  71. board[row][col] = '.';
  72. }
  73.  
  74. public void placeNextNumbers(int row, int col) {
  75. /*
  76. 递归调用回溯函数
  77. 继续放置数字
  78. 直到我们找到解决办法
  79. */
  80. // 如果我们在最后一个牢房里
  81. // 这意味着我们有办法
  82. if ((col == N - 1) && (row == N - 1)) {
  83. sudokuSolved = true;
  84. }
  85. // 如果还没有
  86. else {
  87. // 如果我们排在最后
  88. // 到下一排
  89. if (col == N - 1) backtrack(row + 1, 0);
  90. // go to the next column
  91. else backtrack(row, col + 1);
  92. }
  93. }
  94.  
  95. public void backtrack(int row, int col) {
  96. /*
  97. 回溯
  98. */
  99. // 如果单元格是空的
  100. if (board[row][col] == '.') {
  101. // 对从1到9的所有数字进行迭代
  102. for (int d = 1; d < 10; d++) {
  103. if (couldPlace(d, row, col)) {
  104. placeNumber(d, row, col);
  105. placeNextNumbers(row, col);
  106. // 如果数独问题解决了,就不必回头了。
  107. // 因为独联解决方案是有希望的
  108. if (!sudokuSolved) removeNumber(d, row, col);
  109. }
  110. }
  111. }
  112. else placeNextNumbers(row, col);
  113. }
  114.  
  115. public void solveSudoku(char[][] board) {
  116. this.board = board;
  117.  
  118. // 初始化行、列和框
  119. for (int i = 0; i < N; i++) {
  120. for (int j = 0; j < N; j++) {
  121. char num = board[i][j];
  122. if (num != '.') {
  123. int d = Character.getNumericValue(num);
  124. placeNumber(d, i, j);
  125. }
  126. }
  127. }
  128. backtrack(0, 0);
  129. }
  130. }

时间复杂度:O(9!^9)

运行结果:

【程序85】
报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
1被读作"one 1"("一个一") , 即11。
11 被读作"two 1s"("两个一"), 即21。
21 被读作"one 2", "one 1"("一个二","一个一"), 即1211。
给定一个正整数 n(1 ≤n≤ 30),输出报数序列的第 n 项。

  1. /**
  2. * 报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
  3. * 1. 1
  4. * 2. 11
  5. * 3. 21
  6. * 4. 1211
  7. * 5. 111221
  8. * 1被读作"one 1"("一个一") , 即11。
  9. * 11 被读作"two 1s"("两个一"), 即21。
  10. * 21 被读作"one 2", "one 1"("一个二","一个一"), 即1211。
  11. * 给定一个正整数 n(1 ≤n≤ 30),输出报数序列的第 n 项。
  12. */
  13. public class Subject85 {
  14.  
  15. public static void main(String[] args) {
  16. System.out.println(new Subject85().countAndSay(6));
  17. }
  18.  
  19. public String countAndSay(int n) {
  20. if(n == 1){
  21. return "1";
  22. }else{
  23. String str = countAndSay(n-1);
  24. char[] chArr = str.toCharArray();
  25. StringBuilder strTmp = new StringBuilder("");
  26. char ch = chArr[0] ;
  27. int count = 0;
  28. for (int i = 0; i < chArr.length; i++) {
  29. if(ch == chArr[i]){
  30. count++;
  31. }else{
  32. strTmp.append(count).append(ch);
  33. ch = chArr[i];
  34. count = 1;
  35. }
  36. }
  37. strTmp.append(count).append(ch);
  38. return strTmp.toString();
  39. }
  40. }
  41. }

时间复杂度:O(n)

运行结果:

【程序86】
给定一个无重复元素的数组candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
candidates中的数字可以无限制重复被选取。
说明:
所有数字(包括target)都是正整数。
解集不能包含重复的组合。

  1. import java.util.*;
  2.  
  3. /**
  4. * 给定一个无重复元素的数组candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
  5. * candidates中的数字可以无限制重复被选取。
  6. * 说明:
  7. * 所有数字(包括target)都是正整数。
  8. * 解集不能包含重复的组合。
  9. */
  10. public class Subject86 {
  11.  
  12. private List<List<Integer>> res = new ArrayList<>();
  13. private int[] candidates;
  14. private int len;
  15.  
  16. public static void main(String[] args) {
  17. int[] candidates = new int[]{2,3,6,7};
  18. List<List<Integer>> list = new Subject86().combinationSum(candidates,7);
  19. System.out.println(list);
  20. }
  21.  
  22. private void findCombinationSum(int residue, int start, Stack<Integer> pre) {
  23. if (residue == 0) {
  24. // Java 中可变对象是引用传递,因此需要将当前 path 里的值拷贝出来
  25. res.add(new ArrayList<>(pre));
  26. return;
  27. }
  28. // 优化添加的代码2:在循环的时候做判断,尽量避免系统栈的深度
  29. // residue - candidates[i] 表示下一轮的剩余,如果下一轮的剩余都小于 0 ,就没有必要进行后面的循环了
  30. // 这一点基于原始数组是排序数组的前提,因为如果计算后面的剩余,只会越来越小
  31. for (int i = start; i < len && residue - candidates[i] >= 0; i++) {
  32. pre.add(candidates[i]);
  33. // 【关键】因为元素可以重复使用,这里递归传递下去的是 i 而不是 i + 1
  34. findCombinationSum(residue - candidates[i], i, pre);
  35. pre.pop();
  36. }
  37. }
  38.  
  39. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  40. int len = candidates.length;
  41. if (len == 0) {
  42. return res;
  43. }
  44. // 优化添加的代码1:先对数组排序,可以提前终止判断
  45. Arrays.sort(candidates);
  46. this.len = len;
  47. this.candidates = candidates;
  48. findCombinationSum(target, 0, new Stack<>());
  49. return res;
  50. }
  51. }

时间复杂度:O(2^n)

运行结果:

【程序87】
给定一个数组candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
candidates中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。

  1. import java.util.ArrayList;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. import java.util.Stack;
  5.  
  6. /**
  7. * 给定一个数组candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
  8. * candidates中的每个数字在每个组合中只能使用一次。
  9. * 说明:
  10. * 所有数字(包括目标数)都是正整数。
  11. * 解集不能包含重复的组合。
  12. */
  13. public class Subject87 {
  14.  
  15. private List<List<Integer>> res = new ArrayList<>();
  16.  
  17. public static void main(String[] args) {
  18. int[] candidates = new int[]{10,1,2,7,6,1,5};
  19. List<List<Integer>> list = new Subject87().combinationSum2(candidates,7);
  20. System.out.println(list);
  21. }
  22.  
  23. public List<List<Integer>> combinationSum2(int[] candidates, int target) {
  24. int len = candidates.length;
  25. if (len == 0) {
  26. return res;
  27. }
  28. // 优化添加的代码1:先对数组排序,可以提前终止判断
  29. Arrays.sort(candidates);
  30. findCombinationSum(target, 0, new Stack<>(),candidates);
  31. return res;
  32. }
  33.  
  34. private void findCombinationSum(int residue, int start, Stack<Integer> pre, int[] candidates) {
  35. if (residue == 0) {
  36. // Java 中可变对象是引用传递,因此需要将当前 path 里的值拷贝出来
  37. List list= new ArrayList<>(pre);
  38. res.add(list);
  39. return;
  40. }
  41. // 优化添加的代码2:在循环的时候做判断,尽量避免系统栈的深度
  42. // residue - candidates[i] 表示下一轮的剩余,如果下一轮的剩余都小于 0 ,就没有必要进行后面的循环了
  43. // 这一点基于原始数组是排序数组的前提,因为如果计算后面的剩余,只会越来越小
  44. for (int i = start; i < candidates.length && residue - candidates[i] >= 0; i++) {
  45. if( i-1 >= 0 && candidates[i] == candidates[i-1]){
  46. continue;
  47. }
  48. pre.add(candidates[i]);
  49. // 【关键】因为元素可以重复使用,这里递归传递下去的是 i 而不是 i + 1
  50. findCombinationSum(residue - candidates[i], i, pre,this.copyArr2(candidates,i));
  51. pre.pop();
  52. }
  53. }
  54.  
  55. public int[] copyArr2(int[] candidatesTmp,int index){
  56. int[] candidates = new int[candidatesTmp.length-1];
  57. for (int i = 0,j = 0; i < candidatesTmp.length; i++) {
  58. if(index == i){
  59. continue;
  60. }else{
  61. candidates[j++] = candidatesTmp[i];
  62. }
  63. }
  64. return candidates;
  65. }
  66. }

时间复杂度:O(n!)

运行结果:

以上题目均来自:https://leetcode-cn.com/ ,如果你热爱编码,热爱算法,该网站一定适合你。

《Java练习题》进阶练习题(四)的更多相关文章

  1. java语言进阶(四)_Map_斗地主案例

    第一章 Map集合 1.1 概述 现实生活中常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射.Java提供了专门的集合类用来存放这种 ...

  2. java基础进阶篇(四)_HashMap------【java源码栈】

    目录 一.前言 二.特点和常见问题 二.接口定义 三.初始化构造函数 四.HashMap内部结构 五.HashMap的存储分析 六.HashMap的读取分析 七.常用方法 八.HashMap 的jav ...

  3. 6、50道JAVA基础编程练习题跟答案

    50道JAVA基础编程练习题 [程序1] 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 程序分析 ...

  4. 50道JAVA基础编程练习题

    50道JAVA基础编程练习题 [程序1] 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少? 程序分析 ...

  5. 50道JAVA基础编程练习题 - 题目

    50道JAVA基础编程练习题[1]题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? [2]题目:判断 ...

  6. 【视频+图文】Java经典基础练习题(三):输入3个整数,并将其由小到大输出

    目录 一.视频讲解 二.思路分析 总结: 三.代码+详解+结果 四.彩蛋 能解决题目的代码并不是一次就可以写好的 我们需要根据我们的思路写出后通过debug模式找到不足再进行更改 多次测试后才可得到能 ...

  7. Java线程间通信方式剖析——Java进阶(四)

    原创文章,同步发自作者个人博客,转载请在文章开头处以超链接注明出处 http://www.jasongj.com/java/thread_communication/ CountDownLatch C ...

  8. J2EE进阶(十四)超详细的Java后台开发面试题之Spring IOC与AOP

    J2EE进阶(十四)超详细的Java后台开发面试题之Spring IOC与AOP 前言   搜狐畅游笔试题中有一道问答题涉及到回答谈谈对Spring IOC与AOP的理解.特将相关内容进行整理.    ...

  9. 【视频+图文】Java基础经典练习题(一)输出2-100之间的素数,及素数个数

    目录 第一题:判断2-100之间有多少个素数,并输出所有素数. 1.视频讲解: 2.思路分析: 代码讲解:以i=4为例 4.为大家准备了彩蛋: 能解决题目的代码并不是一次就可以写好的 我们需要根据我们 ...

  10. Java的进阶之道

    Java的进阶之道 一.温馨提示 尽量用google查找技术资料.(条件允许的话) 有问题在stackoverflow找找,大部分都已经有人回答. 多看官方的技术文档. ibm developerwo ...

随机推荐

  1. 网络图片的获取以及二级缓存策略(Volley框架+内存LruCache+磁盘DiskLruCache)

    在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量.对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理 ...

  2. 在React旧项目中安装并使用TypeScript的实践

    前言 本篇文章默认您大概了解什么是TypeScript,主要讲解如何在React旧项目中安装并使用TypeScript. 写这个的目的主要是网上关于TypeScript这块的讲解虽然很多,但都是一些语 ...

  3. Linux(CentOS65)

    首先下载VMware,然后下载CentOS镜像文件,VM的版本尽量高一点,因为软件一般都有向下兼容性,如果版本太低,可能匹配不了CentOS. 安装VMTools工具 主要用于虚拟主机显示优化与调整, ...

  4. Linux LVM 配置

    本文出自 “www.kisspuppet.com” 博客,请务必保留此出处http://dreamfire.blog.51cto.com/418026/1084729 许多Linux使用者安装操作系统 ...

  5. Spring Boot整合Mybatis报错InstantiationException: tk.mybatis.mapper.provider.base.BaseSelectProvider

    Spring Boot整合Mybatis时一直报错 后来发现原来主配置类上的MapperScan导错了包 由于我使用了通用Mapper,所以应该导入通用mapper这个包

  6. Linux01机和Linux02机的切换 和秘钥的配置

    先试一下 01机和02机是否可以切换成功 使用 ssh root@ip地址 输入密码 ifconfig查看ip是否正确 切换回01机 01机配置的秘钥 查看隐形文件 01机配置秘钥 输入  ssh-c ...

  7. JavaScript实战实例剖析——(激励倒计时日历)

    如今JavaScript在前端开发中的地位越来越高,掌握JavaScript的深度往往能决定你职业道路深远,这次通过制作 带着倒计时功能的激励日历的小实例,进一步细致的掌握JavaScript的语法与 ...

  8. oracle 触发器(自增写法)

    触发器trigger 触发器我们也可以认为是存储过程,是一种特殊的存储过程. 存储过程:有输入参数和输出参数,定义之后需要调用 触发器:没有输入参数和输出参数,定义之后无需调用,在适当的时候会自动执行 ...

  9. WebGPU学习(一): 开篇

    介绍 大家好,本系列从0开始学习WebGPU API,并给出相关的demo. WebGPU介绍 WebGPU相当于DX12/Vulkan,能让程序员更灵活地操作GPU,从而大幅提升性能. 为什么要学习 ...

  10. HTML 表格 各标签使用的标准顺序(心得)

    <table>   <caption>标题</caption>     <colground>     <col>     <col& ...