leetcode算法练习
24.两两交换链表中的节点
思路&注意点:
1. 创建虚拟头结点来使得操作头结点和非头结点一致
2. 当前指针cur指向哪里 那么就可以操作他所指的后两个元素 例如:1-2-3-4-5-null 那么cur指向2的时候 可以操作3 4这两个结点 即cur指针一定要指向要反转两个节点的前一个结点 弄清这一点才能知道什么时候遍历结束
3. 对于while (prev.next != null && prev.next.next != null)中 条件的书写顺序 必须要这样写 不能倒过来写成 prev.next.next != null && prev.next != null 因为如果倒过来 prev.next如果为空 再取.next会引起空指针异常 并且要用&& 而不是|| 因为奇数节点就不需要交换了 所以只有满足后面有偶数个节点的时候才会进入循环
步骤:
方法:
/**
* Definition for singly-linked list.
* 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; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null) {
return null;
}
// 创建虚拟头结点
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode cur = dummyHead;
// 为偶数结点 cur.next == null遍历结束
// 为奇数结点 cur.next.next == null遍历结束 奇数节点最后一个就不需要交换了 所以只有满足后面有偶数个节点的时候才会进入循环
while (cur.next != null && cur.next.next != null) {
ListNode temp = cur.next;
ListNode temp1 = cur.next.next.next;
cur.next = cur.next.next;
cur.next.next = temp;
temp.next = temp1;
cur = cur.next.next;
}
return dummyHead.next;
}
}
19.删除链表的倒数第N个节点
思路&注意点:
1. 关键在于找到倒数第n个结点
2. 删除一个结点 那操作指针一定要移动到该结点的前一个结点
3. 定义两个指针:一个快指针 一个慢指针 快指针先移动n步 而后快慢指针一起移动 直到快指针指向了空结点null 那么此时慢指针就指向了要删的那一个结点 可以进行删除操作
但是!!! 由2所说——慢指针应该指向要删除结点的前一个结点 故快指针应该移动n + 1步!!!这样当快指针指向空结点null的时候 慢指针指向了要被删除元素的前一个元素
/**
* Definition for singly-linked list.
* 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; }
* }
*/
/**
* Definition for singly-linked list.
* 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; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode();
dummyHead.next = head;
ListNode fast = dummyHead;
ListNode slow = dummyHead;
n++;
while (n-- != 0 && fast != null) {
fast = fast.next;
}
// 如果上面没有n++ 那么当n大于链表长度的话 那么fast已经指向空了 那么fast = fast.next; 就是对快指针进行一个空指针的操作了
// fast = fast.next;
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummyHead.next;
}
}
面试题 02.07.链表相交
思路&注意点:
1. 求出两个链表的长度 并求出两个链表长度的差值 然后让curA移动到 和curB末尾对齐的位置
2. 再比较curA和curB是否相同 如果不相同 同时向后移动curA和curB 如果遇到curA == curB 则找到交点 否则循环退出返回空指针
3. 比较的是不是数值相等 而是指针相等 因此 应该是 if (curA == curB)
不要把值和节点的概念混淆起来 节点是一个实例 占用一块空间 值只是它的成员变量 值怎样和节点本身没有任何关系 一个实例只由它的地址唯一确定
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int lenA = 0, lenB = 0;
// 求链表A的长度
while (curA != null) {
lenA++;
curA = curA.next;
}
// 求链表B的长度
while (curB != null) {
lenB++;
curB = curB.next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头 lenA为其长度
if (lenB > lenA) {
//1. swap (lenA, lenB);
int tmpLen = lenA;
lenA = lenB;
lenB = tmpLen;
//2. swap (curA, curB);
ListNode tmpNode = curA;
curA = curB;
curB = tmpNode;
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap-- > 0) {
curA = curA.next;
}
// 遍历curA 和 curB 遇到相同则直接返回
while (curA != null) {
if (curA == curB) {
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
}
142.环形链表II
思路&注意点:
1. 判断是否有环
2. 找到环的入口
3. 判断链表是否有环 利用快慢指针来判断 如果两个指针相遇就意味着有环
快指针每次走2格 慢指针每次走1格
如何先到利用快慢指针?
因为如果是一条直线(没有环) 那么快慢指针永远不会相遇
如果有环 快慢指针一定会环内相遇 快指针进入环后慢指针进入环 快指针其实就是追慢指针的一个过程 快指针对于慢指针移动来说 每次移动一个结点
4. 快指针和慢指针相遇 一定是快指针去追慢指针 故而 快指针肯定至少在环里转了一圈了(因为如果快指针没有转一圈 怎么去追慢指针呢?)
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
// 因为是一次走两步 因此还要判断fast.next != null
while (fast != null && fast.next != null) {
// 慢指针走一步
slow = slow.next;
// 快指针走两步
fast = fast.next.next;
// 快慢指针相遇 代表有环
if (slow == fast) {
// 相遇的点
ListNode index1 = fast;
// 从头开始
ListNode index2 = head;
// 两个指针 从头结点和相遇结点 各走一步 直到相遇 相遇点即为环入口
while (index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}