一、合併兩個有序鏈表(簡單)
將兩個有序鏈表合併爲一個新的有序鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。
示例:
輸入: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;
}
}