Leetcode Notes

代码随想录刷题笔记

454 四数相加II

https://leetcode.com/problems/4sum-ii/description/

HashMap 统计前两个数组所有变量之和 并统计次数
比较后两个数组所有变量之和 是否存在 存在即+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 int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4){
Map<Integer, Integer> map = new HashMap<>();

int result = 0;

for (int i : nums1){
for (int j : nums2){
int sum = i + j;
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
}

for (int i : nums3){
for (int j : nums4){
int sum = i + j;

result += map.getOrDefault(-sum, 0);
}

}

return result;
}
}

383 赎金信

https://leetcode.com/problems/ransom-note/submissions/1133507064/

本质上就是242 有效的字母异位词

把magazine里面的字母记录进array哈希表里面
之后对比magazine 哈希表里面的数值和ransomNote, 如果有负数,说明用超了,return false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public boolean canConstruct(String ransomNote, String magazine){
int[] mag = new int[26];

for(int i = 0; i < magazine.length(); i++){
mag[magazine.charAt(i) - 'a']++;
}
for(int i = 0; i < ransomNote.length(); i++){
mag[ransomNote.charAt(i) - 'a']--;
}

for(int i : mag){
if (i < 0){
return false;
}
}

return true;
}
}

15 三数之和

  1. 哈希法可以做,但不好去重,因为去重操作繁琐
  2. 双指针可解
    把数组排序后, 进行操作
    设置i在0位置,left在i+1位置,right在数组最后
    如果i+left+right > 0, right–;
    i+left+right = 0, left++
    i+left+right < 0, left++;

需要考虑a,b,c的去重
因为数据不能重复

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
class Solution{
public List<List<Integer>> threeSum(int[] nums){
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);

for (int i = 0; i < nums.length; i++){
if (nums[i] > 0){
return result;
}

if (i > 0 && nums[i] == nums[i-1]){
continue;
}

int left = i+1;
int right = nums.length - 1;
while (left < right){
int sum = nums[i] + nums[left] + nums[right];

if (sum > 0){
right--;
} else if (sum < 0){
left++;
} else {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));

while (right > left && nums[right] == nums[right- 1]) right--;
while (right > left && nums[left] == nums[left+1]) left++;

right--;
left++;
}
}
}

return result;
}
}

18 四数之和

逻辑上和三数之和一个思路,使用双指针
在三数之和上套一层for循环

比454 四数相加II 难,因为是在同数组下进行查找,并且需要去重

四数相加逻辑是在原来的nums[i]的基础上再套一层nums[k]进行计算
那么5数之和,6数之和同一个逻辑

与三数之和不同的剪支,在一层逻辑上是与三数之和的0不同,因为负值相加会更小,所以只适用于正数
一定要注意要先整理数组nums

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
class Solution{
public List<List<Integer>> fourSum(int[] nums, int target){
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);

for (int i = 0; i < nums.length; i++){
if (nums[i] > 0 && nums[i] > target){
return result;
}

if (i > 0 && nums[i] == nums[i-1]){
continue;
}
for (int k = i + 1; k < nums.length; k++){
if (k > i + 1 && nums[k] == nums[k-1]){
continue;
}

int left = k+1;
int right = nums.length - 1;
while (left < right){
int sum = nums[i] + nums[k] + nums[left] + nums[right];

if (sum > target){
right--;
} else if (sum < target){
left++;
} else {
result.add(Arrays.asList(nums[i],nums[k],nums[left],nums[right]));
while (right >left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;

right--;
left++;
}
}

}
}

return result;
}

}

哈希表理论基础

解决的核心问题:快速判断一个元素是否出现在集合里面
时间复杂度为O(1)

哈希函数: 将值附到哈希表中所用的函数,不可避免出现同时映射到同一个索引下标上

