题目:合并两个已排序链表
难度:Easy
题目内容:
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
翻译:
合并两个已排序的链表,并将其作为一个新链表返回。新的链表应该通过将前两个列表的节点拼接在一起。
Example:
Input: 1->2->4, 1->3->4 Output: 1->1->2->3->4->4
我的思路:数据结构——已经指定链表,且不需要特殊操作,就用链表;
算法——对一个链表进行遍历,如果链表2节点的值小于当前节点,就插入到它前面。
因为是单链表,所以需要前驱指针一起移动才能进行操作,并且还需要一个头节点作为返回用。
我的代码:
1 public ListNode mergeTwoLists(ListNode l1, ListNode l2) { 2 if (l1 == null || l2 == null) { 3 return l1 == null ? l2 : l1; 4 } 5 6 ListNode head = new ListNode(0); 7 ListNode pre = head; 8 head.next = l1; 9 10 while (l1 != null && l2 != null) { 11 if (l2.val < l1.val) { 12 pre.next = l2; 13 l2 = l2.next; 14 pre.next.next = l1; 15 16 pre = pre.next; // pre归位 17 continue; // 插入结束,l1不需要移动 18 } 19 pre = l1; 20 l1 = l1.next; 21 } 22 if (l2 != null) { 23 pre.next = l2; // l2 还有剩的话说明l1已经为null,直接接在pre后面即可 24 } 25 return head.next; 26 }
我的复杂度: 时间: O(M + N) 空间: O(1)
编码过程出现问题:
1、在插入l2之后此时l1 是不需要移动的——>continue 出错用例:[5]、[1,2,4]
2、插入后,continue之前,还得把pre往前移动 出错用例:[5]、[1,2,4]
参考答案代码:
1 public ListNode mergeTwoLists(ListNode l1, ListNode l2){ 2 if(l1 == null) return l2; 3 if(l2 == null) return l1; 4 if(l1.val < l2.val){ 5 l1.next = mergeTwoLists(l1.next, l2); 6 return l1; 7 } else{ 8 l2.next = mergeTwoLists(l1, l2.next); 9 return l2; 10 } 11 }
答案复杂度: 时间:O(N+M) 空间:O(N+M)
答案思路:
利用递归的思想,每次比较之后,将小的那个节点的后继改为下一次递归返回的节点,然后返回自己。所以最后会返回l1与l2中小的那一个节点,而它的后继是一条完整的由小到大的节点。
比较:我的那个方法空间复杂度比较好,而且由于没有使用递归运行相对较快。而答案方法则简洁明了。
补充:
23题:Merge k Sorted Lists
题目:就是21题的两个链表改成了k个
难度:Hard
我的思路:选取节点的思路肯定还是一样,选取k个链表表头中最小的那一个作为下一个节点,但是由于是k个所以在最内层也就是lists[]某一个或某几个为null的时候,就不能像两个链表一样直接返回另外一个那么简单了,就需要把原来lists中的null都给删除,但是由于是数组,所以操作起来比较麻烦得借用list。
这种类似于多排序数组归并,其实最佳选择是堆排序,不过我嫌麻烦就直接顺序搜索最小值了。
我的代码:
1 class Solution { 2 public ListNode mergeKLists(ListNode[] lists) { 3 if (lists.length == 0) { 4 return null; 5 } 6 7 //*** 去除null节点 **// 8 List<ListNode> l = new ArrayList<ListNode>(Arrays.asList(lists)); 9 boolean hasNull = l.remove(null); 10 lists = l.toArray(new ListNode[l.size()]); 11 // end // 12 13 if (hasNull) { 14 return mergeKLists(lists); 15 } 16 17 ListNode min = getMinNode(lists); 18 min.next = mergeKLists(lists); 19 20 return min; 21 } 22 23 public static ListNode getMinNode(ListNode[] lists) { 24 int minIndex = 0; 25 for (int i = 1; i < lists.length; i++) { 26 if (lists[i].val < lists[minIndex].val) { 27 minIndex = i; 28 } 29 } 30 31 ListNode minNode = lists[minIndex]; 32 lists[minIndex] = lists[minIndex].next; // min取出后,lists要更新 33 34 return minNode; 35 } 36 }
结果:最后一两个用例栈溢出
我的复杂度: 时间:O (N*M) N是lists的长度,M是lists中最长的那一个
空间:超级大。。。(因为每一次的去除null,都会新建一个lists的对应的ArrayList,并且toArray又新建了一个对应的Array),所以栈溢出了
优化:
那就用简单粗暴的方法去除null:
// 替换去除null // for (int i = 0; i < lists.length; i++) { if (lists[i] == null) { ListNode[] temp = new ListNode[lists.length-1]; for (int j = 0; j < temp.length; j++) { if (j < i) { temp[j] = lists[j]; } else { temp[j] = lists[j+1]; } } hasNull = true; lists = temp; i--; } } // end//
这样一来就只多建了一个temp的Array。
【注意】:最后在数组删除元素之后不要忘了 i -- ,因为当前的i已经被删除了,最新的lists第i个已经是以前的第i+1个,如果继续循环那么将直接 i ++ 就会跳过之前的那个 i + 1。
优化后结果:不溢出了。。。但是运行超时了。
好吧看来还是得用堆排序——优先队列。
答案代码:
1 class Solution { 2 public ListNode mergeKLists(ListNode[] lists) { 3 ListNode res=null; 4 5 System.out.println("lists.length "+ lists.length); 6 if(lists == null || lists.length== 0) 7 return res; 8 9 PriorityQueue pq = new PriorityQueue(); 10 for(int i=0; i<lists.length; i++){ 11 if(lists[i]!=null){ 12 pq.offer(new PQNode(lists[i].val, 0, i)); 13 lists[i] = lists[i].next; 14 } 15 } 16 17 18 for(int i=0; !pq.isEmpty(); i++){ 19 PQNode n = (PQNode)pq.poll(); 20 int value = n.value; 21 int index = n.index; 22 int listNumber = n.listNumber; 23 if(res==null){ 24 res = new ListNode(value); 25 } 26 else{ 27 ListNode temp = res; 28 while(temp.next!=null){ 29 temp=temp.next; 30 } 31 temp.next = new ListNode(value); 32 } 33 if(lists[listNumber]!=null){ 34 pq.offer(new PQNode(lists[listNumber].val, 0, listNumber)); 35 lists[listNumber] = lists[listNumber].next; 36 37 } 38 } 39 return res; 40 } 41 } 42 43 class PQNode implements Comparable<PQNode>{ 44 int value; 45 int index; 46 int listNumber; 47 48 public PQNode(int value, int index, int listNumber){ 49 this.value = value; 50 this.index = index; 51 this.listNumber = listNumber; 52 } 53 54 public int compareTo(PQNode obj){ 55 if(this.value < obj.value) return -1; 56 if(this.value > obj.value) return 1; 57 return 0; 58 } 59 }
答案复杂度: 时间:O (N*logM + M) 空间:O(M)
答案思路:思路没变,不过就是在取k个数组中最小的时候使用了一个优先级队列——最开始将k个链表头取出建立一个优先级队列(java.util.PriorityQueue),然后每次poll
出队头后再放进此队头原来所在链表的链表头,当发现此表头为null的时候,则优先级队列的大小减一。直到队列为空。