刷题复习(一)链表-双指针
https://labuladong.gitee.io/algo/di-ling-zh-bfe1b/shuang-zhi-0f7cc/
1、合并两个有序链表
思路清晰,双链表有个根节点记录开头
/**
* 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 mergeTwoLists(ListNode list1, ListNode list2) {
ListNode root = new ListNode();
ListNode temp = root;
while (list1 != null && list2 != null) {
if (list1.val > list2.val) {
temp.next = list2;
list2 = list2.next;
} else {
temp.next = list1;
list1 = list1.next;
}
temp = temp.next;
}
if (list1 == null) {
temp.next = list2;
} else {
temp.next = list1;
}
return root.next;
}
}
2、分隔链表
这题需要将链表断开,需要记录表头和表尾
/**
* 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 {
//输入:head = [1,4,3,2,5,2], x = 3
//输出:[1,2,2,4,3,5]
//示例 2:
//
//输入:head = [2,1], x = 2
//输出:[1,2]
public ListNode partition(ListNode head, int x) {
ListNode temp1 = new ListNode();
ListNode temp2 = new ListNode();
ListNode root1 = temp1;
ListNode root2 = temp2;
while (head != null) {
if (head.val < x) {
temp1.next = head;
temp1 = temp1.next;
} else {
temp2.next = head;
temp2 = temp2.next;
}
head = head.next;
}
temp1.next = root2.next;
temp2.next = null;
return root1.next;
}
}
3、合并K个升序链表
不难的hard,优先队列 PriorityQueu要学会使用,用优先队列就不难了
PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length,(o1,o2)->(o1.val-o2.val));
通过lambda方式构造小顶堆,默认就是小顶堆
/**
* 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 mergeKLists(ListNode[] lists) {
if(lists== null ||lists.length== 0){
return null;
}
ListNode root = new ListNode(0);
ListNode temp = root;
PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length,(o1,o2)->(o1.val-o2.val));
for(ListNode node: lists){
if(node != null){
pq.add(node);
}
}
while(!pq.isEmpty()){
ListNode cur = pq.poll();
temp.next = cur;
if(cur.next!=null){
pq.add(cur.next);
}
temp = temp.next;
}
return root.next;
}
}
4、删除链表的倒数第 N 个结点
这道题目有技巧,需要寻找倒数第k个节点
代码应该是如下
// 返回链表的倒数第 k 个节点
ListNode findFromEnd(ListNode head, int k) {
ListNode p1 = head;
// p1 先走 k 步
for (int i = 0; i < k; i++) {
p1 = p1.next;
}
ListNode p2 = head;
// p1 和 p2 同时走 n - k 步
while (p1 != null) {
p2 = p2.next;
p1 = p1.next;
}
// p2 现在指向第 n - k + 1 个节点,即倒数第 k 个节点
return p2;
}
完整解答,尤其需要注意测试用例会出现k个数删除倒数第k个,由于算法是寻找倒数k+1的所以需要用虚拟节点root节点去防止出现这种情况
/**
* 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 root = new ListNode(0);
root.next= head;
ListNode cur=help(root,n+1);
cur.next = cur.next.next;
return root.next;
}
public ListNode help(ListNode head ,int n){
ListNode temp1 = head;
for(int i=0;i<n;i++){
temp1= temp1.next;
}
ListNode temp2 = head;
while(temp1!=null){
temp2=temp2.next;
temp1=temp1.next;
}
return temp2;
}
}
5、单链表的中点
快慢指针可以轻松解决,注意增加fast.next不为空的判断
class Solution {
public ListNode middleNode(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
这个代码可以改成链表是否有环,有环的判断是快慢指针相遇
如果 fast
最终遇到空指针,说明链表中没有环;如果 fast
最终和 slow
相遇,那肯定是 fast
超过了 slow
一圈,说明链表中含有环。
boolean hasCycle(ListNode head) {
// 快慢指针初始化指向 head
ListNode slow = head, fast = head;
// 快指针走到末尾时停止
while (fast != null && fast.next != null) {
// 慢指针走一步,快指针走两步
slow = slow.next;
fast = fast.next.next;
// 快慢指针相遇,说明含有环
if (slow == fast) {
return true;
}
}
// 不包含环
return false;
}
6、环形链表 II
经典,先让俩个指针相交,再改变指向同时运动
class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast, slow;
fast = slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) break;
}
// 上面的代码类似 hasCycle 函数
if (fast == null || fast.next == null) {
// fast 遇到空指针说明没有环
return null;
}
// 重新指向头结点
slow = head;
// 快慢指针同步前进,相交点就是环起点
while (slow != fast) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
7、相交链表
我们可以让 p1
遍历完链表 A
之后开始遍历链表 B
,让 p2
遍历完链表 B
之后开始遍历链表 A
,这样相当于「逻辑上」两条链表接在了一起。
/**
* 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 tempA=headA;
ListNode tempB=headB;
while(tempA!= tempB){
if(tempA==null){
tempA=headB;
}else{
tempA =tempA.next;
}
if(tempB==null){
tempB=headA;
}else{
tempB =tempB.next;
}
}
return tempA;
}
}
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// p1 指向 A 链表头结点,p2 指向 B 链表头结点
ListNode p1 = headA, p2 = headB;
while (p1 != p2) {
// p1 走一步,如果走到 A 链表末尾,转到 B 链表
if (p1 == null) p1 = headB;
else p1 = p1.next;
// p2 走一步,如果走到 B 链表末尾,转到 A 链表
if (p2 == null) p2 = headA;
else p2 = p2.next;
}
return p1;
}
标签:head,ListNode,复习,val,fast,next,链表,刷题
From: https://www.cnblogs.com/wusier/p/17861831.html