记录哈希表类型的算法题,提供多思路多解法
[TOC]
1 数对和
1 | //暴力解法,超时,时间:O(n2) 空间:O(n) |
2 | public List<List<Integer>> pairSums(int[] nums, int target) { |
3 | List<List<Integer>> res = new ArrayList<>(); |
4 | boolean[] visited = new boolean[nums.length]; |
5 | for(int i=0;i<nums.length-1;i++){ |
6 | for(int j=i+1;j<nums.length;j++){ |
7 | if(!visited[i]&&!visited[j]&&nums[i]+nums[j]==target){ |
8 | List<Integer> tmp = new ArrayList<>(); |
9 | tmp.add(nums[i]); |
10 | tmp.add(nums[j]); |
11 | res.add(tmp); |
12 | visited[i]=true; |
13 | visited[j] =true; |
14 | } |
15 | } |
16 | } |
17 | return res; |
18 | } |
1 | //Hash表方法 时间:O(n) 空间:O(n) |
2 | public List<List<Integer>> pairSums(int[] nums, int target) { |
3 | List<List<Integer>> res = new ArrayList<>(); |
4 | HashMap<Integer, Integer> map = new HashMap<>();//key :nums[i] value: 出现的次数 |
5 | for (int i = 0; i < nums.length; i++) { |
6 | Integer count = map.get(target - nums[i]); |
7 | if (count != null) {//包含且大于 |
8 | res.add(Arrays.asList(nums[i], target - nums[i])); |
9 | if (count == 1) |
10 | map.remove(target - nums[i]); |
11 | else |
12 | map.put(target - nums[i], count - 1); |
13 | } else { |
14 | map.put(nums[i], map.getOrDefault(nums[i], 0) + 1); |
15 | } |
16 | } |
17 | return res; |
18 | } |
1 | //另外一种,双指针算法,需要对数组进行排序,时间:O(nlogn) |
2 | public List<List<Integer>> pairSums(int[] nums, int target) { |
3 | //对数组进行排序 |
4 | Arrays.sort(nums); |
5 | List<List<Integer>> ans = new LinkedList<>(); |
6 | int left = 0, right = nums.length - 1; |
7 | while (left < right) { |
8 | int sum = nums[left] + nums[right]; |
9 | //如果两个的和小于目标值,那么left指针向右走一步继续寻找 |
10 | if (sum < target) |
11 | ++left; |
12 | //如果两个的和大于目标值,那么right指针向左走一步继续寻找 |
13 | else if (sum > target) |
14 | --right; |
15 | //如果刚好等于要找的target值,那么加入结果集中,并且left指针和right指针分别向右和向左走一步(因为一个数只能属于一个数对) |
16 | else |
17 | ans.add(Arrays.asList(nums[left++], nums[right--])); |
18 | } |
19 | return ans; |
20 | } |
2 最佳直线
1 | //暴力,利用直线两点公式 a*x+b*y+c=0 (x1,y1)与(x2,y2)计算出a=y1-y2,b=x2-x1,c=(x1 - x2) * y1 - (y1 - y2) * x1 |
2 | //时间:O(n3) ,(x1,y1)——> i;(x2,y2)--> j , |
3 | public int[] bestLine(int[][] points) { |
4 | int[] res = new int[2]; |
5 | int max = 0; |
6 | for (int i = 0; i < points.length; i++) { |
7 | for (int j = i + 1; j < points.length; j++) { |
8 | int count = 1;//统计直线上的点数 |
9 | for (int[] point : points) { |
10 | if (isLine(points[i], points[j], point)) { |
11 | count++; |
12 | } |
13 | } |
14 | if (count > max) { |
15 | res[0] = i; |
16 | res[1] = j; |
17 | max = count; |
18 | } else if (max == count) { |
19 | if (res[0] == i && res[1] > j) |
20 | res[1] = j; |
21 | } |
22 | |
23 | } |
24 | } |
25 | return res; |
26 | } |
27 | private boolean isLine(int[] p1, int[] p2, int[] p3) { |
28 | int x1 = p1[0], y1 = p1[1], x2 = p2[0], y2 = p2[1]; |
29 | int a = y1 - y2, b = x2 - x1, c = (x1 - x2) * y1 - (y1 - y2) * x1; |
30 | return a * p3[0] + b * p3[1] + c == 0; |
31 | } |
1 | //Hash表,还是利用公式:ax+by+c=0 |
2 | //对于给定的a,b,c,可以唯一确定一条直线,且a,b,c三者的最大公约数为1,否则为重复直线,如4x+2y+1=0与8*x+4y+2=0属于同一条直线 |
3 | //基本思路:points[i][0]作为x1,points[i][1]作为y1,points[j][0]:x2,points[j][1]:y2 |
4 | //1 利用公式计算a,b,c的值:a=y1-y2,b=x2-x1,c=a * x2 + b * y2 |
5 | //2 计算a,b,c的最大公约数 |
6 | //3 让a,b,c依次除以最大公约数,直到3者不可约 |
7 | //4 以a,b,c作为key, 出现的点数作为value,,每次固定(x1,y1),遍历其他点,找出最大重复点数 |
8 | class PointValue { |
9 | int value;//重复点 |
10 | int x;//横坐标 |
11 | int y;//纵坐标 |
12 | |
13 | PointValue(int value, int x, int y) { |
14 | this.value = value; |
15 | this.x = x; |
16 | this.y = y; |
17 | } |
18 | } |
19 | public int[] bestLine(int[][] points) { |
20 | int[] res = {Integer.MAX_VALUE, Integer.MAX_VALUE}; |
21 | int max = 0; |
22 | for (int i = 0; i < points.length; i++) { |
23 | HashMap<String, PointValue> map = new HashMap<>(); |
24 | int x1 = points[i][0], y1 = points[i][1]; |
25 | for (int j = i + 1; j < points.length; j++) { |
26 | int x2 = points[j][0], y2 = points[j][1]; |
27 | int a = y1 - y2, b = x2 - x1; |
28 | int c = a * x2 + b * y2; |
29 | int gcd_num = gcd(gcd(a, b), c); |
30 | a /= gcd_num; |
31 | b /= gcd_num; |
32 | c /= gcd_num; |
33 | String key = a + "," + b + "," + c; |
34 | if (map.containsKey(key)) { |
35 | //原有的直线,保留较小的i,j |
36 | PointValue pointValue = new PointValue(map.get(key).value + 1, Math.min(map.get(key).x, i), Math.min(map.get(key).y, j)); |
37 | map.put(key, pointValue); |
38 | |
39 | } else { |
40 | //新增的直线 |
41 | PointValue value = new PointValue(1, i, j); |
42 | map.put(key, map.getOrDefault(key, value)); |
43 | } |
44 | if (max < map.get(key).value) { |
45 | //找到最长直线,更新res |
46 | res[0] = map.get(key).x; |
47 | res[1] = map.get(key).y; |
48 | max = map.get(key).value; |
49 | } else if (max == map.get(key).value) { |
50 | if (res[0] == i && res[1] > j) |
51 | //若i相等,res[1]取较小者 |
52 | res[1] = j; |
53 | } |
54 | }// end for j |
55 | } |
56 | return res; |
57 | } |
58 | private static int gcd(int a, int b) { |
59 | if (b == 0) |
60 | return a; |
61 | return gcd(b, a % b); |
62 | } |
3 有效的数独
1 | public boolean isValidSudoku(char[][] board) { |
2 | char space = '.'; |
3 | int row = board.length, col = board[0].length; |
4 | HashMap<Integer, Set<Object>> boxMap = new HashMap<>(); |
5 | //保证 3*3唯一性,key为当前点所在的区域,value:区域set,保证插入不重复 |
6 | /** box = i/3*3+j/3; |
7 | * 0 1 2 |
8 | * 3 4 5 |
9 | * 6 7 8 |
10 | **/ |
11 | for (int i = 0; i < row; i++) { |
12 | Set<Object> rowSet = new HashSet<>(); |
13 | Set<Object> colSet = new HashSet<>(); |
14 | for (int j = 0; j < col; j++) { |
15 | if (board[i][j] != space && !rowSet.add(board[i][j]))//保证当前行唯一 |
16 | return false; |
17 | if (board[j][i] != space && !colSet.add(board[j][i]))//保证当前列唯一 |
18 | return false; |
19 | int box = i / 3 * 3 + j / 3;//计算当前节点所在区域的位置 |
20 | if (board[i][j] != space) { |
21 | if (boxMap.containsKey(box)) { |
22 | Set<Object> boxSet = boxMap.get(box); |
23 | if (!boxSet.add(board[i][j]))//3*3宫内重复,返回false |
24 | return false; |
25 | } else { |
26 | Set<Object> boxSet = new HashSet<>(); |
27 | boxSet.add(board[i][j]); |
28 | boxMap.put(box, boxSet); |
29 | } |
30 | } |
31 | } |
32 | } |
33 | return true; |
34 | } |
4 字母异位词分组
1 | //1 字符串排序,kv存储 时间:O(NKlogK) K为字符串的最大长度 空间:O(NK) |
2 | public List<List<String>> groupAnagrams(String[] strs) { |
3 | HashMap<String, List<String>> map = new HashMap<>(); |
4 | for(String str:strs){ |
5 | char[] chars = str.toCharArray(); |
6 | Arrays.sort(chars); |
7 | String key = String.valueOf(chars);//排序后相同的key放在一起 |
8 | map.put(key,map.getOrDefault(key,new ArrayList<>())); |
9 | map.get(key).add(str); |
10 | } |
11 | return new ArrayList(map.values()); |
12 | } |
13 | |
14 | //2 官方题解,利用字母出现的次数计数作为key 时间:O(NK) 空间:O(NK) |
15 | public List<List<String>> groupAnagrams(String[] strs) { |
16 | HashMap<String, List<String>> map = new HashMap<>(); |
17 | for (String str : strs) { |
18 | int[] count = new int[26]; |
19 | for (char c : str.toCharArray()) { |
20 | count[c - 'a']++; |
21 | } |
22 | StringBuilder sb = new StringBuilder(); |
23 | /** |
24 | * 如: aba,baa,aab |
25 | * key: .2.1.0.0.0.0,,, |
26 | **/ |
27 | for (int i : count) { |
28 | sb.append("."); |
29 | sb.append(i); |
30 | } |
31 | String key = sb.toString(); |
32 | map.put(key, map.getOrDefault(key, new ArrayList<>())); |
33 | map.get(key).add(str); |
34 | } |
35 | return new ArrayList(map.values()); |
36 | } |
5 复制带随机指针的链表
1 | /**深拷贝:B为A的拷贝,A变化不影响B的变化,即在内存中访问的不同的地址,即使参数相同,但内存地址不同 |
2 | *思路:map映射新的Node |
3 | *时间:O(n),空间:O(n) |
4 | **/ |
5 | public Node copyRandomList(Node head) { |
6 | if (head == null) |
7 | return head; |
8 | HashMap<Node, Node> map = new HashMap<>(); |
9 | Node cur = head; |
10 | while (cur != null) { |
11 | //先new出单个新节点 |
12 | map.put(cur, new Node(cur.val)); |
13 | cur = cur.next; |
14 | } |
15 | cur = head; |
16 | while (cur != null) { |
17 | //再在map中获取新的next节点和random节点 |
18 | map.get(cur).random = map.get(cur.random); |
19 | map.get(cur).next = map.get(cur.next); |
20 | cur = cur.next; |
21 | } |
22 | return map.get(head); |
23 | } |
1 | //思路二:遍历链表,代替map映射关系,每个节点后clone一个新节点 |
2 | //ABC,AA'BB'CC',取出A'B'C' |
3 | //时间:O(N),空间:O(1) |
4 | public Node copyRandomList(Node head) { |
5 | if(head==null) |
6 | return null; |
7 | //clone新节点 next |
8 | Node cur =head; |
9 | while(cur!=null){ |
10 | Node tmp = cur.next; |
11 | cur.next = new Node(cur.val); |
12 | cur.next.next = tmp; |
13 | cur =tmp; |
14 | } |
15 | //将新clone的节点赋值random |
16 | cur =head; |
17 | while(cur!=null){ |
18 | if(cur.random!=null) |
19 | cur.next.random = cur.random.next; |
20 | cur=cur.next.next; |
21 | } |
22 | //将新节点抽出来 |
23 | cur = head; |
24 | Node ans = cur.next; |
25 | Node res =ans; |
26 | while(res.next!=null){ |
27 | Node tmp = res.next; |
28 | cur.next = tmp; |
29 | res.next = tmp.next; |
30 | cur =tmp; |
31 | res = tmp.next; |
32 | } |
33 | cur.next =null; |
34 | return ans; |
35 | } |