C语言习题(1-50)

1.编写一个程序,找出一个整数数组中的最大值和最小值。要求使用函数实现,并在主函数中进行调用。

答案:

#include <stdio.h>

void findMinMax(int arr[], int size, int *min, int *max) {
    *min = *max = arr[0]; // 初始化最小值和最大值为数组的第一个元素
    for (int i = 1; i < size; i++) {
        if (arr[i] < *min) {
            *min = arr[i]; // 更新最小值
        }
        if (arr[i] > *max) {
            *max = arr[i]; // 更新最大值
        }
    }
}

int main() {
    int arr[] = {5, 3, 9, 1, 7};
    int size = sizeof(arr) / sizeof(arr[0]);
    int min, max;
    
    findMinMax(arr, size, &min, &max);
    
    printf("最小值:%d\n", min);
    printf("最大值:%d\n", max);
    
    return 0;
}

解析:
这道题要求编写一个函数 findMinMax 来找出给定整数数组中的最大值和最小值,并在主函数中调用该函数。在 findMinMax 函数中,我们使用两个指针变量 minmax 来保存最小值和最大值。我们首先将它们初始化为数组的第一个元素 arr[0],然后遍历数组的剩余元素。对于每个元素,我们比较它是否小于 min,如果是,则更新 min 的值;比较它是否大于 max,如果是,则更新 max 的值。最后,在主函数中,我们声明了一个整数数组 arr 并初始化它,然后计算数组的大小。接下来,我们声明了两个变量 minmax,它们用于保存最小值和最大值。然后,我们调用 findMinMax 函数,并传递数组 arr、数组大小以及 minmax 的地址作为参数。最后,我们在主函数中打印出最小值和最大值的结果。

这道题考察了对函数的使用和指针的理解。通过将指针作为参数传递给函数,我们可以在函数内部修改变量的值,并将结果返回给调用者。在主函数中,我们通过传递指针来获取函数计算的最小值和最大值。

2.编写一个程序,接受用户输入的一个字符串,统计并输出字符串中的字母、数字和其他字符的个数。

答案:

#include <stdio.h>
#include <ctype.h>

void countCharacters(char *str, int *letters, int *digits, int *others) {
    *letters = *digits = *others = 0; // 初始化字母、数字和其他字符的计数器为0
    
    while (*str != '\0') {
        if (isalpha(*str)) {
            (*letters)++; // 字母计数器加1
        } else if (isdigit(*str)) {
            (*digits)++; // 数字计数器加1
        } else {
            (*others)++; // 其他字符计数器加1
        }
        
        str++; // 移动到下一个字符
    }
}

int main() {
    char str[100];
    printf("请输入一个字符串:");
    fgets(str, sizeof(str), stdin);
    
    int letters, digits, others;
    countCharacters(str, &letters, &digits, &others);
    
    printf("字母个数:%d\n", letters);
    printf("数字个数:%d\n", digits);
    printf("其他字符个数:%d\n", others);
    
    return 0;
}

解析:
这道题要求编写一个函数 countCharacters 来统计给定字符串中的字母、数字和其他字符的个数,并在主函数中调用该函数。在 countCharacters 函数中,我们使用三个指针变量 lettersdigitsothers 来保存字母、数字和其他字符的个数。我们首先将这三个计数器初始化为0。然后,我们使用一个循环遍历字符串中的每个字符。对于每个字符,我们使用 isalpha 函数判断它是否是字母,如果是,则将字母计数器加1;使用 isdigit 函数判断它是否是数字,如果是,则将数字计数器加1;否则,将其他字符计数器加1。最后,在主函数中,我们声明了一个字符数组 str 来接受用户输入的字符串,并使用 fgets 函数读取用户输入。然后,我们声明了三个变量 lettersdigitsothers,用于保存字母、数字和其他字符的个数。接下来,我们调用 countCharacters 函数,并传递字符串 str 和计数器变量的地址作为参数。最后,我们在主函数中打印出字母个数、数字个数和其他字符个数的结果。

这道题考察了字符串处理和字符分类函数的使用。通过遍历字符串中的每个字符,并使用相应的函数判断字符的类型,我们可以统计出字符串中字母、数字和其他字符的个数。

3.编写一个程序,接受用户输入的一个正整数n,计算并输出n的阶乘。

答案:

#include <stdio.h>

unsigned long long factorial(unsigned int n) {
    if (n == 0 || n == 1) {
        return 1; // 0的阶乘和1的阶乘均为1
    } else {
        unsigned long long result = 1;
        for (unsigned int i = 2; i <= n; i++) {
            result *= i; // 逐步累乘得到阶乘
        }
        return result;
    }
}

int main() {
    unsigned int n;
    printf("请输入一个正整数:");
    scanf("%u", &n);
    
    unsigned long long result = factorial(n);
    printf("%u的阶乘:%llu\n", n, result);
    
    return 0;
}

解析:
这道题要求编写一个函数 factorial 来计算给定正整数n的阶乘,并在主函数中调用该函数。在 factorial 函数中,我们使用一个循环从2到n,逐步累乘得到n的阶乘。我们首先判断特殊情况,如果n为0或1,则阶乘结果为1。否则,我们初始化一个变量 result 为1,然后使用循环从2开始迭代到n,将每个数乘以 result,最终得到n的阶乘。在主函数中,我们声明了一个无符号整数变量 n,用于接受用户输入的正整数。然后,我们调用 factorial 函数,并传递 n 作为参数,将计算得到的阶乘结果保存在变量 result 中。最后,我们在主函数中打印出输入的正整数和计算得到的阶乘结果。

这道题考察了循环和函数的使用。通过使用循环逐步累乘,我们可以计算出给定正整数的阶乘。在主函数中,我们将用户输入作为参数传递给函数,并将计算结果输出。

4.编写一个程序,接受用户输入的一个整数n,判断该整数是否为素数(质数)。若是素数,则输出"是素数";否则输出"不是素数"。

答案:

#include <stdio.h>

int isPrime(int n) {
    if (n <= 1) {
        return 0; // 小于等于1的数不是素数
    }
    
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            return 0; // 存在能整除n的数,不是素数
        }
    }
    
    return 1; // n没有能整除它的数,是素数
}

int main() {
    int n;
    printf("请输入一个整数:");
    scanf("%d", &n);
    
    if (isPrime(n)) {
        printf("%d是素数\n", n);
    } else {
        printf("%d不是素数\n", n);
    }
    
    return 0;
}

解析:
这道题要求编写一个函数 isPrime 来判断给定整数n是否为素数,并在主函数中调用该函数。在 isPrime 函数中,我们首先判断特殊情况,如果n小于等于1,则不是素数。然后,我们使用一个循环从2开始迭代到n的平方根,判断是否存在能整除n的数。如果存在能整除n的数,则n不是素数;否则,n是素数。在主函数中,我们声明了一个整数变量 n,用于接受用户输入的整数。然后,我们调用 isPrime 函数,并传递 n 作为参数进行判断。根据函数返回的结果,我们在主函数中打印出相应的输出。

这道题考察了循环和条件判断的使用。通过使用循环判断n是否能被2到平方根范围内的数整除,我们可以确定n是否为素数。在主函数中,我们根据函数的返回结果输出相应的结果。

5.编写一个程序,接受用户输入的一个正整数n,计算并输出n行的杨辉三角形。

答案:

#include <stdio.h>

