Sunday, May 14, 2017

438. Find All Anagrams in a String

三刷 06/2022
Version #1 Sliding Window
Time O(N) - N is the length of the longer string
Space O(26) ~ O(1)

Runtime: 10 ms, faster than 82.16% of Java online submissions for Find All Anagrams in a String.
Memory Usage: 48.1 MB, less than 46.53% of Java online submissions for Find All Anagrams in a String.
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int[] count = new int[26];
        for (int i = 0; i < p.length(); i++) {
            count[p.charAt(i) - 'a']++;
        }
        List<Integer> result = new ArrayList<>();
        int matchCount = 0;
        int start = -1;
        // (start, end]
        for (int end = 0; end < s.length(); end++) {
            int index = s.charAt(end) - 'a';
            count[index]--;
            if (count[index] >= 0) {
                matchCount++;
            }
            while (count[index] < 0) {
                start++;
                int sIndex = s.charAt(start) - 'a';
                count[sIndex]++;
                if (count[sIndex] > 0) {
                    matchCount--;
                }
            }
            if (matchCount == p.length()) {
                result.add(start + 1);
            }
        }
        return result;
    }
}


while (right < s.length()) {
//move right everytime, if the character exists in p's hash, decrease the count //current hash value >= 1 means the character is existing in p if (hash[s.charAt(right++)]-- >= 1) count--; //when the count is down to 0, means we found the right anagram //then add window's left to result list if (count == 0) list.add(left); //if we find the window's size equals to p, then we have to move left (narrow the window) to find the new match window //++ to reset the hash because we kicked out the left //only increase the count if the character is in p //the count >= 0 indicate it was original in the hash, cuz it won't go below 0 if (right - left == p.length() && hash[s.charAt(left++)]++ >= 0) count++;  }

一刷
注意counter数的是需要的char个数
如果char不在target里面,那么右指针-1左指针+1,两者只会互相抵消 永远不会大于0
只有当left扫过大于0的才是曾经存在于target里面的char,才能影响counter
95.80 %

public class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> result = new ArrayList<>();
        if (s == null || p == null || s.length() < p.length()) return result;
        //only lowercase letters
        int[] hash = new int[26];
        int counter = 0; //keep track of #desired letters
        int index;
        //Generate the original count of each char
        for (int i = 0; i < p.length(); i++) {
            index = p.charAt(i) - 'a';
            hash[index]++;
            counter++;
        }
        int left = 0, right = 0;
        while (right < s.length()) {
            index = s.charAt(right++) - 'a';
            if (--hash[index] >= 0) {
                counter--;
            }
         
            while (counter == 0) {
                if ((right - left) == p.length()) {
                    result.add(left);
                }
                index = s.charAt(left++) - 'a';
                if (++hash[index] > 0) counter++;
            }
        }
        return result;
    }
}

二刷
88.62 %
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        if (s == null || p == null) return null;
        List<Integer> result = new ArrayList<>();
        int pLength = p.length();
     
        int[] counter = new int[26];
        int match = 0;
        for (int i = 0; i < pLength; i++) {
            counter[p.charAt(i) - 'a']++;
            match++;
        }
        // [left, right] ->  right - left + 1 == pLength -> remove left
        //s . . . . . .
        int left = 0, right = 0;
        for (right = 0; right < s.length(); right++) {
            if (--counter[s.charAt(right) - 'a'] >= 0) match--;
            if (match == 0) result.add(left);
            if (right - left + 1 == pLength) {
                if (++counter[s.charAt(left) - 'a'] > 0) match++;
                left++;
            }
        }
        return result;
    }
}

450. Delete Node in a BST

二刷 05/2022

Version #1 Recursion
更改了TreeNode 的值,所以不是optimal
写了一个bug就是当root被删除的时候,代替root的点需要是左边的最大点或者右边的最小点,而不是简单的root.left或者root.right
反例
               5
             /   \
           3     6
          /  \     \
        2   4     7
