文章目錄
鏈表需要我們掌握的題目不是很多,只要掌握這20幾道最最經典的題目,應對面試絕對綽綽有餘。
題目鏈接
1.兩數相加:完成題解
2.合併兩個有序鏈表:完成題解
3.排序鏈表:完成題解(需要再做)
4.反轉鏈表:完成題解
5.兩兩交換鏈表中的節點:完成題解
6.相交鏈表:完成題解(需要再做)
7.K 個一組翻轉鏈表
8.反轉鏈表 II回顧的時候再做,不太會啊。
9.刪除排序鏈表中的重複元素:完成題解
10.刪除鏈表的倒數第N個節點:完成題解
11.迴文鏈表:完成題解(需要再做)
12.分隔鏈表
13.奇偶鏈表:完成題解(需要再做)
14.合併K個排序鏈表:完成題解(分治算法需要再做)
15.重排鏈表:完成題解。
16.有序鏈表轉換二叉搜索樹
17.環形鏈表 II
18.對鏈表進行插入排序:完成題解(需要再做)
題目分類
鏈表基礎
1.兩數相加
思路
非常簡單的一道鏈表題,我們需要考慮的就是當前鏈表的值相加後是否會大於等於10,我們設置一個boolean變量addone,如果大於10就賦值爲true。
整體思路先是
1.兩個鏈表同時判斷,每次都將鏈表向後移,直到一個鏈表爲空
2.分別判斷兩個鏈表是否還不爲空,不爲空就逐個後移(注意這裏不能直接讓我們的result.next=不空的鏈表,因爲可能我們的addone爲true,所以要加上1)
3.最後還要繼續判斷addone是否爲true,爲true,繼續向result添加一個爲1的ListNode。
代碼
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode result =new ListNode(-1);
ListNode res = result;
//設置一個進位變量
boolean addone = false;
while(l1!=null&&l2!=null){
int l1num = l1.val;
int l2num = l2.val;
int now = l1num+l2num;
if(addone)
now+=1;
if(now>=10){
result.next = new ListNode(now-10);
addone = true;
}else{
result.next = new ListNode(now);
addone = false;
}
result = result.next;
l1 = l1.next;
l2 = l2.next;
}
//l1還有剩餘(不能直接result.next=l1,要一個一個往後去判斷,因爲addone可能爲true)
while(l1!=null){
int l1num = l1.val;
int now = l1num;
if(addone)
{
now+=1;
}
if(now>=10){
result.next = new ListNode(now-10);
addone = true;
}else{
result.next = new ListNode(now);
addone = false;
}
l1 = l1.next;
result = result.next;
}
//l2還有剩餘
while(l2!=null){
int l2num = l2.val;
int now = l2num;
if(addone)
{
now+=1;
}
if(now>=10){
result.next = new ListNode(now-10);
addone = true;
}else{
result.next = new ListNode(now);
addone = false;
}
l2 = l2.next;
result = result.next;
}
//防止addone還存在,還需要繼續進位
if(addone)
{
result.next = new ListNode(1);
}
return res.next;
}
}
2.反轉鏈表
思路
這個問題比較簡單,只要指定一個pre指針就好,每次首先提取出head的下一個指針並付給next,讓head的next指向pre,然後pre移動到當前的head,最後head賦值next即可,持續循環。直到找到我們head的next爲null時,就不將head賦值爲next,直接返回。
代碼
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
while(head!=null){
ListNode next = head.next;
head.next = pre;
pre = head;
if(next==null)
break;
head = next;
}
return head;
}
}
3.相交鏈表
思路
這道題有很多思路,比如HashMap之類的思路,我們這裏只考慮最優解。
考慮最優解:讓a節點走到a鏈表尾部的時候,去指向b鏈表的頭部
當b節點指向b鏈表的尾部時,去指向a鏈表的頭部,這樣最終會在相交點相遇。
雖然起點不同,但路程是相同的,最終會相遇。
A在前面比B少走1個格,所以會先到終點,然後再走B的路,
B雖然在前面比A多走一個格,但再走A的路時,會比A少走一格,因此最終會在c1相遇。
再來一個圖就更加清晰,假如兩個鏈表相交,我們可以設第一個鏈表不相交的部分爲a1長度,第二個鏈表不相交的部分爲b1長度,設相交長度爲c。那麼可以看出a走的是a1+c+b1,而b走的是b1+c+a1,因此是相同的路徑。
代碼
這裏要注意一下,
1.有可能有不相交的鏈表,相交的話,就是a,b節點在第二次走的過程肯定會相遇,那麼只要有一個節點第二次走到終點,那麼就說明他們肯定是不想交的。
2.如果兩個鏈表有一個爲空就不可以相交。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//一個爲空就不可能相交
if(headA==null||headB==null)
return null;
ListNode a = headA;
ListNode b = headB;
//這裏要注意,有可能是沒有交點的,那麼當一個節點第二次到達終點就說明沒有交點
int atime = 0;
int btime = 0;
while(a!=b&&atime<2&&btime<2)
{
a = a.next;
b = b.next;
if(a==null){
a = headB;
atime++;
}
if(b==null){
b = headA;
btime++;
}
}
if(atime==2||btime==2)
return null;
return a;
}
}
4.迴文鏈表
思路
需要首先找到鏈表的中點,並將鏈表中點之後的部分鏈表全部翻轉,之後再比較兩個鏈表是否完全相等。值得一提的是,如果鏈表長度爲奇數,slow會指向中間元素,如果長度爲偶數,則會指向中間兩個值中的左邊值,因此我們第二段鏈表的頭都是slow.next。還需要注意要將兩部分斷開。
代碼
注意邊界條件,爲空的情況。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
/*
分成兩段即可,然後翻轉後面那部分
使用快慢指針找到中點
*/
if(head==null)
return true;
ListNode fast = head;
ListNode slow = head;
while(fast.next!=null&&fast.next.next!=null){
fast = fast.next.next;
slow = slow.next;
}
//分開slow之後的
ListNode pre = null;
ListNode now = slow.next;
//System.out.println(now.val);
slow.next = null;
//翻轉完成,以now爲頭部
while(now!=null)
{
ListNode next = now.next;
now.next = pre;
pre = now;
if(next==null)
break;
now = next;
}
//System.out.println(now.val);
//當奇數長度時,head鏈表的長度就多包含個最中間的節點,因此要判斷當兩個都不爲空,而不是head不爲空
while(head!=null&&now!=null)
{
if(head.val!=now.val){
System.out.println("now:"+now.val+"head:"+head.val);
return false;
}
head = head.next;
now = now.next;
}
return true;
}
}
5.刪除排序鏈表中的重複元素
思路
可以判斷當前head與head.next是否相等,如果相等的話就讓head.next指向head.next.next,不斷的更改head.next的值,直到head.next與head不同,再把head賦值給head.next。
代碼
/**
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head==null)
return null;
ListNode res = head;
while(head!=null)
{
//一直往後走直到head.next與head不同,再把head賦給head.next
while(head.next!=null&&head.next.val==head.val)
{
head.next = head.next.next;
}
head = head.next;
}
return res;
}
}
6.刪除鏈表的倒數第N個節點
思路一:兩次遍歷
首先遍歷鏈表,得到鏈表的長度,就可以知道我們要往後走幾步以找到我們要刪除的節點。
代碼一
這裏要注意pre不是指向head,而是要指向一個新的節點,讓這個節點的下一個指向head,防止要刪除的是第一個節點。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(n==0||head==null)
return head;
int deepth = finddeepth(head);
int times = deepth-n;
//這裏用這個pre,省的要刪除第一個節點。
ListNode pre = new ListNode(-1);
pre.next = head;
ListNode res = pre;
//讓head指向需要刪除的節點,讓pre指向需要刪除節點前面的節點
while(times>0){
pre = pre.next;
head = head.next;
times--;
}
pre.next = head.next;
return res.next;
}
public int finddeepth(ListNode head)
{
int i = 0;
while(head!=null)
{
i++;
head = head.next;
}
return i;
}
}
思路二:一次遍歷(最優解)
上圖來自程序員小吳的題解。
其實一個圖就可以看明白思路了。
最優解:使用雙指針,p與q
其實就想要q到達null的時候,p距離q爲n+1(如果算上null的話),這樣p指向的下一個節點就是倒數第n個節點了
初始的時候,我們讓p和q都處於一個新的節點處,這個節點的next指向head
先讓q向後移動n+1位,這樣讓p和q之間保持n+1的距離,之後在共同移動,直到q爲null。
代碼二
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//最優解:使用雙指針,p與q
//其實就想要q到達null的時候,p距離q爲n,這樣p指向的下一個節點就是倒數第n個節點了
//初始的時候,我們讓p和q都處於一個新的節點處,這個節點的next指向head
//先讓q向後移動n+1位,之後共同移動,直到q爲null。
ListNode p = new ListNode(-1);
p.next = head;
ListNode q = p;
ListNode res = p;
while(n+1>0)
{
q = q.next;
n--;
}
while(q!=null)
{
p = p.next;
q = q.next;
}
//已經找到倒數第n個節點,p爲倒數第n個節點前面的節點
p.next = p.next.next;
return res.next;
}
}
7.兩兩交換鏈表中的節點
思路
其實就是兩兩交換鏈表中的節點,就注意這裏要判斷的和以往的不同,要判斷head不爲空同時head.next也不爲空,這樣才能交換head和head.next。還需要注意的就是pre節點要指向一個啞結點哦。
代碼
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
if(head==null)
return null;
ListNode pre = new ListNode(-1);
pre.next = head;
ListNode res = pre;
//防止剩餘單個,所以要判斷當前以及當前的下一個是否都不爲空。
while(head!=null&&head.next!=null){
ListNode secondnext = head.next.next;
ListNode second = head.next;
head.next = secondnext;
second.next = head;
pre.next = second;
pre = head;
head = head.next;
}
return res.next;
}
}
8.奇偶鏈表
思想
把奇數節點放到一個鏈表中,把偶數節點放到另一個鏈表,到時候讓奇數的尾指向偶數的頭即可
需要奇數頭結點,尾結點,偶數頭結點與偶數尾結點
奇數頭結點和偶數頭結點都需要指向一個新的節點,同時指向的節點不同,但兩個節點的next都指向head。
代碼
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode oddEvenList(ListNode head) {
if(head==null)
return null;
//把奇數放到一個鏈表中,把偶數放到另一個鏈表,到時候讓奇數的尾指向偶數的頭即可
//需要奇數頭結點,尾結點,偶數頭結點與偶數尾結點
//奇數頭結點和偶數頭結點都需要指向一個新的節點,同時指向的節點不同,但兩個節點的next都指向head。
ListNode pre1 = new ListNode(-1);
ListNode pre2 = new ListNode(-2);
pre1.next = head;
pre2.next = head;
ListNode oddpre = pre1;
ListNode evenpre = pre2;
ListNode oddwei = pre1;
ListNode evenwei = pre2;
ListNode res = pre1;
int i =1;
while(head!=null){
if(i%2!=0){
oddwei.next = head;
oddwei = oddwei.next;
}
else{
evenwei.next = head;
evenwei = evenwei.next;
}
ListNode xiayige = head.next;
head.next = null;
head = xiayige;
i++;
}
//這裏有可能偶數節點沒有做操作,所以要區分一下(只有一個節點的時候,就不需要鏈接了)
if(oddwei!=evenpre.next)
oddwei.next = evenpre.next;
return res.next;
}
}
一些模板與總結
1.找鏈表中點
快慢指針一起走,結束後慢指針指向的就是中點。值得一提的是,如果鏈表長度爲奇數,slow會指向中間元素,如果長度爲偶數,則會指向中間兩個值中的左邊值。
ListNode fast = head;
ListNode slow = head;
while(fast.next!=null&&fast.next.next!=null){
fast = fast.next.next;
slow = slow.next;
}
2.反轉鏈表
public ListNode reverseList(ListNode head) {
ListNode pre = null;
while(head!=null){
ListNode next = head.next;
head.next = pre;
pre = head;
if(next==null)
break;
head = next;
}
return head;
}
3.前節點
前節點要麼是一個新的節點,然後新節點的next指向head,以便防止對鏈表的第一個節點有影響,要麼就是一個null。
ListNode pre = new ListNode(-1);
pre.next = head;
要麼就是空,方便翻轉鏈表。
ListNode pre = null;
4.思路
基本要麼直接就能求解,要麼就需要反轉一半鏈表,要麼就是用雙指針能有一些規律,最好多用筆畫一畫,鏈表的題目總涉及交換節點位置之類的,一不小心就會出現問題,因此切記用筆寫一寫。
對鏈表進行排序
1.合併兩個有序鏈表
思路
很簡單的題,使用歸併排序就好,同時比較兩個鏈表的當前值,誰比較小就把誰插入到新鏈表中,直至有一個鏈表長度爲0。之後還需要兩個判斷,單獨判斷l1鏈表是否爲空以及單獨判斷l2鏈表是否爲空,因爲有可能上一次判斷中有鏈表還剩餘值,直接把這個鏈表剩餘的值都插入到新鏈表中即可。
代碼
可能有人會問,不需要考慮一些特殊情況麼,比如兩個ListNode都爲空,如果這種情況下,就會直接走到return result.next,也就是返回null。
/**
* 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 node = new ListNode(-1);
ListNode result = node;
//第一次判斷
while(l1!=null&&l2!=null){
int left = l1.val;
int right = l2.val;
if(left<right){
node.next = l1;
l1 = l1.next;
}else{
node.next = l2;
l2 = l2.next;
}
node = node.next;
}
//第二次判斷
if(l1!=null){
node.next = l1;
}
if(l2!=null)
{
node.next = l2;
}
return result.next;
}
}
2.排序鏈表
思路
這裏偷一張圖,這道題的思路分爲三部分:(1)找中點,(2)分割,(3)合併。
(1)找中點,找中點是通過快慢指針來找到中間節點,當快指針到終點或無法跳兩格,慢指針就找到了中間節點
(2)分割,將slow後面的斷開,並將前後段繼續帶入函數繼續對兩段進行中斷分割…
分割結束的條件是,當前鏈表只剩下一個節點,就直接返回這個節點即可。
(3)合併,當我們把鏈表{2,3}分開之後,會分別返回鏈表:{2}與鏈表:{3},我們需要將這個鏈表合併,合併後將結果返回上一層遞歸,以便到上一層繼續合併。合併就是上一道題的歸併合併算法。
例子:我們以3,2,4,6舉例,遞歸(1)首先找中點,找到中點之後,將鏈表分爲兩個鏈表{3,2},以及{4,6}並分別代入遞歸(2)與遞歸(3),等待兩個遞歸的結果並將兩個鏈表合併;遞歸(2)首先找到中點,將鏈表分爲{3}(遞歸4),{2}(遞歸5);遞歸(4)直接返回{3},遞歸(5)直接返回{2};回到遞歸(2),將兩個鏈表進行合併,合併爲{2,3},這就是遞歸(2)的返回值;繼續走遞歸(3),同理,之後遞歸(3)會返回{4,6};回到遞歸(1),將{2,3}與{4,6}合併,就是{2,3,4,6}也就是我們的結果。
代碼
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
/*
1.快慢指針找到中間節點(當快指針到終點或無法跳兩格,慢指針就找到了中間節點)
2.斷開成兩段4-2,1-3
3.再斷開爲4,2;1,3
4.合併鏈表:4和2;1和3
5.合併鏈表:(2,4與1,3)使用歸併排序
*/
if(head==null)
return null;
//只剩一個就直接返回吧(比如當前傳入的鏈表中只剩下4自己)
if(head.next==null)
return head;
ListNode fast = head;
ListNode slow = head;
while(fast.next!=null&&fast.next.next!=null)
{
fast = fast.next.next;
slow = slow.next;
}
//斷開兩段
ListNode slowright = slow.next;
slow.next = null;
ListNode left = sortList(head);
ListNode right = sortList(slowright);
ListNode result = new ListNode(-1);
ListNode res = result;
//合併
while(left!=null&&right!=null)
{
int nowleft = left.val;
int nowright = right.val;
if(nowleft<nowright){
result.next = left;
left = left.next;
}
else{
result.next = right;
right = right.next;
}
result = result.next;
}
if(left!=null){
result.next = left;
}
if(right!=null){
result.next = right;
}
return res.next;
}
}
3.對鏈表進行插入排序
思路
其實思路很簡單,但是代碼寫起來容易出現bug,先說下思路吧。插入排序就是將一個值插入到我們已經排序好的鏈表中。首先我們將這個值與最大值去比較,如果比最大值大,那麼就放到最大值後面即可,然後更新最大值;如果比最大值小,那麼就需要找他究竟要插入到哪裏去,對於鏈表來說插入比較方便,將比他小的最大值節點找到即可,將這個節點與當前節點相連,當前節點與這個節點的後面相連就完成了。
以上敘述大致可以分爲以下幾個步驟
1.需要一個指針max指向當前已排序的最大值
2.當前指針對應的值如果大於最大值就直接插入即可,否則就進入內部循環
3.進入內部循環,可以使用一個指針pre,從頭向後找,當找到比now小的最大node,
就可以讓node的next指向now,now的next指向node.next即可。
注意事項:
1.我們如何找到比now小的最大node呢,可以判斷while(pre.next.val<now.val)。這樣結束循環時的pre就是比now小的最大node。
2.我們定義的節點(pre,max)不能是個null,要是個有具體值的節點,因爲當我們鏈表中已經有4後,想繼續加進來2,
我們需要讓2加到4前面,如果我們定義的節點都是null就會在第一次判斷head的時候,都賦值爲head(null不能設next)。
這樣就出現了問題,pre是4,就沒辦法把2添加到4前面。pre應該是指向一個不存在於head鏈表中的值,
如果pre.next第一次就比當前的now節點大,就應該直接把now插入到pre與pre.next之間。
3.我們設置一個ListNode res ,並讓pre和max都指向他,我們讓now就是head,最後返回的是res.next。
代碼
這其中有個巨大的坑,就是
//爲什麼一定要加上這句呢?
max.next = headnext;
爲什麼要加上這句呢,還是以4,2,1,3舉例,當我們head走到2的時候,此時鏈表爲:
res->4->2->1->3
pre->res
max->4
head->2
進行判斷後,我們要將2放到res與4之間,
(headnext爲1)
ListNode headnext = head.next;
//找到一個比now小的node,就可以讓node的next指向now,now的next指向node.next即可。
head.next = pre.next;
pre.next = head;
(pre歸位)
pre = res;
(讓head爲1)
head = headnext;
看起來代碼沒什麼問題,但你用手來畫一下就會發現這裏只是將res連接到2,2連接到4,4連接到2這條線根本沒有斷開!
所以當我們最後返回結果的時候就無法返回,因爲鏈表出現了環。所以我們要是用max.next = headnext,來將4連接到2這條線斷開,也就是將max與head的線斷開,之前我們考慮的都是head後面的要斷開,以及pre和pre後面的要斷開,可是沒有考慮前面連着head的也需要斷開。
完整代碼
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode insertionSortList(ListNode head) {
/*插入排序
1.需要一個指針指向當前已排序的最大值
2.當前指針對應的值如果大於最大值就直接插入即可,否則就進入內部循環
3.進入內部循環,可以使用一個指針,從頭向後找,當找到一個比now小的node,就可以讓node的next指向now,now的next指向node.next即可。
*/
ListNode res =new ListNode(Integer.MIN_VALUE);
ListNode pre= res;
ListNode max=res;
while(head!=null){
if(max.val<head.val){
max.next = head;
max = head;
head = head.next;
//max = head;
}
else{
while(pre.next.val<head.val){
pre = pre.next;
}
ListNode headnext = head.next;
//爲什麼一定要加上這句呢?
max.next = headnext;
//找到一個比now小的node,就可以讓node的next指向now,now的next指向node.next即可。
head.next = pre.next;
pre.next = head;
pre = res;
head = headnext;
}
}
return pre.next;
}
}
4.合併K個排序鏈表
第一種算法
對2個鏈表合併K-1次,也就是先將第一個和第二個合併,然後將合併的結果在與第三個鏈表合併,相當於2個鏈表合併了K-1次。
合併的算法就是歸併排序。
代碼
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
//先嚐試合併2個鏈表k-1次(合併第一個與第二個,然後用他們合併好的和第三個合併,直到最後一個)
if(lists.length==0)
return null;
ListNode res = lists[0];
for(int i=0;i<lists.length-1;i++)
{
ListNode node = merge(res,lists[i+1]);
res = node;
}
return res;
}
public ListNode merge(ListNode node1,ListNode node2){
ListNode res = new ListNode(-1);
ListNode head = res;
while(node1!=null&&node2!=null){
int val1 = node1.val;
int val2 = node2.val;
if(val1<val2){
res.next = node1;
node1 =node1.next;
}else{
res.next = node2;
node2 = node2.next;
}
res = res.next;
}
if(node1!=null){
res.next = node1;
}
if(node2!=null){
res.next = node2;
}
return head.next;
}
}
第二種算法
使用分治算法,上一種算法會導致一些節點多次重複判斷,比如list0中的節點,以下圖爲例,list0中的節點如果以上面的算法就會比較5次,與每個list中的節點都要比較一次,而分治算法,就只比較三次,比較的複雜度由N,變成了log2N,在乘上鍊表的個數就是O(k*log2N)
代碼
我們上面的思路用來理解思想,真實的方式是使用如下方法去解決,我們不合並左右的,我們去合併第一個與最後一個,第二個與倒數第二個,以此類推。
代碼需要注意的就是奇數偶數的情況,可以稍微用例子試一試,很容易寫錯,注意外層循環的次數爲(list.length+1)/2次循環,而不是list.length/2次循環,因爲如果長度爲3的話,要進行兩次循環,而非3/2=1,一次循環。
/**
* 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 times = lists.length;
//循環lists.length/2次(可以以中間分開對半合併,第一個與最後一個,第二個與倒數第二個,然後繼續折半)
//剩餘長度至少爲2纔可以做
while(times>1){
//奇數的話比如times爲5,就只需要比較0,4;1,3,而2不需要,因此是times/2=2次
//但是如果是偶數4的話,也需要比較0,3;1,2,同樣爲times/2=2次
for(int i=0;i<times/2;i++){
lists[i] = merge(lists[i],lists[times-1-i]);
}
//而到了這裏,如果奇數的話,5/2就是2了其實是三個,因此應該是(times+1)/2,,而4個的話,(4+1)/2也是2。
times = (times+1)/2;
}
return lists[0];
}
public ListNode merge(ListNode node1,ListNode node2){
ListNode res = new ListNode(-1);
ListNode head = res;
while(node1!=null&&node2!=null){
int val1 = node1.val;
int val2 = node2.val;
if(val1<val2){
res.next = node1;
node1 =node1.next;
}else{
res.next = node2;
node2 = node2.next;
}
res = res.next;
}
if(node1!=null){
res.next = node1;
}
if(node2!=null){
res.next = node2;
}
return head.next;
}
}
反轉鏈表
1.反轉鏈表 II
2.重排鏈表
思路
比較簡單就不放思路了。
代碼
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public void reorderList(ListNode head) {
if(head==null)
return;
//快慢指針找中間節點,然後翻轉中間節點之後的鏈表,並與前面的鏈表拼接。
ListNode fast = head;
ListNode slow = head;
while(fast!=null&&fast.next!=null){
fast = fast.next.next;
slow = slow.next;
}
//斷開中間節點之後的鏈表
ListNode second = slow.next;
slow.next = null;
//翻轉鏈表
ListNode pre = null;
while(second!=null){
ListNode next = second.next;
second.next = pre;
pre = second;
if(next!=null)
second = next;
else
break;
}
/*連接第一段與翻轉的第二段
第二段小於等於第一段的長度
比如奇數長度:12345,第二段就是54
偶數長度1234,第二段就是43
所以第二段不指向空即可
*/
while(second!=null){
ListNode onenext = head.next;
ListNode twonext = second.next;
head.next = second;
second.next = onenext;
head = onenext;
second = twonext;
}
return ;
}
}