哈希碰撞:如果出现不同值映射到同一个索引下标上面,有两种方法处理

  1. 拉链法:把不同的值做成链表,就可以索引了
  2. 线性探测:可以把映射到相同下标的值,分配给空的下标的位置,此方法需保证tableSize > dataSize

常见哈希表实现:数组,set,map

当解决哈希问题时,优先使用unordered_set

牺牲空间来换取时间

242 有效的字母异位词

https://leetcode.com/problems/valid-anagram/description/

数组做哈希表的经典题目
记录元素出现次数时,可以用26长度的数组
记录index 为 charAt(i) - ‘a’ 的次数
然后检查是否都出现过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution{
public boolean isAnagram(String s, String t){
int[] record = new int[26];

for (int i = 0; i < s.length(); i++){
record[s.charAt(i) - 'a']++;
}

for (int i = 0; i < t.length(); i++){
record[t.charAt(i) - 'a']--;
}
for (int i = 0; i < record.length; i++){
if(record[i] != 0){
return false;
}
}

return true;

}

}

349 两个数组的交集

https://leetcode.com/problems/intersection-of-two-arrays/description/

输出结果中每个元素唯一,所以包含去重操作,去重就需要想到set的哈希表

在java中需要使用HashSet

  1. set实现 (推荐)
    将数组一遍历一遍之后存到set1里面,这里去重了
    再将数组一和数组二比较,出现的存到set2里面
    打印成数组

这里可以背下来用法 set.stream().maptoInt(x -> x).toArray();

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
import java.util.*;

class Solution{
public int[] intersection(int[] nums1, int[] nums2){
if (nums1 == null || nums2 == null || nums1.length == 0 || nums2.length == 0){
return new int[0];
}

Set<Integer> set1 = new HashSet<>();
Set<Integer> set2 = new HashSet<>();

for (int i : nums1){
set1.add(i);
}

for (int i : nums2){
if (set1.contains(i)){
set2.add(i);
}
}
// 简便写法
return set2.stream().mapToInt(x -> x).toArray();
// 数组写法

int[] result = new int[set2.size()];
int j = 0;
for (int i : set2){
result[j++] = i;
}
return result;
}

}
  1. ArrayList 实现
    因为题目规定了nums的长度,和nums所存值的大小,所以可以用数组来做

与242 有效的字母异位词 类似,通过创建数组统计次数

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
import java.util.*;
class Solution{
public int[] intersection(int[] nums1, int[] nums2){
int[] hash1 = new int[1001];
int[] hash2 = new int[1001];

for (int i : nums1){hash1[i]++;}
for (int i : nums2){hash2[i]++;}

List<Integer> resList = new ArrayList<>();

for (int i = 0; i < hash1.length; i++){
if (hash1[i] != 0 && hash2[i] != 0){
resList.add(i);
}
}

int[] result = new int[resList.size()];
int j = 0;
for (int i : resList){
result[j++] = i;
}

return result;
}

}

202 快乐数

https://leetcode.com/problems/happy-number/description/

定义快乐数

每一位的平方和一直计算下去,如果有一次结果为1,则为快乐数

可以判断sum是否重复出现,如果重复出现,则为false,有1则为true

getNextSquareSum 写成方法

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 boolean isHappy(int n){
Set<Integer> sums = new HashSet<>();
while (n != 1 && !sums.contains(n)){
sums.add(n);
n = getNextSquareSum(n);
}

return n == 1;
}

public int getNextSquareSum(int n){
int sum = 0;
while (n > 0){
int temp = n % 10;
sum += temp * temp;
n /= 10;
}
return sum;
}

}

1 两数之和

利用map来做,assign key 和 value的pair
把index作为value, 把数组对应的value变成key

查找差值,return两元素数组

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
import java.util.*;
class Solution{
public int[] twoSum(int[] nums, int target){
int[] result = new int[2];

Map<Integer, Integer> map = new HashMap<>();

if (nums == null || nums.length == 1){
return result;
}

for (int i = 0; i < nums.length; i++){
int temp = target - nums[i];

if (map.containsKey(temp)){
result[1] = i;
result[0] = map.get(temp);

}

map.put(nums[i], i);
}

return result;
}

}

