題目
給定一個有序的迴圈鏈表,在其中插入一個值,保持該迴圈鏈表依然有序。
分析
首先看下迴圈鏈表的結構,如所示為一個迴圈鏈表,其尾結點指向頭結點,從而形成一個迴圈。給定的鏈表結點可以是鏈表任意一個結點,這個結點不一定是鏈表頭結點,從而這也增加了該題的難度。
此時若是要在鏈表中插入4,則插入後的鏈表如下所示:
可以看到插入4後,鏈表依然有序。
在解決這個問題前,先來看一個簡化的問題,就是在一個有序單鏈表中插入結點,仍然保證其有序。這個問題的代碼相信多數人都很熟悉,一般都是分兩種情況考慮:
1)如果原來鏈表為空白或者插入的結點值最小,則直接插入該結點並設定為頭結點。
2)如果原來鏈表非空,則找到第一個大於該結點值的結點,並插入到該結點的前面。如果插入的結點值最大,則插入在尾部。
代碼如下:
//鏈表結點定義struct node { int data; struct node *next;};typedef struct node Node;
void sortedInsert3(struct node** headRef, struct node* nd){ if (*headRef==NULL || (*headRef)->data>=nd->data) { //情況1 nd->next = *headRef; *headRef = nd; } else { //情況2 struct node* current = *headRef; while (current->next != NULL && current->next->data < nd->data) current = current->next; nd->next = current->next; current->next = nd; }}
當然也可以把情況1和情況2放到一起考慮,代碼會更簡潔,如下所示:
void sortedInsert2(struct node** headRef, struct node* nd){ struct node** current = headRef; while ( (*current)!=NULL && ((*current)->data<nd->data) ) { current = &((*current)->next); } nd->next = *current; *current = nd;}
上面代碼很簡潔,注意它是怎麼處理特殊情況的。
1)當鏈表為空白或者插入的結點值最小時,則直接跳出迴圈,設定nd->next=NULL, *headRef=nd.
2)其他情況,則current為待插入結點的位置,直接將其插入即可。包括插入到尾部或者任意常規的位置。
解決方案
解決了一個輕量級的問題之後再來看原題,該題目與上面不同的地方在於它是一個迴圈鏈表,那麼情況會有所不同,設待插入的結點值為x,則至少需要考慮下面三種情況:
1.prev->val ≤ x ≤ current->val:
插入到prev和current之間。
2. x是鏈表中最小或者最大的值:
插入x到鏈表頭部的前面。
3.回到起始點:
插入到起始點前面。
第1和2種情況還比較容易考慮到,但是第3種情況往往會被忽略,先給出代碼,再根據代碼來分析這些情況為什麼都需要考慮到。
void insert(Node *& aNode, int x) { //鏈表為空白,建立節點返回 if (!aNode) { aNode = new Node(x); aNode->next = aNode; return; } Node *p = aNode; Node *prev = NULL; do { prev = p; p = p->next; if (x <= p->data && x >= prev->data) break; // 情況1,找到正常位置返回,如例子中插入4到鏈表中 if ((prev->data > p->data) && (x < p->data || x > prev->data)) break; // 情況2,x最小或者最大,插入到鏈表最前面 } while (p != aNode); // 情況3,回到起始結點,停止. Node *newNode = newNode(x); newNode->next = p; prev->next = newNode;}
1)鏈表只有一個結點
如果x等於該結點值,則直接在情況1處理。否則情況3處理。
2)鏈表包含重複值
如果鏈表為1-*1-1,起始結點為第二個1,則在其中插入2時,由情況3處理,即最終插入到初始結點前面。會變成1-2-1-1.迴圈鏈表從第二個1開始,照樣是有序的。