剑指offer——优化时间和空间效率
2017-07-03 22:31
246 查看
剑指offer——优化时间和空间效率
1. 时间效率
面试题29:数组中出现次数超过一半的数字
/*解法一思想:排序后位于中间的数字就是所求数字,中位数(数组第n/大数字)*/ //基于快排Partition函数的O(n)算法 int MoreThanHalfNum(int* numbers,int length){ if (CheckInvalidArray(numbers,length)) return 0; int middle = lenght >> 1; int start = 0; int end = length - 1; int index = Partition(numbers,length,start,end); while (index != middle) { if (index > middle){ end = middle - 1; index = Partition(numbers,length,start,end); } else{ start = middle + 1; index = Partition(numbers,length,start,end); } } int result = numbers(middle); if (!CheckMoreThanHalf(number,length,result)) result = 0; return result; } bool g_bInputInvalid = false; bool CheckInvalidArray(int* numbers,int length){ g_bInputInvalid = false; if (numbers == NULL || lenght<=0 ) g_bInputInvalid = true; return g_bInputInvalid; } int Partition(int* numbers,int length,int start,int end){ if (numbers == NULL || lenght<=0 || start <0 || end>= length) throw new exception("Invalid Parameters"); int index = RandomInRange(start,end);//生成一个start到end之间的随机数 Swap(&numbers[index],&numbers[end]); int small = start - 1; for (index = start;index<end;++index){ if (numbers[index] < numbers[end]){ ++small; if (small != index) Swap(&numbers[index],&numbers[small]); } } ++small; Swap(&numbers[small],&numbers[end]); return small; } //判断中位数出现的次数是否超过一半 bool CheckMoreThanHalf(int *number,int length,int result){ int times = 0; for (int i = 0;i< length;i++){ if (numbers[i] == numbers) times++; } bool isMoreThanHalf = true; if (times*2 <= length){ g_bInputInvalid = true; isMoreThanHalf = false; } return isMoreThanHalf; } /*解法二思想:所求数字出现的次数比其他所有数字出现的次数的和还要大;遍历数组并保存两个数字:一个数组数字,一个次数(若下一个数组元素与当前数字相同,次数加一,否则次数减一;若次数为0,则保存下一个数组元素并设置次数为一。所求数字就是最后保存的数组元素)*/ int MoreThanHalfNum(int* numbers,int length){ if (CheckInvalidArray(numbers,length)) return 0; int result = numbers[0]; int times = 1; for (int i=1;i<length;++i){ if (times == 0){ result = numbers[i]; times = 1; } else if (numbers[i] == result) times++; else times--; } if (!CheckMoreThanHalf(number,length,result)) result = 0; return result; } //注意:解法一会修改输入的数组!!!
面试题30:最小的k个数
//解法一:基于Partition函数的O(n)算法,会修改输入数组,不适合海量数据*/ void GetLeastNumbers(int *input,int n,int* output,int k){ if (input == NULL || output==NULL || k>n ||n<=0 || k<=0) return; int start = 0; int end = length - 1; int index = Partition(numbers,length,start,end); while (index != k-1) { if (index > k-1){ end = middle - 1; index = Partition(numbers,length,start,end); } else{ start = middle + 1; index = Partition(numbers,length,start,end); } } for (int i=0;i<k;++i) output[i] = input[i]; } //基于堆或红黑树的O(nlogk)算法,适合海量数据且不会修改输入数组*/ /*创建一个大小为k的数据容器,遍历数组,若容器内元素少于k个,直接将读取的数组元素存入容器;若容器元素是k个,比较读取的数组元素和容器最大元素,将较小者存入容器。红黑树种查找,删除和插入操作都是O(logk)*/ typedef multiset<int,greater<int>> inSet; typedef multiset<int,greater<int>>::iterator setIterator; void GetLeastNumbers(const vector<int>& data,inSet& leastNumbers,int k){ leastNumbers.clear(); if (k<1 || data.size()<k) return; vector<int>::const_iterator iter = data.begin(); for(;iter!=data.end();++iter){ if ((leastNumbers.size()) < k) leastNumbers.insert(*iter); else{ setIterator iterGreatest = leastNumbers.begin(); if (*iter < *(leastNumbers.begin())){ leastNumbers.erase(iterGeatest); leastNumbers.insert(*iter); } } } }
面试题31:连续子数组的最大和
//解法一:举例分析数组规律 bool g_InvalidInput = false; int FindGreatestSumOfSunArray(int *pData,int nLength){ if ((pData == NULL) || (nLength<=0)){ g_InvalidInput = true; return 0; } g_InvalidInput = false; int nCurSum = 0; int nGreatestSum = -INF; int cur_low = 0; int low,high;//最大子数组的位置 for (int i=0;i<nLength;++i){ nCurSum += pData[i]; if (nCurSum <= 0){ nCurSum = 0; cur_low = i+1; } else if (nCurSum > nGreatestSum){ nGreatestSum = nCurSum; low = cur_low; high = i; } } if (nGreatestSum > -INF) return(low,high,nGreatestSum); } //解法二:应用动态规划法 /*f(i)表示以第i个数字结尾的子数组的最大和,目标就是max(f(i)),0<=i<n 若i=0或f(i-1)<=0,则f(i)=pData[i]; 若i!=0且f(i-1)>0,则f(i)=f(i-1)+pData[i];*/ //代码与上面一致 //解法三:分治法求解最大子数组 /*数组分为两个子数组,最大子数组有三种情况:完全位于左边数组或右边数组,跨越分布于两个子数组*/ int FindMaxSubArray(int* data,int low,int high){ if (low==high) return (low,high,data[low]); else{ int mid = (low+high)/2; (left_low,left_high,left_sum) = FindMaxSubArray(data,low,mid); (right_low,right_high,right_sum) = FindMaxSubArray(data,mid+1,high); (cross_left,cross_high,cross_sum) = FindMaxCrossArray(data,low,mid,high); //比较left_sum,right_sum和cross_sum返回最大值 } } int FindMaxCrossArray(int* data,int low, int mid,int high){ int left_sum = -INF; int sum = 0; int max_left,max_right; for (int i=mid;i>=low;--mid){ sum = sum + data[i]; if (sum > left_sum){ left_sum = sum; max_left = i; } } sum=0; int right)sum=-INF; for (int i=mid;i<high;++mid){ sum = sum + data[i]; if (sum > right_sum){ right_sum = sum; max_right = i; } } return(max_left,max_right,left_sum+right_sum); }
面试题32:从1到n整数中包含1出现的次数
/*解法一:计算每个整数1的次数,需要大量计算不可取*/ //解法二: int NumberOf1Between1AndN(int n){ if (n<=0) return 0; char strN[50]; sprintf(strN,"%d",n);//把格式化的数据写入字符串中,123->"123" return NumberOf1(strN); } int NumberOf1(const char *strN){ if (strN==NULL || *strN < '0' || *strN >'9' || *strN == '\0') return 0; int first = *strN-'0';//最高位 unsigned int length = static_cast<unsigned int>(strlen(strN)); if (length == 1 && first == 0) return 0; if (length == 1 && first > 0) return 1; int numFirstDigit = 0; if (first>1) //numFirstDigit是10000~19999最高位1的个数 numFirstDigit = PowerBase10(length-1); else if (first==1) ////numFirstDigit是10000~n最高位1的个数 numFirstDigit = atoi(strN+1)+1;//atoi将字符串转换成整型数 //假设n=21345,numOtherDigits是1346~21345除最高位外的数位中1的个数 int numOtherDigits = first*(length-1)*PowerBase10(length-2); //逐个求解1~1345中1的个数 int numRecursive = NumberOf1(strN+1); return numFirstDigit+numOtherDigits+numRecursive; } int PowerBase10(unsigned int n){ int result = 1; de36 for (unsigned int i=0;i<n;i++) result *= 10; return result; }
面试题33:把数组排成最小的数
const int g_MaxNumberLength = 10; char *g_StrCombine1 = new char[g_MaxNumberLength*2+1]; char *g_StrCombine2 = new char[g_MaxNumberLength*2+1]; void PrintMinNumber(int *numbers,int length){ if (numbers == NILL || length <=0) return; char **strNumbers = char(**)(new int[length]); for (int i=0;i<length;++i){ strNumbers[i] = new char[g_MaxNumberLength+1]; sprintf(strNumbers[i],"%d",numbers[i]); } //qsort根据你给的比较条件给一个快速排序,排序之后的结果仍然放在原来数组中 /*void qsort( void *base, size_t num, size_t width, int (__cdecl *compare ) int compare (const void *elem1, const void *elem2 ) ); 第一个参数 base 是 需要排序的目标数组名(或者也可以理解成开始排序的地址,因为可以写&s[i]这样的表达式) 第二个参数 num 是 参与排序的目标数组元素个数 第三个参数 width 是单个元素的大小(或者目标数组中每一个元素长度),推荐使用sizeof(s[0])这样的表达式 第四个参数 compare 就是让很多人觉得非常困惑的比较函数啦*/ qsort(strNUmbers,length,sizeof(char*),compare); for (int i=0;i<length;++i) printf("%s",strNumbers[i]); printf("\n"); for (int i=0;i<length;++i) delete[] strNumbers[i]; delete[] strNumbers; } int compare(const void *strNumber1,const void* strNumber2){ strcpy(g_StrCombine1,*(const char **)strNumber1); strcat(g_StrCombine1,*(const char **)strNumber2); strcpy(g_StrCombine2,*(const char **)strNumber2); strcat(g_StrCombine2,*(const char **)strNumber1); return strcmp(g_StrCombine1,g_StrCombine2); }
2. 时间效率与空间效率的平衡
面试题34:第k个丑数(只包含因子2,3,5的数)
//解法一: /*逐个判断每个整数是不是丑数,不高效*/ int GetUglyNumber_Solution1(int index){ if (index <=0) return 0; int number = 0; int uglyFount = 0; while (uglyFound < index){ ++number; if (IsUgly(number)) ++uglyFound; } return number; } bool IsUgly(int number){ while (number%2==0) number = number/2; while (number%3==0) number = number/3; while (number%5==0) number = number/5; return (number==1)? true:false; } //解法二:创建数组保存已找到的丑数 int GetUglyNumber_Solution2(int index){ if (index <=0) return 0; int *pUglyNumbers = new int[index]; pUglyNumbers[0] = 1; int nextUglyIndex = 1; int *pMultiply2 = pUglyNumbers; int *pMultiply3 = pUglyNumbers; int *pMultiply5 = pUglyNumbers; while (nextUglyIndex<index){ int min = Min(*pMultiply2*2,*pMultiply3*3,*pMultiply5*5); pUglyNumbers[nextUglyIndex] = min; while (*pMultiply2*2 <= pUglyNumbers[nextUglyIndex]) ++pMultiply2; while (*pMultiply3*3 <= pUglyNumbers[nextUglyIndex]) ++pMultiply3; while (*pMultiply5*5 <= pUglyNumbers[nextUglyIndex]) ++pMultiply5; ++nextUglyIndex; } int ugly = pUglyNumbers[nextUglyIndex-1]; delete[] pUglyNumbers; return ugly; } int Min(int number1,int number2,int number3){ int min = (number1<number2)?number1:number2; min = (min<number3)?min:number3; return min; }
面试题:第一个只出现一次的字符
char FirstNotRepeatingChar(char *pString){ if (pString==NULL) return '\0'; const int tableSize = 256;//ACSII 8位,Unicode 16位 unsigned int hashTable[tableSize]; for (unsigned int i=0;i<tableSize;++i) hashTable[i] = 0; char *pHashKey = pString; while (*(pHashKey) !='\0') hashTable[*(pHashKey++)]++; char *pHashKey = pString; while (*(pHashKey) !='\0'){ if (hashTable[*pHashKey] == 1) return *pHashKey; ++pHashKey; } return '\0'; } /*举一反三:如果需要判断字符在字符串中出现的次数,可以考虑基于数组创建一个哈希表,以较小的空间消耗换取时间效率*/
面试题36:数组中的逆序对
/*基于归并排序*/ int InversePairs(int *data,int length){ if (data == NULL || length <= 0) return 0; int *copy = new int[length]; for (int i=0;i<length;++i) copy[i] = data[i]; int count = InversePairsCore(data,copy,0,length-1); delete[] copy; return count; } int InversePairsCore(int *data,int *copy,int start,int end) { if (start == end){ copy[start] = data[end]; return 0; } int length = (end-start)/2; int left = InversePairsCore(data,copy,start,start+length); int right = InversePairsCore(data,copy,start+length+1,end);; int i = start+length; int j = end; int indexCopy = end; int count = 0; while (i>=start && j>=start+length+1){ if (data[i] > copy[j]){ copy[indexCopy--] = data[i--]; count += j-start-length; } else{ copy[indexCopy--] = data[j--]; } } for (;i>=start;--i) copy[indexCopy--] = data[i]; for (;j>=start+length;--i) copy[indexCopy--] = data[j]; return left+right+count; }
面试题37:两个链表的第一个公共结点
struct ListNode{ int m_nValue; ListNode *m_pNext; }; ListNode* FindFirstCommonNode(ListNode *pHead1,ListNode *pHead2){ unsigned int nLength1 = GetListLength(pHead1); unsigned int nLength2 = GetListLength(pHead2); int nLengthDif = nLength1 - nLength2; ListNode *pListHeadLong = pHead1; ListNode *pListHeadShort = pHead2; if (nLength1 < nLength2 ){ pListHeadLong = pHead2; pListHeadShort = pHead1; nLengthDif = nLength2 - nLength1; } for (int i=0;i<nLengthDif;++i) pListHeadLong = pListHeadLong->m_pNext; while ((pListHeadLong !=NULL) && (pListHeadShort !=NULL) && (pListHeadLong !=pListHeadShort)){ pListHeadLong = pListHeadLong->m_pNext; pListHeadShort = pListHeadShort->m_pNext; } ListNode *pFirstCommonNode = pListHeadLong; return pFirstCommonNode; } int GetListLength(ListNode *pHead){ unsigned int nLength = 0; ListNode *pNode = pHead; while (pNode->m_pNext != NULL){ nLength++; pNode = pNode->m_pNext; } return nLength; }
相关文章推荐
- 剑指offer-第五章优化时间和空间效率(数组中出现次数超过一半的数字)
- 剑指offer-第五章优化时间和空间效率(把数组排列成最小的数)
- 剑指offer 第五章 优化时间和空间效率
- 【面试编程题】-9剑指offer之优化时间和空间效率
- 剑指offer-第五章优化时间和空间效率(数组中的逆序对的总数)
- 剑指offer-第五章优化时间和空间效率(从1到n的整数中1出现的次数)
- 剑指offer-第五章优化时间和空间效率(最小的k个数)
- 剑指offer-第五章优化时间和空间效率(连续子数组的最大和)
- 【剑指Offer-优化时间和空间效率】连续子数组最大和
- 剑指offer 5.3 时间与空间效率平衡 -丑数
- 剑指offer 37题 【时间空间效率的平衡】两个链表的第一个公共结点
- 剑指offer:(35)时间效率和空间效率的平衡 :第一个只出现一次的字符
- (C++)剑指offer-34:第一个只出现一次的字符位置(时间空间效率的平衡)
- 剑指offer 算法 (时间空间效率的平衡)
- (C++)剑指offer-35:数组中的逆序对(时间空间效率的平衡)(未完成)
- (C++)剑指offer-36:两个链表的第一个公共结点(时间空间效率的平衡)
- 剑指offer 5.3 时间与空间效率2 - 第一个只出现一次的字符
- 剑指offer-5-面试36:数组中的逆序对(时间效率和空间效率的平衡)
- 剑指offer 36题 【时间空间效率的平衡】数组中的逆序对
- 【剑指offer】5.2时间效率——面试题32:从1到n整数中1出现的次数