题目链接:https://leetcode.cn/problems/two-sum/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
暴力做法:
class Solution {
public int[] twoSum(int[] nums, int target) {
for(int i = 0; i < nums.length; i ++){
for(int j = i+1; j < nums.length; j ++){
if(nums[i] + nums[j] == target){
return new int[]{i, j};
}
}
}
return new int[0];
}
}
哈希表:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();// key:数组元素的值,value:数组索引
for(int i = 0; i < nums.length; i ++){
if(map.containsKey(target - nums[i])){
return new int[]{map.get(target-nums[i]), i};
}
map.put(nums[i], i);
}
return new int[0];
}
}
注意:不要先存 map,在去遍历比较。
如:nums=[3,2,4],map 中存储了 3–>0、2–>1、4–>2,但是当比较 map 中是否含有 3 时,直接就返回 [0,0]。
题目链接:https://leetcode.cn/problems/group-anagrams/description/?envType=study-plan-v2&envId=top-100-liked
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> res = new ArrayList<>();
HashMap<String, List<String>> map = new HashMap<>(); // key:排序后的字符串,value 字母异位词分组
for (int i = 0; i < strs.length; i++) {
char[] chs = strs[i].toCharArray();
Arrays.sort(chs); // 将字符串数组排序,排序后的值作为 map 的 key
String key = new String(chs);
if(map.containsKey(key)){
map.get(key).add(strs[i]);
}else{
map.put(key, new ArrayList<>(Arrays.asList(strs[i])));
}
}
for (Map.Entry<String, List<String>> entry: map.entrySet()){
res.add(entry.getValue());
}
return res;
}
}
另一种实现思路,手动构造key的形式:
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> res = new ArrayList<>();
Map<String, List<String>> map = new HashMap<>();
for (int i = 0; i < strs.length; i++) {
char[] chs = strs[i].toCharArray();
int[] letter = new int[26];
for (int j = 0; j < chs.length; j++) {
letter[chs[j] - 'a'] ++;
}
// 将每个出现次数大于 0 的字母和出现次数按顺序拼接成字符串,作为哈希表的键
StringBuilder sb = new StringBuilder();
for(int j = 0; j < letter.length; j ++){
if(letter[j] != 0){
sb.append((char)(j+'a'));
sb.append(letter[j]);
// 因为会出现 "aab","abb"的情况,如果不加会当成同一个key
// 加上后构造的key:aab --> a2b1、abb ---> a1b2
}
}
String key = sb.toString();
List<String> list = map.getOrDefault(key, new ArrayList<>());
list.add(strs[i]);
map.put(key, list);
}
for(Map.Entry<String, List<String>> entry: map.entrySet()){
res.add(entry.getValue());
}
return res;
}
}
题目链接:https://leetcode.cn/problems/single-number/?envType=study-plan-v2&envId=top-100-liked
哈希表做法(算法复杂度较高):
class Solution {
public int singleNumber(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
}
for(Map.Entry<Integer, Integer> entry : map.entrySet()){
if(entry.getValue() == 1){
return entry.getKey();
}
}
return 0;
}
}
异或做法:
a⊕0 = a
a⊕a = 0
a⊕b⊕a = (a⊕a)⊕b = 0⊕b = b
class Solution {
public int singleNumber(int[] nums) {
int ans = 0; // 初值为0,是因为 0⊕任何值 = 任何值
for(int i = 0; i < nums.length; i ++){
ans ^= nums[i];
}
return ans;
}
}
题目链接:https://leetcode.cn/problems/linked-list-cycle/description/?envType=study-plan-v2&envId=top-100-liked
哈希表做法(时间复杂度较高):
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null){
return false;
}
Set<ListNode> set = new HashSet(); // set 记录结点的地址
while(head.next != null){
if(set.contains(head)){
return true;
}
set.add(head);
head = head.next;
}
return false;
}
}
快慢指针做法1:
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null){
return false;
}
ListNode slow, fast;
slow = head;
fast = head.next;
// slow 每次向前走一步,fast 每次向前走两步(可以任意多步)
// 当存在环时,fast 由于走得快,会发生扣圈的情况,且最终与 slow 相遇
// 当不存在环时,fast 可能在某次循环后,发生当前位置为空,或下一位置为空的两种情况,当然由于走的快,最终会返回false。
// 总之,循环的结束条件,要么出现环 slow == fast,要么 fast 先一步为空!
while(slow != fast && fast != null && fast.next != null){
// 注意:fast != null 要先于fast.next != null 来判断,以防止控制帧异常
slow = slow.next;
fast = fast.next.next;
}
return slow == fast;
}
}
快慢指针做法2(思路同下方“环形链表2”):
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
while(true){
if(fast==null || fast.next==null){
return false;
}
slow = slow.next;
fast = fast.next.next;
if(slow==fast){
return true;
}
}
}
}
题目链接:https://leetcode.cn/problems/linked-list-cycle-ii/?envType=study-plan-v2&envId=top-100-liked
哈希表做法(时间复杂度较高):
public class Solution {
public ListNode detectCycle(ListNode head) {
Set<ListNode> set = new HashSet<>();
ListNode p = head;
while(p!=null){
if(set.contains(p)){
return p;
}
set.add(p);
p = p.next;
}
return null;
}
}
快慢指针,实现思路如下:
fast
每次走两个节点, slow
每次走一个节点。环外有 a
个结点,环内有 b
个结点。fast
走了 f
步,slow
走了 s
步。f = 2s
f = s + nb
表示 f
比 s
多走了 n*b
步,即 n
圈。这样表示的原因在于扣圈。f = 2nb, s = nb
slow
指针从开始到环的入口要走 k 步:k = a + nb (n = 0,1,2,…)
s = n*b
,即已经走了 n*b
步了,那么此时只需要再走 a
步即可回到链表入环的起点。public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head, slow = head;
while(true){
if(fast == null || fast.next == null){
return null;
}
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
break;
}
}
fast = head; // fast回到链表起点,与 slow 一同遍历 a 步
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return fast;
}
}