题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路:对于两个链表,已知都是有序的,要将其进行合并,方法是建立第三条链表,始终代表第一个结点的变量是newHead,动态代表链表头结点的变量是newEnd(技巧:一个链表要分别使用两个变量分别表示固定的头结点以及代表链表用来动态向下指的所谓头结点list,其实用list更加贴切,因为list表示的是这条链表的最后一个位置,需要不断赋值并且向下指,先赋值向下移动newEnd.next=head; newEnd=newEnd.next;)每个链表的头指针list1,list2是动态向下指的,将list1和list2中较小的结点作为新链表的结点赋值给newEnd.next,一直进行循环直到有一条链表到达结尾,然后将另一条链表的当前元素赋值给newEnd.next即可,后续不需要再逐个赋值,因为剩余的链表本来就是连续的,自己带有next的关系。
可以使用循环来做,由于操作具有高度的重复性,因此可以使用递归来实现,递归需要抽象出传递函数—不断推进;以及边界条件—基本情形。
方法一:使用循环的非递归方式
//这里list1,list2表示两个链表的头结点
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
//当有一个输入的链表是null时或者全部都是null时
if(list1==null&&list2==null) return null;
if(list1==null&&list2!=null) return list2;
if(list1!=null&&list2==null) return list1;
//作为新链表的头结点—其实是指第一个结点
ListNode newHead=null;
//作为新链表的尾结点—其实是指动态的当前结点
ListNode newEnd=null;
//确定新链表的头结点,需要返回;技巧:头结点要专门有一个变量来记住它,即链表要有一个头结点对象,以及一个代表链表的对象(用来next下指)
if(list1.val<=list2.val){
newHead=list1;
list1=list1.next;
}else{
newHead=list2;
list2=list2.next;
}
//这里用newEnd来代表整条链表(的头结点—动态的指针,其实对于一个链表总是给出第一个结点来代表整个链表,可以把这个结点不看做头结点而是看做当前结点),newHead专门用来记住当前结点,以便最后进行返回;可以改进:对于链表设置一个虚拟的结点作为固定第一个结点。
ListNode dummy = new ListNode(-1);--- dummy代表一个虚拟的结点,可以看做是第-1个结点ListNode list = dummy; ---list代表整个链表的当前结点,需要给list赋当前结点的值然后再逐个向下移动指针,这样就不需要上面单独求出第一个结点到底是list1.val还是list2.val,可以都放在循环体里面统一处理然后最终只要返回dummy.next结点即代表整条链表的头结点。
newEnd=newHead;
ListNode head=null;
while(list1!=null && list2!=null){
//谁小谁作为剩余链表的头结点
if(list1.val<=list2.val){
//先取出值再逐个向下移动
head=list1;
list1=list1.next;
}else{
head=list2;
list2=list2.next;
}
//先给next赋值再将指针向下移动
newEnd.next=head;
newEnd=newEnd.next;
}
if(list1==null){
head=list2;
}
if(list2==null){
head=list1;
}
newEnd.next=head;
//返回新的链表
return newHead;
}
}
方法二:使用递归方法实现
//递归实现
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null){
return list2;
}else if(list2==null){
return list1;
}
if(list1==null&&list2==null){
return null;
}
ListNode newList = null;
if(list1.val<list2.val){
newList = list1;
newList.next = Merge(list1.next,list2);
}else{
newList = list2;
newList.next = Merge(list1,list2.next);
}
return newList;
}
}
对于递归的理解:
写递归要抽象出需要进行反复调用的递归过程究竟是什么。在这道题目中,一直反复调用这样一个过程:将两个链表合并得到一个较小的结点并返回,然后将它赋值到已有链表的next对象上面。
即总是先比较两个结点的大小,将小的结点作为newList进行返回,然后再对剩余的两条链做合并比较,返回较小的结点。