分治系列——315. Count of Smaller Numbers After Self[hard]

題目

You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].

Example:

Given nums = [5, 2, 6, 1]

To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.

Return the array [2, 1, 1, 0].

大意:求出每個數字右側比它小的個數


解法一(超時)

遍歷窮舉,全部比較一遍(然而是 HARD,寫的時候我就猜到會超時了)



/*class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        vector<int> result(nums.size(), 0);

	for (int i = 0; i < nums.size(); i++) {
		for (int j = i + 1; j < nums.size(); j++) {
			if (nums[i] > nums[j])
				result[i]++;
		}
	}

	return result;
    }
};*/



解法二

後來,看討論區,說是利用歸併排序,比較交換次數得到結果。可是,歸併兩個有序數組時,每個數字下標是在改變的,怎麼樣去記錄交換次數呢?後來翻到了這篇博客,https://siukwan.sinaapp.com/?p=1049    ——博主創建了一個結構保存 value,index和交換次數,受到啓發之後開始編寫,同時,編寫歸併排序的借鑑於博客  http://blog.csdn.net/morewindows/article/details/6678165


當然我只是借鑑他們的思想,代碼裏還有我的一些思考,現在貼出代碼並進行解釋:



class Solution {
public:

//(1)
   struct Combine
{
	int value;
	int index;
	int right;

	Combine(int v, int i, int r) {
		value = v;
		index = i;
		right = r;
	}

	bool operator <= (Combine& t) {
		return (this->value <= t.value);
	}

};

//(2)
void twoArray(vector<Combine>&a, int first1, int first2, int last,vector<Combine> b) {

	int i = first1;
	int j = first2+1;
	//int k = b.size();
	//vector<int> result(a.size() , 0);

	while (i <= first2 && j <= last) {
		if (a[i] <= a[j])
			b.push_back( a[i++]);
		else {
		    
		  //(4)
			for(int h = i; h <= first2;h++)
				a[h].right++;

			b.push_back(a[j++]);

		}
	}

	while (i <= first2)
		b.push_back(a[i++]);

	while (j <= last)
		b.push_back(a[j++]);

	for (int o = 0; o < b.size(); o++) {
		a[first1+o] = b[o];
	}

	b.clear();
	//b = result;
}

//(3)
void sort22(vector<Combine>& a, int first, int last, vector<Combine> b) {

	if (first < last) {
		int mid = (first + last) / 2;

		sort22(a, first, mid,b);
		sort22(a, mid+1, last,b);

		 twoArray(a, first, mid,last,b);
	}
}
/*設計一個數據結構,這個數據結構記錄了原始數組的val
,原始數組的idx,以及所求值cnt,然後對這個新的數據結構數組進行mergeSort,同時進行統計。*/
vector<int> countSmaller(vector<int>& nums) {
	vector<Combine> tmp,b;
	for (int i = 0; i < nums.size(); i++) {
		tmp.push_back(Combine(nums[i], i, 0));
	}

	sort22(tmp, 0, tmp.size() - 1, b);

	vector<int> hhh(nums.size(), 0);

	for (int i = 0; i < nums.size(); i++) {
		hhh[tmp[i].index] = tmp[i].right;
	}

	return hhh;
}
};




解釋

首先簡單解釋  歸併排序:

歸併排序由兩個函數構成,一個是,twoArray, 一個是sort22。 前一個用來把兩個有序數組合併成一個,後一個把兩個數組變成有序數組(當數組只剩下 1 個元素的時候就是有序的了),兩個函數傳入的index:first、mid、last要十分注意(我調試了好久才弄明白)。twoArray用了一個臨時數組b,因爲全部是vector,簡化了不少操作。


註釋(1)

一個結構,存儲了value,原數組的值,index,該值在原數組的下標,以及 right 歸併中的交換次數,也就是右側小於它的數。

註釋(2)、註釋(3)  看歸併排序的介紹


註釋(4)

本來以爲只是簡單的計數交換次數,然而並不是。考慮  5  2  6  1,第一次歸併之後:2 5 1 6,接下來,2  1 發生交換,但是  5  裏的 right 應該也要增加一,於是我在歸併的時候加入



//(4)
			for(int h = i; h <= first2;h++)
				a[h].right++;


整道題,主要是體會歸併排序,完全是在我寫出歸併的基礎上改的,而且歸併巨難寫……


發佈了37 篇原創文章 · 獲贊 0 · 訪問量 7024
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章