天天AC系列(十二):K個一組翻轉鏈表

1 題目

LeetCode第25題,每K個節點一組進行翻轉,剩下不足K個的保留原狀.java

在這裏插入圖片描述

2 直接翻轉

將鏈表分紅三部分,已翻轉,待翻轉,未翻轉三部分:node

在這裏插入圖片描述

首先,用一個指針t表示要插入的位置的前驅,一邊把head移動k遍,一邊插入在t後面(下圖假設k=3):git

在這裏插入圖片描述

int i=0;
for(;i<k && head != null;++i)
{
    temp = head.next;
    head.next = t.next;
    t.next = head;
    head = temp;
}

直接插在t的後面,一輪循環以後,移動t與head.github

在這裏插入圖片描述

t的新位置爲未插入head以前的head的位置,所以在插入以前把head的位置保存下來,直接使t移動到該位置,head的位置爲天然移動到的位置,不需改變。算法

ListNode nextTPosition = head;
ListNode temp;
int i=0;
for(;i<k && head != null;++i)
{
    temp = head.next;
    head.next = t.next;
    t.next = head;
    head = temp;
}
if(i == k)
    t = nextTPosition;

接着再翻轉,到達7後,7不須要翻轉,由於剩下的節點數不足:ide

在這裏插入圖片描述

這時就須要i發揮做用了,i表示已翻轉的節點的值,由於只是一次遍歷,每遍歷k次便翻轉k次,若i小於k,因爲已經翻轉了剩下的i個節點,所以須要再將這剩下的i個節點翻轉一次:優化

if(i == k)
    t = nextTPosition;
else
{
    for(head = t.next,t.next=null;head!=null;)
    {
        temp = head.next;
        head.next = t.next;
        t.next = head;
        head = temp;
    }
    break;
}

對剩下的i個節點再次翻轉時,不須要修改t的位置,使head指向t.next,再把t.next置爲null,由於此時t爲3d

4->7

若不把t.next置爲null,在指針

head.next = t.next

這一步會使head.next指向錯誤的t.next,致使會在最後一個節點不斷循環。
翻轉最後的i個節點後,跳出循環,返回結果。code

在這裏插入圖片描述

3 遞歸

遞歸的話思路也相似,遍歷k次,翻轉k個,若還有須要翻轉的節點,遞歸翻轉,若沒有,翻轉剩下的i個節點。

if(i == k)
    t.next = reverse(head,k);

大部分代碼與循環相同就不貼了,最大的不一樣是這裏,這裏的t爲原來未遍歷前的head,由於改爲遞歸後,不須要使用t做爲移動的指針指示插入的位置,t.next就至關於翻轉後的最後一個節點,把遞歸的結果插入到這個節點的後面。

在這裏插入圖片描述

4 使用額外空間--棧

由於題目規定只能使用常數的額外空間,所以應該只有這兩種方法了,可是,若是容許使用額外的空間,可使用棧優化直接翻轉的算法。

由於出棧的次序正是翻轉的順序,每遍歷k個節點就壓棧k個節點,若剩餘不足k個節點,把head連上dummy,若還有多餘的節點或者恰好遍歷完,把出棧的節點依次連上主鏈。

while(true)
{
    Stack<ListNode> s = new Stack<>();
    ListNode temp = head;
    int i=0;
    for(;i<k && head != null;++i)
    {
        s.add(new ListNode(head.val));
        head = head.next;
    }
    if(i == k)
    {
        for(i=0;i<k;++i)
        {
            t.next = s.pop();
            t = t.next;
        }
    }
    else if(head == null)
    {
        t.next = temp;
        break;
    }
}

其中for循環爲遍歷壓棧,i==k判斷是否翻轉鏈表。

5 源碼

github

碼雲

相關文章
相關標籤/搜索