1 Linked List
P21. Merge Two Sorted Lists
关键点
Linked List的初始化和更新(new 和 list.next的关系)
//定义一个链表的头和尾,并都初始化为null
ListNode head=null,tail=null;
//再循环里需要每次更新时,进行如下操作
if(head==null){
head=new ListNode(nval);
tail=head;
}else{
tail.next=new ListNode(nval);
tail=tail.next;
}
易错点
循环条件是||而不是&&
可以这么想:停止循环的时候是两个list都为空,才不连了
所以下次想不清楚循环条件的时候可以反过来想停止循环的条件
while(l1!=null||l2!=null)
P19. Remove Nth Node From End of List
关键点
- 不知道Linked List长度的情况下,很难找到倒数第N个元素,所以一个思路就是先得到长度,再计算出要删的点是从头数第几个,将它前后的ListNode相连
- 要先设置一个空节点dummy,为了处理删掉的点是head的情况(所以最后return的是dummy.next而不是head)
易错点
统计sum(总共几个ListNode)和计算是从头数第几个,用一个栗子试试看。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy=new ListNode(0);
dummy.next=head;
ListNode first=head;
int sum=0;
while(first!=null){
first=first.next;
sum++;
}
System.out.println(sum);
sum=sum-n;
first=dummy;
while(sum>0){
first=first.next;
sum--;
}
first.next=first.next.next;
return dummy.next;
}
}
P24. Swap Nodes in Pairs
关键点
-
设置一个dummy空节点,来避免head移动影响结果的情况
-
比如1->2->3->4 先考虑交换前2个数,然后交换后2个数基本和之前一致
图解见https://blog.csdn.net/qq_34269988/article/details/89492526?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
步骤
1 断开1->2,连接1->3
2 断开2->3, 连接 2 ->1
3 断开dummy->1,连接dummy->2
这个过程中dummy,1,2变量分别对应pre, first, second -
之后变成dummy->2->1->3->4, 交换3,4时 pre移动到1,即代码里的first
易错点
while的边界条件:要交换的first和second都存在,只存在first或者都不存在就不用交换
code
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummy= new ListNode(0);
dummy.next=head;
ListNode pre=dummy;
while(pre.next!=null&&pre.next.next!=null){
ListNode first=pre.next;
ListNode second=pre.next.next;
first.next=second.next;
second.next=first;
pre.next=second;
pre=first;
}
return dummy.next;
}
}
2 Two Pointers
P26. Remove Duplicates from Sorted Array
关键点
题目比较简单,但有2个比较特殊的地方
- 不能使用别的数组,所以所有的操作都在输入数组实现,JAVA并不方便改变数组大小,所以最后的结果其实是数组的一部分
- 最后的返回值很巧妙,返回的是int类型表示数组长度,结果却是一个数组(相当于初始位置根据数组长度寻址)
易错点
i 表示现在修改的位置,所以返回长度时要+1
P27. Remove Element
易错点
为什么本题最后返回的是i而不是i+1
因为每次给nums[i]赋值之后,都给i+1,所以最后返回的 i 是现在修改的位置+1,即最后要返回的长度。
public int removeElement(int[] nums, int val) {
int i = 0;
for (int j = 0; j < nums.length; j++) {
if (nums[j] != val) {
nums[i] = nums[j];
i++;
}
}
return i;
}
3 Binary Search
P29. Divide Two Integers
关键点
- 根据被除数和除数的正负决定结果的正负,然后都转化为正数计算(想到)
- 如果每次使用lef-divisor,如果dividend较大,会导致超时
解决方法:每次不是-divisor,而是利用二进制,比如29/8=3,3=2+1
29(lef)除以8,8(div)左移一位,16(t)小于29,再左移一位,超过29(跳出while (lef >= (t << 1))循环),于是29(lef)减去之前的16,res加上2(即p的值,左移一位等于扩大两倍)。第二次循环时因为此时的13小于8的二倍,故加上1,整个循环结束,最终结果为2+1=3,符合要求。
易错点
- 这个问题的边界条件:[-2的31次,2的31次-1],所以如果直接使用int ,将负数dividend取绝对值时会出错,所以先把dividend类型变成长整型,再取绝对值
- 最后对结果做一个判断,如果越界,返回特殊值;不然返回转换回int的值
关键代码
long lef=Math.abs((long)dividend);
long div=Math.abs((long)divisor);
while (lef >= div) {
long t = div, p = 1;
while (lef >= (t << 1)) {
p <<= 1;
t <<= 1;
}
res += p;
lef -= t;
}
4 Math
周赛 P1390. 四因数
关键点
- 一个数有四个因数分为两种情况:1 这个数能分解成2个素数的乘积(这两个素数不相等) 2 这个数是素数的立方数
- 素数筛子:完成第一点判断的关键是得到1到max(输入数组nums的最大值)-1中的全部素数,应用普通循环判断容易超时,使用素数筛子来提高程序运行效率(具体思路见code注释)
易错点
举一些特别栗子来说明
- 9,因数{1,3,9},所以分解成2素数的积后,还要判断不相等
- 8,因数{1,2,4,8},8=2 * 4,4不是素数,但4=2 * 2,不会增加新因数,即8是2的立方数,满足四因数条件
代码和注释
class Solution {
public int sumFourDivisors(int[] nums) {
if(nums.length==0)
return 0;
Arrays.sort(nums);
//获取最大的数mnum,1到mnum-1即为生成素数的范围
int mnum=nums[nums.length-1];
int res=0;
//该数组表示是否为素数,初始全为1(当全是素数),后续把一些筛掉变成0
int[] isPrim=new int[mnum];
for(int i=0;i<mnum;i++)
isPrim[i]=1;
//素数筛子
for(int i=2;i*i<mnum;i++){
for(int j=i*i;j<mnum;j+=i){
isPrim[j]=0;
}
}
for(int num:nums){
for(int i=2;i<=Math.sqrt(num);i++){
if(isPrim[i]==0) continue;//合数则跳过
if(num%i==0){
//对应关键点1中的两种情况
if((isPrim[num/i]==1&&num!=i*i)||num==i*i*i){
res+=1+num+i+num/i;
}
//break的解释:一个数等于两个因数的积时,就能判断是否是四因数
break;
}
}
}
return res;
}
}