数组

704 二分查找 左闭右闭,左闭右开 两种方法 checked!
27 移除元素 暴力解法 快慢指针 左右指针(while查找右边第一个不是val的值, 无需考虑right是什么值) checked!
977 有序数组的平方 左右指针法(右指针– !) checked!
209 长度最小的子数组 滑动窗口解决(左右指针,比较方法) checked!
59 螺旋数组II 模拟四次左闭右开(循环不变量原则) checked!

链表

203 移除链表元素 有dummy node 有 pre, 无dummy node 有 pre checked!
无dummy node 无 pre (while只判断cur,while之中的while判断cur.next) double checked!
707 设计链表 单链表 双链表 (此题切记,在插入方法,和删除方法中对size的运算) checked!
206 反转链表 无递归,正向递归,反向递归(像背下来的) checked!
反向同函数递归 double checked!
24 两两交换链表中的节点 无递归 checked!
有递归(需要着重复习) unchecked!!!!!!
19 删除链表倒数第N个节点 两次遍历,一次遍历(快慢指针) checked!
160 链表相交 两个for loop, 交换A 和 B checked!
142 环形链表II 快慢指针,注意while loop的条件,如何查找环入口 checked!

拓展

704 二分查找 拓展
35 搜索插入位置 简单的二分法之后,return left checked!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution{
public int searchInsert(int[] nums, int target){
int left = 0, right = nums.length - 1;

while (left <= right){
int mid = left + ((right - left) >> 1);

if (nums[mid] == target){
return mid;
} else if (nums[mid] < target){
left = mid + 1;
} else {
right = mid - 1;
}

}

return left;

}
}

34 在排序数组中查找元素的第一个和最后一个位置
二分法先查找出一个target的位置,后两个指针向两侧滑动到不是target值的位置 checked!
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 int[] searchRange(int[] nums, int target){
int index = binarySearch(nums, target);

if (index == -1) return new int[] {-1,-1};

int left = index;
int right = index;

while (left >= 0&& nums[left] == target ) left--;

while (right <= nums.length - 1 && nums[right] == target) right++;

return new int[] {left+1,right-1};

}

public int binarySearch(int[] nums, int target){
int left = 0, right = nums.length - 1;

while (left<= right){
int mid = left + ((right - left) >> 1);

if (nums[mid] == target){
return mid;
} else if (nums[mid] < target){
left = mid + 1;
} else {
right = mid - 1;
}

}
return -1;
}
}

27 移除元素 拓展
26 删除排序数组中的重复项 checked!
快慢指针检测,注意条件的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution{
public int removeDuplicates(int[] nums){
if (nums.length < 2) return nums.length;

int slowIndex = 0, fastIndex = 0;

while (fastIndex < nums.length){
if (nums[slowIndex] == nums[fastIndex]){
fastIndex++;

} else{
slowIndex++;
nums[slowIndex] = nums[fastIndex];
fastIndex++;
}
}

return slowIndex+1;
}
}

