文章目录
一.合并两个有序链表【力扣21】
题目表述
解法1、循环+双指针
- new一个结果链表,一个p节点
- 链表1、2谁小,p.next节点指向谁
- 谁空,p.next就指向另一个链表
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
ListNode result=new ListNode(0);
ListNode p=result;
while(l1!=null&& l2!=null){
if(l1.val<l2.val){
p.next=l1;
l1=l1.next;
}else{
p.next=l2;
l2=l2.next;
}
p=p.next;
}
if(l1==null) p.next=l2;
if(l2==null) p.next=l1;
return result.next;
}
解法2、递归
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
} else if (l2 == null) {
return l1;
} else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
二、删除有序列表的重复元素【力扣83】
题目表述
解法1、常规解法
- 创建一个p节点,让p=head.
- 如果p等于p.next,跳过p.next,让p.next=p.next.next
- 如果p不等于p.next,符合题意,p节点移动,p=p.next
- 返回head
public ListNode deleteDuplicates(ListNode head) {
if(head==null) return head;
ListNode p= head;
while(p.next!=null){
if(p.next.val==p.val){
p.next=p.next.next;
}else{
p=p.next;
}
}
return head;
// return p.next;//[1,2]
// return p.next.next;//[2]
// return p.next.next.next;//[]
}
解法2、递归
if(head == null||head.next==null) return head;
head.next=deleteDuplicates(head.next);
return head.val==head.next.val?head.next:head;
三、环形链表【力扣141】
题目表述
- 抓住主要题干:题目给出链表的头节点,判断链表是否有环
解法:快慢指针相遇问题
- 注意健壮性,除了head可能为空,还有构成环的因素
- 慢指针一步走一个节点,快指针一步走两个节点
- 如果相遇,就有环的存在,返回true
- 如果没有,跳出循环,返回false
public class Solution {
public boolean hasCycle(ListNode head) {
if(head==null) return false;
ListNode slow=head;
ListNode quick=head;
// 一个或者两个节点构不成环
while(quick.next!=null&&quick.next.next!=null){
slow=slow.next;
quick=quick.next.next;
if(slow==quick){
return true;
}
}
return false;
}
}
四、环形链表返回头节点【力扣142】
题目表述
- 在141的基础上,返回环的开头节点
解法
如果有环,就让慢指针重新回到开始位置,快慢指针速度均为1,快慢指针再次相遇就是环开头的位置
- 快慢指针第一次相遇是确定环
- 快慢指针第二次相遇是确定环开头的位置
public class Solution {
public ListNode detectCycle(ListNode head) {
Boolean logo=false;
if(head==null) return head;
ListNode slow=head;
ListNode quick=head;
while(quick.next!=null&&quick.next.next!=null){
slow=slow.next;
quick=quick.next.next;
if(slow==quick){
logo=true;
break;
}
}
if(logo==true){
slow=head;
while(slow!=quick){
slow=slow.next;
quick=quick.next;
}
return slow;
}
return null;
}
}
五、相交链表【力扣160】
题目表述
解法:相遇
- 长的链表,节点先移动到与短链表相同长度的位置
- 两个链表的节点在同时移动,如果相同,则相遇
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode ha=headA;
ListNode hb=headB;
int la=0;
int lb=0;
int cha=0;
// while循环,遍历出a,b链表的长度
while(ha.next!=null){
ha=ha.next;
la++;
}
while(hb.next!=null){
hb=hb.next;
lb++;
}
// 对比长度,长度长的链表,统一赋值给ha
if(la<lb){
ha=headB;
hb=headA;
cha=lb-la;
}else{
ha=headA;
hb=headB;
cha=la-lb;
}
// ha先移动到相同长度的位置
for(int i=0;i<cha;i++){
ha=ha.next;
}
while(ha!=null&&hb!=null){
// 如果移动到相同节点,也就是相交节点,就返回该节点
if(ha==hb){
return ha;
}
ha=ha.next;
hb=hb.next;
}
// 没有相遇,就返回空
return null;
}
}
大佬算法
作者:jyd
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/solution/mian-shi-ti-0207-lian-biao-xiang-jiao-sh-b8hn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// 双指针,A指针遍历A链表,再遍历链表B,B指针则反
ListNode A = headA, B = headB;
// 当相遇的时候,返回该时结点
while (A != B) {
// 如果已经遍历完毕,就去遍历另一个链表
A = A != null ? A.next : headB;
B = B != null ? B.next : headA;
}
return A;
}
}
六、反转链表【力扣206】
题目表述
解法
- 临时存储:先创建一个临时节点,用于存储p.next
- 改变指针方向:p的下一个节点方向指向前一个节点
- 改变右内容:代表前节点的pre,继续移动到p的位置
- 改变左内容:此时的p向后移动,等于原先p.next的值
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null) return head;
ListNode pre=null;
ListNode p=head;
while(p!=null){
// 1.先创建一个临时节点,用于存储p.next
ListNode temp=p.next;
// 2.p的下一个节点应该指向前一个节点
p.next=pre;
// 3.代表前节点的pre,继续移动到p的位置
pre=p;
// 4.此时的p向后移动,等于原先p.next的值
p=temp;
}
return pre;
}
}
六-1、从尾到头打印链表【剑指offer06】
题目描述
- 注意:返回的是整型数组
- 数组的长度是固定的,所以设置长度变量
解法
- 注意数组没有add语法
- temp2[i]=pre.val;
class Solution {
public int[] reversePrint(ListNode head) {
int[] temp1=new int[0];
int size=0;
if(head==null) return temp1;
ListNode pre=null;
ListNode p=head;
while(p!=null){
size++;
ListNode tt=p.next;
p.next=pre;
pre=p;
p=tt;
}
int[] temp2=new int[size];
for(int i=0;i<size;i++){
temp2[i]=pre.val;
pre=pre.next;
}
return temp2;
}
}
七、链表的中间结点【力扣876】
题目描述
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
解法
- 快慢指针,快指针速度为2,慢指针速度为1
class Solution {
public ListNode middleNode(ListNode head) {
ListNode slow=head;
ListNode quick=head;
while(quick!=null&&quick.next!=null){
slow=slow.next;
quick=quick.next.next;
}
return slow;
}
}
八、链表中倒数第k个结点【剑指offer22】
题目描述
- 注意:此题返回的是k以后的链表
解法
- 快慢指针,快指针和慢指针速度一致
- 快指针先走k步,这样慢指针就慢走K步
- 慢指针达到的位置就是倒数第k个结点
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode slow=head;
ListNode quick=head;
for(int i=0;i<k;i++){
quick=quick.next;
}
while(quick!=null){
quick=quick.next;
slow=slow.next;
}
return slow;
}
}
八-1、删除链表的倒数第k个结点,八的升级【力扣19】
题目描述
- 给定一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
解法
- 思路与上面一致
- 最后的连接,要跳过删除的结点
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head.next==null) return null;
ListNode slow=head;
ListNode quick=head;
for(int i=0;i<n;i++){
quick=quick.next;
}
if(quick==null) return head.next;
while(quick.next!=null){
quick=quick.next;
slow=slow.next;
}
slow.next=slow.next.next;
return head;
}
}
八-2、返回倒数第k个结点 八的升级【力扣面试题02.02】
题目表述
解法
- 思路和八一样,快慢指针
- 直接返回值即可,.val
class Solution {
public int kthToLast(ListNode head, int k) {
ListNode slow=head;
ListNode quick=head;
for(int i=0;i<k;i++){
quick=quick.next;
}
while(quick!=null){
slow=slow.next;
quick=quick.next;
}
return slow.val;
}
}
九、链表排序【力扣148】
题目表述
解法
- 归排或者快排,数组是元素组成,而链表是元素+方向,所以要把链表进行拆分
- 只要不断的比较链表的头结点的值,将较短的放入合并后的链表中,并更新头结点就可以了。
- 有位大佬提出虚拟结点,在此引用
public class Offer077 {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
// list 为链表后半段的头结点,在 splitList 函数中完成了将链表从中间截断的处理
ListNode list = splitList(head);
head = sortList(head);
list = sortList(list);
return mergeList(head, list);
}
private ListNode splitList(ListNode head) {
// 为链表增加一个虚拟节点
// 当 fast 达到尾端时,slow 刚好指向前半段链表的最后一个节点
ListNode virtualHead = new ListNode(-1, head);
ListNode fast = virtualHead, slow = virtualHead;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode result = slow.next;
slow.next = null;
return result;
}
private ListNode mergeList(ListNode head1, ListNode head2) {
// 增加虚拟头结点,可以统一操作
// 不用刻意考虑链表中存在空节点的情况,也不用特意增加判断合并后的链表的头结点是什么
ListNode virtualHead = new ListNode();
ListNode node = virtualHead;
ListNode node1 = head1, node2 = head2;
while (node1 != null && node2 != null) {
// 将两个链表中较小的加入到合并后的链表中
if (node1.val <= node2.val) {
node.next = node1;
node1 = node1.next;
} else {
node.next = node2;
node2 = node2.next;
}
node = node.next;
}
if (node1 != null) {
node.next = node1;
} else if (node2 != null) {
node.next = node2;
}
return virtualHead.next;
}
}
作者:huaLuoYueQue
链接:https://leetcode.cn/problems/7WHec2/solution/hua-luo-yue-que-fen-er-zhi-zhi-tu-jie-li-95mj/
来源:力扣(LeetCode)