假設鏈表節點的數據結構爲:
typedef struct node
{
int data;
struct node* next;
}ListNode,*pListNode;
要求實現將一個單向鏈表逆置並檢測如果鏈表成環返回FALSE,從參數返回新的鏈表頭。
BOOL Reverse(pListNode& head);
這個問題看似簡單,實際上太能發揮了。首先因爲是單向鏈表,next不能簡單的改變方向,要記錄下即將被斷開的節點,而即將被斷開的節點它的next也是同樣的問題。再者就是要如何檢測有環。
單看逆置問題,通常的方法應該要用到3個指針:當前的p,下一個q,下一個的下一個head。 記下第三個指針,用第二個指向第一個,然後後移一步重複動作直到q沒有下一個,再封口。
{
if (head != NULL && head->next != NULL)
{
pListNode p = head;
pListNode q = head->next;
p->next = NULL;
while (q->next != NULL)
{
head = q->next;
q->next = p;
p = q;
q = head;
}
head->next = p;
}
return; //此法未處理成環問題
}
單看檢測成環問題,需要兩個指針,一個向後一次移動一步,一個向後一次移動兩步。假如他們能夠相遇就是成環,他們任意一個走完鏈表都不成環。
{
if( head==NULL)return;
pListNode step1 = head;
pListNode step2 = head;
while(step1->next)
{
step1 = step1->next;
if(step2->next)step2 = step2->next;
else return TRUE; //不成環
if(step2->next)step2 = step2->next;
else return TRUE; //不成環
if(step1==step2)return FALSE;
}
return TRUE; //不成環
}
以上兩種方法都單獨解決了部分問題,能不能同時解決兩個問題呢?
於是我想到了遞歸。且不說它的弊端,單就解決問題而言是可行的。
BOOL Reverse(pListNode& head) //不能處理成環檢測
{
pListNode p = head;
if(p->next)
{
head = p->next;
Reverse(head);
p->next->next = p;
}
else
{
return FALSE;
}
return FALSE;
}
這個方法的問題是沒法處理成環的問題。 在遞歸的背景下,解決成環的思路就想到如何標記一下處理過的node。於是我想到一個另類的做法。使用一個輔助結構體記錄前後兩個節點,把節點本身的next標爲’1’,如果處理過程中發現next爲1就是成環啦。
struct aid
{
struct node* left;
struct node* next;
};
BOOL Reverse(pListNode& head) //同時處理逆置和環檢測
{
if(!head)return FALSE;
pListNode p = head;
if(p->next)
{
if(p->next==1)return TRUE; //成環
aid *a = new aid;
a->left = p;
a->right = p->next;
head = p->next;
p->next = 1;
if(Reverse(head)==TRUE)
{
delete a;
return TRUE;
}
a->right->next = a->left;
a->left->next = 0;
delete a;
}
else
{
return FALSE;
}
return FALSE;
}
注意,以上代碼都沒有實測!請以思路爲主自行參考嘗試實現。