void printPascalTriangle(int n) {
    int triangle[n][n];
    
    // 初始化第一列和对角线上的元素为1
    for (int i = 0; i < n; i++) {
        triangle[i][0] = 1;
        triangle[i][i] = 1;
    }
    
    // 填充其余元素
    for (int i = 2; i < n; i++) {
        for (int j = 1; j < i; j++) {
            triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j];
        }
    }
    
    // 打印杨辉三角形
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= i; j++) {
            printf("%d ", triangle[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int n;
    printf("请输入一个正整数:");
    scanf("%d", &n);
    
    printPascalTriangle(n);
    
    return 0;
}

解析:
这道题要求编写一个函数 printPascalTriangle 来计算并输出给定正整数n行的杨辉三角形,并在主函数中调用该函数。在 printPascalTriangle 函数中,我们首先声明一个二维数组 triangle,用于保存杨辉三角形的元素。然后,我们使用两个循环进行填充操作。首先,我们初始化第一列和对角线上的元素为1,因为它们都是杨辉三角形的边界元素。然后,我们使用嵌套的循环来填充其余元素。对于每一行的第j个元素,它的值等于上一行的第j-1个元素和第j个元素的和。最后,我们使用另外两个嵌套的循环来打印杨辉三角形的元素。在主函数中,我们声明一个整数变量 n,用于接受用户输入的正整数。然后,我们调用 printPascalTriangle 函数,并将 n 作为参数进行计算和输出。

这道题考察了二维数组和嵌套循环的使用。通过使用二维数组来保存杨辉三角形的元素,并通过嵌套循环进行填充和打印操作,我们可以生成指定行数的杨辉三角形。

6.编写一个程序,接受用户输入的一个正整数n,判断并输出该整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都相同的数。

答案:

#include <stdio.h>

int isPalindrome(int n) {
    int reversedNum = 0;
    int originalNum = n;
    
    while (n > 0) {
        reversedNum = reversedNum * 10 + n % 10; // 反转数字
        n /= 10;
    }
    
    if (reversedNum == originalNum) {
        return 1; // 是回文数
    } else {
        return 0; // 不是回文数
    }
}

int main() {
    int n;
    printf("请输入一个正整数:");
    scanf("%d", &n);
    
    if (isPalindrome(n)) {
        printf("%d是回文数\n", n);
    } else {
        printf("%d不是回文数\n", n);
    }
    
    return 0;
}

解析:
这道题要求编写一个函数 isPalindrome 来判断给定正整数n是否是回文数,并在主函数中调用该函数。在 isPalindrome 函数中,我们首先声明两个变量 reversedNumoriginalNum,其中 reversedNum 用于保存反转后的数字,originalNum 用于保存原始的输入数字n。然后,我们使用一个循环将n逐位反转。在每次循环中,我们将 reversedNum 乘以10并加上n的个位数,然后将n除以10,以便下一次循环处理十位数。最后,我们将反转后的数字 reversedNum 与原始的输入数字 originalNum 进行比较,如果相等,则n是回文数;否则,n不是回文数。在主函数中,我们声明一个整数变量 n,用于接受用户输入的正整数。然后,我们调用 isPalindrome 函数,并将 n 作为参数进行判断。根据函数的返回结果,我们在主函数中打印出相应的输出。

这道题考察了循环和数字反转的使用。通过将数字逐位反转,并与原始的输入数字进行比较,我们可以判断给定的正整数是否是回文数。

7.编写一个程序,接受用户输入的一个字符串,判断该字符串是否是回文字符串。回文字符串是指正序和倒序读都相同的字符串。

答案:

#include <stdio.h>
#include <string.h>

int isPalindrome(const char* str) {
    int len = strlen(str);
    int i = 0;
    int j = len - 1;
    
    while (i < j) {
        if (str[i] != str[j]) {
            return 0; // 不是回文字符串
        }
        i++;
        j--;
    }
    
    return 1; // 是回文字符串
}

int main() {
    char str[100];
    printf("请输入一个字符串:");
    scanf("%s", str);
    
    if (isPalindrome(str)) {
        printf("%s是回文字符串\n", str);
    } else {
        printf("%s不是回文字符串\n", str);
    }
    
    return 0;
}

解析:
这道题要求编写一个函数 isPalindrome 来判断给定字符串是否是回文字符串,并在主函数中调用该函数。在 isPalindrome 函数中,我们首先使用 strlen 函数获取字符串的长度。然后,我们使用两个指针 ij 分别指向字符串的开头和结尾,并进行比较操作。在每次循环中,我们比较指针所指向的字符是否相等,如果不相等,则字符串不是回文字符串。如果相等,则将指针 i 向后移动一位,将指针 j 向前移动一位,继续进行下一轮的比较。当 i 大于等于 j 时,表示已经比较完字符串的一半,且字符都相等,字符串是回文字符串。在主函数中,我们声明一个字符数组 str,用于接受用户输入的字符串。然后,我们调用 isPalindrome 函数,并将 str 作为参数进行判断。根据函数的返回结果,我们在主函数中打印出相应的输出。

这道题考察了字符串处理和指针的使用。通过使用两个指针从字符串的开头和结尾向中间移动,并比较对应的字符是否相等,我们可以判断给定的字符串是否是回文字符串。

8.编写一个程序,接受用户输入的一个整数n,计算并输出斐波那契数列的前n项。

答案:

#include <stdio.h>

void printFibonacciSeries(int n) {
    if (n <= 0) {
        return;
    }
    
    int first = 0;
    int second = 1;
    
    printf("斐波那契数列的前%d项:\n", n);
    printf("%d ", first);
    
    if (n >= 2) {
        printf("%d ", second);
    }
    
    for (int i = 3; i <= n; i++) {
        int next = first + second;
        printf("%d ", next);
        first = second;
        second = next;
    }
    
    printf("\n");
}

int main() {
    int n;
    printf("请输入一个整数:");
    scanf("%d", &n);
    
    printFibonacciSeries(n);
    
    return 0;
}

解析:
这道题要求编写一个函数 printFibonacciSeries 来计算并输出斐波那契数列的前n项,并在主函数中调用该函数。在 printFibonacciSeries 函数中,我们首先判断特殊情况,如果n小于等于0,则不需要进行计算。然后,我们声明两个变量 firstsecond,分别用于保存斐波那契数列的前两个数。我们先输出第一个数 first,然后判断n是否大于等于2,如果是,则输出第二个数 second。接下来,我们使用一个循环从第三个数开始计算并输出剩余的斐波那契数列。在每次循环中,我们计算下一个数 next,并将其输出。然后,我们更新 firstsecond 的值,将 second 赋值给 first,将 next 赋值给 second,以便下一次循环的计算。最后,我们在每行数字之间输出空格,并在斐波那契数列结束后换行。在主函数中,我们声明一个整数变量 n,用于接受用户输入的整数。然后,我们调用 printFibonacciSeries 函数,并将 n 作为参数进行计算和输出。

这道题考察了循环和变量更新的使用。通过使用循环和变量更新来计算并输出斐波那契数列的前n项,我们可以生成指定数量的斐波那契数列。

9.编写一个程序,接受用户输入的一个整数n,计算并输出n的阶乘。

答案:

#include <stdio.h>

int calculateFactorial(int n) {
    int factorial = 1;
    
    if (n < 0) {
        printf("输入的整数不能为负数。\n");
        return -1;
    }
    
    for (int i = 1; i <= n; i++) {
        factorial *= i;
    }
    
    return factorial;
}

int main() {
    int n;
    printf("请输入一个整数:");
    scanf("%d", &n);
    
    int result = calculateFactorial(n);
    
    if (result != -1) {
        printf("%d的阶乘为:%d\n", n, result);
    }
    
    return 0;
}

解析:
这道题要求编写一个函数 calculateFactorial 来计算给定整数n的阶乘,并在主函数中调用该函数。在 calculateFactorial 函数中,我们首先判断特殊情况,如果n小于0,则输入的整数无效,输出错误提示并返回-1。否则,我们声明一个变量 factorial 并将其初始化为1,用于保存阶乘的结果。然后,我们使用一个循环从1到n,将每个数字依次乘以 factorial。最后,我们返回计算得到的阶乘值。在主函数中,我们声明一个整数变量 n,用于接受用户输入的整数。然后,我们调用 calculateFactorial 函数,并将 n 作为参数进行计算。根据函数的返回结果,我们在主函数中打印出相应的输出。

这道题考察了循环和条件判断的使用。通过使用循环和变量更新来计算给定整数的阶乘,我们可以得到阶乘的结果。同时,需要注意处理特殊情况,如输入的整数为负数时给出错误提示。

10.编写一个程序,接受用户输入的一个正整数n,计算并输出n的所有因数。

答案:

#include <stdio.h>

void calculateFactors(int n) {
    printf("%d的因数有:", n);
    
    for (int i = 1; i <= n; i++) {
        if (n % i == 0) {
            printf("%d ", i);
        }
    }
    
    printf("\n");
}

int main() {
    int n;
    printf("请输入一个正整数:");
    scanf("%d", &n);
    
    calculateFactors(n);
    
    return 0;
}

解析:
这道题要求编写一个函数 calculateFactors 来计算给定正整数n的所有因数,并在主函数中调用该函数。在 calculateFactors 函数中,我们首先输出提示信息。然后,我们使用一个循环从1到n,判断每个数字是否是n的因数。如果是,就输出该数字。一个数n的因数是能整除n的正整数。最后,我们在输出完所有因数后换行。在主函数中,我们声明一个整数变量 n,用于接受用户输入的正整数。然后,我们调用 calculateFactors 函数,并将 n 作为参数进行计算和输出。

这道题考察了循环和条件判断的使用。通过使用循环和取模运算来计算给定正整数的所有因数,我们可以找到n的所有能够整除n的正整数。

11.编写一个程序,接受用户输入的一个正整数n,判断并输出n是否为素数。素数是只能被1和自身整除的正整数。

答案:

#include <stdio.h>

int isPrime(int n) {
    if (n <= 1) {
        return 0; // 不是素数
    }
    
    for (int i = 2; i <= n / 2; i++) {
        if (n % i == 0) {
            return 0; // 不是素数
        }
    }
    
    return 1; // 是素数
}

int main() {
    int n;
    printf("请输入一个正整数:");
    scanf("%d", &n);
    
    if (isPrime(n)) {
        printf("%d是素数\n", n);
    } else {
        printf("%d不是素数\n", n);
    }
    
    return 0;
}

解析:
这道题要求编写一个函数 isPrime 来判断给定正整数n是否为素数,并在主函数中调用该函数。在 isPrime 函数中,我们首先判断特殊情况,如果n小于等于1,则不是素数。然后,我们使用一个循环从2到n的一半,判断是否存在能够整除n的数。如果存在,说明n不是素数;否则,n是素数。在循环中,我们使用取模运算来判断是否能够整除,如果能够整除,则返回0表示不是素数。最后,如果循环结束都没有找到能整除n的数,则返回1表示是素数。在主函数中,我们声明一个整数变量 n,用于接受用户输入的正整数。然后,我们调用 isPrime 函数,并将 n 作为参数进行判断。根据函数的返回结果,我们在主函数中打印出相应的输出。

这道题考察了循环和条件判断的使用。通过使用循环和取模运算来判断给定正整数是否为素数,我们可以确定一个数是否只能被1和自身整除。

12.编写一个程序,接受用户输入的一个正整数n,计算并输出n的前n个斐波那契数。

答案:

#include <stdio.h>

void calculateFibonacciNumbers(int n) {
    printf("前%d个斐波那契数:\n", n);
    
    if (n >= 1) {
        printf("0 ");
    }
    
    if (n >= 2) {
        printf("1 ");
    }
    
    int a = 0;
    int b = 1;
    
    for (int i = 3; i <= n; i++) {
        int c = a + b;
        printf("%d ", c);
        a = b;
        b = c;
    }
    
    printf("\n");
}

int main() {
    int n;
    printf("请输入一个正整数:");
    scanf("%d", &n);
    
    calculateFibonacciNumbers(n);
    
    return 0;
}

解析:
这道题要求编写一个函数 calculateFibonacciNumbers 来计算给定正整数n的前n个斐波那契数,并在主函数中调用该函数。在 calculateFibonacciNumbers 函数中,我们首先输出提示信息。然后,我们根据n的值来确定是否输出斐波那契数列的前两个数。接下来,我们使用变量 ab 分别表示斐波那契数列的前两个数,初始化为0和1。使用一个循环从第三个数开始计算并输出剩余的斐波那契数列。在每次循环中,我们计算下一个数 c,并将其输出。然后,我们更新 ab 的值,将 b 赋值给 a,将 c 赋值给 b,以便下一次循环的计算。最后,在每行数字之间输出空格,并在斐波那契数列结束后换行。在主函数中,我们声明一个整数变量 n,用于接受用户输入的正整数。然后,我们调用 calculateFibonacciNumbers 函数,并将 n 作为参数进行计算和输出。

这道题考察了循环和变量更新的使用。通过使用循环和变量更新来计算给定正整数的前n个斐波那契数,我们可以生成指定数量的斐波那契数列。同时,根据n的值,我们决定是否输出斐波那契数列的前两个数。

13.编写一个程序,接受用户输入的一个正整数n,判断并输出n是否为回文数。回文数是指正序(从左向右)和倒序(从右向左)读都相同的数。

答案:

#include <stdio.h>

int isPalindrome(int n) {
    int original = n;
    int reversed = 0;
    
    while (n > 0) {
        int remainder = n % 10;
        reversed = reversed * 10 + remainder;
        n /= 10;
    }
    
    if (original == reversed) {
        return 1; // 是回文数
    } else {
        return 0; // 不是回文数
    }
}

int main() {
    int n;
    printf("请输入一个正整数:");
    scanf("%d", &n);
    
    if (isPalindrome(n)) {
        printf("%d是回文数\n", n);
    } else {
        printf("%d不是回文数\n", n);
    }
    
    return 0;
}

解析:
这道题要求编写一个函数 isPalindrome 来判断给定正整数n是否为回文数,并在主函数中调用该函数。在 isPalindrome 函数中,我们首先保存原始的数值 n,然后声明一个变量 reversed 并初始化为0,用于保存反转后的数值。接下来,我们使用一个循环将 n 反转,并将每一位上的数字加入到 reversed 中。在循环中,我们通过取模运算获取 n 的最后一位数字,并将其加入到 reversed 中,然后将 n 除以10去掉最后一位数字。最后,我们将反转后的数值 reversed 与原始数值 n 进行比较。如果它们相等,说明 n 是回文数;否则,不是回文数。在主函数中,我们声明一个整数变量 n,用于接受用户输入的正整数。然后,我们调用 isPalindrome 函数,并将 n 作为参数进行判断。根据函数的返回结果,我们在主函数中打印出相应的输出。

这道题考察了循环、变量操作和条件判断的使用。通过将正整数反转,并与原始数值进行比较,我们可以判断一个数是否是回文数。

14.给定一个整数数组nums和一个目标值target,在数组中找出和为目标值的两个整数,并返回它们的索引。假设每个输入只对应一个答案,并且同一个元素不能使用两次。你可以按任意顺序返回答案。

#include <stdio.h>

int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
    for (int i = 0; i < numsSize - 1; i++) {
        for (int j = i + 1; j < numsSize; j++) {
            if (nums[i] + nums[j] == target) {
                int* result = (int*)malloc(2 * sizeof(int));
                result[0] = i;
                result[1] = j;
                *returnSize = 2;
                return result;
            }
        }
    }
    
    return NULL;
}

int main() {
    int nums[] = {2, 7, 11, 15};
    int target = 9;
    int returnSize;
    
    int* result = twoSum(nums, sizeof(nums) / sizeof(nums[0]), target, &returnSize);
    
    if (result != NULL) {
        printf("和为目标值的两个整数的索引是:%d和%d\n", result[0], result[1]);
        free(result);
    }
    
    return 0;
}

解析:
这道题目给出了一个整数数组 nums 和一个目标值 target,要求在数组中找到两个元素的和等于目标值,并返回它们的索引。首先,在 twoSum 函数中,我们使用两层循环遍历数组中的每对元素。对于每对元素 nums[i]nums[j],我们判断它们的和是否等于目标值 target。如果相等,我们创建一个大小为2的整数数组 result,并将索引 ij 存入其中,然后将 result 的地址返回,并通过指针 returnSize 返回数组的大小2。如果没有找到符合条件的元素对,则返回NULL。在主函数中,我们声明一个整数数组 nums,并给出示例的初始值。然后,我们调用 twoSum 函数,传入数组、目标值以及一个指向 returnSize 变量的指针。根据函数的返回结果,我们判断是否找到符合条件的元素对,并打印它们的索引。需要注意的是,如果找到符合条件的元素对,我们需要手动释放动态分配的内存。

这道题目考察了数组和循环的使用。通过使用两层循环遍历数组中的元素对,我们可以找到和为目标值的两个元素,并返回它们的索引。

15.给定一个字符串s,编写一个函数将其反转并返回。例如,输入字符串 “hello”,反转后返回 “olleh”。

#include <stdio.h>
#include <string.h>

void reverseString(char* s) {
    int length = strlen(s);
    int left = 0;
    int right = length - 1;
    
    while (left < right) {
        char temp = s[left];
        s[left] = s[right];
        s[right] = temp;
        left++;
        right--;
    }
}

int main() {
    char s[] = "hello";
    
    printf("原字符串:%s\n", s);
    
    reverseString(s);
    
    printf("反转后的字符串:%s\n", s);
    
    return 0;
}

解析:
这道题目给出了一个字符串 s,要求编写一个函数 reverseString 将其反转,并返回反转后的字符串。在 reverseString 函数中,我们首先使用 strlen 函数获取字符串的长度,然后使用双指针的方法进行字符串反转。我们使用变量 leftright 分别表示字符串的左右指针,初始时分别指向字符串的第一个字符和最后一个字符。通过交换 leftright 指针所指向的字符,然后将 left 向右移动一位,将 right 向左移动一位,循环进行,直到 left 不再小于 right。在主函数中,我们声明一个字符数组 s,并给出示例的初始值。然后,我们调用 reverseString 函数进行字符串反转,并打印反转后的字符串。

这道题目考察了字符串的操作和双指针的应用。通过使用双指针技巧,我们可以在原字符串上进行字符交换,从而实现字符串的反转。

16.给定一个非空字符串s,编写一个函数,返回使字符串s中的字符按照出现频率降序排列的新字符串。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// 定义字符出现频率的比较函数,用于排序
int compare(const void* a, const void* b) {
    // 获取字符出现频率的差值
    int freqDiff = ((const int*)b)[1] - ((const int*)a)[1];
    // 如果频率相同,则按照字符的ASCII码升序排列
    if (freqDiff == 0) {
        return ((const int*)a)[0] - ((const int*)b)[0];
    }
    // 否则,按照频率的差值降序排列
    return freqDiff;
}

char* frequencySort(char* s) {
    int length = strlen(s);
    int freq[128] = {0}; // 统计每个字符的出现频率
    
    // 统计每个字符的出现频率
    for (int i = 0; i < length; i++) {
        freq[(int)s[i]]++;
    }
    
    // 创建一个二维数组,用于存储字符和对应的频率
    int** freqArray = (int**)malloc(length * sizeof(int*));
    for (int i = 0; i < length; i++) {
        freqArray[i] = (int*)malloc(2 * sizeof(int));
        freqArray[i][0] = (int)s[i]; // 存储字符的ASCII码
        freqArray[i][1] = freq[(int)s[i]]; // 存储字符的出现频率
    }
    
    // 按照字符的出现频率降序排列
    qsort(freqArray, length, sizeof(int*), compare);
    
    // 根据排序结果生成新的字符串
    char* result = (char*)malloc((length + 1) * sizeof(char));
    int index = 0;
    for (int i = 0; i < length; i++) {
        for (int j = 0; j < freqArray[i][1]; j++) {
            result[index++] = (char)freqArray[i][0];
        }
    }
    result[length] = '\0';
    
    // 释放动态分配的内存
    for (int i = 0; i < length; i++) {
        free(freqArray[i]);
    }
    free(freqArray);
    
    return result;
}

int main() {
    char s[] = "tree";
    
    printf("原字符串:%s\n", s);
    
    char* result = frequencySort(s);
    
    printf("按照频率降序排列的新字符串:%s\n", result);
    
    free(result);
    
    return 0;
}

解析:
这道题目给出了一个非空字符串 s,要求编写一个函数 frequencySort,返回按照字符出现频率降序排列的新字符串。在函数中,我们首先使用数组 freq 统计每个字符的

出现频率,遍历字符串中的每个字符,通过 ASCII 码将其映射到 freq 数组中,并进行频率计数。然后,我们创建一个二维数组 freqArray,用于存储字符和对应的频率,其中每个元素是一个大小为2的一维数组,分别存储字符的 ASCII 码和出现频率。接下来,我们使用 qsort 函数对 freqArray 进行排序,按照字符的出现频率降序排列,同时考虑字符的 ASCII 码升序排列作为辅助排序条件。然后,我们根据排序结果生成新的字符串 result,通过遍历排序后的 freqArray,将每个字符按照出现频率依次添加到 result 中。最后,我们在 result 字符串的末尾添加结束符 \0,并释放动态分配的内存。在主函数中,我们声明一个字符数组 s,并给出示例的初始值。然后,我们调用 frequencySort 函数进行字符频率排序,并打印结果。最后,记得释放动态分配的内存。

这道题目考察了字符串处理、数组操作和排序算法的应用。通过统计字符的出现频率,使用二维数组进行排序,并根据排序结果生成新的字符串,我们可以实现按照字符出现频率降序排列的功能。

17.给定一个整数数组nums,编写一个函数将数组中的所有0移动到数组的末尾,同时保持非零元素的相对顺序不变。例如,输入数组[0, 1, 0, 3, 12],移动后数组变为[1, 3, 12, 0, 0]。

#include <stdio.h>

void moveZeroes(int* nums, int numsSize) {
    int insertPos = 0; // 插入位置
    
    // 遍历数组,将非零元素前移
    for (int i = 0; i < numsSize; i++) {
        if (nums[i] != 0) {
            nums[insertPos++] = nums[i];
        }
    }
    
    // 将剩余位置补0
    while (insertPos < numsSize) {
        nums[insertPos++] = 0;
    }
}

int main() {
    int nums[] = {0, 1, 0, 3, 12};
    int numsSize = sizeof(nums) / sizeof(nums[0]);
    
    printf("原数组:");
    for (int i = 0; i < numsSize; i++) {
        printf("%d ", nums[i]);
    }
    printf("\n");
    
    moveZeroes(nums, numsSize);
    
    printf("移动后的数组:");
    for (int i = 0; i < numsSize; i++) {
        printf("%d ", nums[i]);
    }
    printf("\n");
    
    return 0;
}

解析:
这道题目给出了一个整数数组 nums,要求编写一个函数 moveZeroes 将数组中的所有0移动到数组的末尾,同时保持非零元素的相对顺序不变。在函数中,我们使用一个变量 insertPos 来记录插入位置,初始时为0。我们遍历数组 nums,当遇到非零元素时,将其前移至 insertPos 位置,并将 insertPos 自增1。这样,经过遍历后,所有非零元素都被移动到了数组的前面,且相对顺序保持不变。接着,我们使用一个循环将剩余位置补0,即将 insertPos 后的所有元素都设置为0。在主函数中,我们声明一个整数数组 nums,并给出示例的初始值。然后,我们调用 moveZeroes 函数进行0的移动,并打印移动后的数组。

这道题目考察了数组的操作和元素的前移。通过遍历数组,将非零元素前移,然后将剩余位置补0,我们可以实现将数组中所有0移动到末尾的功能,并保持非零元素的相对顺序不变。

18.给定一个整数数组nums和一个整数目标值target,编写一个函数,在数组中找出两个数之和等于目标值,并返回它们的索引。假设每个输入只有一个答案,并且不可以重复利用相同的元素。例如,输入数组[2, 7, 11, 15],目标值为9,返回[0, 1]。

#include <stdio.h>
#include <stdlib.h>

int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
    int* result = (int*)malloc(2 * sizeof(int)); // 存储结果的数组
    
    // 使用双重循环遍历数组,寻找两个数之和等于目标值
    for (int i = 0; i < numsSize - 1; i++) {
        for (int j = i + 1; j < numsSize; j++) {
            if (nums[i] + nums[j] == target) {
                result[0] = i;
                result[1] = j;
                *returnSize = 2;
                return result;
            }
        }
    }
    
    // 若未找到满足条件的结果,则返回空指针
    *returnSize = 0;
    return NULL;
}

