Easy

两数之和 -哈希

跳转原题

题目描述

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
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 去做。也可以用两层循环暴力去做。


移动零 - 双指针

跳转原题

题目描述

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
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++;
}
}
}

}
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
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元素时左右指针才会分开。


相交链表 - 链表

跳转原题

题目描述

  • 时间复杂度:O(m+n)
  • 空间复杂度:O(m+n)
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
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
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;
}
}
  • 时间复杂度:O(m+n)
  • 空间复杂度:O(1)
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
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
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;
}
}

反转链表 - 链表

跳转原题

题目描述

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
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
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
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;

}
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
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)里,然后用头尾双指针判断数组是否是回文即可。

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
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
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
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;
}
// head == rear 时递归完成,此时nextHead.next为null(所以不能返回),随便返回一个不为null的Node代表true就行(比如head)
return head != rear ? nextHead.next : head;
}
}

  1. 使用快慢指针在一次遍历中找到中间结点:慢指针一次走一步,快指针一次走两步,快慢指针同时出发。当快指针移动到链表的末尾时,慢指针恰好到链表的中间。通过慢指针将链表分为两部分。(若链表有奇数个节点,则中间的节点应该看作是前半部分。)
  2. 反转链表的后半部分:从慢指针后一个节点开始,反转后半部分链表。反转链表
  3. 比较两个链表的结点
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
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;
}
}


环形链表 - 链表

跳转原题

题目描述

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
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
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
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;

}
}

遍历所有结点,如果有环,在回访到之前的结点时会发生哈希冲突。

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
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;
}
}