赞
踩
类似于归并排序,对两个有序数组进行合并即可,但是空间复杂度是O(n+m);
class Solution { public void merge(int[] nums1, int m, int[] nums2, int n) { int[] ans = new int[n + m]; int i = 0, j = 0; int cnt = 0; while (i < m && j < n) { if (nums1[i] < nums2[j]) { ans[cnt++] = nums1[i++]; } else { ans[cnt++] = nums2[j++]; } } while(i < m) ans[cnt++] = nums1[i++]; while(j < n) ans[cnt++] = nums2[j++]; for (int k = 0; k < cnt; ++k) { nums1[k] = ans[k]; } } }
优化:有一点小优化吧,可以从后往前合并装入nums1的后面,这样不会影响nums1的元素,同样也节省了n+m的空间(本题的数据量小,所以看不出)。
class Solution { public void merge(int[] nums1, int m, int[] nums2, int n) { int i = m - 1; int j = n - 1; int cnt = n + m - 1; while(i >= 0 && j >= 0) { if (nums1[i] < nums2[j]) { nums1[cnt--] = nums2[j--]; } else { nums1[cnt--] = nums1[i--]; } } while(i >= 0) nums1[cnt--] = nums1[i--]; while(j >= 0) nums1[cnt--] = nums2[j--]; } }
原地移除,用一个类似于指针的变量ind,从0开始遍历,如果与val相同则一直往后走,如果不同则压入到ind这个位置
class Solution {
public int removeElement(int[] nums, int val) {
int ind = 0;
int i = 0;
while (i < nums.length) {
int j = i;
while (j < nums.length && nums[j] == val) j++;
if (j < nums.length) nums[ind++] = nums[j];
i = j + 1;
}
return ind;
}
}
与上一个题相似,过滤掉相同的数字即可。
class Solution {
public int removeDuplicates(int[] nums) {
int cnt = 1;
int n = nums.length;
for (int i = 0; i < n;) {
int j = i + 1;
while(j < n && nums[j] == nums[i]) ++j;
if (j >= n) break;
nums[cnt++] = nums[j];
i = j;
}
return cnt;
}
}
用头指针和尾指针,每次比较的时候必须满足两个指针指向的是数字或字符,其他的符号都跳过。
class Solution { public boolean isPalindrome(String s) { s = s.toLowerCase(); int len = s.length(); int i = 0, j = len - 1; char[] ch = s.toCharArray(); while(i < j) { while(i < len && !check(ch[i])) ++i; while(j >= 0 && !check(ch[j])) --j;//前面两句是过滤非字符或数字 if (i > j) { break; } if (ch[i] >= 'a') { ch[i] -= 32; } if (ch[j] >= 'a') { ch[j] -= 32; }// 如果是字符,则统一转为小写 if (ch[i] != ch[j]) { return false; } ++i; --j; } return true; } private boolean check(char ch) { if (ch <= 'z' && ch >= 'a') { return true; } if (ch <= 'Z' && ch >= 'z') { return true; } if (ch <= '9' && ch >= '0') { return true; } return false; } }
由滑动窗口思想:设两个指针,尾指针一直走,当满足某个条件时,头指针也跟着走。
条件:当子数组和大于target时,不断缩小窗口,找到最小的窗口。
class Solution { public int minSubArrayLen(int target, int[] nums) { int ans = nums.length; int ind = 0; int sum = 0; for (int i = 0; i < nums.length; ++i) { sum += nums[i]; while (sum >= target) { sum -= nums[ind]; ans = Math.min(ans, i - ind + 1); ind++; } } if (ind == 0) { // sum>=target没有执行,不存在子数组 return 0; } return ans; } }
创建三个set数组,分别是对存“行”,“列”,“区域”的数字,如果对应的set中有值,那么就不是有效的。否则就添加
这里最主要的是怎样判断是否为同一个区域。
可以先对9x9的表格通过i/3,j/3划分为9个3x3区域然后每个区域的值都是
(0, 0), (0, 1), (0, 2)
(1, 0), (1, 1)…
再通过将二维数组变为一维数组的计算公式i * row + j。就是9个区域。
class Solution { public boolean isValidSudoku(char[][] board) { Set<Character>[] rows = new HashSet[9]; Set<Character>[] cols = new HashSet[9]; Set<Character>[] area = new HashSet[9]; for (int i = 0; i < 9; ++i) { rows[i] = new HashSet<>(); cols[i] = new HashSet<>(); area[i] = new HashSet<>(); } for (int i = 0; i < 9; ++i) { for (int j = 0; j < 9; ++j) { if (board[i][j] > '9' || board[i][j] < '0') continue; if (rows[i].contains(board[i][j])) { return false; } else { rows[i].add(board[i][j]); } if (cols[j].contains(board[i][j])) { return false; } else { cols[j].add(board[i][j]); } int t= i / 3 * 3 + j / 3; //System.out.println(i + " " + j + " " + t); if (area[t].contains(board[i][j])) { return false; } else { area[t].add(board[i][j]); } } } return true; } }
比较r和m的26个字符个数,如果r的某个字符个数大于m的某个字符个数,则r不能由m的字符组成。
class Solution { public boolean canConstruct(String r, String m) { int[] rCnt = new int[26]; int[] mCnt = new int[26]; for (int i = 0; i < r.length(); ++i) { rCnt[r.charAt(i) - 'a']++; } for (int i = 0; i < m.length(); ++i) { mCnt[m.charAt(i) - 'a']++; } for (int i = 0; i < 26; ++i) { if (rCnt[i] > mCnt[i]) { return false; } } return true; } }
简单模拟
class Solution { public List<String> summaryRanges(int[] nums) { List<String> ans = new ArrayList<>(); int n = nums.length; for (int i = 0; i < n;) { int j = i; while(j < n -1 && nums[j] + 1 == nums[j + 1]) j++; if (j == i) { ans.add("" + nums[i]); ++i; } else { ans.add(nums[i] + "->" + nums[j]); i = j + 1; } } return ans; } }
如果能够匹配则删除栈顶元素,如果不能匹配就进栈,最后判断是否为空。
class Solution { static final Map<Character, Character> map = new HashMap<>() {{ put('(',')'); put('{','}'); put('[',']'); }}; public boolean isValid(String s) { char[] ch = s.toCharArray(); Stack<Character> stack = new Stack<>(); for (char c : ch) { if (stack.isEmpty()) { stack.push(c); continue; } Character peek = stack.peek(); if (map.containsKey(peek) && map.get(peek) == c) { stack.pop(); } else { stack.push(c); } } return stack.isEmpty(); } }
链表长度最长1e4,头结点一直往后走,如果次数超过链表长度,必定有环(投机取巧了)
public class Solution {
public boolean hasCycle(ListNode head) {
int cnt = 0;
while(head != null) {
head = head.next;
cnt++;
if (cnt > 10005) {
return true;
}
}
return false;
}
}
正解:快慢指针,根据Floyd判圈法,又称为龟兔赛跑算法,乌龟每次走一格,兔子每次走两格,如果有环,兔子提前进入环并且一直在环内运动,而当乌龟进入环时,兔子一定会追到乌龟。
public class Solution { public boolean hasCycle(ListNode head) { if (head == null || head.next ==null) { return false; } ListNode slow = head; ListNode fast = head.next; while(slow != fast) { if (fast == null || fast.next == null) { return false; } slow = slow.next; fast = fast.next.next; } return true; } }
模拟走一遍二叉树同时记录层数。
class Solution {
int ans = 0;
public int maxDepth(TreeNode root) {
dfs(root, 0);
return ans;
}
public void dfs(TreeNode root, int level) {
if (root == null) {
ans = Math.max(ans, level);
return;
}
dfs(root.left, level + 1);
dfs(root.right, level + 1);
}
}
二叉树的层序遍历,取每层的最后一个节点即可。
class Solution { public List<Integer> rightSideView(TreeNode root) { List<Integer> ans = new ArrayList<>(); if (root == null) { return ans; } int cnt = 0, lastVal = 0; Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); while(!queue.isEmpty()) { cnt = queue.size();// 每层有多少个节点 while(cnt-- > 0) { TreeNode curNode = queue.poll(); if (curNode.left != null) queue.add(curNode.left); if (curNode.right != null) queue.add(curNode.right); lastVal = curNode.val; } ans.add(lastVal);//每层的最后一个节点 } return ans; } }
二叉搜索树的中序遍历是升序,而升序的相邻节点可以得到一个最小值,即答案所求。
class Solution { int ans = Integer.MAX_VALUE; int pre = -1; // 记录中序遍历的前一个节点,初始化为-1,是还没找到中序遍历的第一个节点 public int getMinimumDifference(TreeNode root) { dfs(root); return ans; } private void dfs(TreeNode root) { if (root == null) { return; } dfs(root.left); if (pre == -1) { pre = root.val; } else { ans = Math.min(ans, root.val - pre); pre = root.val; } dfs(root.right); } }
dfs没走过的为1的格子
class Solution { int N = 305; static int[][] dir = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}}; boolean[][] vis = new boolean[N][N]; int n, m, ans; public int numIslands(char[][] grid) { m = grid.length; n = grid[0].length; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (vis[i][j] || grid[i][j] == '0') continue; dfs(grid, i, j); //System.out.println(i + "<->" + j); ans++; } } return ans; } private void dfs(char[][] g, int x, int y) { vis[x][y] = true; for (int i = 0; i < 4; ++i) { int curX = x + dir[i][0]; int curY = y + dir[i][1]; if (curX < 0 || curY < 0 || curX >= m || curY >= n || g[curX][curY] == '0' || vis[curX][curY]) continue; dfs(g, curX, curY); } } }
题实在没读懂,参考解析。
利用广度优先搜索,先走格子,如果遇到梯子或蛇就直接传送(传送不花时间)。
class Solution { int n; int[] g; public int snakesAndLadders(int[][] board) { n = board.length; boolean isR = true; g = new int[n * n + 1]; int ind = 0; for (int i = n - 1; i >= 0; --i) { for (int j = (isR ? 0 : n - 1); isR ? j < n : j >= 0; j += isR ? 1 : -1) { // 经典的神龙摆尾 g[++ind] = board[i][j]; } isR = !isR; } int ans = bfs(); return ans; } private int bfs() { Queue<Integer> queue = new LinkedList<>(); // 当前到的点 Map<Integer, Integer> m = new HashMap<>(); // 用于存当前点和步数 m.put(1, 0); queue.add(1); while(!queue.isEmpty()) { int cur = queue.poll(); int step = m.get(cur); if (cur == n * n) return step; for (int i = 1; i <= 6; ++i) { int nxt = cur + i; if (nxt > n * n) continue; if (g[nxt] != -1) nxt = g[nxt]; //直接传送 if (m.containsKey(nxt)) continue; // 已经走过 m.put(nxt, step + 1); queue.add(nxt); } } return -1; } }
字典树模版题
class Trie { static final int N = (int) 1e5 + 9; static int[] cnt = new int[N]; static int[][] tree = new int[N][26]; static int ind = 0; public Trie() { for (int i = ind; i >= 0; --i) { Arrays.fill(tree[i], 0); } Arrays.fill(cnt, 0); ind = 0; } public void insert(String word) { int p = 0; for (int i = 0; i < word.length(); ++i) { int u = word.charAt(i) - 'a'; if (tree[p][u] == 0) tree[p][u] = ++ind; p = tree[p][u]; } cnt[p]++; } public boolean search(String word) { int p = 0; for (int i = 0; i < word.length(); ++i) { int u = word.charAt(i) - 'a'; if (tree[p][u] == 0) return false; p = tree[p][u]; } return cnt[p] != 0; } public boolean startsWith(String prefix) { int p = 0; for (int i = 0; i < prefix.length(); ++i) { int u = prefix.charAt(i) - 'a'; if (tree[p][u] == 0) return false; p = tree[p][u]; } return true; } } /** * Your Trie object will be instantiated and called as such: * Trie obj = new Trie(); * obj.insert(word); * boolean param_2 = obj.search(word); * boolean param_3 = obj.startsWith(prefix); */
将每个键对应的字母存到String里,然后dfs变量每一个键,遍历到的键存到一个数组里,指导存了digits.size()个。
class Solution { private static final String[] MAPPING = new String[]{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; private final List<String> ans = new ArrayList<>(); private char[] digits, path; public List<String> letterCombinations(String digits) { int n = digits.length(); if (n == 0) return List.of(); this.digits = digits.toCharArray(); path = new char[n]; dfs(0); return ans; } private void dfs(int i) { if (i ==digits.length) { // 如果已经按完 ans.add(new String(path)); return; } for (char c : MAPPING[digits[i] - '0'].toCharArray()) { // 枚举按每一个键 path[i] = c; // 考虑先后顺序,先出现的和后出现的做匹配 dfs(i + 1); } } }
以每个区间的中点作为根节点进行建树,这是一个分治的过程。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return dfs(nums, 0, nums.length - 1);
}
private TreeNode dfs(int[] nums, int L, int R) {
if (L > R) {
return null;
}
int mid = (L + R) >> 1;
TreeNode root = new TreeNode(nums[mid]);
root.left = dfs(nums, L, mid - 1);
root.right = dfs(nums, mid + 1, R);
return root;
}
}
思维题,用一个sum变量记录当前子数组和的最大值,如果小于0,则不要这段数组,因为任何数加一个小于0的数都会变小。边加边取最大值即可。
class Solution {
public int maxSubArray(int[] nums) {
int ans = Integer.MIN_VALUE;
int sum =0;
for (int num : nums) {
if (sum < 0) {
sum = 0;
}
sum += num;
ans = Math.max(ans, sum);
}
return ans;
}
}
二分查找模版题
class Solution { public int searchInsert(int[] nums, int target) { int l = 0, r = nums.length - 1; while (l <= r) { int mid = (l +r) >> 1; if (nums[mid] == target) { return mid; } else if (nums[mid] > target) { r = mid - 1; } else { l = mid + 1; } } return l; } }
用优先队列来做,因为优先队列底层就是堆实现的(默认小根堆),然后开一个k+1大小的小根堆,每次维持堆里的元素只有k个,最后输出堆顶,也就是队首即可。
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> queue = new PriorityQueue<>(k + 1);
for (int num : nums) {
queue.offer(num);
if (queue.size() == k + 1) {
queue.poll();
}
}
return queue.peek();
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。