單鏈表的就地逆置(頭插法和遞迴法)【轉】
單鏈表的就地逆置是指輔助空間O(1)的逆置方法,有兩種方法:普通迴圈(頭插法重新建立帶頭節點的新連結串列)和遞迴。下面我們詳細介紹這兩種方法:
方法一:頭插法
演算法思想:逆置連結串列初始為空,表中節點從原連結串列中依次“刪除”,再逐個插入逆置連結串列的表頭(即“頭插”到逆置連結串列中),使它成為逆置連結串列的“新”的第一個結點,如此迴圈,直至原連結串列為空。
-
void converse(LinkList *head)
-
{
-
LinkList *p,*q;
-
p=head->next;
-
head->next=NULL;
-
while(p)
-
{
-
/*向後挪動一個位置*/
-
q=p;
-
p=p->next;
-
/*頭插*/
-
q->next=head->next;
-
head->next=q;
-
}
-
}
頭插法圖解:
補充:
對於不含頭結點的單鏈表的頭插法原地逆置
-
ListNode* converse(ListNode *head)
-
{
-
ListNode* dumyOfInsertedList = new ListNode(-1);//一個啞結點,用於建立新的連結串列
-
if(head == NULL) return NULL;
-
ListNode* unInsertedHead = head;// 使用unInsertedHead和inInsertHead_next作為待逆置連結串列的首結點和次首結點的便籤
-
ListNode* unInsertedHead_next = head->next;
-
while(unInsertedHead)// 頭插直到原連結串列為空
-
{
-
unInsertedHead->next = dumyOfInsertedList->next;
-
dumyOfInsertedList->next = unInsertedHead;
-
unInsertedHead = unInsertedHead_next;
-
if(unInsertedHead_next!=NULL)// 注意:最後一個結點時,unInsertedHead_next為NULL沒有next成員變數
-
{
-
//cout<<"head:"<< unInsertedHead->val<<"next:"<<unInsertedHead_next->val<<endl;
-
unInsertedHead_next = unInsertedHead_next->next;
-
}
-
}
-
return dumyOfInsertedList->next;
-
}
方法二:遞迴
演算法思想:先假定有一個函式,可以將以head為頭結點的單鏈表逆序,並返回新的頭結點。利用這個函式對問題進行求解:將連結串列分為當前表頭結點和其餘部分,遞迴的過程就是,先將表頭結點從連結串列中拆出來,然後對其餘部分進行逆序,最後將當前的表頭結點連結到逆序連結串列的尾部。遞迴的終止條件就是連結串列只剩一個節點時,直接返回這個節點。
-
/*
-
dfs深度遍歷這個單鏈表
-
在遞迴的時候,不作處理
-
在回溯時,標記回溯開始的位置(新的頭結點),將節點和節點的下一節點逆置,將新的頭結點傳遞給上一層遞迴,
-
接下來逆置上一個節點和上一節點的下一個節點(逆置已逆置連結串列的尾節點),並傳遞標記的頭結點,直到回溯完成
-
我們就得到了逆轉的單鏈表和它的頭結點
-
dfs(當前狀態)
-
{
-
if(邊界狀態) //head==NULL說明連結串列為空,不用處理, 從頭到尾我們需要處理的最後一個節點是倒數第二個節點,將它和倒數第一逆置,因此,遍歷到倒是第一個節點就是邊界條件
-
{
-
記錄 //標記頭結點位置並傳遞(這裡選用返回值,還可以通過全域性變數)給上一層遞迴
-
}
-
dfs(下一狀態)
-
//回溯部分 //節點和下一節點(逆置這已逆置連結串列的尾結點和),將新的頭結點傳遞給上一層遞迴
-
}
-
*/
-
ListNode *reverse(ListNode *head)
-
{
-
if(head==NULL || head->next ==NULL)
-
return head;
-
/*遞迴*/
-
ListNode* headOfReverse = reverse(head->next);
-
// cout<<head->next<<" "<<headOfReverse<<endl;
-
/*回溯:將當前表頭結點連結到逆序連結串列的尾部*/
-
head->next->next = head;
-
head->next = NULL;
-
return headOfReverse;
-
}
遞迴法圖解: