LeetCode刷题笔记_第一周

栈与队列

1047.删除字符串中所有相邻重复项(简单)

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

输入:“abbaca”
输出:“ca”
解释:
例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

使用deque栈/双指针,推荐使用双指针

思路:用top模拟栈顶指针,用res串模拟栈

public String removeDuplicates(String s){
	StringBuffer res = new StringBuffer();
	int top = -1;
	for (int i = 0; i < s.length(); i++) {
	    if (top >= 0 && res.charAt(top) == s.charAt(i)){
	        res.deleteCharAt(top);
	        top--;
	    }else {
	        res.append(s.charAt(i));
	        top++;
	    }
	}
	return res.toString();
}

150.逆波兰表达式求值(中等)

有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

输入:tokens = [“2”,“1”,“+”,“3”,“*”]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

使用Deque

public int evalRPN(String[] tokens) {
      Deque<Integer> deque = new LinkedList<>();
      for (String i : tokens) {
          if("+".equals(i)){
              deque.addFirst(deque.removeFirst() + deque.removeFirst());
          }else if ("-".equals(i)){
              deque.addFirst((-deque.removeFirst()) + deque.removeFirst());
          }else if ("*".equals(i)){
              deque.addFirst(deque.removeFirst() * deque.removeFirst());
          }else if ("/".equals(i)){
              int after = deque.removeFirst();
              int before = deque.removeFirst();
              deque.addFirst(before / after);
          }else{
              deque.addFirst(Integer.parseInt(i));
          }
      }
      return deque.removeFirst();
  }	
}

239.滑动窗口最大值(困难)

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 。

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]

思路:
创建一个自己的队列类,其中使用双端队列实现poll、add、peek。
在每次poll(num[i - k])时,判断是否是上一个窗口的最大值,若是,则弹出。
在每次add(num[i])时,判断入队的元素是否比队尾元素大,若是则队尾元素删除,直到不比他小或到队头。


class MyQueue{
    Deque<Integer> deque = new LinkedList<>();
    public void poll(int val){
        if(!deque.isEmpty() && deque.peekFirst() == val){
            deque.removeFirst();
        }
    }

    public void add(int val){
        while(!deque.isEmpty() && deque.peekLast() < val){
            deque.removeLast();
        }
        deque.addLast(val);
    }

    public int peek(){
        return deque.peekFirst();
    }
}
public int[] maxSlidingWindow(int[] nums, int k) {
    if(nums.length == 1) return nums;
    int[] res = new int[nums.length - k + 1];
    int num = 0;
    MyQueue myQueue = new MyQueue();
    for (int i = 0; i < k; i++) {
        myQueue.add(nums[i]);
    }
    res[num++] = myQueue.peek();

    for (int i = k; i < nums.length; i++) {
        myQueue.poll(nums[i - k]);
        myQueue.add(nums[i]);
        res[num++] = myQueue.peek();
    }
    return res;
}
    

347.前 K 个高频元素(中等)

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

思路:
1.使用map统计各个元素的出现频率

map.put(num, map.getOrDefault(num, 0) + 1)

2.使用PriorityQueue类型队列模拟小根堆
new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue())

用于统计频率前k高的元素,队列中一直保持size不超过k。

 public int[] topKFrequent(int[] nums, int k) {
   int[] res = new int[k];
   HashMap<Integer, Integer> map = new HashMap<>();
   for (int num : nums) {
       map.put(num, map.getOrDefault(num, 0) + 1);
   }
   PriorityQueue<Map.Entry<Integer, Integer>> queue= new PriorityQueue<>(((o1, o2) -> o1.getValue() - o2.getValue()));
   for (Map.Entry<Integer, Integer> entry :
           queue) {
       queue.offer(entry);
       if(queue.size() > k){
           queue.poll();
       }
   }
   for (int i = k - 1; i >= 0; i--) {
       res[i] = queue.poll().getValue();
   }
   return res;
}

二叉树

递归算法的三要素

  1. 确认递归函数的参数和返回值
  2. 确定终止条件
  3. 确定单层递归的逻辑

144.二叉树的前序遍历(简单)

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

输入:root = [1,null,2,3]
输出:[1,2,3]

