- 148. 排序链表 代码很复杂">148. 排序链表 代码很复杂
- 72. 编辑距离">72. 编辑距离
- 10. 正则表达式匹配">10. 正则表达式匹配
- 32. 最长有效括号">32. 最长有效括号
- 300. 最长递增子序列">300. 最长递增子序列
- 88. 合并两个有序数组">88. 合并两个有序数组
- 41. 缺失的第一个正数">41. 缺失的第一个正数
- 440. 字典序的第K小数字">440. 字典序的第K小数字
- 198. 打家劫舍">198. 打家劫舍
- 213. 打家劫舍 II">213. 打家劫舍 II
- 337. 打家劫舍 III">337. 打家劫舍 III
- 79. 单词搜索">79. 单词搜索
- 78. 子集">78. 子集
- 90. 子集 II">90. 子集 II
- 322. 零钱兑换">322. 零钱兑换
- 518. 零钱兑换 II">518. 零钱兑换 II
148. 排序链表 代码很复杂

- 链表的归并排序
常数空间复杂度需要
- 自底向上归并
- for(int i = 1;i<len;i*=2)进行归并
now = dummy.next; // 注意:now=head是不对的,因为head在归并之后不一定位于头节点
class Solution {public ListNode sortList(ListNode head) {ListNode dummy = new ListNode(-1);ListNode pre = dummy;dummy.next = head;ListNode now = head;int len = 0;while(now!=null){now=now.next;len++;}// 划分长度for(int i = 1; i < len;i*=2) {// 初始化第一个位置的节点now = dummy.next; // 注意:now=head是不对的,因为head在归并之后不一定位于头节点pre = dummy;while(now!=null) {ListNode ha = now; // 前一段的最开始int n = 1;while(n<i) { // 走给定长度的距离n++;if(now!=null){now = now.next;} else {break;}}ListNode hb = null; // 第二段的最开始// 第一段的长度>=i,将第一段的最后指向nullif(now!=null){hb = now.next;now.next = null;now = hb;}// 划分第二段n=1;while(n<i) {n++;if(now!=null){now = now.next;}else{break;}}// 下一段的开始ListNode next = null;if(now!=null){next = now.next;now.next = null;now = next;}pre.next = merge(ha,hb);while(pre.next!=null) {pre = pre.next; // 后面归并的pre,代表下一段的head之前}}}return dummy.next;}// 二路归并ListNode merge(ListNode h1,ListNode h2) {ListNode dummy = new ListNode(-1);ListNode pre = dummy;while(h1!=null && h2!=null) {if(h1.val<h2.val){pre.next = h1;h1 = h1.next;} else {pre.next = h2;h2 = h2.next;}pre = pre.next;}if(h1!=null) {pre.next = h1;} else if(h2!=null) {pre.next = h2;}return dummy.next;}}
72. 编辑距离

如果A[i]==B[j] ,那么dp[i][j] = dp[i-1][j-1];
- 如果不等于,那么dp[i][j] = 1 + Math.min(Math.min(dp[i-1][j-1],dp[i][j-1]),dp[i-1][j]);
初始状态是填充一个空格,方便初始化
- word2=” “+word2; word1=” “+word1;

class Solution {public int minDistance(String word1, String word2) {word2=" "+word2;word1=" "+word1;int len1 = word1.length();int len2 = word2.length();if(len1==1) return len2-1;if(len2==1) return len1-1;int[][] dp = new int[len1][len2];dp[0][0] = 0; // 空格跟空格相等// 空格跟字符的编辑距离就是第二个串长度for(int i = 1; i < len1;i++) {dp[i][0] = i;}for(int i = 1; i < len2;i++) {dp[0][i] = i;}for(int i = 1;i<len1;i++){for(int j = 1;j < len2;j++) {if(word1.charAt(i)==word2.charAt(j)){// 当前字符相等相等dp[i][j] = Math.min(Math.min(dp[i-1][j-1],dp[i][j-1]+1),dp[i-1][j]+1);}else {// 当前字符不等dp[i][j] = 1 + Math.min(Math.min(dp[i-1][j-1],dp[i][j-1]),dp[i-1][j]);}}}return dp[len1-1][len2-1];}}
10. 正则表达式匹配


class Solution {public boolean isMatch(String s, String p) {// 填充空格,方便初始化s = " "+s;p = " "+p;int m = s.length();int n = p.length();boolean[][] dp = new boolean[m][n];dp[0][0]=true;for(int i = 2; i < n;i+=2) {if(p.charAt(i)=='*'){ // a*b*c这样格式的串dp[0][i] = true;}else{break;}}for(int i = 1; i< m ;i++) {dp[i][0] = false;for(int j = 1; j < n ;j++) {if(p.charAt(j)=='*') {dp[i][j] = dp[i][j-2] || (dp[i-1][j] && match(s.charAt(i),p.charAt(j-1)));} else{if(match(s.charAt(i),p.charAt(j))) {dp[i][j] = dp[i-1][j-1];}}}}// for(int i = 0 ; i < m;i++) {// for(int j = 0; j < n;j++) {// System.out.print(dp[i][j]+" ");// }// System.out.println();// }return dp[m-1][n-1];}boolean match(char a,char b){if(b=='.'){return true;}return a==b;}}
32. 最长有效括号

统计以当前字符作为结尾的有效括号长度
- 当前是(,略过
- 当前是)
- 如果前一个是(,则dp[i]=dp[i-2]+2
- 如果前一个是)
- 得出前一个对应的长度len,判断dp[i-len-1]是不是(
- 如果是:dp[i] = dp[i-1]+2,
- 再加上dp[i-len-2] ()(())这样的情况
- 如果是:dp[i] = dp[i-1]+2,
- 得出前一个对应的长度len,判断dp[i-len-1]是不是(

class Solution {public int longestValidParentheses(String s) {char[] ch = s.toCharArray();int[] dp = new int[ch.length];if(ch.length<=1)return 0;dp[0] = 0;int max = 0;for(int i = 1; i < ch.length;i++) {if(ch[i]==')'){if(ch[i-1]=='(') { //....()dp[i] = 2;if(i-2>=0) {dp[i] += dp[i-2];}} else { // ....))int len = dp[i-1];if(i-len-1>=0 && ch[i-len-1]=='(') {dp[i] = len +2;if(i-dp[i]>=0) {dp[i] += dp[i-dp[i]];}}}max = Math.max(max,dp[i]);}}// System.out.println(Arrays.toString(dp));return max;}}
300. 最长递增子序列

- 设置一个数组,pos[i]代表长度为i+1的子序列的末尾的那个最大值

class Solution {public int lengthOfLIS(int[] nums) {int len = nums.length;if(len<=1)return len;int[] pos = new int[len+1]; // pos[i]存储的是i+1长度的递增子序列中最大的元素位置pos[0] = nums[0];int usePosLen = 1;for(int i = 1;i < len; i++) {int p = find(pos,0,usePosLen,nums[i]);pos[p] = nums[i];if(p==usePosLen)usePosLen++;}return usePosLen;}// 找大于等于target的第一个数的位置int find(int[] pos,int left,int right,int target) {while(left<right) {int m = left+right>>1;if(target>pos[m]) {left = m+1;} else {right=m;}}return left;}}
88. 合并两个有序数组

因为1的长度是足够的,所以在1的最后位置开始往前插入

class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {if(m==0) {for(int i = 0 ; i < n;i++) {nums1[i] = nums2[i];}return;}if(n==0)return;int index1 = m-1; // num1指针int index2 = n-1; // num2指针int index = m+n-1; // 排序后的数组的指针while(index1>=0 && index2>=0) {if(nums1[index1] > nums2[index2]) {nums1[index] = nums1[index1];index1--;} else{nums1[index] = nums2[index2];index2--;}index--;}// 如果数组2还有元素,往后退while(index2>=0) {nums1[index--] = nums2[index2--];}// 不需要管数组1有没有元素return;}}
41. 缺失的第一个正数
.
- 参考剑指offer哪一题,可以归位
- 返回值在[1,len+1]中产生
- 对于不在这个区间的数字,可以忽略
- 交换的两个数字不能一样,要不死循环
while(nums[i]>=1 && nums[i]<=len && nums[nums[i] - 1] != nums[i])
借鉴:如果给定一个数组且需要给出空间复杂度为1的,可以使用原数组作为hash进行修改
class Solution {public int firstMissingPositive(int[] nums) {int len = nums.length;if(len<=0){return 1;}for(int i = 0 ; i < len;i++) {while(nums[i]>=1 && nums[i]<=len && nums[nums[i] - 1] != nums[i]) {System.out.println(i);int t = nums[nums[i]-1];nums[nums[i]-1] = nums[i];nums[i] = t;}}for(int i = 0 ; i < len;i++) {if(nums[i]!=i+1)return i+1;}return len+1;}}
440. 字典序的第K小数字




count=k 的时候,注意因为之前k—了,所以count=1的时候说明的是这个前缀只有一种长度的,k=1的时候说明的是当前前缀开头的下一个的元素(因为k=0的时候代表的是当前前缀开头的那个元素)
- 例如:1,10,100;题目给定的k=3
- 首先cur=1;K—;k=2
- 代表查找cur=1开头的前缀的下两个元素
- getcount = 3;代表以1开头的有三个元素
- 因为count>k(3>2);所以k—;cur*=10;
class Solution {public int findKthNumber(int n, int k) {long start = 1;while(k>0) {long count = find(start,n); // 当前的数量if(count>=k) { // 被当前前缀包围k--; // 减去start占用的数量if(k==0){return (int)start;}start*=10; // 比如start=1的时候,如果包含在1前缀里面,那么下一个该比较的就是10了} else {k-=count; // 比如start=10的时候,如果不包含在10前缀里面,那么下一个该比较的就是11了start++;}}return 0;}// 以start作为前缀的的数字的数量long find(long start,int n) {long end = start+1;long res = 0;while(start<=n) {res += Math.min(end,n+1)-start; // n+1是为了对应start=1,n=1这样的情况,end是start开头的前缀的第一个不符合的,n+1应该和end对应start *= 10;end *= 10;}return res;}}
198. 打家劫舍


class Solution {public int rob(int[] nums) {int len = nums.length;if(len<=1){return nums[len-1];}int[] dp = new int[len];dp[0] = nums[0];dp[1] = Math.max(nums[0],nums[1]);int max =dp[1];for(int i = 2; i < len;i++) {dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]);max = Math.max(max,dp[i]);}return max;}}
213. 打家劫舍 II
337. 打家劫舍 III


class Solution {int max = 0;public int rob(TreeNode root) {find(root);return max;}// int[0]:包含当前根// int[1]:不包含当前根的最大值public int[] find(TreeNode root) {if(root==null){return new int[]{0,0};}int[] l = find(root.left);int[] r = find(root.right);// 不包括自己的时候,也可能没有用到自己的直接子树int noSelf = Math.max(l[0],l[1])+Math.max(r[0],r[1]);max = Math.max(noSelf,max);// 用到自己的时候,一定不能包含自己的子树int containSelf = root.val+l[1]+r[1];max = Math.max(containSelf,max);return new int[]{containSelf,noSelf};}}
79. 单词搜索

class Solution {int m = 0;int n = 0;public boolean exist(char[][] board, String word) {m = board.length;n = board[0].length;boolean[][] flag = new boolean[m][n];for(int i = 0 ; i < m ; i++) {for(int j = 0; j < n ;j++) {if(find(board,i,j,word,0,flag)){return true;}}}return false;}boolean find(char[][] board,int a,int b,String word,int index,boolean[][] flag) {if(index==word.length()) // 已经匹配完所有的字符return true;if(a>=m || b>=n || a<0 || b<0) // 超出棋盘边界return false;if(flag[a][b]) // 该地方已经被使用return false;if(board[a][b]==word.charAt(index)){flag[a][b]=true;// 上 下 左 右if( find(board,a-1,b,word,index+1,flag) ||find(board,a+1,b,word,index+1,flag) ||find(board,a,b-1,word,index+1,flag) ||find(board,a,b+1,word,index+1,flag) ){return true;}flag[a][b]=false;}return false;}}
78. 子集


class Solution {List<List<Integer>> res = new ArrayList();List<Integer> list = new ArrayList();public List<List<Integer>> subsets(int[] nums) {dfs(nums,0);return res;}void dfs(int[] nums,int start){if(start==nums.length){res.add(new ArrayList(list));return;}list.add(nums[start]);dfs(nums,start+1);list.remove(list.size()-1);dfs(nums,start+1);}}
方法2:
使用位运算
class Solution {public List<List<Integer>> subsets(int[] nums) {List<List<Integer>> res = new ArrayList();List<Integer> list = new ArrayList();int len = nums.length;for(int i = 0; i < (1<<len);i++) {int now = i; // 当前位项int index = 0;list.clear();while(now!=0) {if((now&1)!=0) {list.add(nums[index]);}now>>=1;index++;}res.add(new ArrayList(list));}return res;}}
90. 子集 II

- 排序
相同元素只有前一个已经使用的时候才能继续使用
class Solution {List<List<Integer>> res = new ArrayList();List<Integer> list = new ArrayList();public List<List<Integer>> subsetsWithDup(int[] nums) {Arrays.sort(nums); // 排序,使重复数字连续放置boolean[] flag = new boolean[nums.length];find(nums,flag,0);return res;}void find(int[] nums,boolean[] flag,int start) {if(start==nums.length){res.add(new ArrayList(list));return;}// 不能使用当前元素if(start>0 && nums[start-1]==nums[start] && !flag[start-1]) {find(nums,flag,start+1);} else {// 可以使用当前元素list.add(nums[start]);flag[start]=true;find(nums,flag,start+1);flag[start]=false;list.remove(list.size()-1);find(nums,flag,start+1);}}}
322. 零钱兑换







class Solution {public int coinChange(int[] coins, int amount) {Arrays.sort(coins);int len = coins.length;if(amount==0)return 0;if(len==0 ){return -1;}int[] dp = new int[amount+1];Arrays.fill(dp,amount+1);dp[0] =0 ;for(int i = 1;i<=amount;i++) {for(int j = 0 ; j < coins.length;j++) {if(coins[j]<=i) {dp[i] = Math.min(dp[i],dp[i-coins[j]]+1);}}}// System.out.println(Arrays.toString(dp));return dp[amount]==amount+1?-1:dp[amount];}}
518. 零钱兑换 II


class Solution {public int change(int amount, int[] coins) {int[] dp = new int[amount+1];dp[0]=1;for(int c:coins){for(int j=c;j<=amount;j++) {dp[j] += dp[j-c];}}System.out.println(Arrays.toString(dp));return dp[amount];}}

