LeetCode 19
题目简析:删除倒数第n个的结点
思路分析:两种做法
第一种:先算出总长,然后遍历到指定位置来删除。
public ListNode removeNthFromEnd(ListNode head, int n) {
//先计算总长
int len = 0;
//虚拟头结点
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode fast = dummyHead;
ListNode slow = dummyHead;
while(fast.next != null){
len++;
fast = fast.next;
}
for (int i = 0; i < len-n; i++) {
slow = slow.next;
}
slow.next = slow.next.next;
return dummyHead.next;
}
第二种:双指针,若快指针指向head,就无需再挪动fast多一步,若跟慢指针一样指向虚拟头结点就要再挪动一步.
这里不懂为什么要将快指针移动多一步的朋友可以手动画个链表然后一起移动一下。
public ListNode removeNthFromEnd(ListNode head, int n) {
//双指针法----先让快指针移动n+1步,然后慢指针与快指针一起移动,直到快指针到链表尾部
//方便操作,设立虚拟结点用于返回
ListNode node = new ListNode(0);
node.next = head;
ListNode slow = node,fast = node;
//先让fast移动-----n减减保证移动步数,fast移动过程中是非空,而不是fast.next
while((n--)>0 && fast!=null){
//移动fast
fast = fast.next;
}
//再让fast移动一步
fast = fast.next;
//快慢指针一起动----终止条件,快指针指到了链表末尾
//此时可以发现慢指针的下一个结点就是要删除的结点
while(fast!=null){
fast = fast.next;
slow = slow.next;
}
//删除指定位置的结点
slow.next = slow.next.next;
return node.next;
}
LeetCode 142
题目简析:给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
思路分析:自己摸索了两种做法:
第一种:使用哈希表,若遍历过程中已经出现过了指定元素就直接返回
public ListNode detectCycle(ListNode head) {
//方法1:使用哈希表,若出现过了该元素就直接返回,时间复杂度较高
ListNode pos = head;
HashSet<ListNode> set = new HashSet<>();
while(pos!=null){
if(set.contains(pos)){
return pos;
}else {
set.add(pos);
}
pos = pos.next;
}
return null;
}
第二种:快慢指针法
public ListNode detectCycle(ListNode head) {
//方法2:双指针
//从头开始
ListNode fast = head;
ListNode slow = head;
while(fast!=null && fast.next!=null){
//因为快指针比较快,所以终止条件写上fast就可以了
//移动两个指针
slow = slow.next;
//让快指针每次比慢指针多移动一个节点
fast = fast.next.next;
//两个指针相遇
if(slow==fast){
//经公式推出来相遇点
ListNode l1 = head;
ListNode l2 = fast;
while(l1!=l2){
l1=l1.next;
l2=l2.next;
}
//返回环的入口
return l2;
}
}
return null;
}
LeetCode 24
题目简析:两两交换链表元素
思路分析:我做链表这类更改连接的题目的时候习惯先把原连接关系理清楚,然后取部分结点来操作。
这道题可以采用虚拟头结点,因为两两交换,此方法要更改三个节点的关系
所以while遍历的条件也很明显:
pre.next!=null && pre.next.next!=null
其余的就看代码注释即可
public ListNode swapPairs(ListNode head) {
//虚拟头结点做法
ListNode node = new ListNode(0);
//找到链表
node.next = head;
ListNode pre = node;
//进入循环---pre的下一个和下下不为空,因为是两两交换
while(pre.next!=null && pre.next.next!=null){
//搞清楚链接关系,想要两两交换
//那么就要得到待交换的两个结点以及两者前一个节点
//node指向第三个结点,pre指向第二个结点
//存储三个节点的第一个
ListNode temp = head.next;
//改变链接
//第一个链接
pre.next = head.next;
//第二个链接
head.next = temp.next;
//第三个链接
temp.next = head;
//更新三个节点
pre = head;
head = head.next;
}
return node.next;
}
面试题0207链表相交
题目简析:检查链表所相交的起始节点,若没有就返回null
思路分析:这是我在题解中看到的不错的方法,让我有所启发
设 A 不重合部分长c,B 不重合部分长b,重合部分长c,则有等式: (a+c)+b = (b+c)+a。 先假设a <= b,pa,pb为A、B的指针,每次走一格: 我们假设两个链表的构造如下: A: a1 a2 c1 c2 c3 B: b1 b2 b3 c1 c2 c3 我们不知道 a b c的值,不能直接求解; 所以需要借助模拟遍历,并通过特值来找答案: 我们尝试分别将B接到A后和A接到B后,得到链表如下: a1 a2 c1 c2 c3 b1 b2 b3 c1 c2 c3 b1 b2 b3 c1 c2 c3 a1 a2 c1 c2 c3 可发现在倒数第三节点处既是交点,则我们分别用两个指针遍历上述两个链表,并且指针值相同时即为c1交点。 不用真的链接两个链表,只用在pa到达结尾时跳转到B的开头(pb同理)即可。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null){
return null;
}
ListNode lA = headA;
ListNode lB = headB;
while(lA != lB){
//一旦出现lA == lB就说明开始出现相交,立马返回即可
lA = lA == null ? headB : lA.next;
lB = lB == null ? headA : lB.next;
}
return lA;
}