【LeetCode题解】 ——用O(nlogn)的时间复杂度对链表排序(归并排序)

用O(nlogn)的时间复杂度对链表排序(归并排序)

【LeetCode题解】 ——用O(nlogn)的时间复杂度对链表排序(归并排序)_第1张图片
(为什么写这个题呢?因为恰好我们可以复习下排序算法的时间复杂度),题中要求时间复杂度为O(nlogn),显然从下表可以看出来归并排序和堆排是可以实现的,这里我采用归并排序的方法说一下思路。
【LeetCode题解】 ——用O(nlogn)的时间复杂度对链表排序(归并排序)_第2张图片
思路:我们先利用快慢指针的方法,找出链表的中间节点,然后对左右两个部分分别做同样的操作,直到剩下一个元素,那元素就是有序的。最后再将有序的数组归并。我这里解释一下这段代码:
【LeetCode题解】 ——用O(nlogn)的时间复杂度对链表排序(归并排序)_第3张图片
这个是将两个链表归并起来意思(只是完整代码的一部分),比如list1=1->4; list2=3,我们顺着这个逻辑走一下上述代码
(1)开始list1->val(也就是1)小于list2->val(也就是3),所以进入第三个if条件,list1->next=list1下一个节点和list2归并的结果
(2)也就是list1->val(这里是4)>list2->val(这里是3),所以进入else条件,list2->next=list1和list2->next归并的结果。
(3)由于list->next等于NULL,返回list1,此时的list1是val值为4的这个节点,当前的条件终止,开始返回,由于是递归,它继续返回到上一层,也就是说list2->next=list1=4这个节点,继续执行return list2,list2在这里变成3->4
(4)再次返回给(1),list1->next=list2,此时list2=3->4,所以list1=1->3->4.最后执行return list1这一步,也就是1->3->4.

以上就是这个代码的过程。我直接贴出过了的代码:

class Solution 
{
    public:         
    ListNode *merge(ListNode *list1,ListNode *list2)     
    {        
        if(list1==nullptr)            
            return list2;        
        if(list2==nullptr)            
            return list1;        
        if(list1->val < list2->val)        
        {            
            list1->next = merge(list1->next,list2);            
            return list1;        
        }        
        else        
        {            
            list2->next = merge(list1,list2->next);            
            return list2;        
        }     
    }    
    ListNode *sortList(ListNode *head)     
    {        
        if(!head || !head->next)            
            return head;        
        ListNode *slow=head,*fast=head;        
        while(fast->next && fast->next->next)         {            
            slow = slow->next;            
            fast = fast->next->next;        }        
        fast = slow->next;        
        slow->next = nullptr;        
        slow = head;        
        ListNode *left = sortList(slow);        
        ListNode *right = sortList(fast);        
        return merge(left,right);    
    }
};

上述代码使用到了归并排序,我再简单回忆下归并排序,比如说我们要对两个有序数组进行排序,我们肯定会先比较两个数组第一个数字,取最小的那个数,然后将这个数排除在外,继续比较它的下一个元素和另一个数组元素的大小,我们很容易的就能写出代码:

void MeregeArray(int* array1,int m, int* array2,int n,int* res)
{
	int pos1 = 0, pos2 = 0,pos=0;
	while (pos1 < m&&pos2 < n)
	{
		if (array1[pos1] > array2[pos2])
			res[pos++] = array2[pos2++];
		if (array1[pos1] < array2[pos2])
			res[pos++] = array1[pos1++];
	}
	
	while (pos1 < m)
		res[pos1++] = array1[pos1++];
	while (pos2 < n)
		res[pos2++] = array2[pos2++];
}

但是不要忘了,在上题中,我定义的两个数组可是有序数组,二般情况下数据都不会这么规律,所以该咋办?利用二分法对数组进行分割,分到最后只有一个元素了,那么恭喜,我们的数组就是有序的,也许你很难理解分割成一个元素怎么归并成一个完整数组的过程,往下看:

void MergeArray(int* array,int first,int last,int mid, int* temp)
{
	int i = first, j = mid+1;
	int m = mid, int n = last;
	int k = 0;
	while (i <= m && n <= j)
	{
		if (array[i] <= array[j])
			temp[k++] = array[i++];
		if (array[j] <= array[i])
			temp[k++] = array[j++];
	}
	while (i <= m)
		temp[k++] = array[i++];
	while (j <= n)
		temp[k++] = array[j++];
}
void MergeSort(int*array, int first, int last, int* temp)
{
	if (first < last)
	{
		int mid = (first + last) / 2;
		MergeSort(array, first, mid, temp);
		MergeSort(array, mid + 1, last, temp);
		MergeArray(array, first, last,mid, temp);
	}
}

看的出来归并排序道理其实很简单,那我们现在对它的时间复杂度进行一个分析:如果数列的长度为n,那我们将一个数列分成小数列一共需要logn步,每一步都是合并有序数列的过程,也就是说每一步的时间复杂度为O(n),那总共有n步,所以归并排序的时间复杂度为O(nlogn).

(以上就是博主做题过程中发现的问题,觉得有学到知识,感谢这位大佬的启示归并排序,解决了我的困惑,疯狂笔芯)

你可能感兴趣的:(数据结构,C++)