int main() {
    int nums[] = {2, 7, 11, 15};
    int numsSize = sizeof(nums) / sizeof(nums[0]);
    int target = 9;
    int returnSize;
    
    printf("原数组:");
    for (int i = 0; i < numsSize; i++) {
        printf("%d ", nums[i]);
    }
    printf("\n");
    
    int* result = twoSum(nums, numsSize, target, &returnSize);
    
    if (returnSize == 2) {
        printf("找到两个数之和等于目标值的索引:[%d, %d]\n", result[0], result[1]);
    } else {
        printf("未找到满足条件的结果。\n");
    }
    
    free(result);
    
    return 0;
}

解析:
这道题目给出了一个整数数组 nums、一个整数目标值 target,要求编写一个函数 twoSum 在数组中找出两个数之和等于目标值,并返回它们的索引。在函数中,我们使用双重循环遍历数组 nums,对于每对不同的元素,判断它们的和是否等于目标值 target。如果找到满足条件的两个数,我们将它们的索引存储在动态分配的整数数组 result 中,并将结果数组的大小 returnSize 设置为2。然后,我们返回结果数组 result。如果未找到满足条件的结果,则将 returnSize 设置为0,返回空指针。在主函数中,我们声明一个整数数组 nums,并给出示例的初始值。然后

,我们调用 twoSum 函数查找满足条件的两个数的索引,并打印结果。最后,记得释放动态分配的内存。

这道题目考察了数组的操作和双重循环的应用。通过使用双重循环遍历数组,我们可以找到两个数之和等于目标值的索引,并返回结果。

19.给定一个链表,判断链表中是否有环。如果链表中存在环,则返回true;否则,返回false。

#include <stdio.h>
#include <stdbool.h>

// 链表结点的定义
struct ListNode {
    int val;
    struct ListNode* next;
};

bool hasCycle(struct ListNode* head) {
    // 使用快慢指针判断链表是否有环
    struct ListNode* slow = head; // 慢指针,每次移动一步
    struct ListNode* fast = head; // 快指针,每次移动两步
    
    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;
        
        // 如果快慢指针相遇,则链表有环
        if (slow == fast) {
            return true;
        }
    }
    
    // 链表无环
    return false;
}

