LeetCode常见题目

154 阅读2分钟

92. 反转链表 II

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

    public ListNode reverseBetween(ListNode head, int left, int right) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode handle = dummy;
​
        ListNode preLeftNode = null;
        ListNode preRightNode = null;
        ListNode rightNode = null;
​
        // step1:找到节点
        int index = 0;
        while (dummy.next != null) {
            if (index == left - 1) {
                preLeftNode = dummy;
            }
            if (index == right) {
                rightNode = dummy;
            }
            dummy = dummy.next;
            index++;
​
        }
        // step2:切断链条
        ListNode mid = preLeftNode.next;
        preLeftNode.next = null;
​
        ListNode last = rightNode.next;
        rightNode.next = null;
​
        // step3:revert
        ListNode re = revertNode(mid);
​
        // step4:拼接链条
        preLeftNode.next = re;
        mid.next = last;
        return handle.next;
    }
​
    private ListNode revertNode(ListNode node) {
        ListNode pre = null;
        ListNode cur = node;
        while (cur != null) {
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }

\

树的遍历

前中后序遍历

    public List<Integer> inorderTraversal(TreeNode root) {
        final ArrayList<Integer> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        result.addAll(inorderTraversal(root.left));
        result.add(root.val);
        result.addAll(inorderTraversal(root.right));
        return result;
    }

层次遍历

1.递归

每一层放入List,当前层加到结果集,左右节点加入下一层List,递归调用

    private void getLevel(List<TreeNode> nodes) {
        if(nodes.isEmpty()) {
            return;
        }
        List<Integer> currentLevel = new ArrayList<>();
        List<TreeNode> nextLevel = new ArrayList<>();
        for (TreeNode treeNode : nodes) {
            if (treeNode !=null) {
                currentLevel.add(treeNode.val);
                nextLevel.add(treeNode.left);
                nextLevel.add(treeNode.right);
            }
        }
        if (!currentLevel.isEmpty()) {
            result.add(currentLevel);
        }
        getLevel(nextLevel);
    }

2.BFS

    void bfs(TreeNode root) {
        Queue<TreeNode> nodes = new ArrayDeque<>();
        nodes.add(root);
        while (!nodes.isEmpty()) {
            // 这一层有多少个!!!
            int n = nodes.size();
            List<Integer> current = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                final TreeNode node = nodes.poll();
                if (node != null) {
                    current.add(node.val);
                    if (node.left != null) {
                        nodes.add(node.left);
                    }
                    if (node.right != null) {
                        nodes.add(node.right);
                    }
​
                }
            }
            result.add(current);
        }
    }

DFS模板

void dfs(TreeNode root) {
    if (root == null) {
        return;
    }
    dfs(root.left);
    dfs(root.right);
}

BFS模板

void bfs(TreeNode root) {
    Queue<TreeNode> queue = new ArrayDeque<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode node = queue.poll(); // Java 的 pop 写作 poll()
        if (node.left != null) {
            queue.add(node.left);
        }
        if (node.right != null) {
            queue.add(node.right);
        }
    }
}
​

广搜万能模板

前中后序都可以

    public static List<Integer> inOrder(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        Stack<Object> stack = new Stack<>();
        stack.push(root);
        while (!stack.empty()) {
            Object o = stack.pop();
            if (o instanceof TreeNode) {
                TreeNode node=(TreeNode) o;
                //因为中序遍历是左节点--根节点--右节点
                //即出栈顺序为左节点--根节点--右节点,入栈顺序相反
                if (node.right != null) {
                    stack.push(node.right);
                }
                stack.push(node.val);
                if (node.left != null) {
                    stack.push(node.left);
                }
            }
            else {
                res.add((int)o);
            }
        }
        return res;
    }

11. 盛最多水的容器

image.png

双指针法,否则会超时,谁小往前走一步

34. 在排序数组中查找元素的第一个和最后一个位置

二分法查找

33. 搜索旋转排序数组

找到旋转点,分别进行二分

15. 三数之和

三数之和等于0,一个主节点,遍历,加两个指针

image.png

面试题 02.07. 链表相交

正常方法,遍历链表,放到List中,判断第二个链表是否包含,注意包含指相等而不是值相等

高级方法:双指针分别遍历两个链表,有相同元素就是相交了

合法二叉搜索树

不是当前节点是合法的,且左右节点都合法就可以了,要考虑每个节点允许值的范围

image.png

public boolean isValidBST(TreeNode root, long minVal, long maxVal) {
    if (root == null)
        return true;
    //每个节点如果超过这个范围,直接返回false
    if (root.val >= maxVal || root.val <= minVal)
        return false;
    //这里再分别以左右两个子节点分别判断,
    //左子树范围的最小值是minVal,最大值是当前节点的值,也就是root的值,因为左子树的值要比当前节点小
    //右子数范围的最大值是maxVal,最小值是当前节点的值,也就是root的值,因为右子树的值要比当前节点大
    return isValidBST(root.left, minVal, root.val) && isValidBST(root.right, root.val, maxVal);
}

547. 省份数量

zhuanlan.zhihu.com/p/93647900

这是一道并查集的题,通过不断归并父亲完成计算。

并查集被很多OIer认为是最简洁而优雅的数据结构之一,主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:

  • 合并(Union):把两个不相交的集合合并为一个集合。
  • 查询(Find):查询两个元素是否在同一个集合中。

