数组

来源:互联网 发布:新手学炒股 知乎 编辑:程序博客网 时间:2024/05/16 06:42

1、写一个函数找出一个整数数组中第二大的数。

// 时间复杂度O(n)

const int MINNUMBER =-32767;

int find_sec_max(int data[],int count)

{数组数组

    int maxnumber = data[0];

    int sec_max = MINNUMBER;

    for(int i =1; i < count; i++)

    {

        if(data[i] > maxnumber)

        {

            sec_max = maxnumber;

            maxnumber = data[i];

        }

        else

        {

            if(data[i] > sec_max)

            sec_max = data[i];

        }

    }

    return sec_max;

}

2、寻找平衡点问题

平衡点:比如int numbers[]={1,3,5,7,8,25,4,20}; 25前面的总和为24,25后面的总和也是24,25这个点就是平衡点。假如一个数组中的元素,其前面的部分等于后面的部分,那么这个点的位序就是平衡点,要求返回任何一个平衡点。

int calcBalance(int arr[],int length)

{

    int *left = new int[length]; //left[i]为从第0个到第i-1个的和

    int *right = new int[length]; //right[i]为从第i+1个到第len-1个的和

    int b = length-1;

    for(int i=0; i<length; i++)

    {

    if(i == 0)  

            left[i] = 0;  

        else 

            left[i] = left[i-1]+arr[i-1];  

    }  

    for(; b>=0; b--)  

    {  

        if(b == length-1)  

            right[b] = 0;   

        else 

            right[b] = right[b+1]+arr[b+1];  

        if(left[b] == right[b])

        {

            delete[] left;

            delete[] right;

            return b;

        }

    }

 

    delete[] left;

    delete[] right;

    return -1;

}

3、输入两个整数序列。其中一个序列表示栈的push顺序,判断另一个序列有没有可能是对应的pop顺序。为了简单起见,我们假设push序列的任意两个整数都是不相等的。 比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。

bool IsPossiblePopOrder(constint* pPush, constint* pPop, int nLen)

{

    stack<int> stackData;

    int i = 0;

    int j = 0;

    while(i < nLen || j < nLen)

    {

        if(i < nLen)

        {

            stackData.push(pPush[i]);

            ++ i;

        }

        while(!stackData.empty() &&stackData.top() == pPop[j])

        {

            stackData.pop();

            ++ j;

        }

        if(j == nLen) break;

    }

    return (stackData.empty() && i == nLen);

}

4、一个int 数组,里面数据无任何限制,要求求出所有这样的数a[i],其左边的数都小于等于它,右边的数都大于等于它。

方法一:将数组a[]排序为数组b[],比较两个数组,如果a[i]==b[i],则a[i]即为一个这样的数。时间复杂度为O(NlogN)。

方法二:维护两个数组min[]和max[],max[i]为a[i]左边的数的最大值,min[i]为a[i]右边的最小值,扫描两遍数组就可得到,再比较,如果max[i]<=a[i]<=min[i],则a[i]是一个这样的数。