int main() {
    // 构建一个有环的链表作为示例
    struct ListNode node1, node2, node3, node4;
    node1.val = 1;
    node2.val = 2;
    node3.val = 3;
    node4.val = 4;
    
    node1.next = &node2;
    node2.next = &node3;
    node3.next = &node4;
    node4.next = &node2; // 将node4的next指针指向node2,形成环
    
    bool result = hasCycle(&node1);
    
    if (result) {
        printf("链表中存在环。\n");
    } else {
        printf("链表中不存在环。\n");
    }
    
    return 0;
}

解析:
这道题目给出了一个链表,要求判断链表中是否有环。在函数 hasCycle 中,我们使用快慢指针来判断链表是否有环。快指针每次移动两步,而慢指针每次移动一步。如果链表中存在环,快指针最终会追上慢指针,两者相遇;如果链表中不存在环,快指针最终会到达链表的末尾,此时快慢指针不会相遇。因此,我们在循环中判断快慢指针是否相遇,如果相遇,则链表有环,返回true;如果快指针到达链表末尾,则链表无环,返回false。在主函数中,我们构建了一个有环的链表作为示例,然后调用 hasCycle 函数判断链表中是否存在环,并根据判断结果打印相应的信息。

这道题目考察了链表的操作和快慢指针的应用。通过使用快慢指针遍历链表,我们可以判断链表中是否有环,这是一种常见的判断链表是否有环的方法。

20.给定一个非负整数n,计算并返回n的阶乘。要求使用递归函数实现。

#include <stdio.h>

unsigned long long factorial(unsigned int n) {
    // 递归结束条件:n为0或1时,直接返回1
    if (n == 0 || n == 1) {
        return 1;
    }
    
    // 递归计算 n 的阶乘:n! = n * (n-1)!
    return n * factorial(n - 1);
}

int main() {
    unsigned int n = 5;
    unsigned long long result = factorial(n);
    
    printf("%u的阶乘为:%llu\n", n, result);
    
    return 0;
}

解析:
这道题目给定一个非负整数 n,要求计算并返回 n 的阶乘。在函数 factorial 中,我们使用递归的方式实现阶乘的计算。递归的结束条件是 n 为0或1时,此时阶乘的结果为1,直接返回1。对于其他情况,我们使用递归计算 n 的阶乘,即 n! = n * (n-1)!。在主函数中,我们给定一个非负整数 n,调用 factorial 函数计算 n 的阶乘,并将结果打印出来。

这道题目考察了递归函数的应用。通过将阶乘的计算问题划分为更小规模的子问题,并利用递归函数不断求解子问题,最终得到整个问题的解。在实现递归函数时,我们需要考虑递归的结束条件和递归调用的方式,确保递归能够正确地终止并得到正确的结果。

21.给定一个整数数组 nums,找出数组中两个数的最大异或值,并返回该值。

#include <stdio.h>

int findMaxXOR(int* nums, int numsSize) {
    int maxXOR = 0; // 最大异或值
    
    // 遍历数组中的每个元素
    for (int i = 0; i < numsSize; i++) {
        for (int j = i + 1; j < numsSize; j++) {
            // 计算两个数的异或值
            int currentXOR = nums[i] ^ nums[j];
            
            // 更新最大异或值
            if (currentXOR > maxXOR) {
                maxXOR = currentXOR;
            }
        }
    }
    
    return maxXOR;
}

int main() {
    int nums[] = {3, 10, 5, 25, 2, 8}; // 示例数组
    int numsSize = sizeof(nums) / sizeof(nums[0]);
    
    int result = findMaxXOR(nums, numsSize);
    
    printf("数组中两个数的最大异或值为:%d\n", result);
    
    return 0;
}

解析:
这道题目给定一个整数数组 nums,要求找出数组中两个数的最大异或值,并返回该值。在函数 findMaxXOR 中,我们使用两重循环遍历数组中的每个元素,并计算当前两个数的异或值 currentXOR。然后,我们与当前的最大异或值 maxXOR 进行比较,如果 currentXOR 大于 maxXOR,则更新 maxXOR 的值。最终,返回最大异或值 maxXOR。在主函数中,我们给出了一个示例数组 nums,调用 findMaxXOR 函数计算数组中两个数的最大异或值,并打印结果。

这道题目考察了数组的操作和异或运算的应用。通过遍历数组中的每对元素,并计算它们的异或值,我们可以找到数组中两个数的最大异或值。异或运算具有特殊性质,能够在不使用额外空间的情况下计算两个数的异或值,且异或运算满足交换律和结合律。因此,通过遍历数组中的每对元素,我们能够找到最大的异或值。

22.给定一个整数数组 nums,判断数组是否为单调递增或单调递减数组。如果数组是单调递增数组,则返回 1;如果数组是单调递减数组,则返回 -1;如果数组不满足单调性要求,则返回 0

#include <stdio.h>

int isMonotonic(int* nums, int numsSize) {
    int increasing = 1; // 标记是否为递增数组
    int decreasing = 1; // 标记是否为递减数组
    
    // 遍历数组,判断递增和递减条件
    for (int i = 1; i < numsSize; i++) {
        if (nums[i] < nums[i - 1]) {
            increasing = 0; // 不满足递增条件
        }
        
        if (nums[i] > nums[i - 1]) {
            decreasing = 0; // 不满足递减条件
        }
    }
    
    if (increasing) {
        return 1; // 递增数组
    } else if (decreasing) {
        return -1; // 递减数组
    } else {
        return 0; // 既不是递增数组也不是递减数组
    }
}

int main() {
    int nums1[] = {1, 2, 3, 4, 5}; // 递增数组
    int nums2[] = {5, 4, 3, 2, 1}; // 递减数组
    int nums3[] = {1, 2, 3, 2, 4}; // 不满足单调性要求的数组
    
    int result1 = isMonotonic(nums1, sizeof(nums1) / sizeof(nums1[0]));
    int result2 = isMonotonic(nums2, sizeof(nums2) / sizeof(nums2[0]));
    int result3 = isMonotonic(nums3, sizeof(nums3) / sizeof(nums3[0]));
    
    printf("数组 nums1 是否为单调数组: %d\n", result1);
    printf("数组 nums2 是否为单调数组: %d\n", result2);
    printf("数组 nums3 是否为单调数组: %d\n", result3);
    
    return 0;
}

解析:
这道题目给定一个整数数组 nums,要求判断数组是否为单调递增或单调递减数组。在函数 isMonotonic 中,我们使用两个标记 increasingdecreasing 来记录是否满足递增和递减条件。通过遍历数组,对于相邻的元素进行比较,更新标记的值。如果存在某对相邻元素不满足递增条件,则将 increasing 标记置为 0;如果存在某对相邻元素不满足递减条件,则将 decreasing 标记置为 0。最终,根据标记的值返回相应的结果:如果 increasing1,则返回 1 表示递增数组;

如果 decreasing1,则返回 -1 表示递减数组;如果既不满足递增条件也不满足递减条件,则返回 0 表示不满足单调性要求。在主函数中,我们给出了三个示例数组,并调用 isMonotonic 函数判断数组是否为单调数组,并打印结果。

这道题目考察了数组的操作和条件判断。通过遍历数组,我们可以判断数组是否为单调递增或单调递减数组。在判断过程中,我们使用两个标记来记录是否满足递增和递减条件,并根据标记的值返回相应的结果。

23.给定一个字符串 s,判断它是否是有效的括号序列。只包含字符 '('')''['']''{''}'。要求使用栈的数据结构来实现。

#include <stdio.h>
#include <stdbool.h>

#define MAX_SIZE 100

typedef struct {
    char stack[MAX_SIZE];
    int top;
} Stack;

void initialize(Stack* s) {
    s->top = -1;
}

bool isEmpty(Stack* s) {
    return (s->top == -1);
}

bool isFull(Stack* s) {
    return (s->top == MAX_SIZE - 1);
}

void push(Stack* s, char ch) {
    if (isFull(s)) {
        printf("栈已满,无法插入元素。\n");
        return;
    }
    
    s->stack[++(s->top)] = ch;
}

char pop(Stack* s) {
    if (isEmpty(s)) {
        printf("栈已空,无法弹出元素。\n");
        return '\0';
    }
    
    return s->stack[(s->top)--];
}

bool isValidParentheses(char* s) {
    Stack stack;
    initialize(&stack);
    
    for (int i = 0; s[i] != '\0'; i++) {
        if (s[i] == '(' || s[i] == '[' || s[i] == '{') {
            push(&stack, s[i]);
        } else if (s[i] == ')' || s[i] == ']' || s[i] == '}') {
            if (isEmpty(&stack)) {
                return false; // 有右括号但栈为空,不匹配
            }
            
            char top = pop(&stack);
            
            if ((s[i] == ')' && top != '(') || (s[i] == ']' && top != '[') || (s[i] == '}' && top != '{')) {
                return false; // 括号不匹配
            }
        }
    }
    
    return isEmpty(&stack); // 栈为空表示所有括号都匹配
}

int main() {
    char s1[] = "([])"; // 有效的括号序列
    char s2[] = "([)]"; // 无效的括号序列
    
    bool result1 = isValidParentheses(s1);
    bool result2 = isValidParentheses(s2);
    
    printf("字符串 s1 是否是有效的括号序列:%s\n", result1 ? "是" : "否");
    printf("字符串 s2 是否是有效的括号序列:%s\n", result2 ? "是" : "否");
    
    return 0;
}

解析:
这道题目给定一个字符串 s,要求判断它是否是有效的括号序列。在函数 isValidParentheses 中,我们使用栈的数据结构来实现括号匹配的判断。首先,我们定义了一个结构体 Stack,其中包含一个字符数组 stack 作为栈的存储空间,以及一个整数 top 作为栈顶指针。然后,我们实现了栈的基本操作函数:initialize 用于初始化栈,isEmpty 判断

栈是否为空,isFull 判断栈是否已满,push 将元素入栈,pop 将栈顶元素出栈。

isValidParentheses 函数中,我们遍历字符串 s 中的每个字符。对于左括号字符 '(''[''{',我们将其入栈。对于右括号字符 ')'']''}',我们进行以下操作:

  • 如果栈为空,则表示有右括号但栈为空,不匹配,直接返回 false
  • 否则,从栈中弹出栈顶元素,并与当前的右括号字符进行匹配。如果匹配不成功,则表示括号不匹配,直接返回 false

遍历结束后,如果栈为空,表示所有括号都匹配,返回 true,否则返回 false。在主函数中,我们给出了两个示例字符串,并调用 isValidParentheses 函数判断字符串是否是有效的括号序列,并打印结果。

这道题目考察了栈的应用,特别是在括号匹配问题中的应用。通过使用栈来存储左括号,并在遇到右括号时进行匹配判断,我们可以判断一个字符串是否是有效的括号序列。栈的后进先出特性使得在括号匹配问题中具有较好的解决能力。

24.编写一个程序,实现插入排序算法对一个整数数组进行排序。要求在每次交换元素时打印出当前的数组元素序列。

#include <stdio.h>

void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void insertionSort(int arr[], int size) {
    for (int i = 1; i < size; i++) {
        int key = arr[i];
        int j = i - 1;
        
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        
        arr[j + 1] = key;
        
        printf("当前数组序列:");
        printArray(arr, size);
    }
}

int main() {
    int arr[] = {7, 2, 4, 1, 5, 3}; // 待排序的数组
    int size = sizeof(arr) / sizeof(arr[0]);
    
    printf("初始数组序列:");
    printArray(arr, size);
    
    printf("\n插入排序过程:\n");
    insertionSort(arr, size);
    
    printf("\n排序后的数组序列:");
    printArray(arr, size);
    
    return 0;
}

