用O(nlogn)的时间复杂度对链表排序(归并排序)
(为什么写这个题呢?因为恰好我们可以复习下排序算法的时间复杂度),题中要求时间复杂度为O(nlogn),显然从下表可以看出来归并排序和堆排是可以实现的,这里我采用归并排序的方法说一下思路。
思路:我们先利用快慢指针的方法,找出链表的中间节点,然后对左右两个部分分别做同样的操作,直到剩下一个元素,那元素就是有序的。最后再将有序的数组归并。我这里解释一下这段代码:
这个是将两个链表归并起来意思(只是完整代码的一部分),比如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).
(以上就是博主做题过程中发现的问题,觉得有学到知识,感谢这位大佬的启示归并排序,解决了我的困惑,疯狂笔芯)