递归法:
思路:

  1. 参数为root、res,无返回值
  2. 终止条件为节点为空
  3. 单层递归逻辑为根左右入队列
 public List<Integer> preorderTraversal(TreeNode root) {
    ArrayList<Integer> res = new ArrayList<>();
    preorder(root, res);
    return res;
}
public void  preorder(TreeNode root, List<Integer> res){
    if(root == null) return;
    res.add(root.val);
    preorder(root.left, res);
    preorder(root.right, res);
}

迭代法:
前序遍历顺序:中-左-右,入栈顺序:中-右-左

public List<Integer> preorderTravelsal(TreeNode root){
    ArrayList<Integer> res = new ArrayList<>();
    if(root == null) return res;

    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);
    while(!stack.isEmpty()){
        TreeNode node = stack.pop();
        res.add(node.val);
        if(node.right != null) stack.push(node.right);
        if(node.left != null) stack.push(node.left);
    }
    return res;
}

145.二叉树的中序遍历(简单)

递归法:
递归逻辑为左根右,其他与前序相同。
迭代法:
中序遍历顺序: 左-中-右 入栈顺序: 左-右

public List<Integer> inorderTravelsal(TreeNode root){
   List<Integer> res = new ArrayList<>();
   if (root == null) {
       return res;
   }
   Stack<TreeNode> stack = new Stack<>();
   TreeNode cur = root;
   while (cur != null || !stack.isEmpty()) {
       if (cur != null){
           stack.push(cur);
           cur = cur.left;
       }else {
           cur = stack.pop();
           res.add(cur.val);
           cur = cur.right;
       }
   }
   return res;
}

94.二叉树的后序遍历(简单)

递归:
逻辑为左右根,其他与前序相同。
迭代:
后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    if (root == null){
        return result;
    }
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);
    while (!stack.isEmpty()){
        TreeNode node = stack.pop();
        result.add(node.val);
        if (node.left != null){
            stack.push(node.left);
        }
        if (node.right != null){
            stack.push(node.right);
        }
    }
    Collections.reverse(result);
    return result;
}

102. 二叉树的层序遍历(中等)

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

递归思路:
每当传入checkDeep一个值的时候,则将增加本层数,并且,将值传回上一层的list的个体之中。
与之同时,要对比res队列的大小是否比deep小,若小,则增加一个list个体。

//存储每一层的List
List<List<Integer>> levelRes = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
    checkDeep(root, 0);
    return levelRes;
}
public void checkDeep(TreeNode node, Integer deep){
    if(node == null) return;
    deep++;
    if (levelRes.size() < deep){
        //层级增加时,list的Item也增加
        List<Integer> item = new ArrayList<>();
        levelRes.add(item);
    }
    levelRes.get(deep - 1).add(node.val);

    checkDeep(node.left, deep);
    checkDeep(node.right, deep);
}

迭代思路:
首先往队列中放入当前节点,将当前所有节点的子节点放入队列中,然后排出,则为一层

List<List<Integer>> levelRes = new ArrayList<>();
public void checkDeep(TreeNode node){
    if(node == null) return;
    Deque<TreeNode> deque = new LinkedList<>();
    deque.offerLast(node);

    while(!deque.isEmpty()){
        List<Integer> itemList = new ArrayList<>();
        int len = deque.size();
        //将deque中的值一个一个取出来
        while (len > 0){
            TreeNode tmpNode = deque.pollFirst();
            itemList.add(tmpNode.val);

            if (tmpNode.left != null) deque.offerLast(tmpNode.left);
            if (tmpNode.right != null) deque.offerLast(tmpNode.right);
            len--;
        }
        levelRes.add(itemList);
    }
}

226.翻转二叉树(简单)

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

请添加图片描述

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

思路:
把每个节点的孩子左右翻转一下(可用前序遍历/后序遍历)

public TreeNode invertTree(TreeNode root) {
    if (root == null) return null;
    invertTree(root.left);
    invertTree(root.right);
    swapChildren(root);
    return root;
}
public void swapChildren(TreeNode root){
    TreeNode temp = root.left;
    root.left = root.right;
    root.right = temp;
}

101. 对称二叉树(简单)

给你一个二叉树的根节点 root , 检查它是否轴对称

输入:root = [1,2,2,3,4,4,3]
输出:true

迭代法:使用双端队列,入栈顺序为左节点的左右孩子,右节点的左右孩子。可用单端队列代替

