Redis数据结构

78 阅读8分钟

二叉搜索树

判断其是否是一个有效的二叉搜索树。

public boolean isValidBST(TreeNode root) {
    return valid(root, Long.MIN_VALUE, Long.MAX_VALUE);
}

private boolean valid(TreeNode root, long min, long max) {
    if (root == null) {
        return true;
    }
    if (root.val <= min || root.val >= max) {
        return false;
    }
    
    boolean left = valid(root.left, min, root.val);
    boolean right = valid(root.right, root.val, max);
    return left && right;
    
}

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

    public ListNode mergeKLists(ListNode[] lists) {


        ListNode res = null;
        for (ListNode list: lists) {
            res = merge2Lists(res, list);
        }
        return res;


    }


        
    public ListNode merge2Lists(ListNode l1, ListNode l2) {
        ListNode res = new ListNode(0);
        ListNode tem = res;
   
        while(l1 != null && l2 != null) {
            if (l1.val > l2.val) {
                res.next = l2;
                l2 = l2.next;
            } else {
                res.next = l1;
                l1 = l1.next;
            }
            res = res.next;
        }
        res.next = l1 == null? l2: l1;
        return tem.next;
        
    }

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
class LRUCache {
    
    // 容量
    private int capacity = 10;
    // 当前存储数量
    private int size = 0;
    // 存储节点
    private class ListNode {
        int key;
        int val;
        ListNode pre;
        ListNode next;
        ListNode() {}
        ListNode(int key, int val) { this.key = key; this.val = val; }
    }
    // Key - Value
    private Map<Integer, ListNode> kv = new HashMap<>();
    private ListNode head = new ListNode(0, 0);
    private ListNode tail = head;
    

    

    public LRUCache(int capacity) {
        this.capacity = capacity;
    }
    
    public int get(int key) {
        ListNode tem = kv.get(key);
        if (tem == null) {
            return -1;
        }
        updateNodeLocation(tem);
        return tem.val;
    }
    
    public void put(int key, int value) {
        ListNode node = kv.get(key);
        if (node != null) {
            node.val = value;
            updateNodeLocation(node);
            return;
        }
        ListNode tem = new ListNode(key, value);
        tem.pre = tail;
        tail.next = tem;
        tail = tail.next;
        kv.put(key, tem);
        size++;
  
        // 超过容量, 移除头部节点并从哈希表移除
        if (size > capacity) {
            kv.remove(head.next.key);
            head.next.next.pre = head;
            head.next = head.next.next;
            size--;
        }
    }
    
    private void updateNodeLocation(ListNode node) {
        if (tail == node) return;
        tail.next = node;
        node.next.pre = node.pre;
        node.pre.next = node.next;
        node.pre = tail;
        node.next = null;
        tail = node;
    }
}



