18个笔试考试算法 C语言

目录

1 收集雨水

2 直方图矩形面积

3 数组中的最长连续子序列

4 排列组合

5 两数之和

6 上中位数

7 三个数最大乘积

8 数组中逆序对

9 数字在升序数组中出现的次数(二分查找)

10 旋转数组中最小的数字

12 岛屿数量

13 数组中出现一次的两个数字 

14 数组中出现次数超过一半的数字

15 字符串旋转

16 字符串最长无重复子串

17 最长重复子串

18 三色旗问题


参考了许多前辈的成果,感谢,此次备考中作为练习,问题描述和思路写得比较简单,如有不解,按标题搜索他人题目

1 收集雨水

问题:给定n个非负整数,表示直方图的方柱的高度,同时,每个方柱的宽度假定都为1。若使用这样形状的容器收集雨水,可以盛多少水量?

输入:

eg1: 0 1 0 2 1 0 1 3 2 1 2 1

eg2: 4 2 0 3 2 5

思路:

用一个数组从左边开始遍历,存第i个元素左侧的最高方柱高度;

再用一个数组从右侧开始遍历,存第i个元素右侧的最高方柱高度。

LeftMax:   0 1 1 2 2 2 2 3 3 3 3 3
RightMax:3 3 3 3 3 3 3 2 2 2 1 0

height:     0 1 0 2 1 0 1 3 2 1 2 1

min - h:    0 0 1 0 1 2 1 0 0 1 0 0

则第i个方柱能存储的雨水量就是其左右侧更矮的高度-自身高度

#include <stdio.h>
#define max(a,b) ((a)>(b))?(a):(b)
#define min(a,b) ((a)<(b))?(a):(b)

int main(){
    int n, min;
    printf("Input the number of array:");
    scanf("%d", &n);

    int Array[n], water=0;
    printf("Input the array:");
    for (int i = 0; i < n; ++i)
        scanf("%d", &Array[i]);

    int leftMax[n], rightMax[n];
    leftMax[0] = Array[0];
    rightMax[n-1] = Array[n-1];
    for(int i=1; i<n; i++) {
        leftMax[i] = max(leftMax[i-1], Array[i]);    //i左侧最大值为leftMax[i]
        rightMax[n-i-1] = max(rightMax[n-i], Array[n-i-1]);    //i右侧最大值leftMax[i]
    }

    for(int i=1; i<n-1; i++) {
        min = min(leftMax[i-1], rightMax[i+1]);    //“短板”
        if(min > Array[i])
            water += min - Array[i];
        //        water += max(min(leftMax[i-1], rightMax[i+1]) - Array[i], 0);
    }

    printf("%d", water);
    return 0;
}
Input the number of array:12
Input the array:0 1 0 2 1 0 1 3 2 1 2 1

LeftMax:1 1 2 2 2 2 3 3 3 3 3
RightMax:3 3 3 3 3 3 3 2 2 2 1
Collected Water:6

2 直方图矩形面积

问题:给定n个非负整数,表示直方图的方柱的高度,同时,每个方柱的宽度假定都为1; 试找出直方图中最大的矩形面积。如:给定高度为:2,1,5,6,2,3,最大面积为10

直方图矩形面积

思路:[Update] 以当前高度,维持一个当前最小的高度,向后遍历,一直计算面积

int n = heights.size();
int ans = 0;
// 枚举左边界
for (int left = 0; left < n; ++left) {
    int minHeight = INT_MAX;
    // 枚举右边界
    for (int right = left; right < n; ++right) {
         // 确定高度
         minHeight = min(minHeight, heights[right]);
         // 计算面积
         ans = max(ans, (right - left + 1) * minHeight);
      }
   }
return ans;

来源:力扣(LeetCode)

3 数组中的最长连续子序列

问题:给定无序数组arr,返回其中最长的连续序列的长度(要求值连续,位置可以不连续,例如 3,4,5,6为连续的自然数)

输入:[100,4,200,1,3,2]     输出:4