Deque<TreeNode> deque = new LinkedList<>();
    deque.offerFirst(root.left);
    deque.offerLast(root.right);
    while (!deque.isEmpty()) {
        TreeNode leftNode = deque.pollFirst();
        TreeNode rightNode = deque.pollLast();
        if (leftNode == null && rightNode == null) {
            continue;
        }
        if (leftNode == null || rightNode == null || leftNode.val != rightNode.val){
            return false;
        }
        deque.offerFirst(leftNode.left);
        deque.offerFirst(leftNode.right);
        deque.offerLast(rightNode.right);
        deque.offerLast(rightNode.left);
    }
    return true;
}

递归法:

public boolean isSymmetric(TreeNode root) {
    if(root == null) return false;
    return compare(root.left, root.right);
}
public boolean compare(TreeNode left, TreeNode right){
    if(left == null && right==null){
        return true;
    }else if(left == null && right != null){
        return false;
    }else if (left != null && right == null) {
        return false;
    }else if (left.val != right.val){
        return false;
    }

    boolean outside = compare(left.left, right.right);
    boolean inside = compare(left.right, right.left);
    return outside && inside;
}

104. 二叉树的最大深度(简单)

给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

给定二叉树 [3,9,20,null,null,15,7]

因为二叉树的最大深度是根节点的高度,所以求根节点的高度即可
递归法:
类似于前序遍历

public int getDeepRecursion(TreeNode node){
     if (node == null) return 0;
     int leftDepth = getDeep(node.left);
     int rightDepth = getDeep(node.right);
     int deepth = 1 + Math.max(leftDepth, rightDepth);
     return deepth;
}

迭代法:
使用队列,复现层次遍历的迭代实现的方法。将节点放入队头,并每次

public int getDeepIteration(TreeNode node){
	if(node == null) return 0;
	int depth = 0;
	Deque<TreeNode> deque = new LinkedList<>();
	deque.offerFirst(node);
	while(!deque.isEmpty()){
		int len = deque.size();
		depth++;
		for(int i = 0; i < len; i++){
			TreeNode tmp = deque.pollFirst();
			if(tmp.left != null) deque.add(tmp.left);
			if(tmp.right != null) deque.add(tmp.right);
		}
	}
	return depth;
}

实际上求二叉树的最大深度应为:

int res;
public void getDepth(TreeNode node, int depth){
	res = depth > res ? depth : res;
	if(node.left == null && node.right == null) return;
	if(node.left != null){
		depth++;
		getDepth(node.left, depth);
		depth--;
	}
	if(node.right != null){
		depth++;
		getDepth(node.right, depth);
		depth--;
	}
	return;
}
int maxDepth(TreeNode ){
	res = 0;
	if(root == null) return res;
	getDepth(root, 1);
	return res;
}

559. N 叉树的最大深度(简单)

给定一个 N 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。

输入:root = [1,null,3,2,4,null,5,6]
输出:3

递归法:
将每个

