对于一个链表,我们需要用一个特定阈值完成对它的分化,使得小于等于这个值的结点移到前面,大于该值的结点在后面,同时保证两类结点内部的位置关系不变。
给定一个链表的头结点head,同时给定阈值val,请返回一个链表,使小于等于它的结点在前,大于等于它的在后,保证结点值不重复。
测试样例:
{1,4,2,5},3
{1,2,4,5}
方法一,快排Partition,但不满足两类节点内部的位置关系不变,因为快排不稳定,所以不是正确答案但是可以参考。
class Divide {
public:
ListNode* listDivide(ListNode* head, int val) {
if(head != NULL && head->next != NULL && head->next->next != NULL)
head = partition_list(head, val);
return head;
}
private:
ListNode* partition_list(ListNode* head, const int val){
ListNode* end = head;
ListNode* fin = NULL;
while(end->next != NULL){
end = end->next;
if(end->val == val)
fin = end;
}
if(fin == NULL){
if((fin = end)->val != val) //统一fin为关键值
return NULL;
}
std::swap(end->val, fin->val);
ListNode* index = head;
ListNode* small = NULL;
for(index=head; index!=end; index=index->next){
if(index->val < val){
small = small == NULL ? head : small->next;
if(index != small)
std::swap(index->val, small->val);
}
}
small = small == NULL ? head : small->next;
std::swap(small->val, end->val);
return head; //fin maybe NULL
}
};
值得注意的就是small初始为NULL,所以后面一定要提防段错误在small上发生。
方法二:划分为两个链表,然后合并
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Divide {
public:
ListNode* listDivide(ListNode* head, int val) {
if(head != NULL){
ListNode *less = new ListNode(0); //申请一个头结点,就可以用next,不用考虑指针情况减少段错误
ListNode *greater = new ListNode(0);
ListNode *less_iter = less;
ListNode *greater_iter = greater;
ListNode *iter = head;
while(iter != NULL){
if(iter->val <= val){
less_iter->next = iter;
less_iter = less_iter->next;
}
else if(iter->val > val){
greater_iter->next = iter;
greater_iter = greater_iter->next;
}
iter = iter->next; //迭代
}
greater_iter->next = NULL; //断开,不然会藕断丝连
if(less->next != NULL){ //不要忘了没有小于数的情况
head = less->next;
less_iter->next = greater->next;
}
else
head = greater->next;
delete less;
delete greater;
}
return head;
}
};
我在用该方法时申请了两个头结点为了避免后续对NULL指针的来回判断以及预防段错误的出现,不过还是要注意一些特殊情况,注释中都给明了。
方法三:不使用头结点
class Divide {
public:
ListNode* listDivide(ListNode* head, int val) {
// write code here
ListNode *lefthead=NULL, *leftnode=NULL, *righthead=NULL,*rightnode=NULL;
if (head == NULL || head->next == NULL) {
return head;
}
ListNode *node = head;
while (node != NULL) {
if (node->val<=val) {
if (lefthead == NULL) lefthead = node; //如果不使用头结点,此时要处理,否则段错误
else leftnode->next = node;
leftnode = node;
node = node->next;
}
else{
if (righthead == NULL) righthead = node;
else rightnode->next = node;
rightnode = node;
node = node->next;
}
}
if (lefthead == NULL) return righthead;
if (righthead == NULL) return lefthead;
rightnode->next = NULL;
leftnode->next = righthead;
return lefthead;
}
};
可以看出,为了防止为NULL,在循环中用了if处理,后面也有多处检查是否为NULL,各有优劣吧。
总结就是:如果不使用头结点,那就必须时刻防止给一个NULL指针使用->next来访问next成员!