会变成
               3
             /   \
           2     6
             \     \
              4     7

Time O(Height)
Space O(Height)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) {
            return null;
        }
        if (root.val > key) {
            root.left = deleteNode(root.left, key);
            return root;
        } 
        if (root.val < key) {
            root.right = deleteNode(root.right, key);
            return root;
        } 
        
        if (root.left != null) {
            TreeNode leftMax = root.left;
            while (leftMax.right != null) {
                leftMax = leftMax.right;
            }
            root.val = leftMax.val;
            root.left = deleteNode(root.left, leftMax.val);
        } else if (root.right != null) {
            TreeNode rightMin = root.right;
            while (rightMin.left != null) {
                rightMin = rightMin.left;
            }
            root.val = rightMin.val;
            root.right = deleteNode(root.right, rightMin.val);
        } else {
            root = null;
        }
        return root;
    }
}

Version #2 Iteration

Runtime: 1 ms, faster than 12.04% of Java online submissions for Delete Node in a BST.
Memory Usage: 42.4 MB, less than 97.53% of Java online submissions for Delete Node in a BST.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        TreeNode curr = root;
        TreeNode prev = null;
        while (curr != null && curr.val != key) {
            prev = curr;
            if (curr.val > key) {
                curr = curr.left;
            } else {
                curr = curr.right;
            }
        }
        if (curr == null) { // target key not found
            return root;
        }
        if (prev == null) {
            return deleteRoot(curr);
        }
        if (prev.left == curr) {
            prev.left = deleteRoot(curr);
        } else {
            prev.right = deleteRoot(curr);
        }
        return root;
    }
    private TreeNode deleteRoot(TreeNode root) {
        System.out.println(root.val);
        if (root == null || (root.left == null && root.right == null)) {
            return null;
        }
        if (root.left == null) {
            return root.right;
        }
        if (root.right == null) {
            return root.left;
        }
        TreeNode rightMin = root.right;
        TreeNode prev = root;
        while (rightMin.left != null) {
            prev = rightMin;
            rightMin = rightMin.left;
        }
        if (prev != root) {
            prev.left = rightMin.right;
            rightMin.right = root.right;
        }
        rightMin.left = root.left;
        return rightMin;
    }
}

一刷
这道题感觉简直有一万个坑。。。
Version #1 Non-recursion Method
 75.25 %
在deleteRoot的时候 要区分root.right是不是只有一个点



public class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) return root;
        TreeNode curr = root;
        TreeNode pre = null;
        while (curr != null && curr.val != key) {
            pre = curr;
            if (curr.val > key) curr = curr.left;
            else curr = curr.right;
        }
        if (pre == null) return deleteRoot(curr); //curr == root
        if (pre.left == curr) pre.left = deleteRoot(curr);
        else pre.right = deleteRoot(curr);
        return root;
        
    }
    private TreeNode deleteRoot(TreeNode root) {
        if (root == null) return root;
        if (root.left == null) return root.right;
        if (root.right == null) return root.left;
        TreeNode rightMin = root.right;
        TreeNode pre = root;
        while (rightMin.left != null) {
            pre = rightMin;
            rightMin = rightMin.left;
        }
        
        if (pre != root) {
            pre.left = rightMin.right;
            rightMin.right = root.right;
        }
        rightMin.left = root.left;
        return rightMin;
    }
}




Version #2 Recursion
42.80 %
Definition: public TreeNode deleteNode(TreeNode root, int key)
Recursion之后的返回值 一定要和原来的root接起来!
“root.left = deleteNode(root.left, key)”
不接起来都白搭啊

这个方法感觉不是那么的elegant,因为不是换node而是换值
没有Iteration好,但是简单

