過程
迭代過程中需要三個指針:
-
p
用來指向當前待處理的結點 -
lastp
指向上一個處理完的結點,初始為空。這樣的話,舊鏈表首結點就是新鏈表尾結點,尾結點后會自動填充NULL指針。 -
nextp
指向p
的下一個結點。
例如:當前需要處理2號結點
-
分配nextp
-
反轉p和lastp之間的鏈接
-
更新lastp
-
更新p
有趣的是,三者賦值的代碼首尾相接:
ListNode* nextp=p->next;
p->next=plast;
plast=p;
p=nextp;
循環終止條件
需要注意的是,在處理最后一個結點的時候,因為我們需要保留最后一個結點作為新鏈表的頭部,所以一定是while(p->next)而非while(p)。前者終止時條件觸發,p指向最后一個結點;后者終止時p為空。
此時,最后一個結點的反轉需要額外完成。
CODE
ListNode* inverse(ListNode* head) {
//一個表示當前待處理的結點;一個是上次已經處理的結點
ListNode* p=head,*plast=nullptr;
//初始為null是基于最后結點的next是null的規定
//第一個結點反轉后就是最后的結點,next應該為null
//循環終止條件:如果是while(p)那么循環結束時p為空,舊鏈表的尾部即新鏈表的頭部就丟失了,所以一定要是while(p->next)此時p指向尾結點
while(p->next){
//預先保留p的next指針域,因為會被后面的沖掉
ListNode* nex=p->next;
//完成鏈接的反轉
p->next=plast;
//注意以下兩句的順序
plast=p;
p=nex;
}
//記住,循環結束時,p指向尾結點,它和plast還需要反轉其鏈接。
p->next=plast;
head=p;
return head;
}
反轉鏈表的特定部分
將鏈表123456反轉中間的345號結點,使得最終順序為125436
為了便于后續操作,我們建立了一個逆序函數:
CODE
ListNode* reverL(ListNode* h,ListNode* tnext){
ListNode* p=h,*lastp=nullptr,*nextp;
while(p->next!=tnext){
nextp=p->next;
p->next=lastp;
lastp=p;
p=nextp;
}
p->next=lastp;
return p;
}
該函數加入了一個參數:tnext表示待反轉部分中,最后結點的下一個位置。這樣做可以把該參數設置為NULL來反轉整個鏈表。返回新鏈表的首指針。
注意:整個過程僅僅是改變了結點的指針域,原來的h和tnext指針依舊有效。
K逆序
有一個單鏈表,請設計一個算法,使得每K個節點之間逆序,如果最后不夠K個節點一組,則不調整最后幾個節點。例如鏈表1->2->3->4->5->6->7->8->null,K=3這個例子。調整后為,3->2->1->6->5->4->7->8->null。因為K==3,所以每三個節點之間逆序,但其中的7,8不調整,因為只有兩個節點不夠一組。
這個結構就像一段一段接起來曬的香腸一樣,處理方式也不復雜,簡單來說就是從頭開始數,數夠K個結點,把這K個結點反轉。直到遍歷至尾,整個過程中記得用一個變量保存上一次處理完的部分尾結點,便于拼接。
CODE
ListNode* inverse(ListNode* head, int k) {
if(!head || k<=1 )
return nullptr;
//使用帶頭結點的鏈表,會省很多麻煩
ListNode *H=new ListNode(0);//記得刪除
H->next=head;
//tail用來遍歷,last記錄上一個處理完的區段末尾,初始為H
ListNode *last=H,*tail=head;
int cnt=0;
while(tail){
cnt++;
if(cnt!=k){
tail=tail->next;
}else{
ListNode* tnext=tail->next,*h=last->next;
ListNode* newh=reverL(last->next,tnext);
//被處理區段的前后串接
h->next=tnext;
last->next=newh;
//更新last和tail
last=h;
tail=tnext;
cnt=0;
}
}
ListNode* u=H->next;
delete H;
return u;
}