目录
一、判断链表中是否有环
1.1 题目
1.2 题解
思路:快慢指针,快指针一次走两步,慢指针一次走一步,如果链表中有环,两个指针一定会在环中相遇
代码:
public boolean hasCycle(ListNode head) {
if(head==null) return true;
ListNode slow=head;
ListNode fast=head;
while(fast!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(slow==fast){
return true;
}
}
return false;
}
二、链表中环的入口节点
2.1 题目
2.2 题解
思路:
结论:一个指针从链表头开始走,另一个指针从相遇点开始走,二者将在入口点相遇
代码:
public ListNode EntryNodeOfLoop(ListNode pHead) {
if(pHead==null) return null;
ListNode fast=pHead;
ListNode slow=pHead;
while(fast!=null && fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
break;
}
}
if(fast==null || fast.next==null) return null;
while(pHead!=slow){
pHead=pHead.next;
slow=slow.next;
}
return pHead;
}
三、链表中倒数最后k个节点
3.1 题目
3.2 题解
思路:快慢指针,让快指针走k步,再让两个指针一起走,当快指针走到尾时,慢指针走到倒数第k个节点
代码:
public ListNode FindKthToTail (ListNode pHead, int k) {
ListNode slow=pHead;
ListNode fast=pHead;
while(k>0 ){
if(fast!=null){
fast=fast.next;
}else {
slow=null;
}
k--;
}
while(fast!=null){
slow=slow.next;
fast=fast.next;
}
return slow;
}
四、删除链表的倒数第n个节点
4.1 题目
4.2 题解
思路:快慢指针, 找到要删除节点的前一个节点,然后将要删除的节点删除即可
具体步骤:
step1:定义傀儡节点dummy,dummy.next=head;
step2:slow和fast起出都指向傀儡节点
step3:fast指针从dummy开始,向后走n+1步
step4:slow和fast一起向后走,当fast为null时,slow指向的是要删除节点的前一个位置
step5:删除倒数第n个节点
代码:
public ListNode removeNthFromEnd (ListNode head, int n) {
ListNode dummy=new ListNode(-1);
dummy.next=head;
ListNode slow=dummy;
ListNode fast=dummy;
for(int i=0;i<=n;i++){
if(fast!=null){
fast=fast.next;
}else {
slow=null;
}
}
while(fast!=null){
slow=slow.next;
fast=fast.next;
}
slow.next=slow.next.next;
return dummy.next;
}
五、两个链表的第一个公共结点
5.1 题目
5.2 题解
思路:双指针法,cur1从pHead1开始,cur2从pHead2开始,cur1和cur2每次都走一步,当cur1走到null的时候,就从cur2开始,当cur2走到null的时候,就从cur1开始,这样的遍历过程,能够保证cur1和cur2同时到达第一个公共节点,因为cur1和cur2遍历过的节点数是相等的,即使题中给的两个链表没有公共节点,cur1和cur2也会同时指向空节点
代码:
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode cur1=pHead1;
ListNode cur2=pHead2;
while(true){
if(cur1==cur2){
break;
}
if(cur1==null){
cur1=pHead2;
}else {
cur1=cur1.next;
}
if(cur2==null){
cur2=pHead1;
}else {
cur2=cur2.next;
}
}
return cur1;
}
六、链表相加(二)
6.1 题目
6.2 题解
思路:先将两个链表反转,然后使用两个指针遍历两个链表,将两个链表的对应位相加,相加得到的值用来构造新节点,同时用tmp保存进位,加到下一位上
代码:
public ListNode reverse(ListNode head){
if(head==null) return null;
if(head.next==null) return head;
ListNode pre=null;
ListNode cur=head;
while(cur!=null){
ListNode curNext=cur.next;
cur.next=pre;
pre=cur;
cur=curNext;
}
return pre;
}
public ListNode addInList (ListNode head1, ListNode head2) {
if(head1==null) return head2;
if(head2==null) return head1;
ListNode pHead1=reverse(head1);
ListNode pHead2=reverse(head2);
ListNode dummy=new ListNode(-1);
ListNode cur=dummy;
int val=0;
int tmp=0;
while(pHead1!=null || pHead2!=null){
val=tmp;
if(pHead1!=null){
val+=pHead1.val;
pHead1=pHead1.next;
}
if(pHead2!=null){
val+=pHead2.val;
pHead2=pHead2.next;
}
tmp=val/10;
cur.next=new ListNode(val%10);
cur=cur.next;
}
if(tmp>0){
cur.next=new ListNode(tmp);
}
return reverse(dummy.next);
}
七、单链表的排序
7.1 题目
7.2 题解
思路:归并排序
具体步骤:
step1:当链表为空或只有一个节点时,直接返回当前节点
step2:使用快慢指针的方式,找到当前链表的中间节点mid和中间节点的前一个节点midPre,从midPre位置与原链表断开
step3:原问题转化成了先对head到midPre 和 mid到fast(尾节点) 进行排序,然后再将者两段合并的子问题,使用递归解决
代码:
ListNode merge(ListNode pHead1, ListNode pHead2) {
if(pHead1==null) return pHead2;
if(pHead2==null) return pHead1;
ListNode dummy=new ListNode(-1);
ListNode cur=dummy;
while(pHead1!=null && pHead2!=null){
if(pHead1.val<pHead2.val){
cur.next=pHead1;
pHead1=pHead1.next;
}else {
cur.next=pHead2;
pHead2=pHead2.next;
}
cur=cur.next;
}
if(pHead1!=null){
cur.next=pHead1;
}
if(pHead2!=null){
cur.next=pHead2;
}
return dummy.next;
}
public ListNode sortInList (ListNode head) {
if(head==null || head.next==null) return head;
ListNode midPre=null;
ListNode mid=head;
ListNode fast=head;
while(fast!=null && fast.next!=null){
midPre=mid;
mid=mid.next;
fast=fast.next.next;
}
midPre.next=null;
return merge(sortInList(head), sortInList(mid));
}
八、判断一个链表是否为回文结构
8.1 题目
8.2 题解
思路:先找到链表的中间节点,然后将后半部分进行反转后与前半部分比较,遇到不相等的节点直接返回false
代码:
public ListNode reverse(ListNode head){
ListNode pre=null;
ListNode cur=head;
while(cur!=null){
ListNode curNext=cur.next;
cur.next=pre;
pre=cur;
cur=curNext;
}
return pre;
}
public boolean isPail (ListNode head) {
ListNode slow=head;
ListNode fast=head;
while(fast!=null && fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
ListNode head1=reverse(slow);
while(head!=null && head1!=null){
if(head.val!=head1.val){
return false;
}
head=head.next;
head1=head1.next;
}
return true;
}
九、链表的奇偶重拍
9.1 题目
9.2 题解
思路:准备四个指针,oddHead指向奇数链表的头,odd用来遍历奇数链表,even执行偶数链表的头,evenHead指向偶数链表的头,遍历结束时,odd指向的是奇数链表的最后一个节点,因此让odd.next=evenHead,这样就将整个偶数链表连接在了整个奇数链表的后面
代码:
public ListNode oddEvenList (ListNode head) {
if(head==null) return null;
ListNode odd=head;
ListNode oddHead=head; //奇数头
ListNode evenHead=head.next; //偶数头
ListNode even=head.next;
while(odd!=null && odd.next!=null && even!=null && even.next!=null){
ListNode oddNext=odd.next.next;
ListNode evenNext=even.next.next;
odd.next=oddNext;
even.next=evenNext;
odd=oddNext;
even=evenNext;
}
odd.next=evenHead;
return oddHead;
}