2021-10-18剑指offer-链表篇

· JZ6 从尾到头打印链表

题目地址
描述
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。

如输入{1,2,3}的链表如下图:
在这里插入图片描述

返回一个数组为[3,2,1]

0 <= 链表长度 <= 10000
示例1

输入:
{1,2,3}
复制
返回值:
[3,2,1]

示例2

输入:
{67,0,24,58}
复制
返回值:
[58,24,0,67]

方法一:基于反转链表

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList list=new ArrayList<>();
        if(listNode==null){
            return list;
        }
        //反转链表
        ListNode now=listNode;
        ListNode next=null;
        ListNode pre=null;
        while(now!=null){
            next=now.next;
            now.next=pre;
            pre=now;
            now=next;
        }
        while(pre!=null){
            list.add(pre.val);
            pre=pre.next;
        }
        return list;
    }
}

方法二:递归

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list=new ArrayList<>();
        if(listNode == null){
            return list;
        }
        list=printListFromTailToHead(listNode.next);
        list.add(listNode.val);
        return list;
    }
}

按理说没啥毛病,现在出现以下错误,错误原因还在分析中:
在这里插入图片描述
方法三:利用栈

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
import java.util.*;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list=new ArrayList<>();
        Stack<Integer> stack = new Stack<>();
        while(listNode != null){
            stack.push(listNode.val);
            listNode=listNode.next;
        }
        while(!stack.isEmpty()){
            list.add(stack.pop());
        }
        return list;
    }
}

· JZ24 反转链表

题目地址
描述
输入一个长度为n链表,反转链表后,输出新链表的表头。

数据范围: n\leq1000n≤1000
要求:空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n) 。

如当输入链表{1,2,3}时,
经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
以上转换过程如下图所示:
在这里插入图片描述
示例1

输入:
{1,2,3}
复制
返回值:
{3,2,1}

示例2

输入:
{}
复制
返回值:
{}
复制
说明:
空链表则输出空

代码:

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        ListNode now=head;//当前遍历到的节点
        ListNode pre=null;//记录反转之后链表的头结点
        ListNode next=null;//记录当前遍历节点的下一个节点
        while(now!=null){
            next=now.next;
            now.next=pre;
            pre=now;
            now=next;
        }
        return pre;
    }
}

·JZ25 合并两个排序的链表

题目地址
描述
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围: 0 \le n \le 10000≤n≤1000,-1000 \le 节点值 \le 1000−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},转换过程如下图所示:
在这里插入图片描述
或输入{-1,2,4},{1,3,4}时,合并后的链表为{-1,1,2,3,4,4},所以对应的输出为{-1,1,2,3,4,4},转换过程如下图所示:
在这里插入图片描述
示例1

输入:
{1,3,5},{2,4,6}
返回值:
{1,2,3,4,5,6}

示例2

输入:
{},{}
返回值:
{}

示例3

输入:
{-1,2,4},{1,3,4}
返回值:
{-1,1,2,3,4,4}

代码

/*
同时遍历两个链表,p1指向链表1,p2指向链表2,设置辅助链表3
遍历过程中(遍历条件:p1,p2指向都不为空):
若:p1指向节点的值 < p2指向节点的值
	p1指向的节点加入链表3中,p1向后移动
若:p2指向节点的值 < p1指向节点的值
	p2指向的节点加入链表3中,p2向后移动
若:p2指向节点的值 = p1指向节点的值
    p1指向的节点加入链表3中,p1向后移动
	p2指向的节点加入链表3中,p2向后移动
循环结束之后,若链表1没遍历完,将链表1内容加入链表3中
             若链表2没遍历完,将链表2内容加入链表3中
*/
/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode head=new ListNode(0);
        ListNode list=head;
        while(list1!=null && list2!=null){
            if(list1.val < list2.val){
                list.next=new ListNode(list1.val);
                list=list.next;
                list1=list1.next;
            }else if(list1.val > list2.val){
                list.next=new ListNode(list2.val);
                list=list.next;
                list2=list2.next;
            }else{
                list.next=new ListNode(list1.val);
                list=list.next;
                list1=list1.next;
                list.next=new ListNode(list2.val);
                list=list.next;
                list2=list2.next;
            }
        }
        while(list1!=null){
            list.next=new ListNode(list1.val);
            list=list.next;
            list1=list1.next;
        }
        while(list2!=null){
            list.next=new ListNode(list2.val);
            list=list.next;
            list2=list2.next;
        }
        list=head.next;
        head.next=null;
        return list;
    }
}

·JZ52 两个链表的第一个公共结点

题目地址
描述
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

数据范围: n \le 1000n≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
示例1

输入:
{1,2,3},{4,5},{6,7}
返回值:
{6,7}
说明:
第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分
这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的   

示例2

输入:
{1},{2,3},{}
返回值:
{}
说明:
2个链表没有公共节点 ,返回null,后台打印{} 

分析
如下图所示,根据简单分析,对两个无环单链表,要么不相交;如果相交的话,那么两个链表从相交节点往后的结点都是共同节点。
在这里插入图片描述

代码

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1==null || pHead2==null){
            return null;
        }
        //分别记录链表长度
        int count1=0,count2=0;
        ListNode h1=pHead1;
        ListNode h2=pHead2;
        while(h1.next!=null){
            count1++;
            h1=h1.next;
        }
        while(h2.next!=null){
            count2++;
            h2=h2.next;
        }
        //如果两链表末尾不相同,则一定没有相同部分
        if(h1 != h2){
            return null;
        }
        int bias=count1-count2;
        h1=pHead1;
        h2=pHead2;
        if(bias>0){
            while(bias-->0){
                h1=h1.next;
            }
        }else if(bias<0){
            bias=0-bias;
            while(bias-->0){
                h2=h2.next;
            }
        }
        while(h1!=null && h2!=null){
            if(h1==h2){
                return h1;
            }
            h1=h1.next;
            h2=h2.next;
        }
        return null;
    }
}

·JZ23 链表中环的入口结点

题目地址

描述
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

数据范围: n\le1000n≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台将这2个会组装成一个有环或者无环单链表

返回值描述:
返回链表的环的入口结点即可。而我们后台程序会打印这个节点
示例1

输入:
{1,2},{3,4,5}
返回值:
3
说明:
返回环形链表入口节点,我们后台会打印该环形链表入口节点,即3 

示例3

输入:
{1},{}
返回值:
"null"
说明:
没有环,返回null,后台打印"null"

示例2

输入:
{},{2}
返回值:
2
说明:
只有环形链表节点2,返回节点2,后台打印2

分析
在有环的情况下
定义快慢指针(fast和slow),快指针每次走两步,慢指针每次走一步。都从头结点开始,直到第一次相遇
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
快慢指针相遇之后,快指针回到头结点,此时快指针每次走一步,慢指针每次走一步,再次相遇的结点就是入环结点。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead) {
        if(pHead==null || pHead.next==null || pHead.next.next==null){
            return null;
        }
        ListNode slow=pHead.next;
        ListNode fast=pHead.next.next;
        while(fast != slow){
            if(fast==null || slow==null){
                return null;
            }
            slow=slow.next;
            fast=fast.next!=null?fast.next.next:null;
        }
        fast=pHead;
        while(fast != slow){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值