双指针:
167. 两数之和(e)
633. 两数平方和(e)
345. 反转字符串中的元音字符(e)
重点:HashSet的应用,在HashSet中查找的复杂度是O(1)
680. 回文字符串(e)
重点:因为这道题不是简单判断回文串,由于可以删一个字符(且仅能最多删一个),所以用另一个函数helper,普通的时候就一直l++,r--;第一次遇到不相等,就执行l++或者r--(删除一个字符),而且这个只会执行一次,所以直接另外写一个函数即可
88. 合并两个有序数组(e)
重点:最后
System.arraycopy的参数:(src,srcPos,des,desPos,length)
将src数组中从srcPos开始的长为length的元素复制到des数组中从desPos开始的位置。
141. 判断链表是否有环(e)
重点:一开始要判断head 和 head.next都不能为空!!!!
两种方式:while(fast != slow) 或者 while(fast != null && fast.next != null)
524. 通过删除字母匹配到字典里最长单词(m)
public String findLongestWord(String s, List<String> d) {
String longestWord = "";
for (String target : d) {
int p1 = longestWord.length();
int p2 = target.length();
// longestWord.compareTo(target)即longest在target之前,不用再考虑target
if (p1 > p2 || (p1 == p2 && longestWord.compareTo(target) < 0)) {
continue;
}
// 否则,判断是不是为可能的子串
if (helper(s,target)) {
longestWord = target;
}
}
return longestWord;
}
private boolean helper(String s, String target) {
int i = 0, j = 0;
while (i < s.length() && j < target.length()) {
if (s.charAt(i) != target.charAt(j)) {
i++;
}
else {
i++;
j++;
}
}
if (j == target.length()) {
return true;
}
return false;
}
排序:
215. 数组中第k大的元素(m)
1)快排
重点:java中Random.nextInt(bound)是用来生成一个[0,bound)之间的随机数(左闭右开)
如果使用快排,为了避免最坏的情况退化成O(n2),选择pivot的时候不应该选择nums[left],而应该随机选择
if (end > start) {
Random random = new Random();
int randomIndex = start + 1 + random.nextInt(end-start);
swap(nums,start,randomIndex);
}
2)堆排序
priorityQueue优先队列默认是一个最小堆(就是堆顶元素是最小的,所以只需要维护一个大小为k的优先队列,最后取出堆顶元素(第k大的))
347. 第k个高频元素(m)
1)堆排序:重写compare方法,实现根据map.get(key)的小顶堆(堆顶元素最小)
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return map.get(o1) - map.get(o2);
}
}
);
2)桶排序:bucket的下标就是出现的次数。用一个list数组,list[i]表示出现i次的数字们,最后倒序遍历。
List<Integer>[] bucket = new List[nums.length+1];
for (int key : map.keySet()) {
int value = map.get(key);
if (bucket[value] == null) {
bucket[value] = new ArrayList<>();
}
bucket[value].add(key);
}
List<Integer> list = new ArrayList<>();
for (int i = bucket.length - 1; i >= 0 && list.size() < k; i--) {
if (bucket[i] == null) {
continue;
}
if (bucket[i].size() <= (k - list.size())) {
list.addAll(bucket[i]);
} else {
list.addAll(bucket[i].subList(0, k - list.size()));
}
}
451. 根据字符出现频率排序(m)
桶排序
StringBuilder sb = new StringBuilder();
for (int i = bucket.length - 1; i >= 0; i--) {
if (bucket[i] == null)
continue;
for (char c:bucket[i]
) {
int j = i;
while (j > 0) {
sb.append(c);
j--;
}
}
}
75. 颜色分类
重点:原地排序
1)两次扫描,第一次统计各个数字的个数;第二次直接重写数组
2)一次扫描,三个指针,left永远保存较小值,right永远保存较大值,还有i是当前遍历指针
//当前值==1时, cur++。不作处理.
//当前值==2时,将其和右指针的值交换,右指针--(右半部分是当前指针还未遍历到的地方,其值可能是0,1,2,所以当前cur指针不能++,当元素换过来后继续判断这个换过来的元素。)
//当前值==0时, cur++.将其和左指针交换.(左半部分是cur指针已经遍历过的,l指针其值只会是0/1)
public void sortColors(int[] nums) {
int left = 0;
int right = nums.length-1;
int i = 0;
while (i <= right) {
if (nums[i] == 1) {
i++;
}
// 做完下面这个判断,left一定为0,i可能为0也可能为1
else if (nums[i] == 0) {
if(left!=i) {
int temp = nums[left];
nums[left] = nums[i];
nums[i] = temp;
}
i++;
left++;
}
// 做完下面这判断,right一定是2,而由于有半部分没有被扫描过,所以可能是1,也可能是0,也可能是2,所以i不能--
else {
int temp = nums[right];
nums[right] = nums[i];
nums[i] = temp;
right--;
}
}
}
贪心策略:
455. 分发饼干(e)
435. 无重叠子区间(m)
重写排序,先得到最多的子区间个数,然后总数减去这个值就是需要erase的
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] - o2[1];
}
});
452. 用最少数量的箭弄破气球(m)
同上题,只是这题求的就是不同区间的数目
406. 根据审稿重建队列(m)
个子高的人看不到个子矮的人
核心思想:高个子先站好位,矮个子插入到K位置上,前面肯定有K个高个子,矮个子再插到前面也满足K的要求
- 排序:
- 按高度降序排列。
- 在同一高度的人中,按
k
值的升序排列。
Arrays.sort(people, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] != o2[0] ? (o2[0] - o1[0]) : (o1[1] - o2[1]);
}
});
- 逐个地把它们放在输出队列中,索引等于它们的
k
值。
for (int[] p:people
) {
res.add(p[1],p);
}
- 返回输出队列
121. 买卖股票最佳时机(e)
主要是:改min的时候一定要改max,因为max的出现必须在min之后,
if (prices[i] > max) {
max = prices[i];
}
else if (prices[i] < min) {
min = prices[i];
max = prices[i];
}
else {
continue;
}
122. 买卖股票最佳时机(e)
和上一题不同就是可以连续买入
对于 [a, b, c, d],如果有 a <= b <= c <= d ,那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,因此当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,那么就把 prices[i] - prices[i-1] 添加到收益中。
605. 种花问题(e)
53. 最大子序和(e)
655. 非递减序列(e)
数组元素个数大于3时,判断要修改哪一个数字(要么修改当前的,要么修改后一个,{4,2,3}要么修改4,要么修改2):
1. 一般修改当前数字是最好的方法,因为修改下一个数字的话,很可能影响到下下一个数字。但是,如果[3,4,2,5],当前数字是4的话,如果将4修改成2,那么你会发现,它(2)小于前一个数字3
2. 于是,我们在判断是修改当前数字还是下一个数字时,比较一下,下一个数字是否大于等于当前数字的前一个数字(nums[i+1] >= nums[i-1]),如果大于,那么即可放心修改。如果当前数字是第一个数字,即它前面没有数字的话,也是可以放心修改的。 nums[i] = nums[i+1];
5、如果下一个数字小于当前数字的前一个数字(nums[i+1] < nums[i-1]),那么将下一个数字修改成当前数字即可。 nums[i+1] = nums[i];
public boolean checkPossibility(int[] nums) {
int cnt = 0;
for (int i = 1; i < nums.length; i++) {
if (nums[i] >= nums[i-1]) {
continue;
}
cnt++;
// {3,4,2,5}
if (i - 2 >= 0 && nums[i-2] > nums[i]) {
nums[i] = nums[i-1];
}
// 4,2,3
else {
nums[i-1] = nums[i];
}
}
return cnt <= 1;
}
392. 判断子序列(e)
java indexOf():
-
public int indexOf(int ch): 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
-
public int indexOf(char ch, int fromIndex): 返回从 fromIndex 位置开始查找指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
public boolean isSubsequence(String s, String t) {
int i = -1;
for (char ch:s.toCharArray()
) {
i = t.indexOf(ch,i+1);
if (i == -1) {
return false;
}
}
return true;
}
763. 划分字母区间(m)
定义数组 last[char] 来表示字符 char 最后一次出现的下标。定义 anchor 和 j 来表示当前区间的首尾。如果遇到的字符最后一次出现的位置下标大于 j, 就让 j=last[c] 来拓展当前的区间。当遍历到了当前区间的末尾时(即 i==j ),把当前区间加入答案,同时将 start 设为 i+1 去找下一个区间。
public List<Integer> partitionLabels(String S) { int[] last = new int[26]; // 最后出现的位置 for (int i = 0 ;i < S.length();i++) { last[S.charAt(i) - 'a'] = i; } int start = 0, end = 0; List<Integer> res = new ArrayList<>(); for (int i = 0 ; i < S.length(); i++) { if (last[S.charAt(i) - 'a'] > end) { end = last[S.charAt(i) - 'a']; } if (i == end) { res.add(end-start+1); start = i + 1; } } return res; }