您的位置:首页 > Web前端

剑指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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