public class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) return null;
        if (root.val > key) root.left = deleteNode(root.left, key);
        else if (root.val < key) root.right = deleteNode(root.right, key);
        else {
            if (root.left == null) return root.right;
            if (root.right == null) return root.left;
            //TODO
            TreeNode rightMin = findMin(root.right);
            root.val = rightMin.val;
            root.right = deleteNode(root.right, rightMin.val);
        }
        return root;
    }
    private TreeNode findMin(TreeNode root) {
        while (root.left !=  null) root = root.left;
        return root;
    }
}

Saturday, May 13, 2017

Binary Tree Path Sum

这个题有一个大坑
一般dfs的终止条件是root == null
但是这个题目里面如果走到leaf之后,它的左右child会各把答案加入一次,就错了
所以必须在leaf就终止 就是说root.left == null && root.right == null


Version #2 在当前层加入当前node
有一个bug就是当leaf节点提前return的时候必须要backtracking
每一次return到上一层都要backtracking!!!

public class Solution {
    /**
     * @param root the root of binary tree
     * @param target an integer
     * @return all valid paths
     */
    public List<List<Integer>> binaryTreePathSum(TreeNode root, int target) {
        // Write your code here
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (root == null) return result;
        helper(root, 0, target, new ArrayList<Integer>(), result);
        return result;
    }
    //add current node to the path, add current value to the sum
    private void helper(TreeNode root, int sum, int target, List<Integer> path, List<List<Integer>> result) {
        if (root == null) return;
        path.add(root.val);
        sum += root.val;
        if (root.left == null && root.right == null) {
            if (sum == target) result.add(new ArrayList<Integer>(path));
            path.remove(path.size() - 1);
            return;
        }
        helper(root.left, sum, target, path, result);
        helper(root.right, sum, target, path, result);
        path.remove(path.size() - 1);
    }
}



Version #1 在上一层就加左右的node进入path以及加入sum
public class Solution {
    /**
     * @param root the root of binary tree
     * @param target an integer
     * @return all valid paths
     */
    public List<List<Integer>> binaryTreePathSum(TreeNode root, int target) {
        // Write your code here
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        List<Integer> path = new ArrayList<>();
        path.add(root.val);
        int sum = root.val;
        helper(path, sum, root, target, result);
        return result;
    }
 
    public void helper(
        List<Integer> path,
        int sum,
        TreeNode root,
        int target,
        List<List<Integer>> result
        ) {
            if (root.left == null && root.right == null) {
                if (sum == target) {
                    result.add(new ArrayList<Integer>(path));
                }
            }
         
            if (root.left != null) {
                path.add(root.left.val);
                helper(path, sum + root.left.val, root.left, target, result);
                path.remove(path.size() - 1);
            }
         
            if (root.right != null) {
                path.add(root.right.val);
                helper(path, sum + root.right.val, root.right, target, result);
                path.remove(path.size() - 1);
            }
    }
}

Friday, May 12, 2017

449. Serialize and Deserialize BST

参考 297. Serialize and Deserialize Binary Tree

Version #4 LC DFS Iteration

二刷
Version # 1 DFS Recursion using the property of binary search tree

Since we can guarantee that the root node value is larger than nodes in its left subtree and less than nodes in its right substree, we can deserialize a tree from its pre-order traversal
89.68 %
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        seHelper(root, sb);
        return sb.toString();
    }
    private void seHelper(TreeNode node, StringBuilder sb) {
        if (node == null) {
            return;
        }
        sb.append(node.val);
        sb.append(",");
        seHelper(node.left, sb);
        seHelper(node.right, sb);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if (data == null || data.length() == 0) {
            return null;
        }
        String[] strs = data.split(",");
        return deHelper(strs, 0, strs.length);
    }
   
    // [start, end)
    private TreeNode deHelper(String[] strs, int start, int end) {
        if (start >= end) {
            return null;
        }
        int val = Integer.valueOf(strs[start]);
        TreeNode node = new TreeNode(val);
        int mid = start + 1;
        // left -> [start + 1, mid) right -> [mid, end)
        while (mid < end && Integer.valueOf(strs[mid]) < val) {
            mid++;
        }
        node.left = deHelper(strs, start + 1, mid);
        node.right = deHelper(strs, mid, end);
        return node;
    }
}


