leetcode学习二之链表专题
1.删除链表中的节点(leetcode 237 S.)
题目描述:请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表 – head = [4,5,1,9],它可以表示为:
示例 1:
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:
输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
说明:
- 链表至少包含两个节点。
- 链表中所有节点的值都是唯一的。
- 给定的节点为非末尾节点并且一定是链表中的一个有效节点。
- 不要从你的函数中返回任何结果。
题目中给的函数形式是 **public void deleteNode(ListNode node)**然而给的node正是我们要删除的node,我们都知道删除一个链表的节点是需要找到该链表的前驱的
但是本题给了限制条件所有给定的节点的值都是唯一的这样可以想个办法把要删除节点后面的值赋值给要删除节点,相当于把删除元素后一个节点复制了一份这样我们可以去删除后面的节点来完成任务
示例:对于给定链表[4,5,1,9]我们要删除5这个节点就可以把把值为5的结点变成值为1的节点
[4,1,1,9]然后再把后面的1给删除就得到了[4,1,9]
代码:
public void deleteNode(ListNode node) {
if(node.next != null){
node.val = node.next.val;
}
node.next = node.next.next;
}
2.删除链表中倒数第N个节点(leetcode 19 M.)
题目描述:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
本题需要保证n是有效的
方法一:
遍历链表求出链表的长度n,然后判断n是否满足 1 <= n <= len若不满足抛出异常
然后再扫描一边链表扫描到倒数第n + 1个执行删除操作
代码:
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode p = head;
int len = 0;
while(p != null){
++len;
p = p.next;
}
//删除头
if(len == n){
return head.next;
}
p = head;
for(int i = 1; i < len - n;++i){
p = p.next;
}
p.next = p.next.next;
return head;
}
但是面试官一般只会让我们扫描一次链表就完成删除操作
方法二:
我们可以定义两个指针p和q
p先遍历一遍走n步,然后q在移动,保持p和q中间始终相差n,那么当p移动到最后的时候,q指向的位置就是要删除的元素
代码:
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode p = head;
ListNode q = head;
ListNode prep = head;
while(n-- > 0){
q = q.next;
}
while(q != null){
prep = p;
p = p.next;
q = q.next;
}
//删除头
if(p == head){
p = p.next;
return p;
}
//否则
prep.next = p.next;
return head;
}
3.反转链表(leetcode 206 S.)
题目描述:反转一个链表
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
方法一:
申请一个空间newHead然后将链表中的节点逐个使用头插法插入到newHead中,达到反转的效果
代码:
public ListNode reverseList(ListNode head) {
if(head == null){
return head;
}
//申请节点作为链表的头节点
ListNode newHead = new ListNode(0);
ListNode p = head;
while(p != null){
ListNode q = p.next;
p.next = newHead.next;
newHead.next = p;
p = q;
}
//返回newHead.next
return newHead.next;
}
面试官一般会要求原地反转链表
方法二:
首先判断链表是否为空,为空则返回
1.定义p指向head.next
2.让head.next值为null
3.遍历p,定义q指向p.next(防止数据丢失),p.next指向head,更新head为p,p指向q
4.返回head即可
代码:
public ListNode reverseList(ListNode head) {
if(head == null)
return head;
ListNode p = head.next;
head.next = null;
ListNode q;
while(p != null){
//q保存p的后继
q = p.next;
//p.next指向head
p.next = head;
//更新head为p
head = p;
p = q;
}
return head;
}
方法三:递归实现
对于链表1->2->3->4->5->NULL
如果head为空或者head.next为空那么直接返回head即可
要反转head可以先反转head.next,然后再将head.next指向head
要反转head.next可以反转head.next.next,然后再将head.next.next指向head.next
。。。
如此递归下去
终止条件:
若最后只剩下两个数了也即head.next.next == null,反转两个数容易
只需要将head.next.next = head然后再将head.next置为空(防止出现环)
代码:
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null)
return head;
ListNode p = reverseList(head.next);
head.next.next = head;
head.next = null;
return p;
}
4.环形链表(leetcode)
题目描述:给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
如果一个链表有环我们遍历它会陷入死循环,如果没有环肯定会遍历完毕
这个其实可以类比我们跑步
一个跑的快的人和一个跑的慢的人一起跑步,如果是在一条没有环的直线上跑,跑的慢的是不可能与跑的快的人相遇,反之,如果在一个环形的跑道上,跑的快的人可能会超过跑的慢的人一圈进行相遇
类比跑步的情况,我们可以定义一个快指针fast和一个慢指针low。让fast每次移动两步,让low每次移动一步,如果有环他们总会相遇(网上有相应的证明,fast如果移动三步就不能保证一定相遇了)
代码实现:
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null){
return false;
}
ListNode fast = head;
ListNode lower = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
lower = lower.next;
if(fast == lower){
return true;
}
}
return false;
}
ListNode fast = head;
ListNode lower = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
lower = lower.next;
if(fast == lower){
return true;
}
}
return false;
}