链表拓展
234 回文链表

  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
    class Solution{
    public boolean isPalindrome(ListNode head){
    int length = 0;
    ListNode cur = head;

    while (cur != null){
    cur= cur.next;
    length++;
    }

    int[] headList = new int[length];
    cur = head;
    for(int i = 0; i < length; i++){
    headList[i] = cur.val;
    cur = cur.next;
    }

    int left = 0, right = length - 1;

    while (left <= right){
    if (headList[left]==headList[right]){
    left++;
    right--;
    } else {
    return false;
    }

    }

    return true;

    }

    }

  2. 利用快慢指针

    1. 快慢指针, 快指针进2步,慢指针进1步,找到中点
    2. 在中点前断成两个LinkedList
    3. 反转后面的链表
    4. 对比短链表数量的值看是否相等
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
class Solution{
public boolean isPalindrome(ListNode head){
if (head == null || head.next == null) return true;

ListNode slowIndex = head;
ListNode fastIndex = head;

while (fastIndex != null && fastIndex.next != null){
fastIndex = fastIndex.next.next;
slowIndex = slowIndex.next;
}

ListNode curA = reverseList(slowIndex);
slowIndex = null;
ListNode curB = head;

while (curA != null){
if (curA.val == curB.val){
curA = curA.next;
curB = curB.next;
} else {
return false;
}
}

return true;
}

public ListNode reverseList(ListNode head){
if (head == null) return null;
if (head != null && head.next == null) return head;

ListNode last = reverseList(head.next);
head.next.next = head;
head.next = null;

return last;
}
}

24 两两交换链表中的节点

https://leetcode.com/problems/swap-nodes-in-pairs/description/

正常模拟交换过程

首先建立dummy节点,之后设置第一节点,第二节点,temp
while 判断是否有两个节点可以交换

模拟过程:
存储cur的第三个节点
存储cur的第二个节点
存储cur的第一个节点

cur的next是第二个节点
第二个节点的next是第一个节点
第一个节点的next是第三个节点
之后cur移到第一个节点的位置(即新虚拟节点的位置)

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
class Solution{
public ListNode swapPairs(ListNode head){
ListNode temp;
ListNode firstNode;
ListNode secondNode;
ListNode dummy = new ListNode(-1, head);
ListNode cur = dummy;

while (cur.next != null && cur.next.next != null){
temp = cur.next.next.next;
firstNode = cur.next;
secondNode = cur.next.next;

cur.next = secondNode;
secondNode.next = firstNode;
firstNode.next = temp;

cur = firstNode;

}

return dummy.next;
}


}

递归方法: 类似翻转链表中反向递归方法(需要研究)不是很明白

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution{
public ListNode swapPairs(ListNode head){
if (head == null || head.next == null) return head;
ListNode next = head.next;
ListNode newNode = swapPairs(next.next);
next.next = head;
head.next = newNode;

return next;

}

}

19 删除链表倒数第N个节点

https://leetcode.com/problems/remove-nth-node-from-end-of-list/description/

自己可以写出两次遍历的解法:
第一次找到size
第二次把节点删除

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 removeNthFromEnd(ListNode head, int n){
ListNode dummy = new ListNode(-1,head);
ListNode pre = dummy;
ListNode cur = head;
int size = 0;
while (cur != null){
cur = cur.next;
size++;
}

for (int i = 0; i < size - n; i++){
pre = pre.next;
}

pre.next = pre.next.next;

return dummy.next;
}

}

快慢指针经典题目

设置双指针,快指针先走n步,后快指针和慢指针一起走到快指针的链表尽头,此时删除慢指针所指node

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 removeNthFromEnd(ListNode head, int n){
ListNode dummy = new ListNode(-1, head);

ListNode fastNode = dummy;
ListNode slowNode = dummy;

for (int i = 0; i <= n; i++){
fastNode = fastNode.next;
}

while (fastNode != null){
fastNode = fastNode.next;
slowNode = slowNode.next;
}
slowNode.next = slowNode.next.next;
return dummy.next;

}

}

160 链表相交