一刷
Version #3 LC DFS Recursion
自己写了一个Tuple Class 用了Recursion
是一个general binary tree的写法 没有利用BST的性质

80.02 %
class Tuple {
    int index;
    TreeNode node;
    public Tuple(int index, TreeNode node) {
        this.index = index;
        this.node = node;
    }
}
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
   
        StringBuilder sb = new StringBuilder();
        traversal(root, sb);
        sb.deleteCharAt(sb.length() - 1);
        //System.out.println(sb.toString());
        return sb.toString();
    }
    // pre-order traversal
    private void traversal(TreeNode root, StringBuilder sb) {
        if (root == null) {
            sb.append("#,");
            return;
        }
        sb.append(root.val + ",");
        traversal(root.left, sb);
        traversal(root.right, sb);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if (data == null) return null;
        String[] nodes = data.trim().split(",");
        return build(nodes,  0).node;
    }
    private Tuple build(String[] nodes, int index) {
        if (nodes[index].equals("#")) return new Tuple(index, null);
   
        TreeNode root = new TreeNode(Integer.valueOf(nodes[index]));
        Tuple left = build(nodes, index + 1);
        Tuple right = build(nodes, left.index + 1);
        root.left = left.node;
        root.right = right.node;
        return new Tuple(right.index, root);
    }
}





Binary Tree Path Sum II

/**
 * Definition of TreeNode:
 * public class TreeNode {
 *     public int val;
 *     public TreeNode left, right;
 *     public TreeNode(int val) {
 *         this.val = val;
 *         this.left = this.right = null;
 *     }
 * }
 */
public class Solution {
    /**
     * @param root the root of binary tree
     * @param target an integer
     * @return all valid paths
     */
    public List<List<Integer>> binaryTreePathSum2(TreeNode root, int target) {
        // Write your code here
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (root == null) return result;
        dfs(root, target, new ArrayList<Integer>(), result);
        return result;
    }
    private void dfs(TreeNode root, int target, List<Integer> path, List<List<Integer>> result) {
        if (root == null) return;
        path.add(root.val);
        int sum = 0;
        for (int i = path.size() - 1; i >= 0; i--) {
            sum += path.get(i);
            if (sum == target) {
                List<Integer> subResult = new ArrayList<>();
                for (int j = i; j < path.size(); j++) {
                    subResult.add(path.get(j));
                }
                result.add(subResult);
            }
        }
       
        dfs(root.left, target, path, result);
        dfs(root.right, target, path, result);
        path.remove(path.size() - 1);
    }
}

200. Number of Islands

三刷 06/2022
Version #2 Union Find
有两个比答案写的好的地方
一个就是在Union的时候只向右和向下看,这样可以少搜两个方向
第二个就是不需要一开始用M * N的时间先算出grid==1的count,可以一边搜一边加->又想了一下这里其实没有extra的时间消耗,因为initialize id和size array本来就需要MN的时间,所以这里怎么implement都无所谓
一个可以提高的地方是看到之前一刷的时候写了一个getLabel函数来计算y* cols + x,感觉写的很好这次可以借鉴
Time O(MN)
Space O(MN)
Runtime: 13 ms, faster than 15.14% of Java online submissions for Number of Islands.
Memory Usage: 57.9 MB, less than 21.91% of Java online submissions for Number of Islands.

class Solution {
    class UnionFind {
        private int[] size;
        private int[] id;
        private int count;
        public UnionFind(int N) {
            size = new int[N];
            id = new int[N];
            for (int i = 0; i < N; i++) {
                size[i] = 1;
                id[i] = i;
            }
        }
        
        public int root(int i) {
            while (id[i] != i) {
                id[i] = id[id[i]];
                i = id[i];
            }
            return i;
        }
        