思路:这题思路还是比较多的,这里提供的思路是直接进行排序,随后用循环进行检查是否连续,保存最大连续值即可。

#include <stdio.h>
#include <algorithm>
using namespace std;

int main(){
    int n, max=1, i=1, flag=1, curmax = 1;
    printf("Input the number of array:");
    scanf("%d", &n);

    int Array[n];
    printf("Input the array:");
    for (int i = 0; i < n; ++i)
        scanf("%d", &Array[i]);
    sort(a, sizeof(a));

    int last = Array[0];
    while(i<n){
        if(Array[i]==(last+1)){
            last = Array[i++];
            max = (++curmax)>max?curmax:max;
        } else {
            last = Array[i++];
            curmax = 1;
        }
            
    }

    printf("%d", max);

    return 0;
}

4 排列组合

问题:给出一组数字,返回该组数字的所有排列

方法:分冶法(在循环内部:置为已访问,进行递归,再置为未访问),这题理解起来还是有一定难度的。

char flag[10];
char result[10];

void permutation(int a[], int N, int K, int level) {    //排列
    if(level>=K) {
        for(int i=0; i<level; i++)
            printf("%d ", result[i]);
        printf("\n");
        return ;
    }
    for(int i=0; i<N; i++)
        if(!flag[i]) {
            flag[i] = true;
            result[level] = a[i];
            level++;
            permutation(a, N, K, level);
            level--;
            flag[i] = false;
        }
}

void Combination(int a[], int N, int K, int index, int deep) {    //组合
    if(deep>=K){
        for(int i=0; i<K; i++)
            printf("%d ", result[i]);
        printf("\n");
        return ;
    }
    for (int i = index; i < N; ++i) {
        result[deep] = a[i];
        deep++;
        index++;
        Combination(a, N, K, index, deep);
        deep--;
    }
}


