美文网首页
LeetCode 查找表专题 8:使用树结构

LeetCode 查找表专题 8:使用树结构

作者: 李威威 | 来源:发表于2019-05-27 23:06 被阅读0次

LeetCode 第 220 题:存在重复元素 III

传送门:220. 存在重复元素 III

给定一个整数数组,判断数组中是否有两个不同的索引 ij,使得 nums [i]nums [j] 的差的绝对值最大为 t,并且 ij 之间的差的绝对值最大为 ķ

示例 1:

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

示例 2:

输入: nums = [1,0,1,1], k = 1, t = 2
输出: true

示例 3:

输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false

Java 代码:

import java.util.TreeSet;

/**
 * https://leetcode-cn.com/problems/contains-duplicate-iii/description/
 * 参考资料:https://www.jianshu.com/p/45d7c879f4bf
 *
 * 使用了红黑树(一种特殊的二分查找树)的功能:查询天花板和地板,解决了这个问题。
 *
 */
public class Solution {

    /**
     * 要考虑到整型越界问题,所以要使用长整型
     *
     * @param nums
     * @param k    索引差:使用 TreeSet,使得 TreeSet 一共就存 k 个元素
     * @param t    数值的差
     * @return
     */
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        int len = nums.length;
        // 这里是或者
        if (len <= 1 || k <= 0) {
            return false;
        }
        TreeSet<Long> set = new TreeSet<>();
        int i = 0;
        while (i < len) {
            // 找不符合题目要求的情况
            Long floor = set.floor((long) nums[i]);
            Long ceiling = set.ceiling((long) nums[i]);
            boolean hasFloor = floor != null && nums[i] - floor <= t;
            boolean hasCeiling = ceiling != null && ceiling - nums[i] <= t;

            if (hasFloor || hasCeiling) {
                return true;
            }
            // 注意,这里应该取等号,因为前面在判断的时候,就相当于已经把元素加进去了
            // 而且从 nums[i - k] 表达式中也可以看出
            if (i >= k) {
                set.remove((long) nums[i - k]);
            }
            // 每一次都加入元素
            set.add((long) nums[i]);
            System.out.println("集合" + set);
            i++;
        }
        return false;
    }

    // 这个解法最好理解
    public boolean containsNearbyAlmostDuplicate2(int[] nums, int k, int t) {
        int len = nums.length;
        if (len == 0 || k <= 0 || t < 0) {
            return false;
        }
        TreeSet<Integer> treeSet = new TreeSet<>();
        for (int i = 0; i < len; i++) {
            Integer ceiling = treeSet.ceiling(nums[i]);
            if (ceiling != null && (long) ceiling - (long) nums[i] <= t) {
                return true;
            }
            Integer floor = treeSet.floor(nums[i]);
            if (floor != null && (long) nums[i] - (long) floor <= t) {
                return true;
            }
            treeSet.add(nums[i]);
            if (i >= k) {
                treeSet.remove(nums[i - k]);
            }
        }
        return false;
    }

    public static void main(String[] args) {
        int[] nums = {3,6,0,2};
        int k = 2;
        int t = 2;
        Solution solution = new Solution();
        boolean containsNearbyAlmostDuplicate = solution.containsNearbyAlmostDuplicate(nums, k, t);
        System.out.println(containsNearbyAlmostDuplicate);
    }
}

由于 Python 中没有现成的 BST,因此得手写一个,这个 BST 要支持 floorceiling 操作。

Python 代码:

# 220. 存在重复元素 III
# 给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,
# 使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,
# 并且 i 和 j 之间的差的绝对值最大为 ķ。


# 滑动窗口 + BST

