关于数组的面试题总结(三)

再发一组关于数组的题目,其中部分来自一些公司的面试题(没有合适的oj来测试),部分来自leetcode。

暂时不给出解题思路和代码,待节后有时间再整理补充。(已全部更新


1. 有序数组的二分查找
int binSearch(int array[], int low, int high, int target)  
{  // 递归
    if (low > high) return -1;  
      
    int mid = low + ((high - low) >> 1);  
    if (array[mid] > target)  
        return binSearch(array, low, mid -1, target);  
    if (array[mid] < target)  
        return binSearch(array, mid+1, high, target);  
    return mid;  
}

int binSearch(int array[], int low, int high, int target)  
{  // 非递归
    while(low <= high)  
    {  
        int mid = low + low + ((high - low) >> 1);  
        if (array[mid] > target)  
            high = mid - 1;  
        else if (array[mid] < target)  
            low = mid + 1;  
        else return mid;  
    }  
    return -1;  
}  

扩展:1). 关于数组的面试题1总结中的循环数组的查找;找到循环数组中的最小值;
2). 求整数的平方根sqrt(x),仅返回整数部分,可以采用二分查找的方法去试根。

2. 数组的排序(插入排序、冒泡排序、堆排序、快速排序和归并排序)
// 插入排序 ,虽然查找位置的时候可以用二分查找,但是同样需要移动后半部分的元素,复杂度并不减少
void InsertSort(vector<int> &array)
{
    int i, j;
    for(i = 1; i < array.size(); ++i)
    {
        int tmp = array[i];
        for(j = i-1; j >= 0 && array[j] > tmp; --j)
        {
            array[j+1] = array[j];
        }
        array[j+1] = tmp;
    }
}

// 冒泡排序
void BubbleSort(vector<int> &array)
{
    int n = array.size();
    for(int i = 0; i < n; ++i)
    {
        for(int j = 0; j < n-i-1; ++j)
        {
            if(array[j] > array[j+1])
                swap(array[j], array[j+1]);
        }
    }
}

// 改进的冒泡排序
void BubbleSort2(vector<int> &array)
{
    int n = array.size();
    for(int i = 0; i < n; ++i)
    {
        bool hasChange = false;
        for(int j = 0; j < n-i-1; ++j)
        {
            if(array[j] > array[j+1])
            {
                swap(array[j], array[j+1]);
                hasChange = true;
            }
        }
        // 若本趟扫描没有任何替换,则数组已经有序
        if(!hasChange) break;
    }
}
堆排序,需要注意两点:1). 很多教科书中计算左右子孩子一般是以数组下标为1开始来计数的,当我们从0开始计数时有差异;
2). 在调整堆的函数中一定要添加数组的长度n,而不是直接取vector的size!
// 调整大顶堆
void maxHeapify(vector<int> &array, int idx, int n)
{   // 注意调整函数中需要带上数组的大小,因为排序的时候这个n一直在减小
    int maxIdx = idx;
    if(2*idx+1 < n && array[2*idx+1] > array[maxIdx])
    {
        maxIdx = 2*idx + 1;
    }
    if(2*idx+2 < n && array[2*idx+2] > array[maxIdx])
    {
        maxIdx = 2*idx + 2;
    }

    if(maxIdx != idx)
    {
        swap(array[maxIdx], array[idx]);
        maxHeapify(array, maxIdx, n);
    }
}
// 堆排序
void HeapSort(vector<int> &array)
{
    int n = array.size();
    for(int i = (n-1)/2; i >= 0; --i)
    {
        maxHeapify(array, i, n);
    }
    for(int i = n-1; i > 0; --i)
    {
        swap(array[0], array[i]);
        maxHeapify(array, 0, i);
    }
}
快速排序有两种不同的划分方法,第一种方法是算法导论正文中讲述的,它保证i左边(包括i)所有元素都是小于或者等于pivot,然后采用j从左到右依次判断,若遇到比pivot小的元素就与第i+1位(程序中先++i了,即第一个大于pivot的元素)互换。
另一种方法则是从左右两边同时扫描,保证l左边部分都是较小的元素,r右边部分都是较大的元素,但是尤其需要注意的是while语句中要检查边界,while结束后赋值。若想在左右两边的while循环都结束后再swap两者的值,会很容易出错,最好不要这样写。
// 快速排序
int Partition(vector<int> &array, int l, int r)
{
    int pivot = array[r];
    int i = l - 1; // i及左边的元素都较小
    for(int j = l; j < r; ++j)
    {
        if(array[j] <= pivot) 
        {
            ++i;
            swap(array[i], array[j]);
        }
    }
    swap(array[i+1], array[r]);
    return i+1;
}

int Partition2(vector<int> &array, int l, int r)
{
    int pivot = array[l];
    while(l < r)
    { // 从两端到中间
        while(l < r && array[r] >= pivot)
            --r;
        if(l < r) array[l++] = array[r];
        while(l < r && array[l] <= pivot)
            ++l;
        if(l < r) array[r--] = array[l];
    }
    array[l] = pivot; 
    return l;
}

void QuickSortHelper(vector<int> &array, int l, int r)
{
    if(l >= r) return;
    int mid = Partition(array, l, r); 
    //int mid = Partition2(array, l, r);
    QuickSortHelper(array, l, mid-1);
    QuickSortHelper(array, mid+1, r);
}

void QuickSort(vector<int> &array)
{
    int n = array.size();
    QuickSortHelper(array, 0, n-1);
}
用递归写,需要额外的空间。
// 归并排序
void Merge(vector<int> &array, int low, int mid, int high)
{
    vector<int> tmp;
    int i = low;
    int j = mid + 1;
    while(i <= mid && j <= high)
    {
        if(array[i] <= array[j])
            tmp.push_back(array[i++]);
        else tmp.push_back(array[j++]);
    }
    while(i <= mid)
    {
        tmp.push_back(array[i++]);
    }
    while(j <= high)
    {
        tmp.push_back(array[j++]);
    }
    for(int i = 0; i < tmp.size(); ++i)
    {
        array[low + i] = tmp[i];
    }
}

void MergeSortHelper(vector<int> &array, int l, int r)
{
    if(l >= r) return;
    int mid = l + ((r-l) >> 1);
    MergeSortHelper(array, l, mid);
    MergeSortHelper(array, mid+1, r);
    Merge(array, l, mid, r);
}

void MergeSort(vector<int> &array)
{
    int n = array.size();
    MergeSortHelper(array, 0, n-1);
}


3. 循环数组中的最大字段和(《编程之美》)
这里可以刷这道题: http://www.51nod.com/onlineJudge/questionCode.html# !problemId=1050
long long maxSumSubArray(const vector<int> &array)
{
    long long sum = 0;
    long long tmp = 0, tmp1 = 0;
    long long max = 0;
    long long min = INT_MAX;
    for(int i = 0; i < array.size(); ++i)
    {
        sum += array[i];
        if(tmp > 0) tmp += array[i];
        else tmp = array[i];
        if(tmp > max) max = tmp;

        if(tmp1 < 0) tmp1 += array[i];
        else tmp1 = array[i];
        if(tmp1 < min) min = tmp1;
    }

    max = max > sum - min ? max : sum - min;
    return max;
}


4. 求整数数组中最长的连续序列(Longest Consecutive Sequence)
例如,给定数组 [100, 4, 200, 1, 3, 2] 最长的连续序列是[1,2,3,4],应该输出其长度4
int longestConsecutive(vector<int> &num) 
{
        if(num.size() == 0) return 0;
        int maxlen = 1;
        map<int, int> record;
        for(size_t i = 0; i < num.size(); ++i)
        {
            int val = num[i];
            if(record[val] != 0) continue;
            record[val] = 1;
            int l = record[val - 1];
            int r = record[val + 1];
            //cout<<val<<" "<<l<<" "<<r<<endl;
            record[val - l] = 1 + r + l;
            record[val + r] = 1 + r + l;
            if(1 + r + l > maxlen)
                maxlen = 1 + r + l;
        }
        return maxlen;
    }


5. 求随机整数数组中最长等差数列长度
int longestSubArray(vector<int> &array)
{
    int n = array.size();
    vector<vector<int> > dp;
    vector<int> tmp(n, 2);
    dp.assign(n, tmp);
    sort(array.begin(), array.end());
    int maxLen = 2;
    for(int i = 1; i < n-1; ++i)
    {
        int j = i-1, k = i+1;
        while(j >= 0 && k < n)
        {
            if(array[j] + array[k] > 2 * array[i])
                --j;
            else if(array[j] + array[k] < 2 * array[i])
                ++k;
            else {
                dp[i][k] = 1+dp[j][i];
                if(dp[i][k] > maxLen) maxLen = dp[i][k];
                --j;
                ++k;
            }
        }
    }
    return maxLen;
}


6. 找出大于0的丢失的最小数字
给定一个无序的整数数组,怎么找到第一个大于0,并且不在此数组的整数。比如[1,2,0] 返回 3, [3,4,-1,1] 返回 2。最好能O(1)空间和O(n)时间。
int findLostPosNum(vector<int> &array)
{
    int n = array.size();
    for(int i = 0; i < n; )
    {
        if(array[i] == i+1 || array[i] > n || array[i] <= 0)
        {
            ++i;
        } else swap(array[array[i]-1], array[i]);
    }

    for(int i = 0; i < array.size(); ++i)
    {
        //cout << array[i] << endl;
        if(array[i] != i+1) return i+1;
    }
    return array.back()+1;
}


7. 最长递增子数组 
// 方法一: dp
int LIS1(const vector<int> &array)
{
    // 用dp[i]记录以第i个元素作为最后一个元素的递增子数组的长度
    int n = array.size();
    vector<int> dp(n, 0);
    int lis = 0;
    for(int i = 0; i < n; ++i)
    {
        dp[i] = 1;
        for(int j = 0; j < i; ++j)
        {
            if(array[i] > array[j] && dp[i] < dp[j] + 1)
            {
                dp[i] = dp[j] + 1;
                if(dp[i] > lis)
                {
                    lis = dp[i];
                }
            }
        }
    }
    return lis;
}
// 方法二: dp + binary search
int binSearch(const vector<int> num, int target)
{
    int l = 0, r = (int)num.size() - 1;
    while(l <= r)
    {
        int mid = l + ((r - l) >> 1);
        if(num[mid] == target) return mid;
        else if(num[mid] < target) l = mid+1;
        else r = mid - 1;
    }
    return l;
}

int LIS2(const vector<int> &array)
{
   // 用dp[i]记录长度为i+1的递增子数组的最小的末尾元素值
    if(0 == array.size()) return 0;
    vector<int> dp;
    dp.push_back(array[0]);
    for(int i = 1; i < array.size(); ++i)
    {
        int pos = binSearch(dp, array[i]);
        if(pos == dp.size()) dp.push_back(array[i]);
        else if(dp[pos] > array[i]) dp[pos] = array[i];
    }
    return dp.size();
}

扩展: 叠罗汉。给出几组数据,如(20,30),(24,25),(30,40),(32,41),(x,y)中,x 代表身高,y代表体重。叠罗汉,下面的人要比上面的人身高低、体重小。问最多叠几层?

8. 两个有序数组A,B(长度不等),从A,B中分别任意选取a,b,求min{|a-b|} 
int minDistofAB(const vector<int> &A, const vector<int> &B)
{    // 采用归并的思想,但是不需要真正的归并,时间复杂度O(m+n)
    if(0 == A.size() || 0 == B.size()) return -1;
    int idxa = 0, m = A.size();
    int idxb = 0, n = B.size();
    int minDist = INT_MAX;
    while(idxa < m && idxb < n)
    {
        int tmp = abs(A[idxa] - B[idxb]);
        if(tmp < minDist) minDist = tmp;
        if(0 == minDist) return minDist;

        if(A[idxa] <= B[idxb]) ++idxa;
        else ++idxb;
    }
    return minDist;
}
扩展:有n个有序数组,每个数组中取一个数字,使得得到的数列的最大值与最小值的差异最小。
对于n个有序数组,同样可以采用归并的思想,但是需要利用最小堆保存从每个数组中选取的数字,可以快速计算这些值的最小值,而最大值用一个临时变量保存即可。
关于最小堆的调整,每次移走最小堆的堆顶元素x,而补充该位置的元素则来自与x相同的那个数组,具体做法可以参见败者树。


9. 分糖果问题,n个孩子站成一排,每个人有一个分数值,要求每个孩子至少有一个糖果,且比相邻孩子分数高的孩子得到的糖果也应该比邻居多。
int candy(vector<int> &ratings)
{    // 从左向右扫描一遍,再从右向左扫描一遍
    int n = ratings.size();
    if(0 == n) return 0;
   
    vector<int> candy(n, 1);
    for(int i = 1; i < n; ++i)
    {
        if(ratings[i] > ratings[i-1])
            candy[i] = candy[i-1] + 1;
    }

    int ret = candy[n-1];
    for(int i = n-2; i >= 0; --i)
    {
        if(ratings[i] > ratings[i+1])
        {
            candy[i] = max(candy[i], candy[i+1] + 1);
        }
        ret += candy[i];
    }
    return ret;
}


10. 最大蓄水(Container With Most Water),数组a[0..n-1],其中a[i]表示坐标(i, a[i])有一条垂直于x轴的直线,两条直线和x轴一起可以组成一个蓄水池,根据数组a求出最大蓄水量(宽*高)。
int maxArea(vector<int> &height) 
{
        size_t len = height.size();
        if(len <= 1) return 0;
        size_t low = 0, high = len - 1;
        int maxa = 0;
        while (low < high)
        {
            int h = 0, wide = high - low;
            if(height[low] < height[high])
            {
                h = height[low++];
            } else {
                h = height[high--];
            }
            int tempa = h * wide;
            if(tempa > maxa) maxa = tempa;
        }
        return maxa;
    }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值