解析:
这道题目要求实现插入排序算法对一个整数数组进行排序,并在每次交换元素时打印出当前的数组元素序列。在函数 insertionSort 中,我们使用插入排序算法对数组进行排序。首先,我们从数组的第二个元素开始,将其作为待插入元素 key。然后,我们将 key 与已排序的子数组进行比较,找到合适的位置将 key 插入。在插入的过程中,我们通过不断交换元素的方式实现元素的移动。每次交换元素后,我们打印当前的数组元素序列。

在主函数中,我们给出了一个示例数组 arr,调用 insertionSort 函数对数组进行排序,并打印排序前和排序后的数组元素序列。

这道题目考察了插入排序算法的实现。插入排序是一种简单直观的排序算法,其核心思想是将数组分为已排序和未排序两部分,每次从未排序部分取一个元素并插入到已排序部分的适当位置。通过多次迭代和元素交换操作,最终实现整个数组的排序。在实现过程中,我们可以通过打印数组元素序列的方式来观察排序的过程和结果。

25.给定一个整数数组 nums 和一个整数目标值 target,请编写一个函数 twoSum,返回数组中两个元素的索引,使它们的和等于目标值。假设每个输入只有唯一解,并且同一个元素不能被重复使用。

#include <stdio.h>

#define MAX_SIZE 100

typedef struct {
    int val;
    int index;
} Element;

int compare(const void* a, const void* b) {
    return ((Element*)a)->val - ((Element*)b)->val;
}

int* twoSum(int* nums, int size, int target) {
    Element elements[MAX_SIZE];
    for (int i = 0; i < size; i++) {
        elements[i].val = nums[i];
        elements[i].index = i;
    }
    
    qsort(elements, size, sizeof(Element), compare);
    
    int left = 0;
    int right = size - 1;
    
    while (left < right) {
        int sum = elements[left].val + elements[right].val;
        
        if (sum == target) {
            int* result = (int*)malloc(2 * sizeof(int));
            result[0] = elements[left].index;
            result[1] = elements[right].index;
            return result;
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    
    return NULL;
}

int main() {
    int nums[] = {2, 7, 11, 15}; // 整数数组
    int size = sizeof(nums) / sizeof(nums[0]);
    int target = 9; // 目标值
    
    int* result = twoSum(nums, size, target);
    
    if (result != NULL) {
        printf("两个元素的索引为:%d, %d\n", result[0], result[1]);
        free(result);
    } else {
        printf("未找到满足条件的两个元素。\n");
    }
    
    return 0;
}

解析:
这道题目要求在给定的整数数组 nums 中找出两个元素的索引,使它们的和等于目标值 target。在函数 twoSum 中,我们首先创建一个辅助数组 elements,其中每个元素包含原始数组的值和索引。然后,对辅助数组进行排序,按照元素值的升序排列。接下来,我们使用双指针的方法来查找目标值。初始化左指针 left 指向数组的起始位置,右指针 right 指向数组的末尾位置。通过比较左右指针对应元素的和与目标值的大小,不断调整指针的位置,直到找到满足条件的两个元素或左右指针相遇。

在主函数中,我们给出了一个示例整数数组 nums 和目标值 target,调用 twoSum 函数找到满足条件的两个元素的索引,并打印结果。

这道题目考察了数组的遍历和双指针的应用。通过将原始数组的元素和索引存储在辅助数组中,并对辅助数组进行排序,我们可以通过双指针的方法在有序数组中查找满足条件的两个元素。该题目的解法时间复杂度为 O(nlogn),其中 n 为数组的大小。

26.给定一个整数数组 nums 和一个目标整数 target,请编写一个函数 threeSum,在数组中找到所有不重复的三元组,使得三元组的元素之和等于目标值 target。返回所有满足条件的三元组。

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int first;     // 第一个元素
    int second;    // 第二个元素
    int third;     // 第三个元素
} Triplet;

int compare(const void* a, const void* b) {
    return *(int*)a - *(int*)b;
}

Triplet* threeSum(int* nums, int size, int target, int* returnSize) {
    // 创建结果数组
    Triplet* result = (Triplet*)malloc(MAX_SIZE * sizeof(Triplet));
    *returnSize = 0;
    
    // 对数组进行排序
    qsort(nums, size, sizeof(int), compare);
    
    // 遍历数组
    for (int i = 0; i < size - 2; i++) {
        // 跳过重复的元素
        if (i > 0 && nums[i] == nums[i - 1]) {
            continue;
        }
        
        // 使用双指针查找满足条件的三元组
        int left = i + 1;
        int right = size - 1;
        
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            
            if (sum == target) {
                // 找到满足条件的三元组,将其添加到结果数组中
                result[*returnSize].first = nums[i];
                result[*returnSize].second = nums[left];
                result[*returnSize].third = nums[right];
                (*returnSize)++;
                
                // 跳过重复的元素
                while (left < right && nums[left] == nums[left + 1]) {
                    left++;
                }
                
                while (left < right && nums[right] == nums[right - 1]) {
                    right--;
                }
                
                // 移动指针
                left++;
                right--;
            } else if (sum < target) {
                // 如果和小于目标值,移动左指针向右
                left++;
            } else {
                // 如果和大于目标值,移动右指针向左
                right--;
            }
        }
    }
    
    return result;  // 返回结果数组
}

int main() {
    int nums[] = { -1, 0, 1, 2, -1, -4 };
    int size = sizeof(nums) / sizeof(nums[0]);
    int target = 0;
    int returnSize;
    
    Triplet* result = threeSum(nums, size, target, &returnSize);
    
    printf("满足条件的三元组:\n");
    for (int i = 0; i < returnSize; i++) {
        printf("%d %d %d\n", result[i].first, result[i].second, result[i].third);
    }
    
    return 0;
}


解析:
该问题可以通过双指针的方法来解决。首先,对数组进行排序,以便后续处理。然后,遍历数组,并使用双指针来找到满足条件的三元组。遍历过程中,固定一个元素 nums[i],并使用双指针 leftright 分别指向 nums[i] 后面的两端。根据当前三个元素的和与目标值进行比较,并根据比较结果移动指针。在找到满足条件的三元组时,将其添加到结果数组中,并跳过重复的元素。最后,返回结果数组。

27.给定一个整数数组 nums 和一个目标整数 target,请编写一个函数 binarySearch,实现在有序数组中查找目标元素,并返回其索引。如果目标元素不存在于数组中,返回 -1。

#include <stdio.h>

int binarySearch(int* nums, int size, int target) {
    int left = 0;             // 左边界索引
    int right = size - 1;     // 右边界索引

    while (left <= right) {
        int mid = left + (right - left) / 2;   // 中间元素索引

        if (nums[mid] == target) {
            return mid;                        // 找到目标元素,返回索引
        } else if (nums[mid] < target) {
            left = mid + 1;                     // 目标元素在右半部分,更新左边界索引
        } else {
            right = mid - 1;                    // 目标元素在左半部分,更新右边界索引
        }
    }

    return -1;    // 目标元素不存在于数组中
}

int main() {
    int nums[] = { 2, 4, 6, 8, 10, 12, 14 };
    int size = sizeof(nums) / sizeof(nums[0]);
    int target = 8;

    int result = binarySearch(nums, size, target);

    printf("目标元素在数组中的索引为: %d\n", result);

    return 0;
}

解析:
该问题使用二分查找算法来在有序数组中查找目标元素。首先,初始化左边界索引 left 为 0,右边界索引 right 为数组大小减 1。然后,在循环中计算中间元素索引 mid,并根据中间元素与目标元素的比较结果来更新边界索引。如果中间元素等于目标元素,则找到目标元素,返回其索引。如果中间元素小于目标元素,则目标元素在右半部分,更新左边界索引为 mid + 1。如果中间元素大于目标元素,则目标元素在左半部分,更新右边界索引为 mid - 1。在循环结束后,如果没有找到目标元素,则返回 -1。最后,输出目标元素在数组中的索引。

28.以下是按照您的要求生成的考研类型习题,包含问题、代码和解析,并在每句代码后添加了注释:

问题:
给定一个字符串 s,请编写一个函数 reverseString,将其反转并返回。

#include <stdio.h>
#include <string.h>

void reverseString(char* s) {
    int left = 0;                    // 左边界索引
    int right = strlen(s) - 1;       // 右边界索引

    while (left < right) {
        char temp = s[left];         // 临时变量用于交换字符
        s[left] = s[right];
        s[right] = temp;

        left++;                      // 更新左边界索引
        right--;                     // 更新右边界索引
    }
}

int main() {
    char str[] = "Hello, World!";
    printf("原始字符串:%s\n", str);

    reverseString(str);

    printf("反转后的字符串:%s\n", str);

    return 0;
}

解析:
该问题使用双指针的方法来实现字符串反转。首先,初始化左边界索引 left 为 0,右边界索引 right 为字符串长度减 1。然后,进入循环,交换左右边界对应的字符,并同时更新左边界索引和右边界索引。在循环结束后,字符串的字符顺序被反转。最后,输出反转后的字符串。

29.给定一个整数数组 nums 和一个目标整数 target,请编写一个函数 findTwoSum,找到数组中两个元素的和等于目标值 target 的索引,并返回这两个索引。

#include <stdio.h>

void findTwoSum(int* nums, int size, int target, int* index1, int* index2) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = i + 1; j < size; j++) {
            if (nums[i] + nums[j] == target) {
                *index1 = i;
                *index2 = j;
                return;
            }
        }
    }
}

int main() {
    int nums[] = { 2, 7, 11, 15 };
    int size = sizeof(nums) / sizeof(nums[0]);
    int target = 9;
    int index1, index2;

    findTwoSum(nums, size, target, &index1, &index2);

    printf("索引1:%d\n", index1);
    printf("索引2:%d\n", index2);

    return 0;
}

解析:
该问题使用暴力搜索的方法来寻找数组中两个元素的和等于目标值 target 的索引。使用两层循环遍历数组,对每一对不同的元素进行求和,并与目标值比较。如果找到了满足条件的两个元素,记录它们的索引并返回。在主函数中,声明两个变量 index1index2 用于存储找到的索引。最后,输出这两个索引。

30.给定一个字符串 s,请编写一个函数 countVowels,统计字符串中元音字母的个数,并返回结果。

#include <stdio.h>
#include <ctype.h>
#include <string.h>

int countVowels(char* s) {
    int count = 0;                  // 元音字母计数器

    for (int i = 0; i < strlen(s); i++) {
        char ch = tolower(s[i]);     // 将字符转换为小写形式

        if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') {
            count++;                 // 遇到元音字母,计数器加一
        }
    }

    return count;                   // 返回元音字母个数
}

int main() {
    char str[] = "Hello, World!";
    int vowelsCount = countVowels(str);

    printf("元音字母个数:%d\n", vowelsCount);

    return 0;
}

解析:
该问题使用循环遍历字符串的每个字符,并通过 tolower 函数将字符转换为小写形式,然后与元音字母进行比较。如果字符是元音字母之一(a、e、i、o、u),则计数器加一。最后,返回元音字母的个数。在主函数中,将字符串传递给 countVowels 函数,并输出计算得到的元音字母个数。

31.给定一个整数数组 nums,请编写一个函数 findMax,找到数组中的最大元素,并返回其值。

#include <stdio.h>

int findMax(int* nums, int size) {
    int max = nums[0];           // 假设数组的第一个元素为最大值

    for (int i = 1; i < size; i++) {
        if (nums[i] > max) {
            max = nums[i];       // 找到更大的元素,更新最大值
        }
    }

    return max;                  // 返回最大值
}

int main() {
    int nums[] = { 10, 5, 8, 3, 15, 12 };
    int size = sizeof(nums) / sizeof(nums[0]);

    int max = findMax(nums, size);

    printf("最大元素:%d\n", max);

    return 0;
}

解析:
该问题使用循环遍历数组中的每个元素,并通过比较找到数组中的最大元素。假设数组的第一个元素为最大值,然后依次与后面的元素进行比较,如果找到更大的元素,则更新最大值。最后,返回最大值。在主函数中,声明变量 max 来存储最大元素,并输出该值。

32.给定一个整数数组 nums 和一个目标整数 target,请编写一个函数 findTargetSum,判断数组中是否存在两个元素的和等于目标值 target

#include <stdio.h>

int findTargetSum(int* nums, int size, int target) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = i + 1; j < size; j++) {
            if (nums[i] + nums[j] == target) {
                return 1;            // 找到满足条件的两个元素,返回 1
            }
        }
    }

    return 0;                       // 未找到满足条件的两个元素,返回 0
}

int main() {
    int nums[] = { 2, 4, 6, 8, 10 };
    int size = sizeof(nums) / sizeof(nums[0]);
    int target = 14;

    int result = findTargetSum(nums, size, target);

    if (result) {
        printf("存在两个元素的和等于目标值。\n");
    } else {
        printf("不存在两个元素的和等于目标值。\n");
    }

    return 0;
}

解析:
该问题使用两层循环遍历数组中的所有元素对,并计算它们的和与目标值进行比较。如果找到满足条件的两个元素,即它们的和等于目标值,则返回 1。如果循环结束后仍未找到满足条件的两个元素,则返回 0。在主函数中,将数组、数组大小和目标值传递给 findTargetSum 函数,并根据返回值输出相应的结果。如果返回值为 1,则存在两个元素的和等于目标值;如果返回值为 0,则不存在。

33.给定一个整数数组 nums 和一个目标整数 target,请编写一个函数 findTwoSum,找到数组中两个元素的和等于目标值 target 的索引,并返回这两个索引。

#include <stdio.h>

void findTwoSum(int* nums, int size, int target, int* index1, int* index2) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = i + 1; j < size; j++) {
            if (nums[i] + nums[j] == target) {
                *index1 = i;             // 将找到的索引赋值给 index1
                *index2 = j;             // 将找到的索引赋值给 index2
                return;
            }
        }
    }
}

int main() {
    int nums[] = { 2, 7, 11, 15 };
    int size = sizeof(nums) / sizeof(nums[0]);
    int target = 9;
    int index1, index2;

    findTwoSum(nums, size, target, &index1, &index2);

    printf("索引1:%d\n", index1);
    printf("索引2:%d\n", index2);

    return 0;
}

解析:
该问题使用两层循环遍历数组中的每一对不同的元素,并计算它们的和与目标值进行比较。如果找到满足条件的两个元素,将它们的索引赋值给 index1index2,然后返回。在主函数中,声明两个变量 index1index2,用于存储找到的索引。将数组、数组大小和目标值传递给 findTwoSum 函数,并通过地址传递的方式获取计算结果。最后,输出这两个索引。

34.给定一个字符串 s,请编写一个函数 reverseString,将字符串中的字符顺序反转,并返回结果。

#include <stdio.h>
#include <string.h>

void reverseString(char* s) {
    int length = strlen(s);
    int left = 0;
    int right = length - 1;

    while (left < right) {
        // 交换左右两个字符
        char temp = s[left];
        s[left] = s[right];
        s[right] = temp;

        left++;
        right--;
    }
}

int main() {
    char str[] = "Hello, World!";
    printf("原始字符串:%s\n", str);

    reverseString(str);

    printf("反转字符串:%s\n", str);

    return 0;
}

解析:
该问题使用双指针方法,定义两个指针 leftright 分别指向字符串的首尾字符。通过交换左右指针所指向的字符,实现字符顺序的反转。循环进行交换操作,直到左指针超过右指针,即完成字符串反转。在主函数中,声明一个字符串 str,并输出原始字符串。然后调用 reverseString 函数对字符串进行反转,并输出反转后的结果。

35.给定一个整数数组 nums,请编写一个函数 removeDuplicates,将数组中重复的元素去除,并返回去重后的数组长度。

#include <stdio.h>

int removeDuplicates(int* nums, int size) {
    if (size == 0) {
        return 0;                        // 数组为空,直接返回长度 0
    }

    int index = 0;                       // 去重后的数组索引

    for (int i = 1; i < size; i++) {
        if (nums[i] != nums[index]) {
            index++;                     // 不重复的元素,更新索引
            nums[index] = nums[i];       // 将不重复的元素存入新的位置
        }
    }

    return index + 1;                    // 返回去重后的数组长度
}

int main() {
    int nums[] = { 1, 1, 2, 2, 3, 3, 3, 4, 5 };
    int size = sizeof(nums) / sizeof(nums[0]);

    int length = removeDuplicates(nums, size);

    printf("去重后的数组长度:%d\n", length);

    printf("去重后的数组:");
    for (int i = 0; i < length; i++) {
        printf("%d ", nums[i]);
    }
    printf("\n");

    return 0;
}

解析:
该问题使用双指针方法,定义一个索引变量 index,用于表示去重后的数组的索引位置。遍历数组,当当前元素与前一个元素不相等时,将当前元素存储在 index+1 的位置,并更新 index 的值。这样就能保证去重后的数组中只包含不重复的元素。最后,返回 index+1 作为去重后的数组长度。在主函数中,声明一个整数数组 nums,并调用 removeDuplicates 函数进行去重操作。输出去重后的数组长度和去重后的数组内容。

36.给定一个链表的头节点 head,请编写一个函数 reverseList,将链表反转,并返回反转后的链表的头节点。

#include <stdio.h>
#include <stdlib.h>

struct ListNode {
    int val;
    struct ListNode* next;
};

struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* prev = NULL;
    struct ListNode* curr = head;

    while (curr != NULL) {
        struct ListNode* nextTemp = curr->next;    // 保存当前节点的下一个节点
        curr->next = prev;                         // 当前节点指向前一个节点,完成反转
        prev = curr;                               // 更新前一个节点为当前节点
        curr = nextTemp;                           // 更新当前节点为下一个节点
    }

    return prev;                                  // 返回反转后的链表头节点
}

struct ListNode* createNode(int val) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    newNode->val = val;
    newNode->next = NULL;
    return newNode;
}

void printList(struct ListNode* head) {
    struct ListNode* curr = head;

    while (curr != NULL) {
        printf("%d ", curr->val);
        curr = curr->next;
    }
    printf("\n");
}

int main() {
    struct ListNode* head = createNode(1);
    struct ListNode* second = createNode(2);
    struct ListNode* third = createNode(3);
    struct ListNode* fourth = createNode(4);
    struct ListNode* fifth = createNode(5);

    head->next = second;
    second->next = third;
    third->next = fourth;
    fourth->next = fifth;

    printf("原始链表:");
    printList(head);

    struct ListNode* reversedHead = reverseList(head);

    printf("反转链表:");
    printList(reversedHead);

    return 0;
}

解析:
该问题使用迭代方法进行链表反转。定义两个指针 prevcurr,分别指向前一个节点和当前节点,初始时 prev 为 NULL,curr 指向链表的头节点 head。在循环中,通过临时变量保存当前节点的下一个节点,然后将当前节点指向前一个节点,完成反转操作。接着更新 prev 为当前节点,curr 为下一个节点,继续迭代。当 curr 为 NULL,即遍历到链表尾部时,循环结束。最后返回 prev,即为反转后的链表的头节点。在主函数中,创建了一个包含 5 个节点的链表,并调用 reverseList 函数进行链表反转。输出原始链表和反转后的链表的值。

37.给定一个二叉树的根节点 root,请编写一个函数 inorderTraversal,实现二叉树的中序遍历,并返回遍历结果。

#include <stdio.h>
#include <stdlib.h>

struct TreeNode {
    int val;
    struct TreeNode* left;
    struct TreeNode* right;
};

struct TreeNode* createNode(int val) {
    struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    newNode->val = val;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

void inorderTraversalHelper(struct TreeNode* root, int* result, int* index) {
    if (root == NULL) {
        return;
    }

    inorderTraversalHelper(root->left, result, index);     // 中序遍历左子树

    result[(*index)++] = root->val;                        // 访问当前节点

    inorderTraversalHelper(root->right, result, index);    // 中序遍历右子树
}

int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    int nodeCount = 0;                                     // 统计二叉树节点数
    int* result;

    // 计算二叉树节点数
    void countNodes(struct TreeNode* node) {
        if (node == NULL) {
            return;
        }
        countNodes(node->left);
        nodeCount++;
        countNodes(node->right);
    }
    countNodes(root);

    *returnSize = nodeCount;                               // 设置返回结果的大小
    result = (int*)malloc(nodeCount * sizeof(int));        // 分配内存空间

    int index = 0;                                         // 结果数组的索引
    inorderTraversalHelper(root, result, &index);           // 中序遍历二叉树

    return result;
}

int main() {
    struct TreeNode* root = createNode(1);
    struct TreeNode* node2 = createNode(2);
    struct TreeNode* node3 = createNode(3);
    struct TreeNode* node4 = createNode(4);
    struct TreeNode* node5 = createNode(5);

    root->left = node2;
    root->right = node3;
    node2->left = node4;
    node2->right = node5;

    int size;
    int* result = inorderTraversal(root, &size);

    printf("中序遍历结果:");
    for (int i = 0; i < size; i++) {
        printf("%d ", result[i]);
    }
    printf("\n");

    free(result);

    return 0;
}

解析:
该问题使用递归方法实现二叉树的中序遍历。定义一个辅助函数 inorderTraversalHelper,它接受当前节点 root、存储遍历结果的数组 result 和结果数组的索引 index。在辅助函数中,先递归遍历左子树,然后将当前节点的值存入结果数组,并递归遍历右子树。在主函数 inorderTraversal 中,首先统计二叉树的节点数,然后分配结果数组的内存空间。接着调用辅助函数 inorderTraversalHelper 进行中序遍历,将结果存入结果数组。最后返回结果数组和结果数组的大小。在主函数中,创建了一个二叉树,并调用 inorderTraversal 函数进行中序遍历。输出遍历结果。

38.给定一个字符串 s,请编写一个函数 isValid,判断该字符串中的括号是否匹配。

#include <stdio.h>
#include <stdbool.h>

#define MAX_STACK_SIZE 100

typedef struct {
    char data[MAX_STACK_SIZE];
    int top;
} Stack;

void initializeStack(Stack* stack) {
    stack->top = -1;
}

bool isStackEmpty(Stack* stack) {
    return (stack->top == -1);
}

bool isStackFull(Stack* stack) {
    return (stack->top == MAX_STACK_SIZE - 1);
}

void push(Stack* stack, char element) {
    if (isStackFull(stack)) {
        printf("Error: Stack is full.\n");
        return;
    }

    stack->data[++stack->top] = element;
}

char pop(Stack* stack) {
    if (isStackEmpty(stack)) {
        printf("Error: Stack is empty.\n");
        return '\0';
    }

    return stack->data[stack->top--];
}