https://leetcode.com/problems/intersection-of-two-linked-lists/description/
求两链表交点节点的指针
首先求出两链表的长度
把两个链表的尾部对齐,即把长链表的指针指到和短链表相同长度
如果指针数值相同则相交,否则向后移动指到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
39
40
41
42
43
44
45
public class Solution{
public ListNode getIntersectionNode(ListNode headA, ListNode headB){
ListNode curA = headA;
ListNode curB = headB;

int sizeA = 0;
int sizeB = 0;

while (curA != null){
curA = curA.next;
sizeA++;
}

while (curB != null){
curB = curB.next;
sizeB++;
}

curA = headA;
curB = headB;

if (sizeA < sizeB){
for (int i = 0; i < sizeB-sizeA; i++){
curB = curB.next;
}
} else if (sizeA > sizeB){
for (int i = 0; i < sizeA-sizeB; i++){
curA = curA.next;
}
}

while (curA != null){
if(curA == curB){
return curA;
} else {
curA = curA.next;
curB = curB.next;
}

}
return null;
}

}

142 环形链表II

https://leetcode.com/problems/linked-list-cycle-ii/description/

判断链表内是否有环:
可通过快慢指针来判断,快走两步,慢走一步,这样如果两指针相遇,则链表内定存在环

如果有环如何判断环入口:
通过公式化简可得:
如果快指针走过一圈后就遇到慢指针,则从遇到的点开始和头节点同时走相同步数,两指针相遇的点即为入口

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 ListNode detectCycle(ListNode head){
ListNode slowIndex = head;
ListNode fastIndex = head;

while (fastIndex != null && fastIndex.next != null){
slowIndex = slowIndex.next;
fastIndex = fastIndex.next.next;

if (slowIndex == fastIndex){
ListNode index1 = slowIndex;
ListNode index2 = head;

while (index1 != index2){
index1 = index1.next;
index2 = index2.next;
}

return index1;
}
}

return null;
}
}

链表理论基础

链表是通过指针串联在一起的线性结构。 由data和指针构成

最后一个节点指向null

特殊:
双链表: 由data和两个指针域构成,一个指向下一个节点,一个指向上一个节点
循环链表: 链表收尾相连

存储方式:
在内存中不是连续分布的,是散乱在内存中的

创建链表

这种写法来自kamacoder网上链表基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class LinkedList{
class Node{
int data;
Node next;

public Node(int data){
this.data = data;
this.next = null;
}

}

private int length;
private Node headNode;

public LinkedList(){
this.length = 0;
this.headNode = null;
}
}

代码随想录写法:(ListNode 其实为kamacoder网上Node的写法,但也有区别,这里可以通过constructor构建LinkedList)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ListNode{
int data;
ListNode next;

public ListNode(){
}

public ListNode(int data){
this.data = data;
this.next = null;
}

public ListNode(int data, ListNode next){
this.data = data;
this.next = next;
}
}

leetcode内置(与代码随想录思路一样)

1
2
3
4
5
6
7
8
9
10
11

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;}
}

链表操作
删除节点: 将该节点的指针跳过下一个节点变成.next;
添加节点:将该节点的指针指到新节点,新节点的下一指针指到原来链表的下一个指针
添加删除操作均为O(1)
但是遍历链表找到对应节点为O(N)

对比数组,数组增删为O(N),遍历为O(1)

203 移除链表元素

https://leetcode.com/problems/remove-linked-list-elements/description/

  1. 直接用原来的链表进行操作
    这种方法处理头结点是特殊操作
    处理方式与虚拟头节点差不多,但是要把头节点先拿出来(注意用while处理)

    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
    public ListNode removeElements(ListNode head, int val){
    while(head != null && head.val == val){
    head = head.next;
    }

    if(head == null){
    return head;
    }

    ListNode pre = head;
    ListNode cur = head.next;

    while(cur != null){
    if(cur.val == val){
    pre.next = cur.next;
    } else {
    pre = cur;
    }

    cur = cur.next;
    }

    return head;

    }

  2. 使用虚拟头结点进行操作
    可以统一操作
    首先排除空linkedlist
    新建linkNode dummy 连接head,形成虚拟头节点