public int maxDepthNtreeRecursion(Node root) {
	if(root == null) return 0;
	int depth = 0;
	if(root.children != null){
		for(Node child : root.children){
			depth = Math.max(depth, maxDepth(child);
		}
	}
	return depth++;
}

迭代法:
使用层序遍历的思想

public int maxDepthNtreeIteration(Node root){
    if (root == null) return 0;
    int depth = 0;
    Deque<Node> deque = new LinkedList<>();
    deque.offerLast(root);
    while (!deque.isEmpty()){
        depth++;
        int len = deque.size();
        while (len > 0){
            Node tmp = deque.pollFirst();
            for (int i = 0; i < tmp.children.size(); i++) {
                if (tmp.children.get(i) != null){
                    deque.offerLast(tmp.children.get(i));
                }
            }
            len--;
        }
    }
    return depth;
}

111. 二叉树的最小深度(简单)

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

输入:root = [3,9,20,null,null,15,7]
输出:2

迭代法:

public int minDepthCursion(TreeNode root){
    if (root == null) return 0;
    if (root.left == null && root.right != null) {
		return minDepthCursion(root.right) + 1;
	}
    
    if (root.left != null && root.right == null) {
		return minDepthCursion(root.left) + 1;
	}
    return 1 + Math.min(rightDepth, leftDepth);
}

递归法:

public int minDepethInteration(TreeNode root){
    if (root == null) return 0;
    int depth = 0;
    Deque<TreeNode> deque = new LinkedList<>();
    deque.offerLast(root);
    while (!deque.isEmpty()) {
        depth++;
        int len = deque.size();
        for (int i = 0; i < len; i++) {
            TreeNode tmp = deque.pollFirst();
            if (tmp.left != null && tmp.right == null) deque.offerLast(tmp.left);
            if (tmp.left == null && tmp.right != null) deque.offerLast(tmp.right);
            if (tmp.left == null && tmp.right == null) return depth;
        }
    }
    return depth;
}

222. 完全二叉树的节点个数(中等)

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

输入:root = [1,2,3,4,5,6]
输出:6

迭代法:

public int countNodesIteration(TreeNode root){
    if (root == null) return 0;
    int res = 0;
    Deque<TreeNode> deque = new LinkedList<>();
    deque.offerLast(root);
    while (!deque.isEmpty()) {
        int len = deque.size();
        for (int i = 0; i < len; i++) {
            TreeNode tmp = deque.pollFirst();
            res++;
            if (tmp.left != null) deque.offerLast(tmp.left);
            if (tmp.right != null) deque.offerLast(tmp.right);
        }
    }
    return res;
}

递归法:

public int countNodesCursion(TreeNode root) {
    if (root == null) return 0;
    int leftCount = countNodesCursion(root.left);
    int rightCount = countNodesCursion(root.right);
    int nodeCount = leftCount + rightCount + 1;
    return nodeCount;
}

符合满二叉树的写法:
注意 :<< : 左移运算符,num << 1,相当于num乘以2||>> : 右移运算符,num >> 1,相当于num除以2
满二叉树的节点数为2^depth - 1
即:10 >> 3 = 10 / (2 * 2 * 2), 10 << 3 = 10 * ( 2 * 2 * 2)
思路:
因为它输入已经确定是完全二叉树,所以只需判断最左边的深度是否与最右边的深度一致,若一致则为满二叉树,可用公式 2^depth - 1 计算。

public int countNodesFullTree(TreeNode root){
     if (root == null) return 0;
     int leftDepth = getDepth(root.left);
     int rightDepth = getDepth(root.right);
     if (leftDepth == rightDepth){//左子树为满二叉树
         return (1 << leftDepth) + countNodesFullTree(root.right);
     }else {//右子树为满二叉树
         return (1 << rightDepth) + countNodesFullTree(root.left);
     }
 }
 //只判断最左边的
 private int getDepth(TreeNode root){
     int depth = 0;
     while (root != null){
         root = root.left;
         depth++;
     }
     return depth;
 }

110. 平衡二叉树(简单)

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

输入:root = [3,9,20,null,null,15,7]
输出:true

思路:比较高度,需要使用后序遍历。
1.递归参数及返回值:

int getHeight(TreeNode node)

2.终止条件:

node == null

3.单层递归逻辑
判断左右子树的高度,若不同则返回-1 ,若相同则返回当前当前二叉树高度。

最终代码:(PS: abs是返回绝对值)

	public boolean isBalanced(TreeNode root) {
        return getHeight(root) != -1;
    }
    public int getHeight(TreeNode node){
        if(node == null) return 0;
        int leftHeight = getHeight(node.left);
        if(leftHeight == -1) return -1;
        int rightHeight = getHeight(node.right);
        if(leftHeight == -1) return -1;
        if(Math.abs(leftHeight - rightHeight) > 1){
            return -1;
        }
        return Math.max(leftHeight, rightHeight) + 1;
    }

257. 二叉树的所有路径(简单)

不止用了递归,还用了回溯
给定一个二叉树,返回所有从根节点到叶子节点的路径。

输入:root = [1,2,3,null,5]
输出:[“1->2->5”,“1->3”]

 public List<String> binaryTreePathsCursion(TreeNode root){
    List<String> res = new ArrayList<>();
    if (root == null) return res;
    List<Integer> paths = new ArrayList<>();
    traversal(root, paths, res);
    return res;
}
private void traversal(TreeNode root, List<Integer> paths, List<String> res){
    paths.add(root.val);
    if (root.left == null && root.right == null){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < paths.size() - 1; i++) {
            sb.append(paths.get(i)).append("->");
        }
        sb.append(paths.get(paths.size() - 1));
        res.add(sb.toString());
        return;
    }
    if (root.left != null){
        traversal(root.left, paths, res);
        paths.remove(paths.size() - 1);
    }
    if (root.right != null){
        traversal(root.right, paths, res);
        paths.remove(paths.size() - 1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值