【LeetCode 004】各種分類討論,已更新AC

【寫不出來不肯睡覺系列。。。。。。】


成敗 <=== 細節 <=== 清晰頭腦 <=== 冷靜

======================================================================================================

思想小總結

昨晚睡覺前突然想着寫道算法題,打開LeetCode選了個Hard的就開始了。

題目很清楚,給定兩個有序數組,求出在兩個有序數組結合起來後的中位數,其實就是求在整體中第K小數,要求時間複雜度是log級別。

題目的本質就成了找第K小數。

log級的算法,那麼思路很清楚,就是分治法,且原數組已經有序,那麼基本肯定就是二分法了。

已經有序了,那就是定需要查找的值key,很自然想到了拿一個數組的第一個值去另一個數組搜索。

搜索後,可以確定,兩個數組綜合後的,前cur個的整體順序,拿很自然在拿另一個數組的下一個元素在之前那個數組搜索,一直迭代即可。

看圖後思路變很清楚。


實質就是第k小數

其實就是找在num1數組 + num2數組中的,第k = (len1 + len2) / 2小的數




很快寫好了,結果懵逼了。因爲長度奇數,偶數,下標-1,有可能跳到另一個數組,等等分類討論情況。

結果,因爲沒有仔細想清楚,想到什麼情況就分什麼情況了,結果越分越多,越分越亂,結果就是寫了狗屎一樣的代碼,一般寫到這種情況,基本95%都是錯的了,由於比較困了,思路也變得不是很清晰,但是寫不出來又覺得很不爽,

接着就想啊改啊,測啊,改啊,調啊,試啊,。。。。惡性傻逼循環。。。。一般不可能在這種情況下解決問題的。

就有了。。。。。。。。。。。。。。。。。。。。。。。。。。簡直讓我在凌晨,舍友們都已經關燈睡覺了,抓狂啊,這一大排的Wrong Answer,我內心是崩潰的

就這樣苦苦掙扎到了凌晨三點,真是不應該。。。。。。

誓不罷休的精神倒是一直沒變,雖然是件好事,不過應該注意一下身體比較好。


直到今天有時間了,好好重新分析了一下,仔細分類討論一下,冷靜。

思路一下變得很清晰,很快重新實現了,而且代碼明顯短了,明顯漂亮了。的確,一般正確的代碼都應該是比較短,比較漂亮的。

寫完,提交,一次AC,哈哈,簡直太開心了!

不管寫算法,還是做事情,冷靜的分析,清晰的邏輯真的很重要。

即使你再想,再着急,如果不能好好冷靜下來,那基本上就是前面提到的傻逼惡性循環,然後得到一大排慘不忍睹的WA。

還是讓自己冷靜下來,思路變得清晰的時候,纔有可能漂亮的解決問題。


這樣的經歷其實每個人都有,而且是經常有,經常因爲太想着結果,而變得着急,不冷靜,就方了,這種時候往往結果都比較糟糕。

作爲一個小總結,希望以後可以有更好的意識。


end of 思想總結

======================================================================================================


問題鏈接:


https://leetcode.com/problems/median-of-two-sorted-arrays/


問題描述:


給定有序數組nums1, nums2, 找出在num1和nums2綜合後的中位數,要求時間複雜度log級別

問題本質,相當於找出num1+nums2的第k小數。


解題思路:


核心思路如圖:



圖1. cur >= cen後回退,目標indexR = minP - (cur - cen)

1. cur < cen 的時候一直重複交叉二分搜素

2. 兩種情況停止搜索,minN用光了,cur >= cen

3. 停止搜索後可以分3種情況

    1)  minN用光了,又分兩種情況

            1> cur < cen, 還沒找到,所以得往沒用光的maxN後面繼續擴展

            2> cur >= cen,意味着已經找到,往前回退cur - cen 格即可

    2) 最後一次在minN二分搜索,且此時滿足,cur >= cen,意味着已經找到,往前回退cur - cen 格即可

    3) 最後一次在maxN二分搜索,且此時滿足,cur >= cen,意味着已經找到,往前回退cur - cen 格即可