用for或while loop 遍历寻找元素,设置pre和current指针,如果cur是val,pre的next是cur的next, 如果不是,pre就是cur。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public ListNode removeElements(ListNode head, int val){
if(head == null){
return head;
}

ListNode dummy = new ListNode(-1, head);

ListNode pre = dummy;
ListNode cur = head;

while(cur != null){
if(cur.val == val){
pre.next = cur.next;
} else {
pre = cur;
}

cur = cur.next;
}
return dummy.next;
}

  1. 不使用虚拟头结点和pre进行操作 (注意判断cur.next的val时使用while)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public ListNode removeELements(ListNode head, int val){
    while(head != null && head.val == val){
    head = head.next;
    }

    if(head == null){
    return head;
    }

    ListNode cur = head;
    while(cur != null){
    while(cur.next != null && cur.next.val == val){
    cur.next = cur.next.next;
    }

    cur = cur.next;
    }

    return head;

    }

707 设计链表

https://leetcode.com/problems/design-linked-list/description/

  1. 单链表方法

包括:
在index插入
在index删除
在index查找
在头节点加入
在尾节点加入

在初始化链表时初始一个dummy节点在最前方,

for loop 里面

cur <= index
pre < index

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

class ListNode{
int val;
ListNode next;

public ListNode(){}
public ListNode(int val){
this.val = val;
this.next = null;

}

}
class MyLinkedList {

int length;
ListNode head;
public MyLinkedList() {
this.length = 0;
this.head = new ListNode(0);
}

public int get(int index) {
if(index < 0||index >= length){
return -1;

}

ListNode cur = head;

for(int i = 0; i <= index; i++){
cur = cur.next;
}

return cur.val;
}

public void addAtHead(int val) {
addAtIndex(0, val);

}

public void addAtTail(int val) {
addAtIndex(length,val);
}

public void addAtIndex(int index, int val) {
if (index > length){
return;
}

if(index < 0){
index = 0;
}
length++;
ListNode pre = head;

for(int i = 0; i < index; i++){
pre = pre.next;
}

ListNode newNode = new ListNode(val);

newNode.next = pre.next;
pre.next = newNode;
}

public void deleteAtIndex(int index) {
if(index < 0 || index >= length){
return;
}
length--;
if(index == 0){
head = head.next;
return;
}

ListNode pre = head;

for(int i = 0;i < index;i++){
pre = pre.next;
}

pre.next = pre.next.next;
}
}

双链表:

在初始化链表时一定要把头尾的dummy node一起创建,并且连接起来,不然会出现.next null pointer 的错误

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

class ListNode{
int val;
ListNode prev,next;

public ListNode(){}
public ListNode(int val){
this.val = val;
}
}
class MyLinkedList {
int length;
ListNode head, tail;

public MyLinkedList() {
this.head = new ListNode(0);
this.tail = new ListNode(0);
this.length = 0;
head.next = tail;
tail.prev = head;
}

public int get(int index) {
if(index < 0 || index > length - 1){
return -1;
}

ListNode cur = head;

for (int i = 0; i <= index; i++){
cur = cur.next;
}

return cur.val;
}

public void addAtHead(int val) {
addAtIndex(0, val);
}

public void addAtTail(int val) {
addAtIndex(length, val);
}

public void addAtIndex(int index, int val) {
if (index > length){
return;
}

if(index < 0){
index = 0;
}

length++;
ListNode pre = head;

for(int i = 0; i < index; i++){
pre = pre.next;
}
ListNode newNode = new ListNode(val);
newNode.next = pre.next;
pre.next.prev = newNode;
newNode.prev = pre;
pre.next = newNode;


}

public void deleteAtIndex(int index) {
if(index < 0 || index > length - 1){
return;
}

length--;

ListNode pre = head;

for(int i = 0; i < index; i++){
pre = pre.next;
}
System.out.println(index);
System.out.println(pre.val);
pre.next.next.prev = pre;
pre.next = pre.next.next;



}
}

206 反转链表

https://leetcode.com/problems/reverse-linked-list/description/

1.双指针法