void FindElements(int *pArray,int len) 

    if(pArray == NULL || len <= 0

        return

 

    int *pMin = new int[len]; 

    int *pMax = new int[len]; 

    int i; 

 

    pMax[0] = pArray[0]; 

    for(i = 1; i < len; i++)      //计算自i往前最大值的辅助数组  

        pMax[i] = (pMax[i-1] >= pArray[i])?pMax[i-1]:pArray[i]; 

 

    pMin[len-1] = pArray[len-1]; 

    for(i = len - 2; i >= 0; i--) //计算自i开始最小值的辅助数组  

        pMin[i] = (pMin[i+1] <= pArray[i])?pMin[i+1]:pArray[i]; 

 

    if(pArray[0] <= pMin[0])    //检查第1个元素是否满足条件  

        cout<<pArray[0]<<''

    for(i = 1; i < len - 1; i++) 

    { 

        if(pArray[i] >= pMax[i] && pArray[i]<= pMin[i])//满足这个关系式的元素符合要求  

            cout<<pArray[i]<<' '

    } 

    if(pArray[len-1] >= pMax[len-1])//检查第len个元素是否满足条件  

        cout<<pArray[i]; 

    cout<<endl; 

 

    delete [] pMin; 

    delete [] pMax; 

    pMin = pMax = NULL; 

5、N个元素的数组循环右移K位,要求时间复杂度为O(N)

//普通实现

void RightShift(int* arr,int N, int K)

{

    K %= N;

    while(K--)

    {

        int t = arr[N-1];

        for(int i = N-1; i >0; i --)

            arr[i] = arr[i-1];

        arr[0] = t;

    }

}

 

//递归实现

void MoveCirce(int *data,int n, int k)

{

    int temp = data[n-1];

    for(int i = n-1; i >0; --i)

        data[i] = data[i-1];

    data[0] = temp;

    -- k;

    if(k > 0) MoveCirce(data,n,k);

}

假设原数组序列为abcd1234,要求变换成的数组序列为1234abcd,即循环右移了4位。比较之后,不难看出,其中有两段的顺序是不变的:1234和abcd,可把这两段看成两个整体。右移K位的过程就是把数组的两部分交换一下。变换的过程通过以下步骤完成:

1.   逆序排列abcd:abcd1234 → dcba1234; 2.   逆序排列1234:dcba1234 → dcba4321; 3.   全部逆序:dcba4321 → 1234abcd。

//翻转函数

void Reverse(int* arr,int b, int e)

{

    for(; b < e; b++, e--)

    {

        int temp = arr[e];

        arr[e] = arr[b];

        arr[b] = temp;

    }

}

 

//循环右移

void RightShift(int* arr,int N, int K)

{

    K %= N;

    Reverse(arr, 0, N-K-1);

    Reverse(arr, N-K, N-1);

    Reverse(arr, 0, N-1);

}

 

//循环左移

void LeftShift(int* arr,int N, int K)

{

    K %= N;

    Reverse(arr, 0, K-1);

    Reverse(arr, K, N-1);

    Reverse(arr, 0, N-1);

}

6、发帖水王:“水王”发帖数目超过了帖子总数的一半

如果一个ID出现的次数超过总数N的一半。那么,无论水王的ID是什么,这个有序的ID列表中的第N/2项(从0开始编号)一定会是这个ID(读者可以试着证明一下)。省去重新扫描一遍列表,可以节省一点算法耗费的时间。如果能够迅速定位到列表的某一项(比如使用数组来存储列表),除去排序的时间复杂度,后处理需要的时间为O(1)。

如果每次删除两个不同的ID(不管是否包含“水王”的ID),那么,在剩下的ID列表中,“水王”ID出现的次数仍然超过总数的一半。看到这一点之后,就可以通过不断重复这个过程,把ID列表中的ID总数降低(转化为更小的问题),从而得到问题的答案。新的思路,避免了排序这个耗时的步骤,总的时间复杂度只有O(N),且只需要常数的额外内存。伪代码如下:

int Find(int* ID,int N)

{

    int candidate, nTimes, i;

    for(i = nTimes = 0; i < N; i++)

    {

        if(nTimes == 0)

        {

            candidate = ID[i], nTimes = 1;

        }

        else

        {

            if(candidate == ID[i])

                nTimes++;

            else

                nTimes--;

        }

    }

    return candidate;

}

扩展问题:统计结果表明,有3个发帖很多的ID,他们的发帖数目都超过了帖子总数目N的1/4。你能从发帖ID列表中快速找出他们的ID吗?

void Find(int* ID,int N, int & candidate1,int & candidate2, int & candidate3)

{

    int nTimes1 = 0;

    int nTimes2 = 0;

    int nTimes3 = 0;

 

    for(int i =0; i < N; i++)

    {

        if (nTimes1 == 0)

        {

            candidate1 = ID[i], nTimes1 = 1;

        }

        else

        {

            if (candidate1 == ID[i])

            {

                nTimes1++;

            }

            else if (nTimes2 == 0)

            {

                candidate2 = ID[i], nTimes2 = 1;

            }

            else

            {

                if (candidate2 == ID[i])

                {

                    nTimes2++;

                }

                else

                {

                    if (nTimes3 ==0)

                    {

                        candidate3 = ID[i],nTimes3 = 1;

                    }

                    else if (candidate3 == ID[i])

                    {

                        nTimes3++;

                    }

                    else

                    {

                        nTimes1--;

                        nTimes2--;

                        nTimes3--;

                    }

                }

            }

        }

    }

}

7、寻找最大的K个数

寻找N个数中最大的K个数,本质上就是寻找最大的K个数中最小的那个,也就是第K大的数。
线性时间选择算法:RandomizedSelect()算法

//随机划分函数

int random_partition(int a[],int l,int r)

{

    int i = l+rand()%(r-l+1);//生产随机数

    int temp = a[i];

    a[i] = a[l];

    a[l] = temp;

    return partition(a,l,r);//调用普通划分函数

}

 

//线性寻找第k大的数

int random_select(int a[],int l,int r,int k)

{

    int i,j;

    if(l == r) //递归结束

    {

        return a[l];

    }

    i =random_partition(a,l,r);//划分

    j = i-l+1;

    if(k == j) //递归结束,找到第K大的数

        return a[i];

    if(k < j)

    {

        return random_select(a,l,i-1,k);//递归调用,在前面部分查找第K大的数

    }

    else

        return random_select(a,i+1,r,k-j);//递归调用,在后面部分查找第K大的数

}

具体实现:http://www.cnblogs.com/luxiaoxun/archive/2012/08/06/2624799.html

8、快速寻找和等于一个给定的数字的两个数

解法一:穷举法,从数组中取出任意两个数字,计算两者之和是否为给定的数字。时间复杂度为O(N^2)

解法二:假设和为Sum,对于数组中每个数字arr[i]都判断Sum-arr[i]是否在数组中,就变成一个查找问题。提高查找效率,先排序,再用二分查找法等方法进行查找,查找的时间复杂度从O(N)降到O(logN),总的时间复杂度为O(N*logN)。 更快的查找方法:hash表。给定的一个数字,根据hash映射查找另一个数字是否在数组中,只需O(1)的时间,这样总的时间复杂度降低到O(N),但这需要额外的O(N)的hash表存储空间。

解法三:先对数组排序sort(a,n),时间复杂度为O(N*logN),然后按下面的算法(O(N)的时间复杂度)查找,总的时间复杂度为O(N*logN)。用两个指针i和j指向第一和最后一个元素,如果a[j]+a[i]<sum时,++i,增大和值,否则--j,减小和值。

void FindTwoNumbers(int *a,int n, int sum)

{

    sort(a,a+n); // use the STL sort algorithm

    int i = 0;

    int j = n-1;

    while(i < j)

    {

        if(a[i]+a[j] == sum)

        {

            cout<<a[i]<<" "<<a[j]<<endl;

            return;

        }

        else if(a[i]+a[j] < sum)

            ++ i;

        else

            -- j;

    }

    cout<<"No answer!"<<endl;

}

类似的一道题目:求数组中差为给定数字的两个数,先对数组排序,然后用两个指针i和j指向第一和第二个元素,如果a[j]-a[i]<dif时,++j,增大差值,否则++i,减小差值。

void FindTwoNumbers(int *a,int n, int dif)

{

    sort(a,a+n); // use the STL sort algorithm

    int i = 0;

    int j = 1;

    dif = abs(dif);

    while(i < j && j < n)

    {

        if(a[j]-a[i] == dif)

        {

            cout<<a[j]<<" "<<a[i]<<endl;

            return;

        }

        else if(a[j]-a[i] < dif)

            ++ j;

        else

            ++ i;

    }

    cout<<"No answer!"<<endl;

}

9、子数组的最大乘积:给定一个长度为N的整数数组,只能用乘法,不能用除法,计算任意N-1个数组合中乘积最大的一组。

分析:如果可以用除法:那么用整个数组的乘积除以每个元素a[i],结果就是除了除数a[i]的剩下N-1个数的乘积。

方法一:不能用除法,数组为a[],s[i]表示数组前i个元素的乘积,s[0]=1(边界条件),s[i]=s[i-1]*a[i-1](1<=i<=N)。t[i]为数组后(N-i)个元素的乘积,t[N+1]=1(边界条件),t[i]=t[i+1]*a[i](1<=i<=N)。则除了第i个元素外,其他N-1个元素的乘积为:p[i]=s[i-1]*t[i+1]。 从头到尾扫描得到s[i],从尾到头扫描得到t[i],进而线性时间就可以得到p[i]。

方法二:利用N个数的正负分布情况。先扫描一遍,统计处数组中正数个数p,负数个数n,零的个数z,绝对值最小的正数a和负数 b。

如果 零的个数 z >= 2 结果为0
如果 零的个数 z = 1
       如果负数个数n为奇数,结果为0
       如果负数个数n为偶数,结果为除0外的乘积
如果 零的个数 z =0
       如果负数个数n为奇数,结果为去掉绝对值最小负数后的乘积
       如果负数个数n为偶数,结果为去掉绝对值最小正数后的乘积

10、题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。例如输入数组{32,321},则输出这两个能排成的最小数字32132。请给出解决问题的算法,并证明该算法。

根据题目的要求,两个数字m和n排成的数字mn和nm,如果mn<nm,那么我们应该输出mn,也就是m应该排在n的前面。但是m不一定就小于n,如{32,  321},321>32,但是32132<32321。 这道题其实是希望我们能找到一个排序规则,根据这个规则排出来的数组能排成一个最小的数字。要确定排序规则,就得比较两个数字,也就是给出两个数字m和n,我们需要确定一个规则来比较m和n组合的数哪个更小。我们知道m和n组合出来的数位数是一定的,如果直接通过组合出来的数的数值去比较,有点麻烦,需要考虑到的一个潜在问题是m和n都在int能表达的范围内,但把它们拼起来的数字mn和nm就不一定能用int表示了。所以一个直观的方法就是把数字转换成字符串,把组合出来的数转化字符串后,直接比较字符串的大小就可以,通过这个来确定m和n的组合顺序。

来自:http://zhedahht.blog.163.com/blog/static/25411174200952174133707/

#include<iostream>

using namespace std;

 

// 数字最大位数为10

const intg_MaxNumberLength =10;

 

// 比较 strNumber1 strNumber2

// if [strNumber1][strNumber2]> [strNumber2][strNumber1], return value > 0

// if [strNumber1][strNumber2]= [strNumber2][strNumber1], return value = 0

// if [strNumber1][strNumber2]< [strNumber2][strNumber1], return value < 0

int compare(constvoid* strNumber1, constvoid* strNumber2)

{

    char g_StrCombine1[2*g_MaxNumberLength+1];

    char g_StrCombine2[2*g_MaxNumberLength+1];

 

    //[strNumber1][strNumber2]

    strcpy(g_StrCombine1, *(constchar**)strNumber1);

    strcat(g_StrCombine1, *(constchar**)strNumber2);

 

    //[strNumber2][strNumber1]

    strcpy(g_StrCombine2, *(constchar**)strNumber2);

    strcat(g_StrCombine2, *(constchar**)strNumber1);

 

    return strcmp(g_StrCombine1, g_StrCombine2);

}

 

// 输出正整数数组组合的最小数

void PrintMinNumber(int* numbers,int length)

{

    if(numbers == NULL || length <=0)

        return;

 

    // 将数字转换为字符串

    char** strNumbers = new char*[length];

 

    for(int i =0; i < length; ++i)

    {

        strNumbers[i] = new char[g_MaxNumberLength+ 1];

        sprintf(strNumbers[i], "%d",numbers[i]);

    }

 

    // 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 main()

{

    int a[] = {5,32,123};

    PrintMinNumber(a,3);

    return 0;

}

11、给定如下的n*n的数字矩阵,每行从左到右是严格递增, 每列的数据也是严格递增
1 3 7 15 16
2 5 8 18 19
4 6 9 22 23
10 13 17 24 28
20 21 25 26 33

现在要求设计一个算法, 给定一个数k 判断出k是否在这个矩阵中。 描述算法并且给出时间复杂度。

//从右上角开始(从左下角开始也是一样的),然后每步往左或往下走。时间复杂度O(N)

bool stepWise(int mat[][N] ,int key , int &row ,int &col) 

    if(key < mat[0][0] || key > mat[N-1][N-1]) 

        return false

    row = 0

    col = N-1

    while(row < N&& col >= 0

    { 

        if(mat[row][col] == key ) //查找成功  

            return true

        else if(mat[row][col] < key ) 

            ++row; 

        else 

            --col; 

    } 

    return false

}

12、给定一个整数数组a[],求最接近0的子数组和。

解法:构造从第一个元素开始的子数组和sum[i]=a[0]+..+a[i],那么sum[j]-sum[i]就是a[i+1]+...+a[j],即为一个子数组的和,原数组最接近0的子数组和就是sum[]中两数之差绝对值最小的值,两两比较,得到差值的最小值,就是最接近0的子数组和。

int compare(constvoid* key1, constvoid* key2)

{

    return *(int*)(key1) - *(int*)(key2);

}

 

int SubArraySum(constint a[], int len)

{

    int* pCumuSum = new int[len];

 

    pCumuSum[0] = a[0];

    for(int i =1; i < len; ++i)

    {

        pCumuSum[i] = pCumuSum[i-1] + a[i];

    }

 

    qsort(pCumuSum, len, sizeof(int), compare);

 

    int absDiff = abs(pCumuSum[0]);

    int tempAbsDiff;

    for(int i =1; i < len; ++i)

    {

        tempAbsDiff = abs(pCumuSum[i] -pCumuSum[i-1]);

        if(tempAbsDiff < absDiff)

        {

            absDiff = tempAbsDiff;

        }

    }

 

    delete [] pCumuSum;

    return absDiff;

}

13、小飞电梯调度问题:电梯从1层往上走,只允许电梯停在某一层,所有的乘客都从一楼上电梯,到达停下来的那层,所有的乘客从这里爬到自己的目的层。电梯停在哪一层,使得所有乘客爬的楼梯层数之和最少?

方法一:穷举所有的层,计算乘客爬的楼梯层数,得到最优的那一层。

方法二:假设停在第i层,所有爬的楼梯层数为Y。如果N1个乘客目的层在第i层楼以下,有N2个乘客在第i层楼,N3个乘客目的层在第i层楼以上。如果电梯改停在第i+1层,则爬层楼数为Y+(N1+N2-N3)。如果电梯改停在第i-1层,则爬层楼数为Y+(N2+N3-N1)。由此得出:当N1+N2<N3时,电梯改停在第i+1层好,当N2+N3<N1时,电梯改停在第i-1层好。

int getTargetFloor1(int N,int person[])

{

    int targetFloor = -1;

    int minFloor = 65535;

    int nFloor;

 

    for(int i =1; i <= N; ++i)

    {

        nFloor = 0;

        for(int j =1; j < i; ++j)

            nFloor += person[j]*(i-j);

        for(int j = i+1; j <= N; ++j)

            nFloor += person[j]*(j-i);

        if(targetFloor == -1 || nFloor < minFloor)

        {

            targetFloor = i;

            minFloor = nFloor;

        }

    }

 

    return targetFloor;

}

 

int getTargetFloor2(int N,int person[])

{

    int targetFloor = -1;

    int minFloor;

    int N1, N2, N3, i;

 

    targetFloor = 1;

    minFloor = 0;

    for(N1 = 0, N2 = person[1], N3 = 0, i = 2; i <= N; ++i)

    {

        N3 += person[i];

        minFloor += person[i]*(i-1);

    }

    for(i = 2; i <= N; ++i)

    {

        if(N1 + N2 < N3)

        {

            targetFloor = i;

            minFloor += (N1+N2-N3);

            N1 += N2;

            N2 = person[i];

            N3 -= person[i];

        }

        else break;

    }

 

    return targetFloor;

}

 

0 0