#### 给你链表的头结点 `head` ,请将其按 **升序** 排列并返回 **排序后的链表** 。
```java
 public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode fast = head.next;
        ListNode slow = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode tmp = slow.next;
        slow.next = null;
        ListNode left = sortList(head);
        ListNode right = sortList(tmp);
        ListNode pre = new ListNode(0);
        ListNode h = pre;
        while(left != null && right != null) {
           if(left.val < right.val) {
              h.next = left;
              left = left.next;
           } else {
               h.next = right;
               right = right.next;
           }
         h = h.next;
        }
        h.next = left == null? right: left ;
        return pre.next;
        
    }

给你一个链表,删除链表的倒数第 n **个结点,并且返回链表的头结点。

 ListNode pre = new ListNode(0, head);
        ListNode first = head;
        ListNode second = pre;
        for (int i = 0; i < n; i++) {
            first = first.next;
        }
        while(first != null) {
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;
        return pre.next;

链表,两两交换其中相邻的节点,并返回交换后链表的头节点。

        ListNode pre = new ListNode(0, head);
        ListNode tem = pre;
        while (tem.next != null && tem.next.next != null) {
            ListNode left = tem.next;
            ListNode right = tem.next.next;
            
            tem.next = right;
            left.next = right.next;
            right.next = left;
            
            tem = left;
            
        }
        return pre.next;

反转链表

 public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null) {
            ListNode tem = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tem;
        }
        return pre;

    }

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。
    public boolean searchMatrix(int[][] matrix, int target) {
        int i =  matrix.length - 1;
        int j = 0;
        while(i >= 0 && j < matrix[0].length - 1){
            if (matrix[i][j] > target) {
                i--;
            } else if (matrix[i][j] < target) {
                j++;
            } else {
                return true;
            }
        }
        return false;
    }

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

public void rotate(int[][] matrix) {
    int n = matrix.length;
    for (int i = 0; i < n/2; i++) {
        int[] tem = matrix[i];
        matrix[i] = matrix[n -1- i];
        matrix[n -1-i] = tem;
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j< i; j++) {
            int tem = matrix[i][j];
            matrix[i][j] = matrix[j][i];
            matrix[j][i] = tem;
        }
    }
}

给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法

public void setZeroes(int[][] matrix) {
        Set<Integer> row_zero = new HashSet<>();
        Set<Integer> col_zero = new HashSet<>();
        int row = matrix.length;
        int col = matrix[0].length;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (matrix[i][j] == 0) {
                    row_zero.add(i);
                    col_zero.add(j);
                }
            }
        }
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (row_zero.contains(i) || col_zero.contains(j)) matrix[i][j] = 0;
            }
        }  
    }

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

public int firstMissingPositive(int[] nums) {
    int len = nums.length;

    for (int i = 0; i < len; i++) {
        while (nums[i] > 0 && nums[i] <= len && nums[nums[i] - 1] != nums[i]) {
            // 满足在指定范围内、并且没有放在正确的位置上,才交换
            // 例如:数值 3 应该放在索引 2 的位置上
            swap(nums, nums[i] - 1, i);
        }
    }

    // [1, -1, 3, 4]
    for (int i = 0; i < len; i++) {
        if (nums[i] != i + 1) {
            return i + 1;
        }
    }
    // 都正确则返回数组长度 + 1
    return len + 1;
}

private void swap(int[] nums, int index1, int index2) {
    int temp = nums[index1];
    nums[index1] = nums[index2];
    nums[index2] = temp;
}

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

public int[] productExceptSelf(int[] nums) {
    int len = nums.length;
    int[] ans = new int[len];
    ans[0] = 1;
    for (int i = 1; i < len; i++) {
        ans[i] = ans[i-1] * nums[i - 1];
    }
    int temp = 1;
    for (int i = len -2; i >=0; i--) {
        temp *= nums[i+1];
        ans[i] = ans[i]*temp;
    }
    return ans;
}

给定一个整数数组 nums,将数组中的元素向右轮转 k **个位置,其中 k **是非负数。

public void rotate(int[] nums, int k) {
    int len = nums.length ;
    k = k % len;
    reverse(nums, 0, len - 1);
    reverse(nums, 0, k - 1);
    reverse(nums, k, len - 1);
}
    private void reverse(int[] nums, int left, int right) {
    while(left < right) {
        int tem = nums[left];
        nums[left] = nums[right];
        nums[right] = tem;
        left++;
        right--;
    }
}

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

public int[][] merge(int[][] intervals) {
            Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
    int[][] res = new int[intervals.length][2];
    int idx = -1;
    for(int[] interval: intervals) {
        if(idx < 0 || res[idx][1] < interval[0]) {
            res[++idx] = interval;
        } else {
            res[idx][1] = Math.max(interval[1], res[idx][1]);
        }
    }
    return Arrays.copyOf(res, idx + 1);
}

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

public int maxSubArray(int[] nums) {
    int len = nums.length;
    int[] dp = new int[len];
    dp[0] = nums[0];
    for (int i = 1; i < len ;i++) {
        if (dp[i-1] < 0) {
            dp[i] = nums[i];
        } else {
            dp[i] = dp[i - 1] + nums[i];
        }
    }
    int res = dp[0];
    for (int i = 0; i < len; i++) {
        res = Math.max(res, dp[i]);
    }
    return res; 
}

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

public String minWindow(String s, String t) {
    int[] ctnS = new int[128];
    int[] ctnT = new int[128];
    int ansLeft = -1;
    int ansRight = s.length();
    for(int i = 0; i < t.length(); i++) {
        ctnT[t.charAt(i)]++;
    }
    int left = 0;
    for(int right = 0; right < s.length(); right++) {
        ctnS[s.charAt(right)]++;
        while(isCover(ctnT, ctnS)) {
            if(right - left < ansRight - ansLeft) {
                ansRight = right;
                ansLeft = left;
            }
            ctnS[s.charAt(left)]--;
            left++;
        }
    }
    return ansLeft < 0? "": s.substring(ansLeft, ansRight + 1);
}
private boolean isCover(int[] ctnT, int[] ctnS ) {
    for(char i = 'a'; i <= 'z'; i++) {
        if(ctnS[i] < ctnT[i]) {
            return false;
        }
    }
    for(int i = 'A'; i <= 'Z'; i++) {
        if(ctnS[i] < ctnT[i]) {
            return false;
        }
    }
    return true;
}

1 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 *该数组中和为 k 的子数组的个数 。

public int subarraySum(int[] nums, int k) {
    int count = 0;
    int pre = 0;
    HashMap<Integer, Integer> map = new HashMap<>();
    map.put(0, 1);
    for (int i = 0; i < nums.length; i++) {
        pre += nums[i];
        if (map.containsKey(pre - k)) {
            count += map.get(pre-k);
        }
        map.put(pre, map.getOrDefault(pre - k, 0) + 1);
    }
    return count;
}

239. 滑动窗口最大值

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

public int[] maxSlidingWindow(int[] nums, int k) {
    if ( nums == null || nums.length < 2 ) {
        return nums;
    }
    int[] result = new int[nums.length - k + 1];
    LinkedList<Integer> queue = new LinkedList<>();
    for (int i = 0 ; i < nums.length; i++) {
        while(queue.size() > 0 && nums[queue.peekLast()] <= nums[i]) {
            queue.pollLast();
        }
        queue.addLast(i);
        if (queue.peekFirst() <= i - k) {
            queue.pollFirst();
        }
        if (i + 1 >= k) {
            result[i - k + 1] = nums[queue.peekFirst()];
        }
    }
    return result;


}

回溯

1、组合

77

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> res = new ArrayList<>();
        if(k <= 0 || n < k) {
            return res;
        }
        Deque<Integer> path = new ArrayDeque<>();
        dfs(n, k, 1, path, res);
        return res;
    }

    private void dfs(int n, int k, int begin, Deque<Integer> path, List<List<Integer>> res) {
        if(path.size() == k) {
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i = begin; i <=n; i++) {
            path.addLast(i);
            dfs(n, k, i+1, path, res);
            path.removeLast();
        }
    }
}

2、组合总和 III

216

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

class Solution {
    public List<List<Integer>> combinationSum3(int k, int n) {
        Deque<Integer> path = new ArrayDeque<>();
        List<List<Integer>> ans = new ArrayList<>();
        if(n<k || k<=0) {
            return ans;
        }
        dfs(k, n, 1, path, ans);
        return ans;
    }

    private void dfs(int k, int n, int begin, Deque<Integer> path, List<List<Integer>> res) {
        if(k == path.size() && n==0) {
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i = begin; i <= 9; i++) {
            path.addLast(i);
            dfs(k, n - i, i+1, path, res);
            path.removeLast();
        }
    }
}

3 、17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

class Solution {
    public List<String> letterCombinations(String digits) {
        List<String> combinations = new ArrayList<>();  
        if (digits == null || digits.length() == 0) {
            return combinations;
        } 

        HashMap<Character, String[]> map = new HashMap<>(){{
            put('2', new String[]{"a", "b", "c"});
            put('3', new String[]{"d", "e", "f"});
            put('4', new String[]{"g", "h", "i"});
            put('5', new String[]{"j", "k", "l"});
            put('6', new String[]{"m", "n", "o"});
            put('7', new String[]{"p", "q", "r", "s"});
            put('8', new String[]{"t", "u", "v"});
            put('9', new String[]{"w", "x", "y", "z"});
        }};
        Queue<String> queue = new LinkedList<>();
        for(int i = 0; i < digits.length(); i++) {
            dfs(queue, map.get(digits.charAt(i)));
        }
        for(String s: queue) {
            combinations.add(s);
        }
        return combinations;
    }

    private void dfs(Queue<String> queue, String[] letters){
        if (queue.size() == 0) {
            for(String s: letters) {
                queue.add(s);
            }
        } else {
            int len = queue.size();
            for(int i = 0; i < len; i++) {
                String s = queue.poll();
                for(String letter: letters) {
                    queue.add(s+letter);
                }
            }
        }

    }
}