题目
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例1:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
限制:
0 <= 链表长度 <= 1000
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
思路
解题思路:
- 定义一个节点head,作为合并后得到的链表的伪头节点(不存储实际数据)。=>最终return head.next;
再定义一个指针temp,作为辅助指针,指向链表head的尾节点(因为链表head采用的是 尾插法)。
对于链表 L1、L2,不设置辅助指针,可以之间将L1、L2两个指针,边遍历边后移。 - 当L1!= null && L2!=null时,比较L1和L2的val的大小,取其中较小的节点,加入到head所在的链表中(尾插法);
- 直到L1和L2所在的链表中,出现至少一个==null,即L1或L2所在链表,有至少一个链表已经完成遍历时,退出2、的循环,将没有遍历完的链表中的所有剩余节点,加入到head所在的链表中(尾插法);
- 需要注意的是:
=>对于合并的链表head,对其中进行加入节点的操作的时候,采用的是:尾插法。
=>在进行尾插法的操作时,是直接让head所在链表的temp节点的next域指向 要插入的节点,随后更新temp指针,使它指向新的尾节点。
实现
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
//不用定义两个辅助指针,用于遍历l1、l2两个链表。因为l1、l2,实际上就可以作为对应的遍历指针。
ListNode head = new ListNode(0);//头节点(不存数据的那种)
ListNode temp = head;//尾节点(辅助插入节点--尾插法)
//l1、l2不空的情况下
while(l1!=null && l2!=null){
//谁的节点的val小,将谁的先插入到head对应的链表中去
if(l1.val <= l2.val){
temp.next = l1;
temp = temp.next;//指向当前的尾节点
l1 = l1.next; //更新l1
}else{
temp.next = l2;
temp = temp.next;//指向当前的尾节点
l2 = l2.next;//更新l2
}
}
//l1、l2所在的链表中,至少有一条已经遍历完的情况下,
//先找到哪个链表遍历完了(分为3中情况,其实处理起来只有两种需要另外进行处理)
if(l1 == null && l2!= null){
//l1空,l2不空=>将l2中的剩余元素,加入到head所在链表中去
temp.next = l2;
} else if(l1 != null && l2 == null){
//l1不空,l2空=>将l1中的剩余元素,加入到head所在链表中去
temp.next = l1;
/*}else{
//l1、l2都空了
temp.next = null;//其实,感觉不要也可以,因为temp最终一定是指向l1、l2所在链表中的某一个链表的尾节点*/
}
return head.next;
}
}
总结
刚看到这道题的时候,很快就有了思路,但是处理起来却花了很长的时间,主要是因为:对于Java中的链表的理解还是不够清晰。下面进行一个小的 关于Java中链表的理解 的一个总结:
- 链表中的节点的类型,是一个自定义类,所以节点属于引用类型。与前边JavaSE阶段学到的其他的引用类型的变量一样,拥有相同的性质。
- "ListNode tail = null;"具有的实际意义就是:在栈中定义了一个ListNode类型的变量,该变量目前没有指向堆空间中的任何区域(没有造一个对象,使它指向这个新对象;也没有指向一个之前已经造好的对象)。
"tail = head; "所起到的作用:让栈中的 tail变量,指向栈空间中的head指向的堆空间中的区域。在进行下一个这样的赋值(将tail指向堆空间中的其他区域)之前,由于类属于引用类型,对tail进行的所有操作,实际上也是对head进行的所有操作,也就是对它们共同指向的堆空间中的一个区域中的数据进行的操作。 - 注意:
=>这是引用类型的真正内涵,也是 链表中定义辅助变量(辅助指针)的意义所在,不要迷!!!!!!
=>很重要的一点,在Java中,链表的节点,对应的是一个封装好的类 的对象。
行百里者半九十!!!