通過分析,可以把上面4個情況合併成兩個情況:

1)   第一種情況:(圖2情況)

             minN用光了,且 cur < cen, 還沒找到,所以得往沒用光的maxN後面繼續擴展

2) 如果最後一次在maxN裏二分搜索,則將minN和maxN兩個數組的所有數據交換一下,則變成最後一次在minN二分搜素了,所以

      第二種情況:(圖1情況)

最後一次在minN二分搜索,且此時滿足,cur >= cen,意味着已經找到,往前回退cur - cen 格即可




圖2.  minN用光且cur<cen, 則在maxN往後繼續走,目標indexR = maxP + (cen - cur)



總結:

1.

小心分類討論,數組總長度,奇偶情況

2.

二分搜索裏,注意lower和upper的區別

1, 1, 1, 2, 2, 2, 3, 3, 3 查找2

lower:

a[mid - 1]  <    key  <=  a[mid] 

返回的是第一個2的下標

upper: 

a[mid - 1]  <=  key <     a[mid]

返回的是最後一個2的下標 + 1


此處,找到是目前《=key的所有值,故應該用upper


實現:


(頭腦混亂時寫的代碼:),

分類討論的時候簡直是又臭又長,這種情況基本不可能寫對

#include<stdio.h>
#include<iostream>
#include<vector>
using namespace std;

class Solution {
public:
	int upper(vector<int> a, int l, int r, int key){
		if(a.size() == 0) return -1;
		if (key <= a[l]) {
			while(key == a[l]) l++;
			return l;
		}
		int mid;
		while (l <= r) {
			mid = (l + r) >> 1;
			if (a[mid - 1] <= key && key <=a[mid]) return mid;
			if (a[mid - 1] >= key) {
				r = mid - 1;
			} else {
				l = mid + 1;
			}
		}
		return r + 1;
	}

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int minL = nums1.size();
		int maxL = nums2.size();
		vector<int> minN;
		vector<int> maxN;
		if (minL <= maxL) {
			minN = nums1;
			maxN = nums2;
		} else {
			minL = nums2.size();
			maxL = nums1.size(); 
			minN = nums2;
			maxN = nums1;
		}

