1. 24 两两交换
这题用虚拟头节点来换。
1.1 tips
我的写法:
temp.next = cur;
cur.next = pre;
pre.next = cur.next;
temp = pre;
问题出在第三步,我的这个写法其实就是从头往后弄,0接到2,2接到1,1.next接到2.next,问题是我先把2的后面变了,所以第三步1.next就是1,所以会一直循环,
正确的方法是从后向前。先操作1.next,这样就不会出现死循环了
- 这题判断temp后面的两个元素存不存在的方法就是直接在循环的condition里写
(temp.next && temp.next.next)
,和判断链表为空用的(!head)
有异曲同工之妙。 - 这个和反转整个链表一样都是设了三个指针,只不过这个题的temp是用来移动,pre和cur用来存值(在while开头就
let pre =temp.next
,这样后面就算变temp.next,pre也不会变)和比较;- 而在反转整个链表的时候,temp是为了存值,pre和cur来移动。并且整个反转的时候没用头节点。
- js里的ListNode是对象形式的,所以是通过引用赋值,意思是:
如果让b.val =4,因为a和b指向的是同一个对象,所以这么改a也会改变,let a = b ={ val:2 }
如果不想这么改,就把a指向一个新的对象,比如:
类比let a = b = { val: 2 } a = { val: 3 }
pre
和temp.next
是一样的
pre = temp.next
指向的是同一个对象a,然后
temp.next = cur
就指向了另一个对象b,a的值当然不会被影响 - 引申下来,js的基本类型是值赋值,
let a = b=10; b=11;
b赋给了新值,a的值自然也不会改变。
2. 19删除链表的倒数第N个节点
2.1我的思路
找到最后一个->按n开始回头遍历->找到cur->用cur、pre、temp
后面更改思路,发现不用,知道链表长度sz后,让cur指针往后移动sz-n个节点就行了,该节点就是要删除的节点的前一个节点
const res = new ListNode(0,head);
let cur = res;
let sz = 0 ;
while(cur.next !== null){
cur = cur.next;
sz +=1;
}
cur =res;
if(n === sz){
cur.next = cur.next.next;
return res.next;
}
let count =0;
while(count < sz-n ){
// console.log(count,',',sz-n)
cur=cur.next;
count++;
}
cur.next = cur.next.next;
return res.next;
上步也多余了,把if那里可以删掉,因为头节点的引入就是为了处理删除首位的问题的,
2.2法2快慢指针法
让快指针先走n步,再同时移动快慢指针,当快指针指向null时,慢指针指的就是要删的元素,但是我们要删应该是停在他的前一位,所以让
快指针先走n+1步,再同时移动
抽象的理解方式是,我先让快的走n步,这样快的和慢的之间就差了n,所以当再同时移动之后,快的指的是null的时候,慢的就相当于倒数第n个,比如n=1,要的是倒数第一个,让fast先走1,当共同移动后当fast指向null的时候因为fast和slow的布长差1,自然slow指向的就是倒数第一个。
3. 160链表相交
3.1我的思路:
我想的是for套for,来找相同,并且相同的判定标准应该是val =val,.next =.next,然后想着既然是for套for能不能用双指针
3.2教的思路
重点是如果有相交节点,那后面的节点应该完全一样,所以把他们的尾端对其,两个链表起点各放一个指针,同时移动,如果找到相同节点或者都移动到结尾就返回
在js中,怎么算相同节点呢?前面一到题提到了,listnode在js里是用对象定义的,而对象又是引用赋值,所以:
在js中比较两个节点是不是同一个,不是比较他们的val和next是不是相同,而是用===直接比较节点,这个时候在比较的是对该对象的引用,也就是他们是不是同一个对象,是的话他们的后面自然相同
3.3 tips
- 教的代码里用了一段代码来交换节点,目的是为了确保A一定是更长的那个,也就是
[curA,curB] = [curB,curA]
这是ES6里出现的解构赋值。- 我刚听到这个的时候马上想着为什么没把这个直接用到交换节点的那道题上,答案是因为那个是在交换节点,而这个是在交换两个引用,并没有改变链表结构,如果交换节点的时候用结构赋值,就会出现环,因为只是改了引用,.next还是没改,node1的.next就是node2
4. 142.环形链表2
4.1我的思路
不知道怎么判断回来,我的思路是把遍历过的value存起来,但是如果有重复value呢?
4.2思路
1.怎么判断有环(fast和slow步长不同,有环会在环内相遇)
fast步长为2,slow步长为1。会在环内相遇是因为fast套圈了,fast比slow每次快一步,就会一个一个节点的靠近它(三个步长的话可能就跳过去了)画个图就懂了。
相遇的情况是,各再走一步就碰上了。
2.有环的话怎么找入口.
等式
2(x+y) = x+y+n(y+z)
因为x走的路乘2才是y走的路。最后要求的是X,把等式简化了之后得到
x=n(y+z)-y
但是这样看不出x和什么正数有关系,再简化成x=(n-1)(y+z)+z
.因为n是大于等于1的,也就是至少要转1圈和慢指针相遇,n=1时等式为:x=z
只是看等式还是有点抽象,看图,n=100和n=1是一样的,关键的是z的长度,只不过加了很多圈,但是如果从相遇点和head点各放一个指针,他们都会在同一个地方相遇
5.链表总结
什么时候用虚拟头节点:
当需要改变链表结构的时候,比如删除节点,调换节点,一个特例是调换整个链表,用的三个值法。如果用了虚拟头节点,返回值的问题
返回的应该是return dummyNode.next
,因为head指向的原本的第一个节点可能被删除或者移动,从而不再是第一个节点。排除空和只有一个节点的写法
if(!head || !head.next)
该节点的下一个节点不是null的写法
我老写成while(cur.next !==null)
因为担心直接写while(cur.next)
如果碰上=0怎么办,因为0也是falsy value,这就是纯纯的搞错了,因为cur.next是对象,而不是对象的.val,是不可能取到0的。