链表的分化(Partition一分为二)

对于一个链表,我们需要用一个特定阈值完成对它的分化,使得小于等于这个值的结点移到前面,大于该值的结点在后面,同时保证两类结点内部的位置关系不变。

给定一个链表的头结点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成员!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值