		int oneDone = -1;
		if (minL == 0) {
			oneDone = 0;
		} else if(maxL == 0){
			oneDone = 1;
		}
		int curL = 0;
		int cen = (minL + maxL) / 2;
		//printf("%d\n", cen);
		int minP = 0;
		int maxP = 0;
		int index;
		int minMax = 0;
		while (curL < cen) {
			if(oneDone != -1) break;
			if(maxP + 1 < maxL && maxN[maxP] == maxN[maxP + 1]) {
				maxP++;
				curL++;
			}
			index = upper(minN, minP, minL - 1, maxN[maxP]);
		//	while(index < minL && maxN[maxP] == minN[index]) index++;
			curL += index - minP;
			minP = index;
			minMax = 0;
			//printf("cen = %d, curL = %d, minP = %d, maxP = %d, index = %d\n", cen, curL, minP, maxP, index);
			if (minP >= minL){				
				oneDone = 0;
				break;
			}	
			if (curL >= cen) break;

			if(minP + 1 < minL && minN[minP] == minN[minP + 1]) {
				minP++;
				curL++;
			}
			index = upper(maxN, maxP, maxL - 1, minN[minP]);
			//while(index < maxL && minN[minP] == maxN[index]) index++;
			curL += index - maxP;
			maxP = index;
			minMax = 1;
			//printf("cen = %d, curL = %d, minP = %d, maxP = %d, index = %d\n", cen, curL, minP, maxP, index);
			if (maxP >= maxL){
				oneDone = 1;
				break;
			}		
			if (curL >= cen) break;
			//int haha;
			//scanf("%d",&haha);
		}
		double ans = 0;
		if (oneDone == 0) {
			if (curL > cen) {
				int indexR = index - (curL - cen);
				if ((minL + maxL) % 2 == 1) {
					ans = minN[indexR];
				} else {
					int indexL = indexR - 1;
					double cenR = minN[indexR];
					double cenL = minN[indexL];
					if (maxN[maxP] > minN[indexL]) {
						cenL = maxN[maxP];
					}
					ans = (cenR + cenL) / 2.0;
				}
			} else {
				int indexR = maxP + cen - curL;
				if ((minL + maxL) % 2 == 1) {
					ans = maxN[indexR];
				} else {
					int indexL = indexR - 1;
					if (indexL >= 0) {
						ans = (maxN[indexL] + maxN[indexR]) / 2.0;
					}
						
					if (minN[minL - 1] > maxN[indexL]) {
						ans = (minN[minL - 1] + maxN[indexR]) / 2.0;
					}
				}
			}
		} else if (oneDone == 1) {
			if (curL > cen) {
				int indexR = index - (curL - cen);
				if ((minL + maxL) % 2 == 1) {
					ans = maxN[indexR];
				} else {
					int indexL = indexR - 1;
					double cenR = maxN[indexR];
					double cenL = maxN[indexL];
					if (minN[minP] > maxN[indexL]) {
						cenL = minN[minP];
					}
					//printf("%f %f\n", cenR, cenL);
					ans = (cenR + cenL) / 2.0;
				}
			} else {
				int indexR = minP + cen - curL;
				if ((minL + maxL) % 2 == 1) {
					ans = minN[indexR];
				} else {
					int indexL = indexR - 1;
					if (indexL >= 0) {
						ans = (minN[indexL] + minN[indexR]) / 2.0;
					}
					if(maxN[maxL - 1] > minN[indexL]){
						ans = (maxN[indexL] + minN[indexR]) / 2.0;
					}
				}
			}
		} else {
			if (curL == cen) {
				if (minMax == 0) {//
					if ((minL + maxL) % 2 == 1) {
						ans = maxN[maxP];
					} else {
						double cenR = maxN[maxP];
						double cenL = minN[index - 1];
						if(maxP >= 0 && maxN[maxP - 1] > cenL) {
							cenL = maxN[maxP - 1];
						}
						ans = (cenR + cenL) / 2.0;
					}
				} else {//
					if ((minL + maxL) % 2 == 1) {
						ans = minN[minP];
					} else {
						double cenR = minN[minP];
						double cenL = maxN[index - 1];
						if(minP >= 0 && minN[minP - 1] > cenL) {
							cenL = minN[minP - 1];
						}
						ans = (cenR + cenL) / 2.0;
					}
				}
			} else {
				int indexR = index - (curL - cen);
				if (minMax == 0) {
					if ((minL + maxL) % 2 == 1) {
						ans = minN[indexR];
					} else {
						double cenR = minN[indexR];
						double cenL = minN[indexR - 1];
						if (maxN[maxP] > minN[indexR - 1]) {
							cenL = maxN[maxP];
						}
						ans = (cenR + cenL) / 2.0;
					}
				} else {
					if ((minL + maxL) % 2 == 1) {
						ans = maxN[indexR];
					} else {
						double cenR = maxN[indexR];
						double cenL = maxN[indexR - 1];
						if (minN[minP] > maxN[indexR - 1]) {
							cenL = maxN[maxP];
						}
						ans = (cenR + cenL) / 2.0;
					}
				}
			}
		}
		return ans;
    }
};

int main() {
	vector<int> num1;
	num1.push_back(2);
	//num1.push_back(2);
//	num1.push_back(3);

	vector<int> num2;
	num2.push_back(1);
	num2.push_back(3);
	num2.push_back(4);
//	num2.push_back(10);
//	num2.push_back(11);
//	num2.push_back(13);
//	num2.push_back(15);
//	num2.push_back(21);
//	num2.push_back(22);
//	num2.push_back(23);
//	num2.push_back(24);
//	num2.push_back(25);
//	num2.push_back(26);
//	
	
	Solution s;
	double ans = s.findMedianSortedArrays(num1, num2);
	cout << ans << endl;
	//cout << s.upper(num1, 0, 3, 4) << endl;
	return 0;
}


AC代碼:

#include<stdio.h>
#include<iostream>
#include<vector>
using namespace std;

