所用代码 java
两两交换链表中的节点 LeetCode 24
题目链接:两两交换链表中的节点 LeetCode 24 - 中等
思路
本题我的思路是错误的,没有使用虚拟头结点。正确的做法就是要使用虚拟头结点,具体的过程如下代码随想录里面,图片还是比较直观,总分三步走。
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode cur = dummy;
// 保证了不管是奇数个结点还是偶数个结点都不会存在空指针
while (cur.next != null && cur.next.next != null){
// 步骤一结点不用保存,因为cur已经指向他了
// 记录临时结点 步骤二结点,让后向结点指向前向结点
ListNode p1 = cur.next;
// 记录临时结点 步骤三结点 下一次开始位置
ListNode p2 = cur.next.next.next;
// 步骤1:cur指针指向交换结点的后向结点
cur.next = cur.next.next;
// 步骤2:让后向结点指向前向结点
cur.next.next = p1;
// 步骤3:交换后链接链表
cur.next.next.next = p2;
// 自己移动两位,处于下一次交换的前面
cur = cur.next.next;
}
return dummy.next;
}
总结
除了图上已知的三步走以外,另外还有一个重要的细节就是我们需要构建两个结点来保存后面会用到的链表,否则前面的指针移动过后,后面的结点就找不到下一结点的位置。
删除链表的倒数第N个节点 LeetCode 19
题目链接: 删除链表的倒数第N个节点 LeetCode 19 - 中等
思路
该题重要的是找到待删除结点的前向结点
才可以进行删除。
我们假链表长度为k,倒数第n个结点就是正数第n-k+1个结点,但是我们要找的是正数第n-k
位置结点,也是待删除结点的前向位置。
public ListNode removeNthFromEnd(ListNode head, int n) {
// if (head == null) return null; 题目说明size至少为一
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode p1 = dummy.next;
ListNode p2 = dummy;
int size = 0; // 链表长度
while (p1 != null){
size++;
p1 = p1.next;
}
// System.out.println("size: "+size);
int index = size - n; // 找到待删除结点的前向结点
while (index > 0){
p2 = p2.next;
index--;
}
// System.out.println("p2 --"+p2.val);
p2.next = p2.next.next;
return dummy.next;
}
总结
本题我的想法和代码随想录不一样,但是最终实现差不多,但是我觉得我的更容易理解一点。这题只要运用反向的思想就能一下子想出来如何解答。
链表相交
题目链接:链表相交 LeetCode - 简单
思路
链表A 和链表B 右端对齐,长链表保证开始位置从短链表上方开始,这就保证了两个链表尾部的一致性,若出现相等的情况则链表相交。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p1 = headA;
ListNode p2 = headB;
int sizeA = 0;
int sizeB = 0;
int n = 0;
while (p1 != null){
sizeA++;
p1 = p1.next;
}
while (p2 != null){
sizeB++;
p2 = p2.next;
}
// 因为p1 和 p2 已经移动过了,所以要从新指向头结点
p1 = headA;
p2 = headB;
if (sizeA >= sizeB){
// 差值为A比B多的部分,多几位就移几位
n = sizeA - sizeB;
while (n > 0){
// System.out.println("p1每次移动时的值:"+p1.val);
// p1移动的和B右对齐的地方
p1 = p1.next;
n--;
}
while (p1 != null){
// 开始判断,若无,都指向最后的null 退出
if (p1 != p2){
p1 = p1.next;
p2 = p2.next;
}else {
return p1;
}
}
}else {
n = sizeB - sizeA ;
// System.out.println(sizeA +" "+ sizeB +" " +n);
while (n > 0){
// System.out.println("p2每次移动时的值:"+p2.val);
// p2移动的和A右对齐的地方
p2 = p2.next;
n--;
}
// System.out.println("p2 " + p2.val);
while (p1 != null){
// 开始判断,若无,都指向最后的null 退出
if (p1 != p2){
p1 = p1.next;
p2 = p2.next;
}else {
return p1;
}
}
}
return null;
}
总结
1、这道题只要想到就非常的简单,就是对齐-找起始的位置-判断
三步解决,更注重的是一些细节,自己把图画出来,然后推演几步的画就非常的清晰明了。
2、自己经常在一些不该犯错的地方一直犯错,比如有if没有else
后面的逻辑又会进行下去导致程序出错,下次要注意,细节决定成败。
环形链表II
题目链接: 环形链表II LeetCode 142 - 中等
思路
环形链表通常使用快慢指针的方法。快指针fast每次走两步,慢指针slow每次走一步
,若有环的话快慢指针一定会相遇,无环则不会相遇。寻找环的起点前提是链表有环,如图我们知道相遇的时候,快指针走的距离是 x + y + n(z + y),慢指针走的距离是x + y
。假如快指针再多走z的距离就到了环的起点,即距离为:x + y + n(z + y) + z = y + (n + 1)(z + y),
后面的可以看成n圈,而我们从头结点出发再经过x的距离就可以到达环的起点,由此可以得出x = y
,头结点和快指针以相同的速度(一次走一步)前行一定会在环的起点相遇。
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
// 条件只用看快指针就行了,慢指针总落后与快指针
while (fast != null && fast.next != null){
// 快指针走两步,慢指针走一步
fast = fast.next.next;
slow = slow.next;
// 快慢指针相遇,有环
if (fast == slow){
ListNode p = head;
// 头结点定一个指针与快指针都一次走一步,相遇则为环的入口
while (fast != p){
fast = fast.next;
p = p.next;
}
return fast;
}
}
return null;
}
总结
这题考察的是快慢指针的使用,我之前看的时候就研究过所以没花多长时间就做出来了。只要我们记住上面那幅图,做题的时候画出来这幅图就可以非常轻松的解答。