        public void union(int p, int q) {
            int rootP = root(p);
            int rootQ = root(q);
            if (rootP == rootQ) {
                return;
            }
            count--;
            if (size[rootP] < size[rootQ]) {
                id[rootP] = rootQ;
                size[rootQ] += size[rootP];
            } else {
                id[rootQ] = rootP;
                size[rootP] += size[rootQ];
            }
        }
        
        public int count() {
            return count;
        }
        
        public void addCount(int n) {
            count += n;
        }
    }
    public int numIslands(char[][] grid) {
        int rows = grid.length;
        int cols = grid[0].length;
        int N = rows * cols;
        UnionFind uf = new UnionFind(N);
        for (int y = 0; y < rows; y++) {
            for (int x = 0; x < cols; x++) {
                if (grid[y][x] != '1') {
                    continue;
                }
                uf.addCount(1);
                if (x + 1 < cols && grid[y][x + 1] == '1') {
                    uf.union(y * cols + x, y * cols + x + 1);
                }
                if (y + 1 < rows && grid[y + 1][x] == '1') {
                    uf.union(y * cols + x, (y + 1) * cols + x);
                }
            }
        }
        return uf.count();
    }
}


二刷 05/2022

Version #3 BFS

Time O(MN)
Space O(MN)
Runtime: 8 ms, faster than 25.92% of Java online submissions for Number of Islands.
Memory Usage: 57.1 MB, less than 54.74% of Java online submissions for Number of Islands.

class Solution {
    public int numIslands(char[][] grid) {
        // create a visited matrix
        // iterate over all (x, y) values
        // if a grid is a island and has never been visited, add the land number by 1 and expand that island by bfs
        if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) {
            return 0;
        }
        int cnt = 0;
        boolean[][] visited = new boolean[grid.length][grid[0].length];
        for (int y = 0; y < grid.length; y++) {
            for (int x = 0; x < grid[0].length; x++) {
                if (!visited[y][x] && grid[y][x] == '1') {
                    cnt++;
                    expandIsland(grid, y, x, visited);
                }
            }
        }
        return cnt;
    }
    private int[] dx = new int[]{1, 0, -1, 0};
    private int[] dy = new int[]{0, -1, 0, 1};
    private void expandIsland(char[][] grid, int y, int x, boolean[][] visited) {
        Queue<int[]> que = new ArrayDeque<>();
        que.offer(new int[]{y, x});
        visited[y][x] = true;
        while (!que.isEmpty()) {
            int[] curr = que.poll();
            // y = curr[0], x = curr[1]
            for (int i = 0; i < 4; i++) {
                int ny = curr[0] + dy[i];
                int nx = curr[1] + dx[i];
                if (ny < 0 || ny >= grid.length || nx < 0 || nx >= grid[0].length) {
                    continue;
                }
                if (grid[ny][nx] == '1' && !visited[ny][nx]) {
                    visited[ny][nx] = true;
                    que.offer(new int[]{ny, nx});
                }
            }
        }
    }
}


一刷
Version #2 Union Find

 14.61 %
