题目
给定一个有序的循环链表,在其中插入一个值,保持该循环链表依然有序。
分析
首先看下循环链表的结构,如下图所示为一个循环链表,其尾结点指向头结点,从而形成一个循环。给定的链表结点可以是链表任意一个结点,这个结点不一定是链表头结点,从而这也增加了该题的难度。
此时若是要在链表中插入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到链表头部的前面。
插入到起始点前面。
第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开始,照样是有序的。