bool isValid(char* s) {
    Stack stack;
    initializeStack(&stack);

    for (int i = 0; s[i] != '\0'; i++) {
        if (s[i] == '(' || s[i] == '[' || s[i] == '{') {
            push(&stack, s[i]);                     // 遇到左括号入栈
        } else if (s[i] == ')' || s[i] == ']' || s[i] == '}') {
            if (isStackEmpty(&stack)) {
                return false;                        // 栈为空,无法匹配右括号
            }
            
            char top = pop(&stack);                  // 弹出栈顶元素

            if ((s[i] == ')' && top != '(') ||
                (s[i] == ']' && top != '[') ||
                (s[i] == '}' && top != '{')) {
                return false;                        // 右括号与栈顶元素不匹配
            }
        }
    }

    return isStackEmpty(&stack);                      // 字符串遍历完毕后,栈为空则匹配成功
}

int main() {
    char* s1 = "(([]))";
    char* s2 = "{[()]}";
    char* s3 = "({[)}]";
    char* s4 = "([]}";

    printf("字符串 %s 括号是否匹配:%s\n", s1, isValid(s1) ? "Yes" : "No");
    printf("字符串 %s 括号是否匹配:%s\n", s2, isValid(s2) ? "Yes" : "No");
    printf("字符串 %s 括号是否匹配:%s\n", s3, isValid(s3) ? "Yes" : "No");
    printf("字符串 %s 括号是否匹配:%s\n", s4, isValid(s4) ? "Yes" : "No");

    return 0;
}

解析:
该问题使用栈来判断括号的匹配情况。定义了一个栈的结构体,并实现了栈的基本操作函数。在函数 isValid 中,首先初始化一个栈,然后遍历字符串中的每个字符。如果遇到左括号,则将其入栈;如果遇到右括号,则与栈顶元素进行匹配。若栈为空,无法匹配右括号,返回 false;若右括号与栈顶元素不匹配,返回 false。遍历完成后,检查栈是否为空,若为空则说明所有括号都匹配成功,返回 true,否则返回 false。在主函数中,测试了不同的字符串,并输出括号的匹配结果。

39.给定一个有序整数数组 nums 和一个目标值 target,请编写一个函数 search,在数组中查找目标值,并返回其索引。如果目标值不存在于数组中,返回 -1。

#include <stdio.h>

int search(int* nums, int numsSize, int target) {
    int left = 0;                                   // 左边界
    int right = numsSize - 1;                       // 右边界

    while (left <= right) {
        int mid = left + (right - left) / 2;         // 中间元素的索引

        if (nums[mid] == target) {
            return mid;                             // 找到目标值,返回索引
        } else if (nums[mid] < target) {
            left = mid + 1;                         // 目标值在右半部分,更新左边界
        } else {
            right = mid - 1;                        // 目标值在左半部分,更新右边界
        }
    }

    return -1;                                      // 目标值不存在于数组中,返回 -1
}

int main() {
    int nums[] = {2, 5, 8, 12, 16, 23, 38, 42, 55, 67};
    int numsSize = sizeof(nums) / sizeof(nums[0]);
    int target1 = 16;
    int target2 = 25;

    int result1 = search(nums, numsSize, target1);
    int result2 = search(nums, numsSize, target2);

    printf("目标值 %d 的索引: %d\n", target1, result1);
    printf("目标值 %d 的索引: %d\n", target2, result2);

    return 0;
}

解析:
该问题使用二分查找算法在有序数组中查找目标值。定义了左边界 left 和右边界 right 分别初始化为数组的起始索引和结束索引。在每一轮循环中,计算中间元素的索引 mid,并与目标值进行比较。如果中间元素等于目标值,则找到了目标值,返回其索引。如果中间元素小于目标值,则目标值在右半部分,更新左边界为 mid + 1。如果中间元素大于目标值,则目标值在左半部分,更新右边界为 mid - 1。通过不断缩小搜索范围,最终可以找到目标值或确定其不存在于数组中。在主函数中,测试了不同的目标值,并输出其在数组中的索引。

40.以下是按照您的要求生成的考研类型习题,包含问题、代码和解析,并在每句代码后添加了注释:

问题:
给定一个整数数组 nums,请编写一个函数 maxSubArray,计算数组中连续子数组的最大和,并返回该最大和。

#include <stdio.h>

int maxSubArray(int* nums, int numsSize) {
    int maxSum = nums[0];                           // 最大和的初始值为第一个元素
    int currentSum = nums[0];                       // 当前和的初始值为第一个元素

    for (int i = 1; i < numsSize; i++) {
        if (currentSum < 0) {
            currentSum = nums[i];                    // 如果当前和小于零,舍弃前面的子数组
        } else {
            currentSum += nums[i];                   // 如果当前和大于等于零,累加当前元素
        }

        if (currentSum > maxSum) {
            maxSum = currentSum;                     // 更新最大和
        }
    }

    return maxSum;
}

int main() {
    int nums[] = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
    int numsSize = sizeof(nums) / sizeof(nums[0]);

    int maxSum = maxSubArray(nums, numsSize);

    printf("连续子数组的最大和为:%d\n", maxSum);

    return 0;
}

解析:
该问题使用动态规划的思想来计算数组中连续子数组的最大和。定义了两个变量 maxSumcurrentSum,分别表示最大和和当前和。首先将 maxSumcurrentSum 初始化为数组的第一个元素。然后从数组的第二个元素开始遍历,判断当前和 currentSum 是否小于零,如果小于零,则舍弃前面的子数组,将当前元素作为新的起点;如果大于等于零,则将当前元素累加到当前和 currentSum 中。在每次累加后,判断当前和 currentSum 是否大于最大和 maxSum,如果是,则更新最大和。通过遍历整个数组,最终可以得到连续子数组的最大和。在主函数中,测试了给定数组,并输出连续子数组的最大和。

41.给定一个整数数组 nums,请编写一个函数 removeDuplicates,将数组中重复的元素去除,并返回去重后的数组的长度。假设原数组已经按照非递减顺序排序。

#include <stdio.h>

int removeDuplicates(int* nums, int numsSize) {
    if (numsSize == 0) {
        return 0;                                    // 空数组,返回长度为 0
    }

    int uniqueIndex = 0;                             // 唯一元素的索引

    for (int i = 1; i < numsSize; i++) {
        if (nums[i] != nums[uniqueIndex]) {
            uniqueIndex++;                            // 发现新的唯一元素,更新唯一元素的索引
            nums[uniqueIndex] = nums[i];               // 将唯一元素放置在正确的位置上
        }
    }

    return uniqueIndex + 1;                           // 唯一元素的个数即为去重后的数组的长度
}

int main() {
    int nums[] = {1, 1, 2, 2, 2, 3, 4, 4, 5};
    int numsSize = sizeof(nums) / sizeof(nums[0]);

    int length = removeDuplicates(nums, numsSize);

    printf("去重后的数组长度为:%d\n", length);
    printf("去重后的数组为:");

    for (int i = 0; i < length; i++) {
        printf("%d ", nums[i]);
    }

    printf("\n");

    return 0;
}

解析:
该问题要求对已排序的整数数组进行去重操作,并返回去重后的数组的长度。使用双指针的方法来解决该问题。首先判断数组是否为空,若为空,则直接返回长度为 0。定义一个变量 uniqueIndex 来记录当前唯一元素的索引,初始值为 0。从数组的第二个元素开始遍历,如果当前元素与唯一元素不相等,则说明发现了一个新的唯一元素,将其放置在唯一元素的下一个位置,并更新 uniqueIndex 的值。通过遍历整个数组,即可完成去重操作。最后返回 uniqueIndex + 1,即去重后的数组的长度。在主函数中,测试了给定数组,并输出去重后的数组的长度和去重后的数组。

42.给定一个字符串 str,请编写一个函数 reverseString,将字符串中的字符逆序排列,并返回逆序后的字符串。

#include <stdio.h>
#include <string.h>

void reverseString(char* str) {
    int left = 0;                                    // 左指针,指向字符串起始位置
    int right = strlen(str) - 1;                     // 右指针,指向字符串结束位置

    while (left < right) {
        char temp = str[left];                       // 交换左右指针所指向的字符
        str[left] = str[right];
        str[right] = temp;

        left++;                                     // 左指针右移
        right--;                                    // 右指针左移
    }
}

int main() {
    char str[] = "Hello, World!";

    printf("原始字符串:%s\n", str);

    reverseString(str);

    printf("逆序后的字符串:%s\n", str);

    return 0;
}

解析:
该问题要求对给定的字符串进行逆序排列。使用双指针的方法来解决该问题。首先定义一个左指针 left,指向字符串的起始位置,以及一个右指针 right,指向字符串的结束位置。通过循环遍历字符串,将左指针所指向的字符与右指针所指向的字符进行交换,然后将左指针右移,右指针左移,继续进行下一轮的交换操作,直到左指针大于等于右指针为止。最终可以将字符串中的字符逆序排列。在主函数中,测试了给定字符串,并输出逆序后的字符串。

43.给定一个正整数 num,请编写一个函数 isPerfectSquare,判断该数是否为完全平方数。

#include <stdio.h>

int isPerfectSquare(int num) {
    if (num < 0) {
        return 0;                                    // 负数不是完全平方数
    }

    if (num <= 1) {
        return 1;                                    // 0和1是完全平方数
    }

    int left = 1;                                    // 左边界
    int right = num;                                 // 右边界

    while (left <= right) {
        long long mid = left + (right - left) / 2;    // 使用二分查找,将中间值设置为 long long 类型以防止溢出

        if (mid * mid == num) {
            return 1;                                // 找到完全平方数
        } else if (mid * mid < num) {
            left = mid + 1;                          // 在右半部分继续查找
        } else {
            right = mid - 1;                         // 在左半部分继续查找
        }
    }

    return 0;                                        // 没有找到完全平方数
}

int main() {
    int num = 16;

    if (isPerfectSquare(num)) {
        printf("%d 是完全平方数\n", num);
    } else {
        printf("%d 不是完全平方数\n", num);
    }

    return 0;
}

解析:
该问题要求判断给定的正整数是否为完全平方数。使用二分查找的方法来解决该问题。首先判断特殊情况,若给定的数小于0,则直接返回 0,因为负数不是完全平方数;若给定的数为0或1,则直接返回 1,因为0和1是完全平方数。对于其他正整数,使用二分查找来找到其平方根。定义左边界 left 为1,右边界 right 为给定数本身。通过循环进行二分查找,每次取中间值 mid,判断 mid * mid 是否等于给定数,如果是,则说明找到了完全平方数,返回 1;如果 mid * mid 小于给定数,说明完全平方数在右半部分,更新左边界为 mid + 1;如果 mid * mid 大于给定数,说明完全平方数在左半部分,更新右边界为 mid - 1。最终,如果循环结束仍未找到完全平方数,则返回 0。在主函数中,测试了给定数,并输出判断结果。

44.给定一个整数数组 nums 和一个目标值 target,请编写一个函数 twoSum,找出数组中和为目标值的两个元素的索引,并返回这两个索引值。

#include <stdio.h>

int* twoSum(int* nums, int numsSize, int target) {
    static int result[2];                            // 存储结果的数组

    for (int i = 0; i < numsSize - 1; i++) {
        for (int j = i + 1; j < numsSize; j++) {
            if (nums[i] + nums[j] == target) {
                result[0] = i;
                result[1] = j;
                return result;                        // 找到目标值,返回结果
            }
        }
    }

    return NULL;                                     // 没有找到目标值,返回空指针
}

int main() {
    int nums[] = {2, 7, 11, 15};
    int numsSize = sizeof(nums) / sizeof(nums[0]);
    int target = 9;

    int* indices = twoSum(nums, numsSize, target);

    if (indices != NULL) {
        printf("和为目标值的两个元素的索引为:%d, %d\n", indices[0], indices[1]);
    } else {
        printf("没有找到和为目标值的两个元素\n");
    }

    return 0;
}