利用temp存储下次的值, 在把prev连接到现在的cur上,在用temp更新cur

相当于把链表下的每一个值一个一个拿出来连接到prev上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class Solution{
public ListNode reverseList(ListNode head){
ListNode prev = null;
ListNode cur = head;
ListNode temp = null;


while (cur != null){
temp = cur.next;
cur.next = prev;
prev = cur;
cur = temp;

}

return prev;

}
}
  1. 递归法

逻辑和双指针是一样的,也是把next的值存到temp里面,然后反转
递归时,prev就是cur, cur就是temp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution{
public ListNode reverseList(ListNode head){
return reverse(null, head);
}

public ListNode reverse(ListNode prev, ListNode cur){
if(cur == null){
return prev;
}

ListNode temp = cur.next;
cur.next = prev;

return reverse(cur,temp);
}

}

3.从后向前递归 (不太明白,需要再研究)

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null) return null;
if(head.next == null) return head;

ListNode last = reverseList(head.next);

head.next.next = head;
head.next = null;
return last;
}
}

977 有序数组的平方

https://leetcode.com/problems/squares-of-a-sorted-array/description/

暴力解法: 先平方得到平方后的array,后排序 O(N+NlogN)

这里不写解法,需要自己写sort algo或者直接用语言自带的sort function

双指针解法更快:
因为数组两端肯定出一个最大值,向中间得到越来越小的平方数,则我们可以使用左右双指针。
左指针和右指针的平方做对比,打的写在输出数组的最后的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution{
public int[] sortedSquares(int[] nums){
int left = 0, right = nums.length - 1;
int[] result = new int[nums.length];
int index = nums.length - 1;

while(left <= right){
int leftS = nums[left] * nums[left];
int rightS = nums[right] * nums[right];

if(leftS < rightS){
result[index--] = rightS;
right--;
} else {
result[index--] = leftS;
left++;
}
}

return result;
}

}

注意两者在更新右区间的区别,设置右起始值,和更新右值有区别。

209 长度最小的子数组

https://leetcode.com/problems/minimum-size-subarray-sum/description/
给定一个都是正数的数组,找出最小连续子数组之和大于或等于target。

暴力解法:
两个for循环找出所有可能性, 超时不具体实现

滑动窗口:
不断调节子序列的起始位置和终止位置,从而得到结果
因为所需时间复杂度为O(N)所以循环索引必定为滑动窗口的终止位置;
即滑动窗口的起始位置需要在用一个for loop里实现动态调节。

滑动窗口的精髓就在于:设置滑动窗口的终点位置为索引,动态调节滑动窗口的起始位置

具体实现:
sum += 终止位置的值;
如果(这一步的判断是while,不是if, 如果sum超过target,就把起始位置一直移到不超过为止,记录result)sum超过了target,记录result 后 sum减掉起始位置的值后起始位置前移一格。

最后比较result和Integer max

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 int minSubArrayLen(int target, int[] nums){
int left = 0;
int sum = 0;

int result = Integer.MAX_VALUE;

for (int right = 0; right < nums.length; right++){
sum+=nums[right];
while(sum >= target){
result = Math.min(result, right-left + 1);
sum -= nums[left];
left++;
}
}

return result == Integer.MAX_VALUE ? 0 : result;

}

}

59 螺旋矩阵II

https://leetcode.com/problems/spiral-matrix-ii/description/

给定一个整数n,画出从1到n^2的螺旋数组。

此题为模拟题型,模拟顺时针画法

循环不变量原则 (二分法也遵循了这一原则)

模拟顺时针画矩阵的过程:

填充上行从左到右
填充右列从上到下
填充下行从右到左
填充左列从下到上

遵循左闭右开原则一直顺时针画边。

以4为例 模拟总过程
二维数组new int [4][4]

first loop
[0,0][0,1][0,2]
[0,3][1,3][2,3]
[3,3][3,2][3,1]
[3,0][2,0][1,0]

