剑指offer——优化时间和空间效率

剑指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;
    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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值