· 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;
}
}