当然,这样的定义未免太过学术化,看完后恐怕不太能理解它具体有什么用。所以我们先来看看并查集最直接的一个应用场景:亲戚问题

200. 岛屿数量

并查集可以做

使用dfs更加便捷,遍历过的地方变成0

        private void dfs(char[][] grid, int i, int j){
            if(i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') {
                return;
            }
            grid[i][j] = '0';
            dfs(grid, i + 1, j);
            dfs(grid, i, j + 1);
            dfs(grid, i - 1, j);
            dfs(grid, i, j - 1);
        }

463. 岛屿的周长

对于一个岛屿,看他的上下左右4条边,如果不是岛屿,总条数加1

    public int islandPerimeter(int[][] grid) {
        int res = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (grid[i][j] == 1) {
                    if (i == 0 || grid[i - 1][j] == 0) res++;
                    if (i == grid.length - 1 || grid[i + 1][j] == 0) res++;
                    if (j == 0 || grid[i][j - 1] == 0) res++;
                    if (j == grid[0].length - 1 || grid[i][j + 1] == 0) res++;
                }
            }
        }
        return res;
    }

1143. 最长公共子序列

动态规划

if (text1.charAt(i-1) == text2.charAt(j-1)) {
    dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}

82. 删除排序链表中的重复元素 II

思路一:直接遍历删除元素

    public ListNode deleteDuplicates(ListNode head) {
        // write code here
        if (head == null) {
            return head;
        }
        ListNode current = new ListNode(-1);
        current.next = head;
        ListNode handle = current;
​
        while (current.next.next != null && current.next != null) {
            if (current.next.val == current.next.next.val) {
                int target = current.next.val;
                while (current.next != null && target == current.next.val) {
                    current.next = current.next.next;
                }
            } else {
                current = current.next;
            }
        }
        return handle.next;
    }

思路二:递归删除

对于A-A-B类型的节点

如果当前节点和下一个节点一样,当前节点指向到下一个不一样的

A-B-B

如果当前节点和下一个不一样,当前节点肯定不需要删除,A.next = deleteDup(a.next)

146. LRU 缓存

自定义双向链表+hashmap实现

hashmap负责判断当前key是否存在

注意点

1.删除元素同时删除map中的

2.增删除元素要维护size数值

    class Node {
        int key;
​
        int value;
​
        Node pre;
​
        Node next;
​
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
​
        public Node() {
        }
    }
​
    int size;
​
    int cap;
​
    HashMap<Integer, Node> map = new HashMap<>();
​
    Node head;
​
    Node tail;
​
    public LRUCache(int capacity) {
        cap = capacity;
        size = 0;
        head = new Node();
        tail = new Node();
        head.next = tail;
        tail.pre = head;
    }
​
    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        Node node = map.get(key);
        moveToHead(node);
        return node.value;
    }
​
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            Node node = map.get(key);
            node.value = value;
            moveToHead(node);
        } else {
            if (cap <=   size) {
                Node removeNode = removeTail();
                map.remove(removeNode.key);
                size--;
            }
            final Node node = new Node(key, value);
            map.put(key, node);
            addToHead(node);
            size++;
        }
    }
​
    private void moveToHead(Node node) {
        remove(node);
        addToHead(node);
    }
​
    private void addToHead(Node node) {
        node.pre = head;
        node.next = head.next;
        head.next.pre = node;
        head.next = node;
    }
​
    private void remove(Node node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }
​
    private Node removeTail() {
        final Node res = tail.pre;
        remove(res);
        return res;
    }

使用自带数据结构实现

class LRUCache {
    int capacity;
    Map<Integer, Integer> map;
​
    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new LinkedHashMap<>();
    }
​
    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        // 先删除旧的位置,再放入新位置
        Integer value = map.remove(key);
        map.put(key, value);
        return value;
    }
​
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            map.remove(key);
            map.put(key, value);
            return;
        }
        map.put(key, value);
        // 超出capacity,删除最久没用的,利用迭代器删除第一个
        if (map.size() > capacity) {
            map.remove(map.entrySet().iterator().next().getKey());
        }
    }
}

剑指 Offer II 095. 最长公共子序列

dp

    public int longestCommonSubsequence(String text1, String text2) {
        if (text1 == null || text2 == null || text1.length() == 0 || text2.length() == 0) {
            return 0;
        }
        int len = text1.length();
        int width = text2.length();
        // 多方一个元素 让第一个元素具有普遍性,不需要特殊处理
        int[][] dp = new int[len + 1][width + 1];
​
        for (int i = 1; i <= len; i++) {
            char c1 = text1.charAt(i - 1);
            for (int j = 1; j <= width; j++) {
                char c2 = text2.charAt(j - 1);
                if (c2 == c1) {
                    // 非常重要!  获取左上角的
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
​
        }
        return dp[len][width];
    }

322. 零钱兑换

public int coinChange(int[] coins, int amount) {

    // 给一个不可能的值做初值,区分找不到
    int max = amount+1;
    // 表示当前金额需要的最少硬币个数
    int[] dp = new int[amount + 1];
    // 赋初值
    Arrays.fill(dp, max);
    dp[0] = 0;

    // 遍历金额 一定要允许 = amount
    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 - coins[j]] + 1, dp[i]);
            }
        }
    }
    // 返回前确认返回值是否有效
    return dp[amount] > amount ? -1 : dp[amount];
}