class Solution {
public:
	int upper(vector<int> a, int l, int r, int key){ // <= key <, 例如1,1,1,2裏找1,返回2的下標3
		if(a.size() == 0) return 0;

		int mid;
		while (l <= r) {
			mid = (l + r) >> 1;
			if (a[mid - 1] <= key && key < a[mid]) return mid;
			if (a[mid - 1] > key) {
				r = mid - 1;
			} else {
				l = mid + 1;
			}
		}
		return l;
	}

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int minL = nums1.size();
		int maxL = nums2.size();
		vector<int> minN; // 存儲最大元素較小的數組
		vector<int> maxN; // 存儲最大元素較大的數組
		if (minL == 0 || ((maxL != 0) && nums1[minL - 1] <= nums2[maxL - 1])) {
			minN = nums1;
			maxN = nums2;
		} else {
			minN = nums2;
			maxN = nums1;
		}
		minL = minN.size();
		maxL = maxN.size();

		int minNDone = minL == 0 ? 1 : 0;		
		int isOdd = (minL + maxL) % 2 == 1 ? 1 : 0;
		int cur = 0;				 // 已確定第cur小數字,從0開始
		int cen = (minL + maxL) / 2; // 目標下標 = 當(minL+maxL)%2,==1時cen一個點,==0時,cen和cen-1兩個點之和/2
		int minP = 0; // 數組的當前‘頭’
		int maxP = 0;
		int index = 0;
		int minMax; // = 0,表示在minN剛剛二分搜索。= 1表示剛剛在maxN二分搜索
		while (cur < cen) {
			if(minNDone == 1) break;	
			// maxN的'頭'在minN裏二分搜索 ======================================================
			index = upper(minN, minP, minL - 1, maxN[maxP]);
			cur += index - minP;
			minP = index;
			minMax = 0;
			//printf("cen = %d, cur = %d, minP = %d, maxP = %d, index = %d\n", cen, cur, minP, maxP, index);
			if (minP >= minL){				
				minNDone = 1;
				break;
			}	
			if (cur >= cen) break;
			
			//minN的'頭'在maxN裏二分搜索 ======================================================
			index = upper(maxN, maxP, maxL - 1, minN[minP]);
			cur += index - maxP;
			maxP = index;
			minMax = 1;
			//printf("cen = %d, cur = %d, minP = %d, maxP = %d, index = %d\n", cen, cur, minP, maxP, index);		
			if (cur >= cen) break;
		}

		double ans = 0;
		int indexR;
		// 小的數組用完了,且已經確定大小的下標 < 目標下標,	在maxN往後找,目標indexR = maxP + cen - cur;
		if (cur < cen) {
			indexR = maxP + cen - cur;
			if (isOdd == 1) {
				ans = maxN[indexR];
			} else {
				ans = (maxN[indexR] + maxN[indexR - 1]) / 2.0;
			}
			return ans;
		}
		
		// 最後一次二分搜索在maxN裏結束,則minN和maxN交換身份,統一爲最後一次在minN裏二分搜索結束
		if (minMax == 1) {
			vector<int> tmp = maxN;
			maxN = minN;
			minN = tmp;
			int tmpP = maxP;
			maxP = minP;
			minP = tmpP;
		}

		if (cur == cen) {
		// 正好命中
			if (isOdd == 1) {
				ans = maxN[maxP];
			} else {
				ans = (maxN[maxP] + minN[minP - 1]) / 2.0; 
			}	
		} else {
		// 在這次二分搜索的那小段裏,已經超過,所以要在minN回退,目標indexR = minP - (cur - cen);
			indexR = minP - (cur - cen);
			if (isOdd == 1) {
				ans = minN[indexR];
			} else {
				ans = (minN[indexR] + minN[indexR - 1]) / 2.0;
			}
		}
		return ans;
    }
};

int main() {
	vector<int> num1;
	num1.push_back(1);
	num1.push_back(2);

	vector<int> num2;
	num2.push_back(1);
	num2.push_back(2);

	Solution s;
	double ans = s.findMedianSortedArrays(num1, num2);
	cout << ans << endl;
	return 0;
}

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