起始位置+1 +1
second loop
[1,1]
[1,2]
[2,2]
[2,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
class Solution {
public int[][] generateMatrix(int n) {
int[][] result = new int[n][n];
int count = 1;
int offset = 1;
int start = 0;
int loop = n/2;

int i = 0, j = 0;
while(loop-- > 0){
for(j = start; j < n - offset; j++){
result[start][j] = count++;
}
for(i = start; i < n - offset; i++){
result[i][j] = count++;
}
for(;j > start; j--){
result[i][j] = count++;
}

for(;i > start; i--){
result[i][j] = count++;
}

offset++;
start++;

}

if(n%2 == 1){
result[start][start] = count;
}

return result;
}
}

数组理论基础

数组存放在连续内存空间上的相同类型数据的集合

数组下标从0开始
数组内存空间的地址是连续的

Java 二维数组排布不是连续的

704 二分查找

Leetcode 704

考虑二分法的时机

数组有序,且无重复元素 (如果有重复元素,注意滑动窗口)

实现

  • 左闭右闭
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 int binarySearch(int[] nums, int target){
if(target < nums[0] || target > nums[nums.length - 1]){
return -1
}

int left = 0, right = nums.length-1;

while(left <= right){
int mid = left + ((right - left) >> 1);
if (nums[mid] == target){
return mid;
} else if (nums[mid] < target){
left = mid + 1;
} else if (nums[mid] > target){
right = mid - 1;
}
}

return -1;
}
}
  • 左闭右开
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 int binarySearch(int[] nums, int target){
if(target < nums[0] || target > nums[nums.length - 1]){
return -1;
}

int left = 0, right = nums.length;

while(left < right){
int mid = left + ((right - left) >> 1);

if (nums[mid] == target){
return mid;
} else if (nums[mid] < target){
left = mid + 1;
} else if (nums[mid] > target){
right = mid;
}
}
return -1;
}
}

循环不变量原则

注意右区间相关区别

  • 设置右起始值
  • 更新右值有区别
  • while条件的区别

27 移除元素

Leetcode 27

移除元素

数组元素删除过程复杂,因为数组长度不能更改,需要注意

实现

  1. 暴力解法
  • 查到val之后,把剩余元素用for loop向前覆盖一位
  • size自减1
  • i不自增,重新检查覆盖之后的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution{
public int removeElement(int[] nums, int val){
int size = nums.length;

for(int i = 0; i < size; i++){
if(nums[i] == val){
for(int j = i + 1; j < size; j++){
nums[j-1] = nums[j];
}

size--;
i--;

}
}

return size;
}
}
  1. 快慢同向指针
  • 如果快指针找到val,慢指针不变
  • 如果快指针没找到val,慢指针位置被快指针位置元素覆盖,慢指针向下移一位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution{
public int removeElement(int[] nums, int val){
int slowIndex = 0;

for (int fastIndex = 0; fastIndex < nums.length; fastIndex++){
if(val != nums[fastIndex]){
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}

return slowIndex;
}
}
  1. 相向双指针
  • 右指针移到不是val的位置
  • 右指针的val覆盖左指针是val的位置

需要两个while循环 查找右指针第一个指向不是val的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution{
public int removeElement(int[] nums, int val){
int left = 0, right = nums.length - 1;

while(right >= 0 && nums[right] == val) right--;

while(left <= right){
if (nums[left] == val){
nums[left] = nums[right--];
}

left++;

while(right >= 0 && nums[right] == val) right--;
}

return left;
}
}
  • 直接覆盖右指针的值,不需要判断右指针的值

不需要两个while循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution{
public int removeElement(int[] nums, int val){
int left = 0, right = nums.length - 1;

while(left <= right){
if(nums[left] == val){
nums[left] = nums[right];
right--;
} else {
left++;
}
}

return left;
}
}

记得更新右指针的位置

0%