class Solution {
    class UF {
private int[] id;
private int[] size;
private int N;
public int count;
public UF(char[][] grid) {
this.count = 0;
this.N = grid.length * grid[0].length;
id = new int[N];
size = new int[N];
for (int y = 0; y < grid.length; y++) {
for (int x = 0; x < grid[0].length; x++) {
if (grid[y][x] == '1') {
count++;
}
int currLabel = getLabel(y, x);
id[currLabel] = currLabel;
size[currLabel] = 1;
}
}
}

private int root(int i) {
while (id[i] != i) {
id[i] = id[id[i]];
i = id[i];
}
return i;
}

public void union(int p,  int q) {
int rootP = root(p);
int rootQ = root(q);
if (rootP == rootQ) {
return;
}
this.count--;
if (size[rootP] < size[rootQ]) {
id[rootP] = rootQ;
size[rootQ] += size[rootP];
} else {
id[rootQ] = rootP;
size[rootP] += size[rootQ];
}
}
public boolean connected(int p, int q) {
return root(p) == root(q);
}
}

private int rows;
private int cols;
public int numIslands(char[][] grid) {
if(grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) {
return 0;
}
this.rows = grid.length;
this.cols = grid[0].length;
UF uf = new UF(grid);
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
if (grid[y][x] == '1') {
if (y != rows - 1 && grid[y + 1][x] == '1') {
uf.union(getLabel(y, x), getLabel(y + 1, x));
}
if (x != cols - 1 && grid[y][x + 1] == '1') {
uf.union(getLabel(y, x), getLabel(y, x + 1));
}
}
}
}
return uf.count;
}

private int getLabel(int y, int x) {
return y * cols + x; 
}
}



Version #1 DFS
Explanation from LeetCode:
"The algorithm works as follow:
  1. Scan each cell in the grid.
  2. If the cell value is '1' explore that island.
  3. Mark the explored island cells with 'x'.
  4. Once finished exploring that island, increment islands counter.
The arrays dx[], dy[] store the possible moves from the current cell. Two land cells ['1'] are considered from the same island if they are horizontally or vertically adjacent (possible moves (-1,0),(0,1),(0,-1),(1,0)). Two '1' diagonally adjacent are not considered from the same island."

41.53 %
public class Solution {
    private int count = 0;
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) return 0;
        int rows = grid.length;
        int cols = grid[0].length;
        for (int x = 0; x < rows; x++) {
            for (int y = 0; y < cols; y++) {
                if (grid[x][y] == '1') {
                    dfs(grid, x, y);
                    count++;
                }
            }
        }
        return count;
    }
    private int[] dx = {1, 0, -1, 0};
    private int[] dy = {0, 1, 0, -1};
    private void dfs(char[][] grid, int x, int y) {
        grid[x][y] = 'x';
        int nx, ny;
        for (int k = 0; k < 4; k++) {
            nx = x + dx[k];
            ny = y + dy[k];
            if (nx >= 0 && nx < grid.length && ny >= 0 && ny < grid[0].length) {
                if (grid[nx][ny] == '1') dfs(grid, nx, ny);
            }
        }
    }
}

1. Two Sum

一刷
神他妈2sum
原题竟然是第一次写。。。感觉自己想的有点神奇结果发现答案就是差不多这么写的
因为是2sum 所以如果排序用2pointer的话 O(nlogn)是leading term 不是特别的efficient
于是选择了HashMap

57.83 %
public class Solution {
    public int[] twoSum(int[] nums, int target) {
        //key-expected value, value-index
        if (nums == null || nums.length <= 1) return new int[0];
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(nums[i])) {
                return new int[]{map.get(nums[i]), i};
            }
            map.put(target - nums[i], i);
        }
        return new int[0];
    }
}

二刷
这道题没有办法用Arrays.sort() 加双指针的方法, 因为要求return的是index
sort会打乱原来的index
除非建立自己的tuple, 然后sort tuple

Version #1 HashMap

Time O(n)
Space O(n)
Runtime: 4 ms, faster than 68.51% of Java online submissions for Two Sum.
Memory Usage: 45.9 MB, less than 28.04% of Java online submissions for Two Sum.
class Solution {
    public int[] twoSum(int[] nums, int target) {
        // Scan the array and put the {target-nums[i], i} in a hashmap
        // Check if there exists a key of nums[i] in the hashmap
        if (nums == null || nums.length < 2) {
            return new int[0];
        }
        Map<Integer, Integer> hash = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int j = hash.getOrDefault(nums[i], -1);
            if (j != -1) {
                return new int[]{j, i};
            }
            hash.put(target - nums[i], i);
        }
        return new int[0];
    }
}