Easy 两数之和 -哈希 跳转原题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution { public int [] twoSum(int [] nums, int target) { Map<Integer, Integer> map = new HashMap<>(); map.put(target - nums[0 ], 0 ); for (int i = 1 ; 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 []{}; } }
这题有点印象,所以一开始就用的 HashMap 去做。也可以用两层循环暴力去做。
移动零 - 双指针 跳转原题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { public void moveZeroes (int [] nums) { if (nums.length == 1 ){ return ; } int n = nums.length - 1 ; int i = 0 ; int j = 1 ; while (j < nums.length){ if (nums[i] != 0 ){ i++; j++; } else { if (nums[j] != 0 ){ nums[i] = nums[j]; nums[j] = 0 ; i++; j++; } else { j++; } } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution { public void moveZeroes (int [] nums) { int n = nums.length, left = 0 , right = 0 ; while (right < n) { if (nums[right] != 0 ) { int temp = nums[left]; nums[left] = nums[right]; nums[right] = temp; left++; } right++; } } }
大体逻辑相同,区别在于我的解法是左右指针一开始就分开移动;而官方解法是一开始左右指针重叠移动,出现0元素时左右指针才会分开。
相交链表 - 链表 跳转原题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class Solution { public ListNode getIntersectionNode (ListNode headA, ListNode headB) { Set<ListNode> set = new HashSet<>(); ListNode tempA = headA; ListNode tempB = headB; while (tempA != null || tempB != null ){ if (tempA != null ){ if (set.add(tempA)){ tempA = tempA.next; } else { return tempA; } } if (tempB != null ){ if (set.add(tempB)){ tempB = tempB.next; } else { return tempB; } } } return null ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Solution { public ListNode getIntersectionNode (ListNode headA, ListNode headB) { if (headA == null || headB == null ){ return null ; } ListNode tempA = headA; ListNode tempB = headB; while (tempA != null || tempB != null ){ if (tempA == null ){ tempA = headB; } if (tempB == null ){ tempB = headA; } if (tempA == tempB){ return tempA; } tempA = tempA.next; tempB = tempB.next; } return null ; } }
反转链表 - 链表 跳转原题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution { public ListNode reverseList (ListNode head) { if (head == null ){ return null ; } if (head.next == null ){ return head; } ListNode tempA = null ; ListNode tempB = head.next; while (head != null ){ head.next = tempA; tempA = head; head = tempB; if (tempB != null ){ tempB = tempB.next; } } return tempA; } }
1 2 3 4 5 6 7 8 9 10 11 12 class Solution { public ListNode reverseList (ListNode head) { if (head == null || head.next == null ) { return head; } ListNode newHead = reverseList(head.next); head.next.next = head; head.next = null ; return newHead; } }
简单说,迭代就是从头开始反转,递归就是从尾开始反转。
回文链表 - 链表 跳转原题
还有一种最简单的解法:把链表的值读到数组(ArrayList)里,然后用头尾双指针判断数组是否是回文即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution { public boolean isPalindrome (ListNode head) { return check(head, head) == null ? false : true ; } public ListNode check (ListNode head, ListNode rear) { if (rear == null ){ return head; } ListNode nextHead = check(head, rear.next); if (nextHead == null || nextHead.val != rear.val){ return null ; } return head != rear ? nextHead.next : head; } }
使用快慢指针在一次遍历中找到中间结点:慢指针一次走一步,快指针一次走两步,快慢指针同时出发。当快指针移动到链表的末尾时,慢指针恰好到链表的中间。通过慢指针将链表分为两部分。(若链表有奇数个节点,则中间的节点应该看作是前半部分。) 反转链表的后半部分:从慢指针后一个节点开始,反转后半部分链表。反转链表 比较两个链表的结点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Solution { public boolean isPalindrome (ListNode head) { if (head == null ) { return true ; } ListNode firstHalfEnd = endOfFirstHalf(head); ListNode secondHalfStart = reverseList(firstHalfEnd.next); ListNode p1 = head; ListNode p2 = secondHalfStart; boolean result = true ; while (result && p2 != null ) { if (p1.val != p2.val) { result = false ; } p1 = p1.next; p2 = p2.next; } return result; } private ListNode reverseList (ListNode head) { ListNode prev = null ; ListNode curr = head; while (curr != null ) { ListNode nextTemp = curr.next; curr.next = prev; prev = curr; curr = nextTemp; } return prev; } private ListNode endOfFirstHalf (ListNode head) { ListNode fast = head; ListNode slow = head; while (fast.next != null && fast.next.next != null ) { fast = fast.next.next; slow = slow.next; } return slow; } }
环形链表 - 链表 跳转原题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Solution { public boolean hasCycle (ListNode head) { if (head == null ){ return false ; } ListNode fast = head; ListNode slow = head; while (fast.next != null && fast.next.next != null ){ fast = fast.next.next; slow = slow.next; if (fast == slow){ return true ; } } return false ; } }
遍历所有结点,如果有环,在回访到之前的结点时会发生哈希冲突。
1 2 3 4 5 6 7 8 9 10 11 12 public class Solution { public boolean hasCycle (ListNode head) { Set<ListNode> seen = new HashSet<ListNode>(); while (head != null ) { if (!seen.add(head)) { return true ; } head = head.next; } return false ; } }
合并两个有序链表 - 链表 跳转原题
先把两条链表中头结点值较小的那条当成主链 list1,然后在遍历 list1 的过程中,不断把 list2 的当前节点插入到 list1 中合适的位置。最终在 list1 上原地完成合并。
官方解法是维护一个虚拟头节点,然后把两个链表的结点依次按大小顺序拼接起来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class Solution { public ListNode mergeTwoLists (ListNode list1, ListNode list2) { if (list1 == null || list2 == null ){ return list1 == null ? list2 : list1; } ListNode temp; if (list1.val > list2.val){ temp = list1; list1 = list2; list2 = temp; } ListNode head = list1; while (list2 != null ){ if (list1.next == null ){ list1.next = list2; break ; } if (list1.next.val <= list2.val){ list1 = list1.next; } else { temp = list2; list2 = list2.next; temp.next = list1.next; list1.next = temp; list1 = temp; } } return head; } }
如果 l1 当前节点的值小于等于 l2,我们就把 l1 当前的节点接在 prev 节点的后面,同时将 l1 指针往后移一位,对 l2 也做同样的操作,直到 l1 或 l2 指向了 null。(不管我们将哪一个元素接在了后面,我们都需要把 prev 向后移一位)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public ListNode mergeTwoLists (ListNode l1, ListNode l2) { ListNode prehead = new ListNode(-1 ); ListNode prev = prehead; while (l1 != null && l2 != null ) { if (l1.val <= l2.val) { prev.next = l1; l1 = l1.next; } else { prev.next = l2; l2 = l2.next; } prev = prev.next; } prev.next = l1 == null ? l2 : l1; return prehead.next; } }
判断 l1 和 l2 哪一个链表的头节点的值更小,然后递归地决定下一个添加到结果里的节点。如果两个链表有一个为空,递归结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public ListNode mergeTwoLists (ListNode l1, ListNode l2) { if (l1 == null ) { return l2; } else if (l2 == null ) { return l1; } else if (l1.val < l2.val) { l1.next = mergeTwoLists(l1.next, l2); return l1; } else { l2.next = mergeTwoLists(l1, l2.next); return l2; } } }