zoukankan      html  css  js  c++  java
  • 数组笔试题

    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(const int* pPush, const int* 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 int g_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(const void* strNumber1, const void* strNumber2)
    {
        char g_StrCombine1[2*g_MaxNumberLength+1];
        char g_StrCombine2[2*g_MaxNumberLength+1];
    
        // [strNumber1][strNumber2]
        strcpy(g_StrCombine1, *(const char**)strNumber1);
        strcat(g_StrCombine1, *(const char**)strNumber2);
    
        // [strNumber2][strNumber1]
        strcpy(g_StrCombine2, *(const char**)strNumber2);
        strcat(g_StrCombine2, *(const char**)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(const void* key1, const void* key2)
    {
        return *(int*)(key1) - *(int*)(key2);
    }
    
    int SubArraySum(const int 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;
    }
  • 相关阅读:
    codevs 1086 栈 2003年NOIP全国联赛普及组
    1200 同余方程 2012年NOIP全国联赛提高组
    【bzoj4939】【YNOI2016】掉进兔子洞(莫队)
    洛谷P3674 小清新人渣的本愿(莫队)
    Lucas卢卡斯定理
    组合数学习笔记
    洛谷P3178 [HAOI2015]树上操作(线段树)
    洛谷P3258 [JLOI2014]松鼠的新家(树上差分+树剖)
    洛谷P2526 [SHOI2001]小狗散步(二分图匹配)
    bzoj3140: [Hnoi2013]消毒(二分图)
  • 原文地址:https://www.cnblogs.com/luxiaoxun/p/2767805.html
Copyright © 2011-2022 走看看