一、合并两个有序链表(简单)
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
1)java代码非递归实现(9ms 战胜96.25%):
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode pa, pb;
ListNode pc = null;
ListNode l3 = null;
pa = l1;
pb = l2;
int i = 0 ;
if(l1 == null) return l2;
if(l2 == null) return l1;
while((pa!=null)&&(pb!=null)){
if(i == 0){
if(pa.val <= pb.val){
pc = pa;
pa = pa.next;
}else{
pc = pb;
pb = pb.next;
}
l3 = pc;
i++;
}else{
if(pa.val <= pb.val){
pc.next = pa;
pc = pa;
pa = pa.next;
}else{
pc.next = pb;
pc = pb;
pb = pb.next;
}
}
}
pc.next = pa==null ? pb:pa;
return l3;
}
}
2)java代码递归实现(15ms 战胜63.14%):
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) return l2;
if(l2 == null) return l1;
if(l1.val <= l2.val){
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}else{
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
二、合并K个有序链表(困难):
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
1) 方法一(29ms 战胜56.32%):
将多个链表中的元素放到一个ArrayList中 —> 对此ArrayList进行排序 —> 将排好序的数组链表转换成 链表!
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
ListNode head = new ListNode(0);
//将lists中所有元素放入一个数组中
ArrayList arr = new ArrayList();
for(ListNode eachlist : lists){
while(eachlist!=null){
arr.add(eachlist.val);
eachlist = eachlist.next;
}
}
//对数组arr进行排序
Collections.sort(arr);
//将数组转成链表
ListNode result = head;
for(int i=0; i<arr.size(); i++){
ListNode newNode = new ListNode((int)arr.get(i));
head.next = newNode;
head = head.next;
}
return result.next;
}
}
2)方法二(455ms 战胜8.53%):
遍历list数组,每次取K个链表的头结点进行比较,找寻出K个链表头结点中的最小节点,并将其从K个链表集中剔除,再将其添加到新的链表中,直到K个链表都为空为止。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
ListNode result = new ListNode(0);
ListNode point = result;
int min;
int position = -1;
while(true){
min = Integer.MAX_VALUE;
for(int i=0; i<lists.length; i++){
if(lists[i] != null){
if(lists[i].val < min){
min = lists[i].val;
position = i;
}
}
}
if(min == Integer.MAX_VALUE){
break;
}
point.next = lists[position];
point = point.next;
lists[position] = lists[position].next;
}
return result.next;
}
}
3)方法三(8ms 战胜99.72%):推荐此方法
借鉴合并两个单链表的思想,对K个链表进行两两合并,得到最终结果!
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0) return null;
int n = lists.length;
int k ;
while(n>1){
k = (n+1)/2;
for(int i=0; i<n/2; i++){
lists[i] = mergeTwoLists(lists[i], lists[i+k]);
}
n = k;
}
return lists[0];
}
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
三、反转链表(非常重要,一定要非常熟练地写出来):
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
java实现反转链表(战胜100%用户)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null)
return head;
ListNode newHead = null;
ListNode preNode = null;
ListNode curNode = head;
while(curNode != null){
ListNode nextNode = curNode.next;
if(nextNode == null){
newHead = curNode;
}
curNode.next = preNode;
preNode = curNode;
curNode = nextNode;
}
return newHead;
}
}
四、 旋转链表(中等):
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2 输出: 4->5->1->2->3->NULL 解释: 向右旋转 1 步: 5->1->2->3->4->NULL 向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4 输出: 2->0->1->NULL 解释: 向右旋转 1 步: 2->0->1->NULL 向右旋转 2 步: 1->2->0->NULL 向右旋转 3 步: 0->1->2->NULL 向右旋转 4 步: 2->0->1->NULL
java代码实现:
这个题可以用快慢指针来解,快指针先走k步,然后两个指针一起走,当快指针走到末尾时,慢指针的下一个位置是新的顺序的头结点,这样就可以旋转链表了。在这里需要考虑两个特殊情况,第一个就是当原链表为空时,直接返回NULL。第二个就是当k大于链表长度和k远远大于链表长度时,我们首先遍历一遍原链表得到链表长度size,然后k对size取余,这样k肯定小于size,就可以用上面的算法了。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if(head == null) return head;
int size = 0;
ListNode cur = head;
//遍历,得到链表的长度
while(cur != null){
size ++;
cur = cur.next;
}
k %= size;
ListNode fast,slow;
fast = head;
slow = head;
//快指针先走 K 步
for(int i=0; i<k; i++){
fast = fast.next;
}
//快慢指针一起走,快指针到头,慢指针下一个元素就是旋转后的头结点
while(fast.next != null){
fast = fast.next;
slow = slow.next;
}
fast.next = head;
fast = slow.next;
slow.next = null;
return fast;
}
}