解析:
该问题要求在给定的整数数组中找到和为目标值的两个元素,并返回这两个元素的索引。使用两层循环的暴力解法来解决该问题。外层循环遍历数组中的每个元素 nums[i],内层循环从外层循环的下一个元素开始遍历,找到与 nums[i] 相加等于目标值 target 的元素 nums[j],将其索引存储在 result 数组中,并返回结果。如果在两层循环结束后仍未找到满足条件的两个元素,则返回空指针。在主函数中,测试了给定数组和目标值,并输出满足条件的两个元素的索引。

45.给定一个正整数n,编写一个递归函数printNumbers,按顺序打印从1到n的所有数字。

要求:

  • 不能使用循环或迭代器,只能使用递归实现。
  • 考虑输入的边界情况,当n为负数时,不输出任何数字。
#include <stdio.h>

void printNumbers(int n) {
    if (n <= 0) {
        return;                                 // 当n为负数或0时,直接返回
    }

    printNumbers(n - 1);                        // 递归调用,打印从1到n-1的数字
    printf("%d ", n);                           // 打印当前数字n
}

int main() {
    int n = 5;

    printf("从1到%d的数字为:", n);
    printNumbers(n);
    printf("\n");

    return 0;
}

解析:
该问题要求按顺序打印从1到给定正整数n的所有数字,使用递归方式实现。在递归函数printNumbers中,首先判断n的值是否小于等于0,若是则直接返回,表示递归终止条件。否则,递归调用printNumbers(n - 1),打印从1到n-1的所有数字。然后在递归调用之后,打印当前数字n。这样就能够按照从1到n的顺序递归地打印所有数字。在主函数中,测试了给定的正整数n,并输出从1到n的所有数字。

通过递归的方式,每次递归都将问题的规模减小,直到达到递归终止条件。递归函数中的代码在每次递归调用前后执行,实现了按顺序打印数字的效果。

46.给定一个字符串s和一个字符c,编写一个函数countOccurrences,统计字符串s中字符c的出现次数,并返回结果。

要求:

  • 字符串s不为空,且长度不超过1000。
  • 字符c可以是任意ASCII字符。
#include <stdio.h>

int countOccurrences(const char* s, char c) {
    if (s == NULL || *s == '\0') {
        return 0;                             // 若字符串为空或已到达字符串结尾,则返回0
    }

    if (*s == c) {
        return 1 + countOccurrences(s + 1, c); // 若当前字符与目标字符相同,则返回1,并递归统计后续子串中的出现次数
    } else {
        return countOccurrences(s + 1, c);     // 若当前字符与目标字符不同,则递归统计后续子串中的出现次数
    }
}

int main() {
    char s[] = "Hello, World!";
    char c = 'o';

    int occurrences = countOccurrences(s, c);

    printf("字符 '%c' 在字符串 '%s' 中出现的次数为:%d\n", c, s, occurrences);

    return 0;
}

解析:
该问题要求统计字符串s中字符c的出现次数,使用递归方式实现。在递归函数countOccurrences中,首先判断字符串s是否为空或已到达字符串结尾,若是,则返回0,表示递归终止条件。然后判断当前字符是否与目标字符c相同,若是,则返回1加上递归调用countOccurrences(s + 1, c),表示当前字符的出现次数加上后续子串中目标字符的出现次数。若当前字符与目标字符不同,则直接递归调用countOccurrences(s + 1, c),统计后续子串中目标字符的出现次数。通过递归的方式,逐个字符地判断并统计出现次数,最终返回结果。在主函数中,测试了给定的字符串s和字符c,并输出字符c在字符串s中的出现次数。

递归函数在每次递归调用时,通过移动字符串指针s的位置,来实现逐个字符的判断和统计。通过不断缩小问题的规模,直到达到递归终止条件,最终得到结果。

47.给定一个整数数组 nums,编写一个函数 findMaxConsecutiveOnes,统计数组中最长连续的1的个数,并返回结果。

要求:

  • 数组 nums 不为空,且长度不超过 10^4。
  • 数组 nums 中只包含 0 和 1 两种元素。
#include <stdio.h>

int findMaxConsecutiveOnes(int* nums, int numsSize) {
    int maxCount = 0;                          // 最长连续1的个数
    int currentCount = 0;                      // 当前连续1的个数

    for (int i = 0; i < numsSize; i++) {
        if (nums[i] == 1) {
            currentCount++;                     // 遇到1,当前连续1的个数加1
        } else {
            if (currentCount > maxCount) {
                maxCount = currentCount;        // 遇到0,更新最长连续1的个数
            }
            currentCount = 0;                   // 当前连续1的个数清零
        }
    }

    if (currentCount > maxCount) {
        maxCount = currentCount;                // 处理数组末尾的情况
    }

    return maxCount;
}

int main() {
    int nums[] = {1, 1, 0, 1, 1, 1, 0, 1, 1};
    int numsSize = sizeof(nums) / sizeof(nums[0]);

    int maxConsecutiveOnes = findMaxConsecutiveOnes(nums, numsSize);

    printf("最长连续的1的个数为:%d\n", maxConsecutiveOnes);

    return 0;
}

解析:
该问题要求统计整数数组 nums 中最长连续的1的个数,使用迭代方式实现。在迭代过程中,定义两个变量 maxCountcurrentCount,分别表示最长连续1的个数和当前连续1的个数。遍历数组 nums,当遇到元素为1时,当前连续1的个数加1;当遇到元素为0时,判断当前连续1的个数是否大于最长连续1的个数,若是,则更新最长连续1的个数为当前连续1的个数,并将当前连续1的个数清零。遍历结束后,若当前连续1的个数仍大于最长连续1的个数,则更新最长连续1的个数为当前连续1的个数。最后返回最长连续1的个数。在主函数中,测试了给定的整数数组 nums,并输出最长连续的1的个数。

通过迭代的方式遍历数组,根据元素的值来更新计数器,从而统计最长连续1的个数。迭代过程中只需要常数级的额外空间,具有较高的效率。

48.给定一个正整数数组 nums 和一个目标值 target,编写一个函数 twoSum,找到数组中两个数的下标,使得它们的和等于目标值,并返回这两个下标。

要求:

  • 假设每个输入只对应唯一的答案。
  • 数组 nums 中至少存在两个数。
  • 返回的下标要求按照从小到大的顺序排列。
#include <stdio.h>
#include <stdlib.h>

int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
    int* result = (int*)malloc(2 * sizeof(int));   // 分配存放结果的内存空间
    *returnSize = 2;                              // 返回结果的大小

    for (int i = 0; i < numsSize - 1; i++) {
        for (int j = i + 1; j < numsSize; j++) {
            if (nums[i] + nums[j] == target) {
                result[0] = i;                     // 找到两个数的下标
                result[1] = j;
                return result;
            }
        }
    }

    return result;                                // 若没有找到满足条件的下标,则返回默认结果
}

int main() {
    int nums[] = {2, 7, 11, 15};
    int numsSize = sizeof(nums) / sizeof(nums[0]);
    int target = 9;
    int returnSize;

    int* indices = twoSum(nums, numsSize, target, &returnSize);

    printf("数组中和为目标值 %d 的两个数的下标为:%d, %d\n", target, indices[0], indices[1]);

    free(indices);                                // 释放动态分配的内存空间

    return 0;
}

解析:
该问题要求在给定的正整数数组 nums 中找到两个数的下标,使得它们的和等于目标值 target,并返回这两个下标。使用双重循环遍历数组,通过判断两个数的和是否等于目标值,找到满足条件的下标。在主函数中,定义了一个整型指针 result,用于存放结果的下标,并动态分配了2个整型空间给指针 result,用于存放两个下标。在双重循环中,若找到满足条件的两个数,则将下标保存到 result 中并返回。若遍历完整个数组仍未找到满足条件的下标,则返回默认结果。最后,在主函数中输出满足条件的两个数的下标。注意,动态分配的内存空间需要在使用完毕后进行释放。

该问题的解法是暴力搜索法,时间复杂度较高,为 O(n^2),其中 n 为数组的长度。在面对较大规模的输入时,效率较低,可以考虑其他更优化的算法。

49.给定一个字符串 s,编写一个函数 reverseString,将字符串中的字符顺序反转,并返回结果。

要求:

  • 字符串 s 不为空,且长度不超过 10^5。
#include <stdio.h>
#include <string.h>

void reverseString(char* s) {
    int left = 0;                              // 左指针指向字符串开头
    int right = strlen(s) - 1;                  // 右指针指向字符串末尾

    while (left < right) {
        char temp = s[left];                    // 使用临时变量进行字符交换
        s[left] = s[right];
        s[right] = temp;

        left++;                                // 移动指针
        right--;
    }
}

int main() {
    char s[] = "Hello, World!";

    printf("反转前的字符串:%s\n", s);

    reverseString(s);

    printf("反转后的字符串:%s\n", s);

    return 0;
}

解析:
该问题要求将给定字符串 s 中的字符顺序进行反转。解决这个问题可以使用双指针法。首先,定义两个指针 leftright,分别指向字符串的开头和末尾。通过交换 leftright 指针指向的字符,实现字符顺序的反转。在每次交换后,将 left 指针向右移动一位,将 right 指针向左移动一位,直到两个指针相遇或交叉。最后输出反转后的字符串。在主函数中,测试了给定的字符串 s,并输出反转前和反转后的结果。

通过使用双指针法,可以在一次遍历内完成字符串的反转,时间复杂度为 O(n),其中 n 为字符串的长度。该方法不需要使用额外的空间,具有较高的效率。

50.给定一个非负整数 num,编写一个函数 isPerfectSquare,判断该整数是否是完全平方数。

要求:

  • 非负整数 num 的范围为 [0, 2^31 - 1]。
#include <stdio.h>
#include <stdbool.h>

bool isPerfectSquare(int num) {
    long left = 0;                               // 左边界为0
    long right = num;                            // 右边界为num

    while (left <= right) {
        long mid = left + (right - left) / 2;     // 二分查找的中间值

        long square = mid * mid;                  // 计算中间值的平方

        if (square == num) {
            return true;                         // 找到完全平方数,返回true
        } else if (square < num) {
            left = mid + 1;                       // 中间值的平方小于num,调整左边界
        } else {
            right = mid - 1;                      // 中间值的平方大于num,调整右边界
        }
    }

    return false;                                // 没有找到完全平方数,返回false
}

int main() {
    int num = 16;

    if (isPerfectSquare(num)) {
        printf("%d 是完全平方数\n", num);
    } else {
        printf("%d 不是完全平方数\n", num);
    }

    return 0;
}

解析:
该问题要求判断给定的非负整数 num 是否是完全平方数。解决这个问题可以使用二分查找法。定义两个边界 leftright,初始时,left 为0,rightnum。通过二分查找的方式,不断缩小边界范围。在每次查找中,计算中间值 mid 的平方,与 num 进行比较。若平方等于 num,则说明 num 是完全平方数,返回 true。若平方小于 num,则将左边界 left 调整为 mid + 1。若平方大于 num,则将右边界 right 调整为 mid - 1。重复上述步骤,直到找到完全平方数或边界相交。最后返回 false,表示没有找到完全平方数。在主函数中,测试了给定的非负整数 num,并输出结果。

使用二分查找法可以有效地缩小搜索范围,时间复杂度为 O(logn),其中 n 为给定的非负整数 num。通过不断调整边界,快速确定是否存在完全平方数。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值