【LeetCode題解】 ——用O(nlogn)的時間複雜度對鏈表排序(歸併排序)

用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).

(以上就是博主做題過程中發現的問題,覺得有學到知識,感謝這位大佬的啓示歸併排序,解決了我的困惑,瘋狂筆芯)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章