class Solution:
    class TreeNode:
        def __init__(self, x):
            self.val = x
            self.left = None
            self.right = None

    class bst:
        def __init__(self):
            self.root = None

        def __str__(self):
            # 中序遍历 bst
            s = ''
            if self.root is None:
                return ''
            stack = [(1, self.root)]

            while stack:
                type, node = stack.pop()
                if type == 0:
                    s += str(node.val) + ','
                else:
                    if node.right:
                        stack.append((1, node.right))
                    stack.append((0, node))
                    if node.left:
                        stack.append((1, node.left))
            return s[:-1]

        def insert(self, val):
            """
            将 val 插入 bst
            :param val:
            :return:
            """
            self.root = self.__insert(self.root, val)

        def __insert(self, node, val):
            # 在以 node 为根结点的 bst 中,插入 val
            if node is None:
                return Solution.TreeNode(val)
            if val < node.val:
                # 注意:不要写成 return self.__insert(node.left, val)
                node.left = self.__insert(node.left, val)
                return node
            elif val > node.val:
                # 注意:不要写成 return self.__insert(node.right, val)
                node.right = self.__insert(node.right, val)
                return node
            node.val = val
            # 注意:要把 node 返回回去
            return node

        def remove(self, val):
            """
            删除 bst 中等于 val 的结点
            :param val:
            :return:
            """
            if self.root:
                self.root = self.__remove(self.root, val)

        def __remove(self, node, val):
            if node is None:
                return None
            if val < node.val:
                node.left = self.__remove(node.left, val)
                return node
            if val > node.val:
                node.right = self.__remove(node.right, val)
                return node

            if node.left is None:
                new_root = node.right
                node.right = None
                return new_root

            if node.right is None:
                new_root = node.left
                node.left = None
                return new_root

            # 用前驱结点 precursor 代替被删除结点
            precursor = self.__maximum(node.left)
            precursor_copy = Solution.TreeNode(precursor.val)
            precursor_copy.left = self.__remove_max(node.left)
            precursor_copy.right = node.right
            node.left = None
            node.right = None
            return precursor_copy

        def __maximum(self, node):
            while node.right:
                node = node.right
            return node

        def __remove_max(self, node):
            if node.right is None:
                new_root = node.left
                node.left = None
                return new_root
            node.right = self.__remove_max(node.right)
            return node

        def floor(self, val):
            return self.__floor(self.root, val)

        def __floor(self, node, val):
            if node is None:
                return None
            if node.val == val:
                return node.val
            if val < node.val:
                return self.__floor(node.left, val)
            temp_val = self.__floor(node.right, val)
            if temp_val:
                return temp_val
            return node.val

        def ceiling(self, val):
            return self.__ceiling(self.root, val)

        def __ceiling(self, node, val):
            if node is None:
                return None
            if node.val == val:
                return node.val
            if val > node.val:
                return self.__ceiling(node.right, val)
            temp_val = self.__ceiling(node.left, val)
            if temp_val:
                return temp_val
            return node.val

    def containsNearbyAlmostDuplicate(self, nums, k, t):
        """
        :type nums: List[int]
        :type k: int
        :type t: int
        :rtype: bool
        """
        # 思路:滑动窗口

        size = len(nums)
        if size <= 1 or k <= 0:
            return False
        bst = Solution.bst()

        for i in range(size):
            # 体会这个过程:我先查询一次,就好像这个数我放到 bst 中一样
            # 如果符合题意,就直接返回了
            # 如果不符合题意,移除最左边的元素,放入当前遍历的元素

            floor = bst.floor(nums[i])
            if floor is not None and nums[i] - floor <= t:
                return True

            ceiling = bst.ceiling(nums[i])
            if ceiling is not None and ceiling - nums[i] <= t:
                return True
            # 注意这里:先移除最左边的,然后插入
            # 或者先加入,再移除最左边的,其实都可以
            # 总之要保证
            if i >= k:
                bst.remove(nums[i - k])
            bst.insert(nums[i])

        return False


if __name__ == '__main__':
    nums = [8, 4, 0, 2]
    # nums = [1, 5, 8, 14, 19, 24, 37, 48]
    k = 2  # 索引差
    t = 2  # 数值差

    solution = Solution()
    result = solution.containsNearbyAlmostDuplicate(nums, k, t)
    print(result)

    # bst = Solution.bst()
    # bst.insert(12)
    # bst.insert(15)
    # bst.insert(11)
    # bst.insert(6)
    # bst.insert(14)
    # bst.insert(18)
    # bst.insert(9)
    #
    # print(bst)
    # bst.remove(12)
    # print(bst)
    #
    # f = bst.floor(5)
    # print(f)
    #
    # c = bst.ceiling(17)
    # print(c)

思路1:二分去查找。

思路2:使用桶排序。

1、使用二分查找
2、使用树结构

我的解答:https://gist.github.com/liweiwei1419/a047eceb6c30525d420d940cf668665b

(本节完)

相关文章

网友评论

      本文标题:LeetCode 查找表专题 8:使用树结构

      本文链接:https://www.haomeiwen.com/subject/zjottctx.html