第三天
声明:本系列文章仅适合二刷有经验的算法er学习,以后会出详细的每一题的讲解,这里只是简单的说明思路来帮助大家快速刷完Top101,另外博主的算法全程跟着 labuladong 大佬学习,这里特此声明
30.二叉搜索树与双向链表
解题思路
创建两个指针采用中序遍历的方式
代码实现
public class Solution {
TreeNode head = null, pre = null;
public TreeNode Convert(TreeNode pRootOfTree) {
if (pRootOfTree == null) {
return null;
}
Convert(pRootOfTree.left);
if (pre == null) {
head = pRootOfTree;
pre = pRootOfTree;
} else {
pre.right = pRootOfTree;
pRootOfTree.left = pre;
pre = pRootOfTree;
}
Convert(pRootOfTree.right);
return head;
}
}
31.对称的二叉树
解题思路
当进入子问题的两个节点都为空的时候,说明到达了叶子节点,且是同步的,返回true。当进入子问题的两个节点只有一个为空,或元素不变,说明不匹配,返回false
代码实现
public class Solution {
public boolean recursion(TreeNode root1, TreeNode root2) {
if (root1 == null && root2 == null) {
return true;
}
if (root1 == null || root2 == null || root1.val != root2.val) {
return false;
}
return recursion(root1.left, root2.right) && recursion(root1.right, root2.left);
}
boolean isSymmetrical(TreeNode pRoot) {
return recursion(pRoot, pRoot);
}
}
32、合并二叉树
解题思路
判断特殊情况
- t1 与 t2 都为空
- t1 为空
- t2 为空
以前序遍历的方式 new 出新节点,然后递归左右子树
代码实现
public class Solution {
/**
*
* @param t1 TreeNode类
* @param t2 TreeNode类
* @return TreeNode类
*/
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
// write code here
if (t1 == null && t2 == null) {
return null;
}
if (t1 == null) {
return t2;
}
if (t2 == null) {
return t1;
}
TreeNode head = new TreeNode(t1.val + t2.val);
head.left = mergeTrees(t1.left, t2.left);
head.right = mergeTrees(t1.right, t2.right);
return head;
}
}
33.二叉树的镜像
解题思路
先将根节点的左右两个子树的节点以递归的形式逆序,然后逆序根的左右节点
代码实现
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror(TreeNode pRoot) {
// write code here
if (pRoot == null) {
return null;
}
TreeNode left = Mirror(pRoot.left);
TreeNode right = Mirror(pRoot.right);
pRoot.left = right;
pRoot.right = left;
return pRoot;
}
}
34.判断是不是二叉搜索树
解题思路
- 如果根节点为 null ,则是BST
- 采用中序遍历的方式,因为中序遍历后的顺序会是一个有序的形式,先进入左节点判断
- 再进入右节点判断
代码实现
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param root TreeNode类
* @return bool布尔型
*/
int pre = Integer.MIN_VALUE;
public boolean isValidBST (TreeNode root) {
// write code here
if(root == null){
return true;
}
if(!isValidBST(root.left)){
return false;
}
if(root.val < pre){
return false;
}
pre = root.val;
return isValidBST(root.right);
}
}
35.判断是不是完全二叉树
解题思路
首先明确完全二叉树的叶子结点只能出现在最下层或者次下层
- 先判断空树一定是一个完全二叉树
- 初始化一个队列辅助进行层序遍历,加入根节点
- 逐渐从队列中弹出元素访问节点,如果遇到某个节点为空,进行标记,如果后续还有访问,就说明提前出现了叶子节点
- 否则继续加入左右子节点进入队列排队
代码实现
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param root TreeNode类
* @return bool布尔型
*/
Queue<TreeNode> queue = new LinkedList<>();
public boolean isCompleteTree(TreeNode root) {
// write code here
if (root == null) {
return true;
}
queue.offer(root);
TreeNode cur;
boolean notComplete = false;
while (!queue.isEmpty()) {
cur = queue.poll();
if (cur == null) {
notComplete = true;
continue;
}
if (notComplete) {
return false;
}
queue.offer(cur.left);
queue.offer(cur.right);
}
return true;
}
}
36.判断是不是平衡二叉树
解题思路
- 首先去找到要判断二叉树的左右最大深度,如果左右子树绝对值之差大于 1 ,那么说明非平衡二叉树,直接返回 false
- 递归判断所有的子树
代码实现
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
if (root == null) {
return true;
}
int left = deep(root.left);
int right = deep(root.right);
if (left - right > 1 || left - right < -1) {
return false;
}
return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
}
public int deep(TreeNode root) {
if (root == null) {
return 0;
}
int left = deep(root.left);
int right = deep(root.right);
return Math.max(left, right) + 1;
}
}
37.二叉搜索树的最近公共祖先
解题思路
分情况判断
- 左右节点位于根节点的两边,说明最近的公共祖先就是根节点
- 如果都在一边,就递归判断直到结果出现
代码实现
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param root TreeNode类
* @param p int整型
* @param q int整型
* @return int整型
*/
public int lowestCommonAncestor(TreeNode root, int p, int q) {
// write code here
if (root == null) {
return -1;
}
if ((p >= root.val && q <= root.val) || (p <= root.val && q >= root.val)) {
return root.val;
} else if (p <= root.val && q <= root.val) {
return lowestCommonAncestor(root.left, p, q);
} else {
return lowestCommonAncestor(root.right, p, q);
}
}
}
38.在二叉树中找到两个节点的最近公共祖先
解题思路
与上题一样
代码实现
public class Solution {
/**
*
* @param root TreeNode类
* @param o1 int整型
* @param o2 int整型
* @return int整型
*/
public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
// write code here
if(root == null){
return -1;
}
if(root.val == o1 || root.val == o2){
return root.val;
}
int left = lowestCommonAncestor(root.left,o1,o2);
int right = lowestCommonAncestor(root.right,o1,o2);
if(left == -1){
return right;
}
if(right == -1){
return left;
}
return root.val;
}
}
39.序列化二叉树
解题思路
通过前序遍历实现,注意返回格式即可
代码实现
public class Solution {
String SEP = ",";
String NULL = "#";
String Serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
serialize(root, sb);
return sb.toString();
}
void serialize(TreeNode root, StringBuilder sb) {
if (root == null) {
sb.append(NULL).append(SEP);
return;
}
sb.append(root.val).append(SEP);
serialize(root.left, sb);
serialize(root.right, sb);
}
TreeNode Deserialize(String str) {
LinkedList<String> nodes = new LinkedList<>();
for (String s : str.split(SEP)) {
nodes.addLast(s);
}
return deserialize(nodes);
}
TreeNode deserialize(LinkedList<String> nodes) {
if (nodes.isEmpty()) {
return null;
}
String first = nodes.removeFirst();
if (first.equals(NULL)) {
return null;
}
TreeNode root = new TreeNode(Integer.parseInt(first));
root.left = deserialize(nodes);
root.right = deserialize(nodes);
return root;
}
}
40.重建二叉树
解题思路
前序的第一个节点是头结点,然后根据前序中序的特点建树就行
代码实现
public class Solution {
public TreeNode reConstructBinaryTree(int[] pre, int[] vin) {
for (int i = 0; i < vin.length; i++) {
valToIndex.put(vin[i], i);
}
return build(pre, 0, pre.length - 1,
vin, 0, vin.length - 1);
}
HashMap<Integer, Integer> valToIndex = new HashMap<>();
public TreeNode build(int[] pre, int preStart, int preEnd,
int[] vin, int inStart, int inEnd) {
if (preStart > preEnd) {
return null;
}
//根节点的值
int rootVal = pre[preStart];
//根节点在中序遍历数组中的位置
int index = valToIndex.get(rootVal);
int leftSize = index - inStart;
TreeNode root = new TreeNode(rootVal);
root.left = build(pre, preStart + 1, preStart + leftSize,
vin, inStart, index - 1);
root.right = build(pre, preStart + leftSize + 1, preEnd,
vin, index + 1, inEnd);
return root;
}
}
41.输出二叉树的右视图
解题思路
先建树,在深度优先搜索
代码实现
public int[] solve(int[] xianxu, int[] zhongxu) {
// write code here
for (int i = 0; i < zhongxu.length; i++) {
valToIndex.put(zhongxu[i], i);
}
TreeNode root = build(xianxu, 0, xianxu.length - 1, zhongxu, 0, zhongxu.length - 1);
ArrayList<Integer> temp = rightSideView(root);
int[] res = new int[temp.size()];
for (int i = 0; i < temp.size(); i++) {
res[i] = temp.get(i);
}
return res;
}
HashMap<Integer, Integer> valToIndex = new HashMap<>();
//建树
public TreeNode build(int[] pre, int preStr, int preEnd, int[] in, int inStr, int inEnd) {
if (preStr > preEnd) {
return null;
}
int rootVal = pre[preStr];
int index = valToIndex.get(rootVal);
int leftSize = index - inStr;
TreeNode root = new TreeNode(rootVal);
root.left = build(pre, preStr + 1, preStr + leftSize, in, inStr, index - 1);
root.right = build(pre, preStr + leftSize + 1, preEnd, in, index + 1, inEnd);
return root;
}
public ArrayList<Integer> rightSideView(TreeNode root) {
Map<Integer, Integer> map = new HashMap<>();
int maxDepth = -1;
Stack<TreeNode> nodes = new Stack<>();
Stack<Integer> depths = new Stack<>();
nodes.push(root);
depths.push(0);
while (!nodes.isEmpty()) {
TreeNode node = nodes.pop();
int depth = depths.pop();
if (node != null) {
maxDepth = Math.max(maxDepth, depth);
if (map.get(depth) == null) {
map.put(depth, node.val);
}
nodes.push(node.left);
nodes.push(node.right);
depths.push(depth + 1);
depths.push(depth + 1);
}
}
ArrayList<Integer> res = new ArrayList<>();
for (int i = 0; i <= maxDepth; i++) {
res.add(map.get(i));
}
return res;
}
}
55.没有重复项数字的全排列
解题思路
用回溯的板子,记得使用一个used数组来记录是否使用过指定的元素就行
代码实现
public class Solution {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
LinkedList<Integer> track = new LinkedList<>();
boolean[] used;
public ArrayList<ArrayList<Integer>> permute(int[] num) {
used = new boolean[num.length];
backtrack(num);
return res;
}
public void backtrack(int[] nums) {
//base case
if (track.size() == nums.length) {
res.add(new ArrayList<>(track));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i]) {
continue;
}
used[i] = true;
track.addLast(nums[i]);
backtrack(nums);
track.removeLast();
used[i] = false;
}
}
}
56.有重复数字的全排列
解题思路
回溯的板子,记得剪枝就行
代码实现
public class Solution {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
LinkedList<Integer> track = new LinkedList<>();
boolean[] used;
public ArrayList<ArrayList<Integer>> permuteUnique(int[] num) {
Arrays.sort(num);
used = new boolean[num.length];
backtrack(num);
return res;
}
public void backtrack(int[] nums) {
//base case
if (track.size() == nums.length) {
res.add(new ArrayList<>(track));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i]) {
continue;
}
// 如果前面的相邻相等元素没有用过,则跳过
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
used[i] = true;
track.addLast(nums[i]);
backtrack(nums);
used[i] = false;
track.removeLast();
}
}
}
73.最长回文子串
解题思路
枚举所有的可能
代码实现
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param A string字符串
* @return int整型
*/
public int getLongestPalindrome(String A) {
// write code here
int maxLen = 1;
for (int i = 0; i < A.length() - 1; i++) {
maxLen = Math.max(maxLen, Math.max(fun(A, i, i), fun(A, i, i + 1)));
}
return maxLen;
}
public int fun(String s, int begin, int end) {
while (begin >= 0 && end < s.length() && s.charAt(begin) == s.charAt(end)) {
begin--;
end++;
}
return end - begin - 1;
}
}
74.数字字符串转化ip地址
解题思路
- 一次枚举这三个点的位置
- 截取出四段数字
- 条件比较
代码实现
public class Solution {
/**
*
* @param s string字符串
* @return string字符串ArrayList
*/
public ArrayList<String> restoreIpAddresses(String s) {
// write code here
ArrayList<String> res = new ArrayList<>();
int n = s.length();
for (int i = 1; i < 4 && i < n - 2; i++) {
for (int j = i + 1; j < i + 4 && j < n - 1; j++) {
for (int k = j + 1; k < j + 4 && k < n; k++) {
if (n - k >= 4) {
continue;
}
String a = s.substring(0, i);
String b = s.substring(i, j);
String c = s.substring(j, k);
String d = s.substring(k);
if (Integer.parseInt(a) > 255 || Integer.parseInt(b) > 255
|| Integer.parseInt(c) > 255 || Integer.parseInt(d) > 255) {
continue;
}
if ((a.length() != 1 && a.charAt(0) == '0') ||
(b.length() != 1 && b.charAt(0) == '0') || (c.length() != 1 &&
c.charAt(0) == '0') || (d.length() != 1 && d.charAt(0) == '0')) {
continue;
}
String temp = a + "." + b + "." + c + "." + d;
res.add(temp);
}
}
}
return res;
}
}
88.判断是否为回文字符串
解题思路
双指针一个指向头部一个指向尾部,从两边向中间移动
代码实现
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param str string字符串 待判断的字符串
* @return bool布尔型
*/
public boolean judge(String str) {
// write code here
int left = 0;
int right = str.length() - 1;
while (left < right) {
if (str.charAt(left) != str.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
}
89.合并区间
解题思路
- 将所有区间按照起点位置先进行排序
- 排序后第一个区间一定是起点值最小的区间,返回 res ,然后遍历过程区间
- 处理一下重叠问题
代码实现
public class Solution {
ArrayList<Interval> res = new ArrayList<>();
public ArrayList<Interval> merge(ArrayList<Interval> intervals) {
if (intervals.size() == 0) {
return res;
}
Collections.sort(intervals, new Comparator<Interval>() {
@Override
public int compare(Interval o1, Interval o2) {
if (o1.start != o2.start) {
return o1.start - o2.start;
} else {
return o1.end - o2.end;
}
}
});
res.add(intervals.get(0));
int count = 0;
for (int i = 1; i < intervals.size(); i++) {
Interval o1 = intervals.get(i);
Interval origin = res.get(count);
if (o1.start > origin.end) {
res.add(o1);
count++;
} else { //区间有重叠,更新结尾
res.remove(count);
Interval s = new Interval(origin.start, o1.end);
if (o1.end < origin.end) {
s.end = origin.end;
}
res.add(s);
}
}
return res;
}
}
90.最小覆盖子串
解题思路
使用哈希表进行匹配
代码实现
public class Solution {
/**
*
* @param S string字符串
* @param T string字符串
* @return string字符串
*/
public boolean check(int[] hash) {
for (int i = 0; i < hash.length; i++) {
if (hash[i] < 0) {
return false;
}
}
return true;
}
public String minWindow(String S, String T) {
// write code here
int cnt = S.length() + 1;
int[] hash = new int[128];
for (int i = 0; i < T.length(); i++) {
hash[T.charAt(i)] -= 1;
}
int slow = 0, fast = 0;
int left = -1, right = -1;
for (; fast < S.length(); fast++) {
char c = S.charAt(fast);
hash[c]++;
while (check(hash)) {
if (cnt > fast - slow + 1) {
cnt = fast - slow + 1;
left = slow;
right = fast;
}
c = S.charAt(slow);
hash[c]--;
slow++;
}
}
if (left == -1) {
return "";
}
return S.substring(left, right + 1);
}
}
91.反转字符串
解题思路
使用Java自带的StringBuilder进行字符串的反转
代码实现
public class Solution {
/**
* 反转字符串
* @param str string字符串
* @return string字符串
*/
public String solve(String str) {
// write code here
if (str == null || str.length() == 0) {
return "";
}
StringBuilder sb = new StringBuilder(str);
return sb.reverse().toString();
}
}
92.最长无重复字串
解题思路
遇到子串,子数组就是标准的滑动窗口解题模板
- 首先使用一个HashMap来存储arr数组中字符出现的次数
- 遍历map集合,如果某一个元素出现的次数大于1,那么说明左右指针指向了重复的元素,需要缩小左窗口
代码实现
public class Solution {
/**
*
* @param arr int整型一维数组 the array
* @return int整型
*/
//记录窗口内非重复数字
HashMap<Integer, Integer> map = new HashMap<>();
public int maxLength(int[] arr) {
// write code here
int res = 0;
int left = 0, right = 0;
for (; right < arr.length; right++) {
if (map.containsKey(arr[right])) {
map.put(arr[right], map.get(arr[right]) + 1);
} else {
map.put(arr[right], 1);
}
while (map.get(arr[right]) > 1) {
map.put(arr[left], map.get(arr[left]) - 1);
left++;
}
res = Math.max(res, right - left + 1);
}
return res;
}
}
93.盛水最多的容器
解题思路
双指针一个指向开头,一个指向结尾,从两边不断地向中间缩减,维护一个最大容量,最后返回即可
代码实现
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param height int整型一维数组
* @return int整型
*/
public int maxArea(int[] height) {
// write code here
if (height.length < 2) {
return 0;
}
int res = 0;
int left = 0, right = height.length - 1;
while (left < right) {
int capacity = Math.min(height[left], height[right]) * (right - left);
res = Math.max(res, capacity);
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return res;
}
}
94.接雨水问题
解题思路
与上题如出一辙,只不过在这里多维护两个变量,分别记录中间的最大长度
代码实现
public class Solution {
/**
* max water
* @param arr int整型一维数组 the array
* @return long长整型
*/
public long maxWater(int[] arr) {
// write code here
if (arr.length == 0) {
return 0;
}
long res = 0;
int left = 0, right = arr.length - 1;
int maxL = 0;
int maxR = 0;
while (left < right) {
maxL = Math.max(maxL, arr[left]);
maxR = Math.max(maxR, arr[right]);
if (maxR > maxL) {
res += maxL - arr[left++];
} else {
res += maxR - arr[right--];
}
}
return res;
}
}