int main() {
    int n, k;
    printf("Input the number of array:");
    scanf("%d", &n);

    int a[n];
    printf("Input the array:");
    for (int i = 0; i < n; ++i)
        scanf("%d", &a[i]);
    printf("How many numbers to combination:");
    scanf("%d", &k);

    memset(flag, false, sizeof(flag));

    printf("\n\nTotal Combination(%d):\n", k);
    Combination(a, n, k, 0, 0);

    printf("\n\nTotal Permutation:\n");
    permutation(a, n, n, 0);
    return 0;

5 两数之和

问题:(两数之和)给出一个整数数组,请在数组中找出两个加起来等于目标值的数,你给出的函数 twosum需要返回这两个数字的下标( index1, index2), 需要满足 index1小于 index2..

注意:下标是从1开始的假设给出的数组中只存在唯一解

例如:

输入:[20,70,110,150],90

输出:[1, 2]

思路:这里给出的思路是使用unordered_map,理解和操作都很简单。将原数组的元素遍历,若unordered_map存在"差值",则返回输出;若不存在,则插入。


#include <stdio.h>
#include <unordered_map>
using namespace std;

int main(){
    int n, target=0, i;
    printf("Input the number of array:");
    scanf("%d", &n);

    int Array[n];
    printf("Input the array:");
    for (i = 0; i < n; ++i)
        scanf("%d", &Array[i]);
    printf("Target Number:");
    scanf("%d", &target);

    unordered_map<int, int> Map;
    for (i = 0; i < n; ++i){
        auto it = numMap.find(target - Array[i]);
        if(it != numMap.end())
            return {it->second, i};
        numMap[Array[i]] = i;
    }

    return 0;
}

6 上中位数

问题:给定两个有序数组arr1和arr2,已知两个数组的长度都为N,求两个数组中所有数的上中位数。

上中位数:假设递增序列长度为n,若n为奇数,则上中位数为第n/2+1个数;否则为第n个数

输入:[1,2,3,4],[3,4,5,6]

输出:3

总共有8个数,上中位数是第4小的数,所以返回3。

思路:双指针两两比较

    int index1=0, index2=0;
    for(int i=0; i<n-1; i++)
        if(A[index1] <= B[index2])
            index1++;
        else
            index2++;
    printf("%d", min(A[index1], B[index2]));

7 三个数最大乘积

问题:给定一个无序数组,包含正数、负数和0. 要求从中找出3个数的乘积,使得乘积最大, 要求时间复杂度:O(n),空间复杂度:O(1).

输入:[3,4,1,2]

输出:24

思路:强化版的“一维数组找最大/小值”

#include <stdio.h>
#include <limits.h>
#define N 100
int Num[N];

int getMax(int n){
    int min1 = INT_MAX, min2 = INT_MAX;
    int max1 = INT_MIN, max2  = INT_MIN, max3 = INT_MIN;
    for(int i=0; i<n; i++){
        if(Num[i] <= min1){
            min2 = min1; min1 = Num[i];
        } else if( Num[i] <= min2) {
            min2 = Num[i];
        }

        if(Num[i] >= max1) {
            max3 = max2; max2 = max1; max1 = Num[i];
        } else if(Num[i] >= max2){
            max3 = max2; max2 = Num[i];
        } else {
            max3 = Num[i];
        }
    }
    int res1 = max3 * max2 * max1;
    int res2 = max1 * min1 * min2;
    return res1>res2?res1:res2;
}

8 数组中逆序对

问题:(数组中逆序对)在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对, 输入一个数组,求出这个数组中的逆序对的总数P。 并将P对1000000007取模的结果输出。即输出P%1000000007

输入:[1,2,3,4,5,6,7,0]

输出:7

#include <stdio.h>
#define N 100
int Num[N], temp[N];
long long ans=0;

void mergingSort(int low, int high){
    if(low==high)
        return;
    int mid = (low+high)/2;
    mergingSort(low, mid);
    mergingSort(mid+1, high);
    int l, r, k;
    for(l=low, r=mid+1, k=low; k<=high; k++){       //k从low扫描到high
        if(l>mid) temp[k] = Num[r++];               //如果左指针已到正中,加右半部数字
        else if(r>high) temp[k] = Num[l++];         //如果右指针已到末尾,加左半部数字
        else if(Num[l]<=Num[r]) temp[k] = Num[l++]; //如果左数不大于右数,加左数
        else {                                      //如果右数小于左数,加右数,统计逆序对数
            temp[k] = Num[r++];
            ans += mid-l+1;
        }
    }
    for(k=low; k<=high; k++)
        Num[k] = temp[k];
}

int main(){
    int n, i;
    printf("How much numbers:");
    scanf("%d", &n);
    printf("Input the array:");
    for(i=0; i<n; i++)
        scanf("%d", &Num[i]);

    mergingSort(0, n-1);
    printf("%d", (int)ans%1000000007);
}

9 数字在升序数组中出现的次数(二分查找)

问题:统计一个数字在升序数组中出现的次数 [1,2,3,3,3,3,4,5], 3 输出 4 

思路:使用标准的二分查找后,找到的可能是连续数字中的任意一个位置,从该位置出发,向前向后查找相同元素,记录个数。与第2题,矩形面积很像。

#include <stdio.h>
#define N 100
int Num[N];

int binary(int low, int high, int key) {    //标准的二分查找
    int mid;
    while(low < high){
        mid = (low+high)/2;
        if(Num[mid]==key)
            return mid;
        else if(Num[mid]>key)
            high = mid-1;
        else
            low = mid+1;
    }
    return low;
}

int main(){
    int n, i, key;
    printf("How much numbers:");
    scanf("%d", &n);
    printf("Input the array:");
    for(i=0; i<n; i++)
        scanf("%d", &Num[i]);
    printf("Input the Key Number:");
    scanf("%d", &key);

    int index = binary(0, n-1, key);    //二分查找key的下标index
    int count = 0;
    i = index;
    while(Num[i-1] == Num[i]){      //index往前匹配相同的key
        count++;
        i--;
    }
    i = index;
    while(Num[i+1] == Num[i]){      //index往后匹配相同的key
        count++;
        i++;
    }

    printf("%d", count+1);
    return 0;
}

10 旋转数组中最小的数字

问题:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转,输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素

NOTE:给出的所有元素都大于0.若数组大小为0,请返回0.

输入:[3,4,5,1,2]  输出 1

思路:二分查找

#include <stdio.h>
#define N 100
int Num[N];
int binary(int low, int high){
    int mid=0, target=0;
    while(low<=high){
        mid = (low+high)/2;
        if(Num[low]==Num[mid])
            low++;
        else if(Num[mid] <= Num[high]) {
            if(target > Num[mid] && target <= Num[high])
                low = mid;
            else
                high = mid;
        } else {
            if(target >= Num[low] && target < Num[mid])
                high = mid;
            else
                low = mid;
        }
    }
    return mid;
}

int main(){
    int n, i;
    printf("How much numbers:");
    scanf("%d", &n);
    printf("Input the array:");
    for(i=0; i<n; i++)
        scanf("%d", &Num[i]);
    printf("The Minimum in Rotated Sorted Array is:%d", Num[binary(0, n-1)]);
    return 0;
}

12 岛屿数量

问题:给一个01矩阵,1代表是陆地,0代表海洋,如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻、岛屿:相邻陆地可以组成一个岛屿(相邻:上下左右)判断岛屿个数。

输入:[[1.,1,0,0,0],[0,1,0,1,1],[0,0,0,1,1],[0,0,0,0,0],[0,0,1,1,1]]

输出:3

思路:DFS

#include <stdio.h>
#define N 100
int grid[N][N], row, col;
void dfs(int i, int j){
    if(i<0 || j<0 || i>=row || j>=col || grid[i][j]==0 ) return;    //!!
    grid[i][j] = 0;
    dfs(i+1, j);
    dfs(i, j+1);
    dfs(i-1, j);
    dfs(i, j-1);
}
int main(){
    int i, j, count=0;
    printf("Input the row and columns of the Matrix:");
    scanf("%d", &row);
    scanf("%d", &col);
    printf("Input the Matrix:\n");
    for(i=0; i<row; i++)
        for(j=0; j<col; j++)
            scanf("%d", &grid[i][i]);

    for(i=0; i<row; i++)
        for(j=0; j<col; j++){
            if(grid[i][j] != 0) {       //!!
                dfs(i, j);
                count++;
            }
        }

    printf("The number of the island:%d", count);
}

13 数组中出现一次的两个数字 

问题:一个整型数组里除了两个数字之外,其他的数字都出现了两次,请写程序找出这两个只出现一次的数字

思路:出现一次的一个数字可以用异或。出现一次的两个数字可以用map,其实也可以用异或

法一:Map,如果有,删除;如果没有,插入

#include <stdio.h>
#include <map>
using namespace std;

int main() {
    map<int, int> Map;
    int n, i, a;
    printf("How much numbers:");
    scanf("%d", &n);
    printf("Input the array:");
    for(i=0; i<n; i++){
        scanf("%d", &a);
        if(Map.count(a)){
            Map.erase(a);
        } else {
            Map.insert(make_pair(a, a));
        }
    }
    for(map<int, int>::iterator it=Map.begin(); it!=Map.end(); it++)
        printf("%d\t", it->first);
    return 0;
}

法二:用异或得到的数按位与,另见Tinus Chen图解异或分组

    int res=0, i;
    for(i=0; i<n; i++)
        res ^= num[i];

    int bitindex = 0;
    for(i=0; i<32; i++)
        if((res>>i &1) == 1) {  //!!
            bitindex = 1;break;
        }

    int res1=0, res2=0;
    for(i=0; i<n; i++)
        if((num[i] >> bitindex & 1) == 1)
            res1 ^= num[i];
        else
            res2 ^= num[i];

    printf("%d\t%d", res1, res2);

14 数组中出现次数超过一半的数字

问题:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字例如输入一个长度为9的数组(1,2,3,2,2,2,5,4,2).

由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2.如果不存在则输出0

思路:遍历两次,第一次看存不存在这样一个数,第二次找这个数是谁

int findHalf(int Array[], int n){
    int times = 1, num = Array[0];
    for(int i=1; i<n; i++){
        if(times <= 0) {
            times = 1;
            num = Array[i];
        } else if(num == Array[i])
            times++;
        else
            times--;
    }

    if(times>0){
        int count = 0;
        for(int i=0; i<n; i++)
            if(num == Array[i])
                count++;
        if(count>n/2)
            return num;
    }
    return 0;
}

15 字符串旋转

问题:给定两字符串A和B,如果能将A从中间某个位置分割为左右两部分字符串(都不为空串),并将左边的字符串移动到右边字符串后面组成新的字符串可以变为字符申B时返回true。

例如:A='youzan',B='zanyou',A按'you''zan’切割换位后得到'zanyou'和B相同返回true

思路:str=A+A,if(str.contains(B) && A.len == B.len)

bool judge4(char *A, char *B) {
    char C[200];
    if(strlen(A)!=strlen(B))
        return false;
    strcat(A, strcpy(C, A));
    if(strstr(A, B))
        return true;
    return false;
}

16 字符串最长无重复子串

问题:给定一个数组arr,返回arr的最长的无重复子串的长度(无重复指的是所有数字都不相同)

输入:[2,2,3,4,3] 输出3

输入:abcabcbb

思路:根据字符编码 大小 申请 map  key表示字符  value表示最近出现的位置

int pre 遍历到的字符str[i] 以为str[i]结尾的最长无重复字符子串开始位置的前一个位置 初始时候 pre=-1;

int len 记录以每一个字符结尾的情况下 最长的长度 初始len=0

int lengthOfLongestSubstring(char * s){
    //map[256]适用单字符
    //若为int型,则改用c++中的map<int,int> (#include<map> using namespace std;)
    int n = strlen(s), i=0, map[256];
    if(n<=0) return 0;
    memset(map, -1, sizeof(map));

    int len=0,pre=-1,cur=0;
    for(i =0; i<n; i++){
        pre = max(pre, map[s[i]]);
        cur = i-pre;
        len = max(len, cur);
        map[s[i]]=i;
    }

    return len;
}

17 最长重复子串

问题:一个重复字符串是由两个相同的字符串首尾拼接而成, 例如 abcabc便是长度为6的一个重复字符串,而 abcba则不存在重复字符串。

给定一个字符串,请编写一个函数,返回其最长的重复字符子串,若不存在任何重复字符子串, 则返回0.

输入:"ababc"  输出:4

int find(char *A){
    int len = strlen(A);
    int res = 0;
    for(int i=1; i<=len/2; i++){
        int count = 0;
        for(int j=0; j<len-i; j++) {
            if(A[j]==A[j+i])
                count++;
            else
                count = 0;
            if(count==i)
                res = res>count*2?res:count*2;
        }
    }
    return res;
}

18 三色旗问题

问题:有一根绳子,上面有红、白、蓝三种颜色的旗子。绳子上旗子的颜色并没有顺序,现在要对旗子进行分类, 按照蓝色、白色、红色的顺序排列。只能在绳子上进行移动,并且一次只能调换两面旗子,怎样移动才能使旗子移动的次数最少?

输入:RWBWWBRBWR

#include <stdio.h>
#include <cstring>

int main() {
    int i=0,j=0,k=0;
    char color[100];
    scanf("%s", color);
    k = strlen(color)-1;
    while(j<=k){
        while(color[k]=='R') k--;
        if(j<=k)
            switch (color[j]) {
                case 'B':
                    color[i] = 'B'; i++;
                    color[j] = 'W'; j++;
                    break;
                case 'W':
                    j++;
                    break;
                case 'R':
                    color[j] = color[k];
                    color[k] = 'R'; k--;
                    break;
            }
    }
    puts(color);
    return 0;
}

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值