LeetCode 练题记录
tag: 位运算
| |
| public static int reverse(int i) { |
| |
| |
| |
| |
| |
| |
| |
| |
| i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555; |
| |
| |
| i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333; |
| |
| |
| i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| i = (i << 24) | ((i & 0xff00) << 8) | ((i >>> 8) & 0xff00) | (i >>> 24); |
| return i; |
| } |
| |
| |
| class Solution { |
| |
| public int reverseBits(int n) { |
| int rev = 0; |
| for (int i = 0; i < 32 && n != 0; ++i) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| rev |= (n & 1) << (31 - i); |
| |
| |
| |
| |
| |
| n >>>= 1; |
| } |
| return rev; |
| } |
| } |
| |
| |
| class Solution { |
| private static final int M1 = 0x55555555; |
| private static final int M2 = 0x33333333; |
| private static final int M4 = 0x0f0f0f0f; |
| private static final int M8 = 0x00ff00ff; |
| |
| public int reverseBits(int n) { |
| n = n >>> 1 & M1 | (n & M1) << 1; |
| n = n >>> 2 & M2 | (n & M2) << 2; |
| n = n >>> 4 & M4 | (n & M4) << 4; |
| n = n >>> 8 & M8 | (n & M8) << 8; |
| return n >>> 16 | n << 16; |
| } |
| } |
tag: 递归
| class Solution { |
| |
| private Map<Integer, Integer> map = new HashMap<>(); |
| |
| |
| public int numSquares(int n) { |
| if (n <= 1) |
| return n; |
| if (map.containsKey(n)) |
| return map.get(n); |
| int num = (int) Math.sqrt(n); |
| |
| int sum = 0, sumMin = Integer.MAX_VALUE; |
| for (int i = num; i >= 1; i--) { |
| sum = 1 + numSquares(n - i * i); |
| sumMin = sum < sumMin ? sum : sumMin; |
| } |
| map.put(n, sumMin); |
| return sumMin; |
| } |
| } |
tag: Excel
| class Solution { |
| public int titleToNumber(String columnTitle) { |
| char[] title = columnTitle.toCharArray(); |
| int sum = 0; |
| for (char ch : title) { |
| sum = sum * 26 + ch - 'A' + 1; |
| } |
| return sum; |
| } |
| } |
tag: 字符串
| class Solution { |
| public String convertToTitle(int columnNumber) { |
| StringBuilder builder = new StringBuilder(); |
| |
| while (columnNumber-- != 0) { |
| builder.append((char) ('A' + columnNumber % 26)); |
| columnNumber /= 26; |
| } |
| |
| return builder.reverse().toString(); |
| } |
| } |
tag: 回文串、双指针
| class Solution { |
| public boolean isPalindrome(String s) { |
| char[] chS = s.toLowerCase().toCharArray(); |
| int len = s.length(); |
| int i = 0, j = len - 1; |
| while (i < j) { |
| |
| while (j >= 0 && !isCharAndNum(chS[j])) |
| j--; |
| while (i <= len - 1 && !isCharAndNum(chS[i])) |
| i++; |
| |
| if (i < j && chS[i++] != chS[j--]) |
| return false; |
| } |
| return true; |
| } |
| |
| |
| private boolean isCharAndNum(char ch) { |
| if (ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') |
| return true; |
| return false; |
| } |
| } |
tag: 深度优先搜索、二叉树
| class Solution { |
| private int min_d = Integer.MAX_VALUE; |
| |
| public int minDepth(TreeNode root) { |
| if (root == null) |
| return 0; |
| depth(root, 1); |
| return min_d; |
| } |
| |
| private void depth(TreeNode root, int d) { |
| if (d > min_d) |
| return; |
| if (root.left == null && root.right == null) { |
| min_d = min_d > d ? d : min_d; |
| return; |
| } |
| if (root.left != null) |
| depth(root.left, d + 1); |
| if (root.right != null) |
| depth(root.right, d + 1); |
| } |
| } |
tag: 二分查找、牛顿迭代
| class Solution { |
| public int mySqrt(int x) { |
| int l = 0, r = x, ans = -1; |
| while (l <= r) { |
| int mid = l + (r - l) / 2; |
| if ((long) mid * mid <= x) { |
| ans = mid; |
| l = mid + 1; |
| } else { |
| r = mid - 1; |
| } |
| } |
| return ans; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| class Solution { |
| public int mySqrt(int x) { |
| if (x == 0) { |
| return 0; |
| } |
| |
| double C = x, x0 = x; |
| while (true) { |
| double xi = 0.5 * (x0 + C / x0); |
| if (Math.abs(x0 - xi) < 1e-7) { |
| break; |
| } |
| x0 = xi; |
| } |
| return (int) x0; |
| } |
| } |
| |
| class Solution { |
| public int[] plusOne(int[] digits) { |
| List<Integer> ans = new ArrayList<>(); |
| int j = digits.length - 1, carr = 1; |
| while (j >= 0 || carr > 0) { |
| carr += j >= 0 ? digits[j--] : 0; |
| ans.add(carr % 10); |
| carr /= 10; |
| } |
| int len = ans.size(); |
| int[] result = new int[len]; |
| for (int i = 0; i < len; i++) { |
| result[i] = ans.get(len - 1 - i); |
| } |
| return result; |
| } |
| } |
| |
| class Solution { |
| public int[] plusOne(int[] digits) { |
| for (int i = digits.length - 1; i >= 0; i--) { |
| digits[i]++; |
| digits[i] = digits[i] % 10; |
| if (digits[i] != 0) return digits; |
| } |
| digits = new int[digits.length + 1]; |
| digits[0] = 1; |
| return digits; |
| } |
| } |
| |
| 作者:yhhzw |
| 链接:https: |
| 来源:力扣(LeetCode) |
| 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 |
tag: 二维数组、排序
| class Solution { |
| public int[][] reconstructQueue(int[][] people) { |
| |
| |
| |
| Arrays.sort(people, (o1, o2) -> { |
| if (o1[0] - o2[0] == 0) |
| return o2[1] - o1[1]; |
| return o1[0] - o2[0]; |
| }); |
| int n = people.length; |
| int[][] ans = new int[n][]; |
| |
| for (int[] person : people) { |
| int spaces = person[1] + 1; |
| for (int i = 0; i < n; ++i) { |
| if (ans[i] == null) { |
| --spaces; |
| if (spaces == 0) { |
| ans[i] = person; |
| break; |
| } |
| } |
| } |
| } |
| return ans; |
| } |
| } |
tag: 字符串匹配
| |
| class Solution { |
| public int strStr(String haystack, String needle) { |
| int lenH = haystack.length(); |
| int lenN = needle.length(); |
| |
| if (lenN == 0) |
| return 0; |
| |
| if (lenN > lenH) |
| return -1; |
| |
| for (int i = 0; i <= lenH - lenN; i++) { |
| int j = 0; |
| int k = i; |
| while (j < lenN) { |
| if (haystack.charAt(k++) != needle.charAt(j++)) |
| break; |
| if (j == lenN) |
| return i; |
| } |
| } |
| return -1; |
| } |
| } |
tag: 0-1 背包问题、动态规划
| |
| class Solution { |
| public boolean canPartition(int[] nums) { |
| int n = nums.length; |
| |
| if (n < 2) { |
| return false; |
| } |
| int sum = 0, maxNum = 0; |
| |
| for (int num : nums) { |
| sum += num; |
| maxNum = Math.max(maxNum, num); |
| } |
| |
| if (sum % 2 != 0) { |
| return false; |
| } |
| int target = sum / 2; |
| |
| if (maxNum > target) { |
| return false; |
| } |
| |
| |
| |
| |
| boolean[][] dp = new boolean[n][target + 1]; |
| |
| for (int i = 0; i < n; i++) { |
| dp[i][0] = true; |
| } |
| |
| dp[0][nums[0]] = true; |
| for (int i = 1; i < n; i++) { |
| int num = nums[i]; |
| for (int j = 1; j <= target; j++) { |
| |
| |
| |
| |
| |
| if (j >= num) { |
| dp[i][j] = dp[i - 1][j] | dp[i - 1][j - num]; |
| } else { |
| |
| dp[i][j] = dp[i - 1][j]; |
| } |
| } |
| } |
| |
| return dp[n - 1][target]; |
| } |
| } |
tag: 滑动窗口
| |
| public List<Integer> findAnagrams(String s, String p) { |
| |
| int sLen = s.length(), pLen = p.length(); |
| |
| if (sLen < pLen) { |
| return new ArrayList<Integer>(); |
| } |
| List<Integer> ans = new ArrayList<Integer>(); |
| int[] sCount = new int[26]; |
| int[] pCount = new int[26]; |
| |
| for (int i = 0; i < pLen; ++i) { |
| ++sCount[s.charAt(i) - 'a']; |
| ++pCount[p.charAt(i) - 'a']; |
| } |
| |
| |
| if (Arrays.equals(sCount, pCount)) { |
| ans.add(0); |
| } |
| |
| |
| for (int i = 0; i < sLen - pLen; ++i) { |
| |
| --sCount[s.charAt(i) - 'a']; |
| ++sCount[s.charAt(i + pLen) - 'a']; |
| |
| if (Arrays.equals(sCount, pCount)) { |
| ans.add(i + 1); |
| } |
| } |
| return ans; |
| } |
tag: 二叉树、深度优先搜索
| |
| |
| private int number; |
| |
| public int pathSum(TreeNode root, int targetSum) { |
| |
| if (root == null) |
| return 0; |
| |
| Deque<TreeNode> queue = new LinkedList<>(); |
| queue.add(root); |
| |
| TreeNode temp; |
| while (!queue.isEmpty()) { |
| |
| temp = queue.remove(); |
| |
| dfs(temp, 0, targetSum); |
| |
| if (temp.left != null) |
| queue.add(temp.left); |
| |
| if (temp.right != null) |
| queue.add(temp.right); |
| } |
| return number; |
| } |
| |
| private void dfs(TreeNode root, int sum, int targetSum) { |
| |
| if (sum == targetSum) { |
| number++; |
| } |
| |
| if (root.left != null) |
| dfs(root.left, sum + root.val, targetSum); |
| |
| if (root.right != null) |
| dfs(root.right, sum - root.val, targetSum); |
| } |
解题思路:题目要求寻找两个有序数组的中位数,很直接明了的一个思路就是先将两个有序数组合并为一个有序数组,再求中位数medNum = (nums[len / 2] + nums[(len - 1) / 2]) / 2
。时间复杂度为O(m+n),空间复杂度为O(m+n),不能达到题目要求的时间复杂度为O(log(m+n))。
| class Solution |
| { |
| public: |
| double findMedianSortedArrays(vector<int> &nums1, vector<int> &nums2) |
| { |
| return mergeArrays(nums1, nums2); |
| } |
| |
| |
| double mergeArrays(vector<int> &nums1, vector<int> &nums2) |
| { |
| int len1 = nums1.size(); |
| int len2 = nums2.size(); |
| vector<int> nums3; |
| int i = 0, j = 0; |
| while (i < len1 && j < len2) |
| { |
| if (nums1[i] < nums2[j]) |
| { |
| nums3.push_back(nums1[i++]); |
| } |
| else |
| { |
| nums3.push_back(nums2[j++]); |
| } |
| } |
| |
| while (i < len1) |
| { |
| nums3.push_back(nums1[i++]); |
| } |
| |
| while (j < len2) |
| { |
| nums3.push_back(nums2[j++]); |
| } |
| |
| |
| int len3 = nums3.size(); |
| return len3 > 0 ? (nums3[len3 / 2] + nums3[(len3 - 1) / 2]) / 2.0 : 0; |
| } |
| }; |
解题思路:题目要求在数组中找到目标值,给出的数组是有序并且旋转过的。可以直接遍历数组,但时间复杂度达不到题目要求O(logn)。思路是使用二分查找,关键点是二分之后要判断子数组是否有序。
| class Solution { |
| |
| public int search(int[] nums, int target) { |
| int left = 0, right = nums.length - 1; |
| |
| while (left <= right) { |
| int mid = left + (right - left) / 2; |
| if (nums[mid] == target) { |
| return mid; |
| } |
| |
| if (nums[mid] >= nums[left]) { |
| if (target < nums[mid] && target >= nums[left]) { |
| right = mid - 1; |
| } else { |
| left = mid + 1; |
| } |
| } else { |
| if (target > nums[mid] && target <= nums[right]) { |
| left = mid + 1; |
| } else { |
| right = mid - 1; |
| } |
| } |
| } |
| return -1; |
| } |
| } |
解题思路:题目要求在有序数组找出目标值的开始和结束位置。可以直接遍历整个数组求得,时间复杂度为 O(n),但题目要求时间复杂度为 O(log2n)。因为是有序,所以可以用二分查找求得。
| class Solution { |
| public: |
| |
| vector<int> searchRange(vector<int>& nums, int target) { |
| int left = 0, right = nums.size() - 1; |
| |
| |
| while (left <= right) { |
| int mid = left + (right - left) / 2; |
| |
| if (nums[mid] >= target) { |
| right = mid - 1; |
| } else { |
| left = mid + 1; |
| } |
| } |
| |
| int finLeft = (left < nums.size() && nums[left] == target) ? left : -1; |
| |
| |
| left = 0, right = nums.size() - 1; |
| while (left <= right) { |
| int mid = left + (right - left) / 2; |
| |
| if (nums[mid] <= target) { |
| left = mid + 1; |
| } else { |
| right = mid - 1; |
| } |
| } |
| int finRight = (right > -1 && nums[right] == target) ? right : -1; |
| |
| return {finLeft, finRight}; |
| } |
| }; |
解题思路:题目要求将字母异位词进行分组,那么将异位词排序后,就会变成相同的单词。所以可以使用哈希表进行分组,先将单词进行排序,作为键,值存储原来的单词。
| class Solution { |
| public: |
| vector<vector<string>> groupAnagrams(vector<string>& strs) { |
| vector<vector<string>> strV; |
| |
| unordered_map<string, vector<string>> mp; |
| |
| for (auto&& str : strs) { |
| string key = str; |
| sort(key.begin(), key.end()); |
| mp[key].push_back(str); |
| } |
| |
| |
| for (auto it = mp.begin(); it != mp.end(); it++) { |
| strV.push_back(it->second); |
| } |
| |
| return strV; |
| } |
| }; |
滑动窗口:使用左右指针,记录左边界和右边界,右边界阔张,左边界收缩,得到最小覆盖子串。使用 HashMap 记录字符串中每个字符的个数
| class Solution { |
| public: |
| unordered_map<char, int> ori, cnt; |
| |
| string minWindow(string s, string t) { |
| |
| for (const auto &c : t) { |
| ++ori[c]; |
| } |
| |
| int l = 0, r = -1; |
| int len = INT_MAX, ansL = -1, check = t.length(); |
| int s_len = s.length(); |
| while (r < s_len) { |
| |
| if (ori.find(s[++r]) == ori.end()) continue; |
| |
| |
| if (++cnt[s[r]] <= ori[s[r]]) { |
| check--; |
| } |
| |
| |
| while (!check && l <= r) { |
| if (r - l + 1 < len) { |
| len = r - l + 1; |
| ansL = l; |
| } |
| |
| |
| if (ori.find(s[l]) != ori.end()) { |
| if (--cnt[s[l]] < ori[s[l]]) ++check; |
| } |
| ++l; |
| } |
| } |
| |
| return ansL == -1 ? string() : s.substr(ansL, len); |
| } |
| }; |
解题思路:用容器保存每一个子集,每次往容器中的每一个子集添加一个元素,并保留原有子集,直到添加完最后一个元素。也可用回溯法。
| |
| class Solution { |
| public: |
| vector<vector<int>> subsets(vector<int>& nums) { |
| vector<int> subSet; |
| vector<vector<int>> subSets(1, subSet); |
| for (auto&& num : nums) { |
| vector<vector<int>> tmp = subSets; |
| for (auto&& sub : tmp) { |
| sub.push_back(num); |
| subSets.push_back(sub); |
| } |
| } |
| return subSets; |
| } |
| }; |
解题思路:这题的思路和 78. 子集 一样都可以用回溯法,依次匹配每一个字符,如果不匹配,回溯到上一个字符,寻找其他路径。(看了一下官方代码。自己写的一塌糊涂,菜,很多细节都没有处理到位)用偏移量数组来记录方位,可以省去很多冗余代码
| class Solution { |
| public: |
| vector<vector<int>> visited; |
| int len = 0, m = 0, n = 0; |
| bool exist(vector<vector<char>>& board, string word) { |
| m = board.size(); |
| n = board[0].size(); |
| len = word.length(); |
| vector<int> v(n, 0); |
| vector<vector<int>> vv(m, v); |
| visited = vv; |
| for (int i = 0; i < m; ++i) { |
| for (int j = 0; j < n; ++j) { |
| visited[i][j] = 1; |
| if (match(board, word, i, j, 0)) return true; |
| } |
| } |
| return false; |
| } |
| |
| bool match(vector<vector<char>>& board, string& word, int i, int j, int t) { |
| if (board[i][j] != word[t]) { |
| visited[i][j] = 0; |
| return false; |
| } |
| if (t == len - 1) { |
| return true; |
| } |
| bool flag = false; |
| if (i - 1 >= 0 && !visited[i - 1][j]) { |
| visited[i - 1][j] = 1; |
| flag = match(board, word, i - 1, j, t + 1); |
| } |
| if (flag) return true; |
| if (j - 1 >= 0 && !visited[i][j - 1]) { |
| visited[i][j - 1] = 1; |
| flag = match(board, word, i, j - 1, t + 1); |
| } |
| if (flag) return true; |
| if (i + 1 < m && !visited[i + 1][j]) { |
| visited[i + 1][j] = 1; |
| flag = match(board, word, i + 1, j, t + 1); |
| } |
| if (flag) return true; |
| if (j + 1 < n && !visited[i][j + 1]) { |
| visited[i][j + 1] = 1; |
| flag = match(board, word, i, j + 1, t + 1); |
| } |
| visited[i][j] = 0; |
| return flag; |
| } |
| }; |
解题思路:看到这题的时候就想到了卡特兰数,所以用了取巧的方法。针对整数越界,可以使用 double 或者 long long 类型。看了官方的题解,把代码贴上了,作为参考。
| |
| |
| class Solution { |
| public: |
| int numTrees(int n) { |
| |
| double fnum = 1, mnum = 1; |
| for (int i = 2 * n; i > n; --i) { |
| fnum *= i; |
| mnum *= (i - n); |
| } |
| return (int)(fnum / mnum / (n + 1) + 0.5); |
| } |
| }; |
| class Solution { |
| public: |
| int numTrees(int n) { |
| vector<int> G(n + 1, 0); |
| G[0] = 1; |
| G[1] = 1; |
| |
| for (int i = 2; i <= n; ++i) { |
| for (int j = 1; j <= i; ++j) { |
| G[i] += G[j - 1] * G[i - j]; |
| } |
| } |
| return G[n]; |
| } |
| }; |
| |
| |
| |
| class Solution { |
| public: |
| int numTrees(int n) { |
| vector<int> G(n + 1, 0); |
| G[0] = 1; |
| G[1] = 1; |
| |
| for (int i = 2; i <= n; ++i) { |
| for (int j = 1; j <= i; ++j) { |
| G[i] += G[j - 1] * G[i - j]; |
| } |
| } |
| return G[n]; |
| } |
| }; |
| |
| |
| |
| |
解题思路:二叉树的构造,根据前序(NLR:遍历根结点 左子树 右子树)和中序(LNR 左子树 根结点 右子树)遍历的特性,创建二叉树。在前序序列中找到根结点,再遍历中序序列划分左右子树。递归
| class Solution { |
| public: |
| int len; |
| TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) { |
| len = inorder.size(); |
| return buildTreeByPreIn(preorder, inorder, 0, 0, len - 1); |
| } |
| |
| TreeNode *buildTreeByPreIn(vector<int> &preoder, vector<int> &inoder, int p, int s, int e) { |
| if (s > e || p >= len) return nullptr; |
| TreeNode *root = nullptr; |
| for (int i = s; i <= e; ++i) { |
| if (inoder[i] == preoder[p]) { |
| root = new TreeNode(preoder[p]); |
| root->left = buildTreeByPreIn(preoder, inoder, p + 1, s, i - 1); |
| root->right = buildTreeByPreIn(preoder, inoder, p + i - s + 1, i + 1, e); |
| } |
| } |
| return root; |
| } |
| }; |
解题思路:节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值,递归计算左右子节点的最大贡献值。
| class Solution { |
| |
| int maxSum = Integer.MIN_VALUE; |
| |
| public int maxPathSum(TreeNode root) { |
| maxGain(root); |
| return maxSum; |
| } |
| |
| public int maxGain(TreeNode node) { |
| if (node == null) { |
| return 0; |
| } |
| |
| |
| |
| int leftGain = Math.max(maxGain(node.left), 0); |
| int rightGain = Math.max(maxGain(node.right), 0); |
| |
| |
| int priceNewpath = node.val + leftGain + rightGain; |
| |
| |
| maxSum = Math.max(maxSum, priceNewpath); |
| |
| |
| return node.val + Math.max(leftGain, rightGain); |
| } |
| } |
解题思路:使用自底向上的归并排序算法
| class Solution { |
| public ListNode sortList(ListNode head) { |
| |
| if (head == null) { |
| return head; |
| } |
| |
| int length = 0; |
| ListNode node = head; |
| |
| while (node != null) { |
| length++; |
| node = node.next; |
| } |
| |
| ListNode dummyHead = new ListNode(0, head); |
| |
| for (int subLength = 1; subLength < length; subLength <<= 1) { |
| |
| ListNode prev = dummyHead, curr = dummyHead.next; |
| while (curr != null) { |
| |
| ListNode head1 = curr; |
| for (int i = 1; i < subLength && curr.next != null; i++) { |
| curr = curr.next; |
| } |
| |
| ListNode head2 = curr.next; |
| curr.next = null; |
| curr = head2; |
| for (int i = 1; i < subLength && curr != null && curr.next != null; i++) { |
| curr = curr.next; |
| } |
| |
| |
| ListNode next = null; |
| if (curr != null) { |
| next = curr.next; |
| curr.next = null; |
| } |
| |
| ListNode merged = merge(head1, head2); |
| prev.next = merged; |
| while (prev.next != null) { |
| prev = prev.next; |
| } |
| curr = next; |
| } |
| } |
| return dummyHead.next; |
| } |
| |
| |
| public ListNode merge(ListNode head1, ListNode head2) { |
| ListNode dummyHead = new ListNode(0); |
| ListNode temp = dummyHead, temp1 = head1, temp2 = head2; |
| while (temp1 != null && temp2 != null) { |
| if (temp1.val <= temp2.val) { |
| temp.next = temp1; |
| temp1 = temp1.next; |
| } else { |
| temp.next = temp2; |
| temp2 = temp2.next; |
| } |
| temp = temp.next; |
| } |
| if (temp1 != null) { |
| temp.next = temp1; |
| } else if (temp2 != null) { |
| temp.next = temp2; |
| } |
| return dummyHead.next; |
| } |
| } |
tag: 动态规划、滑动数组
53. 最大子序和
| class Solution { |
| public int maxProduct(int[] nums) { |
| |
| int maxF = nums[0], minF = nums[0], ans = nums[0]; |
| int length = nums.length; |
| for (int i = 1; i < length; ++i) { |
| int mx = maxF, mn = minF; |
| maxF = Math.max(mx * nums[i], Math.max(nums[i], mn * nums[i])); |
| minF = Math.min(mn * nums[i], Math.min(nums[i], mx * nums[i])); |
| ans = Math.max(maxF, ans); |
| } |
| return ans; |
| } |
| } |
| class MinStack { |
| private Deque<Integer> minStack; |
| private Deque<Integer> stack; |
| |
| public MinStack() { |
| |
| stack = new LinkedList<Integer>(); |
| minStack = new LinkedList<Integer>(); |
| minStack.push(Integer.MAX_VALUE); |
| } |
| |
| public void push(int val) { |
| stack.push(val); |
| minStack.push(Math.min(minStack.peek(), val)); |
| } |
| |
| public void pop() { |
| stack.pop(); |
| minStack.pop(); |
| } |
| |
| public int top() { |
| return stack.peek(); |
| |
| } |
| |
| public int getMin() { |
| return minStack.peek(); |
| } |
| } |
tag: 动态规划、滚动数组
| class Solution { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public int rob(int[] nums) { |
| int len = nums.length; |
| |
| if (len == 1) |
| return nums[0]; |
| |
| |
| |
| |
| |
| |
| int dp1 = nums[0]; |
| int dp2 = Math.max(nums[0], nums[1]); |
| |
| for (int i = 2; i < len; ++i) { |
| |
| int tmp = dp2; |
| dp2 = dp2 > (dp1 + nums[i]) ? dp2 : (dp1 + nums[i]); |
| dp1 = tmp; |
| } |
| |
| return dp2; |
| } |
| } |
tag: 深度优先、广度优先、并查集
解题思路:使用深度优先搜索 或 广度优先搜索 或 并查集
| |
| class Solution { |
| class UnionFind { |
| int count; |
| int[] parent; |
| int[] rank; |
| |
| |
| public UnionFind(char[][] grid) { |
| count = 0; |
| int m = grid.length; |
| int n = grid[0].length; |
| parent = new int[m * n]; |
| rank = new int[m * n]; |
| for (int i = 0; i < m; ++i) { |
| for (int j = 0; j < n; ++j) { |
| if (grid[i][j] == '1') { |
| parent[i * n + j] = i * n + j; |
| ++count; |
| } |
| rank[i * n + j] = 0; |
| } |
| } |
| } |
| |
| |
| public int find(int i) { |
| if (parent[i] != i) |
| parent[i] = find(parent[i]); |
| return parent[i]; |
| } |
| |
| |
| public void union(int x, int y) { |
| int rootx = find(x); |
| int rooty = find(y); |
| if (rootx != rooty) { |
| if (rank[rootx] > rank[rooty]) { |
| parent[rooty] = rootx; |
| } else if (rank[rootx] < rank[rooty]) { |
| parent[rootx] = rooty; |
| } else { |
| parent[rooty] = rootx; |
| rank[rootx] += 1; |
| } |
| --count; |
| } |
| } |
| |
| public int getCount() { |
| return count; |
| } |
| } |
| |
| |
| public int numIslands(char[][] grid) { |
| if (grid == null || grid.length == 0) { |
| return 0; |
| } |
| |
| int nr = grid.length; |
| int nc = grid[0].length; |
| int[][] directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; |
| UnionFind uf = new UnionFind(grid); |
| for (int r = 0; r < nr; ++r) { |
| for (int c = 0; c < nc; ++c) { |
| if (grid[r][c] == '1') { |
| grid[r][c] = '0'; |
| for (int[] dir : directions) { |
| int tmp_r = r + dir[0], tmp_c = c + dir[1]; |
| if (tmp_r >= 0 && tmp_r < nr && tmp_c >= 0 && tmp_c < nc) { |
| uf.union(r * nc + c, tmp_r * nc + tmp_c); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
| } |
| } |
| |
| return uf.getCount(); |
| } |
| } |
tag: 深度优先搜索、广度优先搜索、拓扑排序
解题思路:根据题意可知,这是一道拓扑排序问题。判断图是否存在回路,如果存在,说明不是拓扑排序
| class Solution { |
| private List<List<Integer>> edges; |
| private int[] visited; |
| private boolean valid = true; |
| |
| public boolean canFinish(int numCourses, int[][] prerequisites) { |
| |
| edges = new ArrayList<>(); |
| for (int i = 0; i < numCourses; i++) { |
| edges.add(new ArrayList<>()); |
| } |
| visited = new int[numCourses]; |
| |
| |
| for (int[] info : prerequisites) { |
| edges.get(info[1]).add(info[0]); |
| } |
| |
| |
| for (int i = 0; i < numCourses && valid; i++) { |
| if (visited[i] == 0) { |
| DFS(i); |
| } |
| } |
| return valid; |
| } |
| |
| |
| public void DFS(int u) { |
| visited[u] = 1; |
| for (int v : edges.get(u)) { |
| if (visited[v] == 0) { |
| DFS(v); |
| |
| if (!valid) { |
| return; |
| } |
| } else if (visited[v] == 1) { |
| valid = false; |
| return; |
| } |
| } |
| visited[u] = 2; |
| } |
| } |
tag: Trie、前缀树、字典树
| |
| |
| |
| class Trie { |
| private Trie[] children; |
| private boolean isEnd; |
| |
| public Trie() { |
| |
| children = new Trie[26]; |
| isEnd = false; |
| } |
| |
| |
| public void insert(String word) { |
| Trie node = this; |
| for (int i = 0; i < word.length(); i++) { |
| char ch = word.charAt(i); |
| int index = ch - 'a'; |
| |
| if (node.children[index] == null) { |
| node.children[index] = new Trie(); |
| } |
| node = node.children[index]; |
| } |
| |
| node.isEnd = true; |
| } |
| |
| |
| public boolean search(String word) { |
| Trie node = searchPrefix(word); |
| return node != null && node.isEnd; |
| } |
| |
| |
| public boolean startsWith(String prefix) { |
| return searchPrefix(prefix) != null; |
| } |
| |
| |
| private Trie searchPrefix(String prefix) { |
| Trie node = this; |
| for (int i = 0; i < prefix.length(); i++) { |
| char ch = prefix.charAt(i); |
| int index = ch - 'a'; |
| if (node.children[index] == null) { |
| return null; |
| } |
| node = node.children[index]; |
| } |
| return node; |
| } |
| } |
tag: 动态规划
| |
| |
| |
| class Solution { |
| public int maximalSquare(char[][] matrix) { |
| int maxSide = 0; |
| |
| if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { |
| return maxSide; |
| } |
| |
| int rows = matrix.length, columns = matrix[0].length; |
| |
| int[][] dp = new int[rows][columns]; |
| |
| for (int i = 0; i < rows; i++) { |
| for (int j = 0; j < columns; j++) { |
| if (matrix[i][j] == '1') { |
| if (i == 0 || j == 0) { |
| dp[i][j] = 1; |
| } else { |
| dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; |
| } |
| |
| maxSide = Math.max(maxSide, dp[i][j]); |
| } |
| } |
| } |
| int maxSquare = maxSide * maxSide; |
| return maxSquare; |
| } |
| } |
tag: 递归
| |
| |
| |
| class Solution { |
| |
| |
| private TreeNode ans; |
| |
| public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { |
| dfs(root, p, q); |
| return ans; |
| } |
| |
| |
| private boolean dfs(TreeNode root, TreeNode p, TreeNode q) { |
| if (root == null) |
| return false; |
| boolean lson = dfs(root.left, p, q); |
| boolean rson = dfs(root.right, p, q); |
| |
| if ((lson && rson) || (root.val == p.val || root.val == q.val) && (lson || rson)) |
| ans = root; |
| return lson || rson || (root.val == p.val || root.val == q.val); |
| } |
| |
| } |
| |
| |
| |
| class Solution { |
| public void moveZeroes(int[] nums) { |
| int len = nums.length, j = 0; |
| for (int i = 0; i < len; i++) { |
| if (nums[i] != 0) { |
| nums[j++] = nums[i]; |
| } |
| } |
| |
| while (j < len) { |
| nums[j++] = 0; |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| class Solution { |
| public List<Integer> findDisappearedNumbers(int[] nums) { |
| List<Integer> disNumList = new ArrayList<>(); |
| int len = nums.length; |
| for (int i = 0; i < len; i++) { |
| |
| while (nums[i] != (i + 1) && nums[i] != -1) { |
| |
| if (nums[nums[i] - 1] == nums[i]) |
| nums[i] = -1; |
| else |
| swap(nums, i, nums[i] - 1); |
| } |
| } |
| |
| for (int i = 0; i < len; i++) { |
| if (nums[i] == -1) { |
| disNumList.add(i + 1); |
| } |
| } |
| return disNumList; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| private void swap(int[] nums, int i, int j) { |
| int temp = nums[i]; |
| nums[i] = nums[j]; |
| nums[j] = temp; |
| } |
| } |
| |
| |
| |
| |
| class Solution { |
| public List<Integer> findDisappearedNumbers(int[] nums) { |
| int n = nums.length; |
| for (int num : nums) { |
| int x = (num - 1) % n; |
| nums[x] += n; |
| } |
| List<Integer> ret = new ArrayList<Integer>(); |
| for (int i = 0; i < n; i++) { |
| if (nums[i] <= n) { |
| ret.add(i + 1); |
| } |
| } |
| return ret; |
| } |
| } |
| class Solution { |
| public List<Integer> findDisappearedNumbers(int[] nums) { |
| int n = nums.length; |
| for (int num : nums) { |
| int x = (num - 1) % n; |
| nums[x] += n; |
| } |
| List<Integer> ret = new ArrayList<Integer>(); |
| for (int i = 0; i < n; i++) { |
| if (nums[i] <= n) { |
| ret.add(i + 1); |
| } |
| } |
| return ret; |
| } |
| } |
tag: 位运算 ^
| |
| |
| |
| public int hammingDistance(int x, int y) { |
| int z = x ^ y; |
| int sum = 0; |
| while (z > 0) { |
| if (z % 2 == 1) |
| sum++; |
| z /= 2; |
| } |
| return sum; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class Solution { |
| public int hammingDistance(int x, int y) { |
| return Integer.bitCount(x ^ y); |
| } |
| } |
| |
| |
| |
| |
| |
| class Solution { |
| public int hammingDistance(int x, int y) { |
| int s = x ^ y, ret = 0; |
| while (s != 0) { |
| s &= s - 1; |
| ret++; |
| } |
| return ret; |
| } |
| } |
tag: 动态规划、滚动数组、背包问题
| public class Question_494 { |
| |
| |
| |
| |
| public int findTargetSumWays(int[] nums, int target) { |
| int sum = 0; |
| |
| for (int num : nums) { |
| sum += num; |
| } |
| int diff = sum - target; |
| |
| if (diff < 0 || diff % 2 != 0) { |
| return 0; |
| } |
| int n = nums.length, neg = diff / 2; |
| |
| int[][] dp = new int[n + 1][neg + 1]; |
| dp[0][0] = 1; |
| for (int i = 1; i <= n; i++) { |
| int num = nums[i - 1]; |
| for (int j = 0; j <= neg; j++) { |
| dp[i][j] = dp[i - 1][j]; |
| if (j >= num) { |
| dp[i][j] += dp[i - 1][j - num]; |
| } |
| } |
| } |
| return dp[n][neg]; |
| } |
| |
| |
| |
| public int findTargetSumWays2(int[] nums, int target) { |
| int sum = 0; |
| for (int num : nums) { |
| sum += num; |
| } |
| int diff = sum - target; |
| if (diff < 0 || diff % 2 != 0) { |
| return 0; |
| } |
| int neg = diff / 2; |
| |
| int[] dp = new int[neg + 1]; |
| dp[0] = 1; |
| for (int num : nums) { |
| for (int j = neg; j >= num; j--) { |
| dp[j] += dp[j - num]; |
| } |
| } |
| return dp[neg]; |
| } |
| } |
tag: 反中序遍历、二叉搜索树

| |
| class Solution1 { |
| public TreeNode convertBST(TreeNode root) { |
| int sum = 0; |
| TreeNode node = root; |
| |
| while (node != null) { |
| |
| if (node.right == null) { |
| sum += node.val; |
| node.val = sum; |
| node = node.left; |
| } else { |
| |
| TreeNode succ = getSuccessor(node); |
| |
| if (succ.left == null) { |
| |
| |
| |
| |
| |
| |
| |
| succ.left = node; |
| |
| node = node.right; |
| } else { |
| |
| succ.left = null; |
| sum += node.val; |
| node.val = sum; |
| node = node.left; |
| } |
| } |
| } |
| return root; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public TreeNode getSuccessor(TreeNode node) { |
| TreeNode succ = node.right; |
| while (succ.left != null && succ.left != node) { |
| succ = succ.left; |
| } |
| return succ; |
| } |
| } |
| |
| class Solution { |
| public TreeNode convertBST(TreeNode root) { |
| Deque<TreeNode> stack = new LinkedList<>(); |
| if (root == null) |
| return root; |
| TreeNode p = root, q = null; |
| |
| while (p != null || !stack.isEmpty()) { |
| |
| if (p != null) { |
| stack.push(p); |
| p = p.right; |
| } else { |
| |
| p = stack.pop(); |
| |
| if (q != null) { |
| p.val += q.val; |
| } |
| q = p; |
| p = p.left; |
| } |
| } |
| return root; |
| } |
| } |
| |
| |
| class Solution { |
| int sum = 0; |
| |
| public TreeNode convertBST(TreeNode root) { |
| if (root != null) { |
| convertBST(root.right); |
| sum += root.val; |
| root.val = sum; |
| convertBST(root.left); |
| } |
| return root; |
| } |
| } |
tag: 深度优先搜索
| class Solution { |
| private int maxDiameter; |
| |
| public int diameterOfBinaryTree(TreeNode root) { |
| diameter(root); |
| return maxDiameter; |
| } |
| |
| |
| private int diameter(TreeNode root) { |
| if (root == null) |
| return 0; |
| int ldiameter = 0, rdiameter = 0; |
| |
| if (root.left != null) |
| ldiameter = 1 + diameter(root.left); |
| |
| if (root.right != null) |
| rdiameter = 1 + diameter(root.right); |
| |
| maxDiameter = maxDiameter > (ldiameter + rdiameter) ? maxDiameter : (ldiameter + rdiameter); |
| |
| return ldiameter > rdiameter ? ldiameter : rdiameter; |
| } |
| } |
tag: 深度优先搜索、广度优先搜索
| |
| |
| |
| class Solution { |
| public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { |
| if (root1 == null) { |
| return root2; |
| } |
| if (root2 == null) { |
| return root1; |
| } |
| TreeNode merged = new TreeNode(root1.val + root2.val); |
| merged.left = mergeTrees(root1.left, root2.left); |
| merged.right = mergeTrees(root1.right, root2.right); |
| return merged; |
| } |
| } |
tag: 单调栈
| |
| |
| |
| class Solution { |
| public int[] dailyTemperatures(int[] temperatures) { |
| int len = temperatures.length; |
| |
| if (len == 1) |
| return new int[] { 0 }; |
| |
| int[] result = new int[len]; |
| Deque<Integer> stack = new LinkedList<>(); |
| |
| for (int i = 0; i < len; i++) { |
| |
| while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) { |
| int index = stack.pop(); |
| result[index] = i - index; |
| } |
| stack.push(i); |
| } |
| |
| |
| while (!stack.isEmpty()) { |
| result[stack.pop()] = 0; |
| } |
| return result; |
| } |
| } |
类似题目:448.找到所有数组中消失的数字
| |
| |
| |
| |
| |
| |
| |
| class Solution { |
| public int findRepeatNumber(int[] nums) { |
| int len = nums.length; |
| |
| for (int i = 0; i < len; i++) { |
| |
| while (nums[i] != i) { |
| |
| if (nums[nums[i]] == nums[i]) |
| return nums[i]; |
| swap(nums, i, nums[i]); |
| } |
| } |
| return 0; |
| } |
| |
| |
| private void swap(int[] nums, int i, int j) { |
| int temp = nums[i]; |
| nums[i] = nums[j]; |
| nums[j] = temp; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| class Solution2 { |
| public int findRepeatNumber(int[] nums) { |
| int len = nums.length; |
| for (int i = 0; i < len; i++) { |
| int j = nums[i] % len; |
| if (nums[j] >= len) |
| return j; |
| nums[j] += len; |
| } |
| return -1; |
| } |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)