【阿里面试】链表排序总结

题目:对单链表进行排序,要求不改变每个节点内的值。

1.首先自然而然地会想到快速排序,毕竟是一个经典的排序算法。但是在排序过程中要求不能改变节点中的值,就必须通过改变节点中的指针来交换节点。而且快速排序的时间复杂度的下限是O(N^2),当链表接近有序时,可能还没有插入排序来的快。

直接上代码(代码是康神 撸的,我太菜撸不出来尴尬

//List Quick Sort
class Solution {
public:
	ListNode* sortList(ListNode* head) {
		if (head == NULL) return head;
		ListNode dummy(-1);
		dummy.next = head;
		quicksort(&dummy, NULL);
		return dummy.next;
	}
private:
	void quicksort(ListNode* pre, ListNode* end) {
		if ( pre->next == end|| pre->next->next == end)
			return;
		ListNode* pivotpos = partition(pre, end);
		quicksort(pre, pivotpos);
		quicksort(pivotpos, end);
	}
	ListNode* partition(ListNode* pre, ListNode* end)
	{
		ListNode* prehead = pre;
		ListNode* head = pre->next;
		ListNode* prepivotpos = pre;
		ListNode* pivotpos = pre->next;
		ListNode* swapnode = pivotpos->next;
		ListNode* preswap = pivotpos;
		int pivot = pivotpos->val;
		while (swapnode != end)
		{
			if (swapnode->val < pivot)
			{
				prepivotpos = pivotpos;
				pivotpos = pivotpos->next;
				if (pivotpos != swapnode)
					swapLink(prepivotpos, pivotpos, preswap, swapnode);
			}
			preswap = swapnode;
			swapnode = swapnode->next;
		}
		swapLink(prehead, head, prepivotpos, pivotpos);
		return pivotpos;
	}
	void swapLink(ListNode* &prepivotpos, ListNode* &pivotpos, ListNode* &preswap, ListNode* &swapnode)
	{
		if (pivotpos->next == swapnode)
		{
			ListNode* end = swapnode->next;
			prepivotpos->next = swapnode;
			swapnode->next = pivotpos;
			pivotpos->next = end;
		}
		else
		{
			ListNode* endpivot = pivotpos->next;
			ListNode* endswap = swapnode->next;
			prepivotpos->next = swapnode;
			swapnode->next = endpivot;
			preswap->next = pivotpos;
			pivotpos->next = endswap;
		}
		swap(pivotpos, swapnode);
	}
};

2.其实这是leetcode上的原题,但是原题并没有规定不能改变节点中的值。上述代码在leetcode中运行超时,主要原因在于,当测试用例中包含大量重复值的时候,快排性能迅速下降,所以改用三路快排的方法,以pivot为基准值把原链表分成小于、等于、大于pivot的三条链表,分别递归排序后再串联。


//List three-way Quick Sort
class Solution {
public:
	ListNode* sortList(ListNode* head) {
		if (NULL == head || NULL == head->next) return head;
		int pivot = head->val;
		ListNode less(-1), equal(-1), greater(-1);
		ListNode *lt = &less, *eq = &equal, *gt = &greater;

		while (head != NULL) {
			if (head->val < pivot) {
				lt->next = head;
				lt = lt->next;
				head = head->next;
				lt->next = NULL;
			}
			else if (head->val > pivot) {
				gt->next = head;
				gt = gt->next;
				head = head->next;
				gt->next = NULL;
			}
			else {
				eq->next = head;
				eq = eq->next;
				head = head->next;
				eq->next = NULL;
			}
		}
		ListNode* left = sortList(less.next);
		ListNode* right = sortList(greater.next);
		if (NULL == left) {
			left = equal.next;
		}
		else {
			lt = &less;
			while (lt->next != NULL) lt = lt->next;
			lt->next = equal.next;
		}
		eq = &equal;
		while (eq->next != NULL) eq = eq->next;
		eq->next = right;
		return left;
	}
};

3.链表排序也适合用归并排序,且时间复杂度的下限是O(NlogN),只是在遍历寻找链表的中间节点的时候比较费时,处理大量数据的时候尤其突出。

//List Merge Sort
class Solution {
public:
	ListNode* sortList(ListNode* head) {
		if (!head || !head->next) return head;
		ListNode *fast = head, *slow = head;
		while (fast->next&&fast->next->next)
		{
			fast = fast->next->next;
			slow = slow->next;
		}
		fast = slow->next;
		slow->next = NULL;
		ListNode *l1 = sortList(head);
		ListNode *l2 = sortList(fast);
		return mergeTwoLists(l1, l2);
	}
private:
	ListNode* mergeTwoLists(ListNode* lhs, ListNode* rhs) {
		ListNode dummy(-1);
		ListNode *cur = &dummy;
		while (lhs&&rhs)
			if (lhs->val < rhs->val)
			{
				cur->next = lhs;
				cur = lhs;
				lhs = lhs->next;
			}
			else
			{
				cur->next = rhs;
				cur = rhs;
				rhs = rhs->next;
			}
		if (lhs)
			cur->next = lhs;
		if (rhs)
			cur->next = rhs;
		return dummy.next;
	}
};

关于三路快排与双基准排序,准备有时间了再写一个总结。

http://www.cnblogs.com/chenying99/archive/2014/07/10/3836816.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值