剑指offer(题记二)

目录

JZ12 矩阵中的路径 ​编辑

JZ13 机器人的运动范围 

JZ20 表示数值的字符串 

  JZ49 丑数

JZ48 最长不含重复字符的子字符串

礼物的最大值(动态规划)

 JZ46 把数字翻译成字符串(动态规划)

JZ14 剪绳子 

 JZ83 剪绳子(进阶版) 动态规划

 JZ45 把数组排成最小的数

JZ43 整数中1出现的次数(从1到n整数中1出现的次数)

JZ38 字符串的排列

JZ34 二叉树中和为某一值的路径(二)

JZ33 二叉搜索树的后序遍历序列

JZ86 在二叉树中找到两个节点的最近公共祖先

JZ54 二叉搜索树的第k个节点

JZ56 数组中只出现一次的两个数字

JZ57 和为S的两个数字

JZ75 字符流中第一个不重复的字符 

JZ74 和为S的连续正数序列 

JZ67 把字符串转换成整数(atoi)  

JZ76 删除链表中重复的结点 

JZ85 连续子数组的最大和(二)

JZ84 二叉树中和为某一值的路径(三) 

JZ77 按之字形顺序打印二叉树

 JZ59 滑动窗口的最大值

从前序与中序遍历序列构造二叉树 (分治)

排序链表(分治)

 1015. 可被 K 整除的最小整数

 深度优先搜索算法

 剑指 Offer 29. 顺时针打印矩阵

剑指 Offer 58 - I. 翻转单词顺序

剑指 Offer 35. 复杂链表的复制 

BM83 字符串变形

BM84 最长公共前缀

BM86 大数加法

 BM19 寻找峰值

 BM93 盛水最多的容器

BM89 合并区间

BM50 两数之和

BM54 三数之和 

JZ12 矩阵中的路径 

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param matrix char字符型二维数组 
 * @param matrixRowLen int matrix数组行数
 * @param matrixColLen int* matrix数组列数
 * @param word string字符串 
 * @return bool布尔型
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
#include <stdbool.h>
bool dfs(char** matrix,int matrixRowLen,int* matrixColLen,char* word,int i,int j,char visited[][* matrixColLen]){
    static int I=0,a;
    if(word[I]!=matrix[i][j]){// 若当前字符不匹配,直接返回false
        return false;
    }
    if(word[I+1]=='\0'){ 已经匹配所有字符,返回true
        return true;
    }
    if(i<matrixRowLen-1 && visited[i+1][j]!='1'){//向下走
        i++;I++;visited[i][j]='1';//标记已访问
        a=dfs(matrix,matrixRowLen,matrixColLen,word,i,j,visited);//继续寻找下一步
        if(a) return true;//满足出口条件就退出整个函数体
        visited[i][j]='0';i--;I--;//不匹配就回溯到这次递归的上一步:该次if()外
    }
    if(i>0 && visited[i-1][j]!='1'){//向上走
        i--;I++;visited[i][j]='1';
        a=dfs(matrix,matrixRowLen,matrixColLen,word,i,j,visited);
        if(a) return true;
        visited[i][j]='0';i++;I--;
    }
    if(j<*matrixColLen-1 && visited[i][j+1]!='1'){//向右走
        j++;I++;visited[i][j]='1';
        a=dfs(matrix,matrixRowLen,matrixColLen,word,i,j,visited);
        if(a) return true;
        visited[i][j]='0';j--;I--;
    }
    if(j>0 && visited[i][j-1]!='1'){//向左走
        j--;I++;visited[i][j]='1';
        a=dfs(matrix,matrixRowLen,matrixColLen,word,i,j,visited);
        if(a) return true;
        visited[i][j]='0';j++;I--;
    }
    return false;//出口:没有路可以走了
}
bool hasPath(char** matrix, int matrixRowLen, int* matrixColLen, char* word ) {
    // write code here
    char visited[matrixRowLen][*matrixColLen];
    for(int i=0;i<matrixRowLen;i++){//以每个结点为起点寻找路径
        for(int j=0;j<*matrixColLen;j++){
            memset(visited,'0',sizeof(visited));
            visited[i][j]='1';
            if(dfs(matrix,matrixRowLen,matrixColLen,word,i,j,visited)){
                return true;
            }
        }
    }
    return false;
}

JZ13 机器人的运动范围 

#include <stdio.h>

// 计算数位之和
int getDigitSum(int num) {
    int sum = 0;
    while (num > 0) {
        sum += num % 10;
        num /= 10;
    }
    return sum;
}

// 判断机器人是否能进入指定格子
int canEnter(int threshold, int row, int col) {
    int sum = getDigitSum(row) + getDigitSum(col);
    return sum <= threshold;
}
// 计算机器人能够达到的格子数量
int movingCount(int threshold, int rows, int cols) {
    // 创建一个二维数组表示方格
    int grid[rows][cols];
    // 初始化所有格子为0,表示未访问过
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            grid[i][j] = 0;
        }
    }
    
    // 递归遍历方格,计算可达格子数量
    int count = dfs(threshold, rows, cols, 0, 0, grid);
    return count;
}

// 深度优先搜索递归函数
int dfs(int threshold, int rows, int cols, int row, int col, int grid[][cols]) {
    // 判断当前格子是否可达或已访问过
    if (row < 0 || row >= rows || col < 0 || col >= cols || !canEnter(threshold, row, col) || grid[row][col] == 1) {
        return 0;
    }
    
    // 标记当前格子已访问
    grid[row][col] = 1;
    
    // 递归遍历四个方向的相邻格子
    int count = 1; // 当前格子可达,数量加1
    count += dfs(threshold, rows, cols, row-1, col, grid); // 上
    count += dfs(threshold, rows, cols, row+1, col, grid); // 下
    count += dfs(threshold, rows, cols, row, col-1, grid); // 左
    count += dfs(threshold, rows, cols, row, col+1, grid); // 右
    
    return count;
}

JZ20 表示数值的字符串 

#include <stdbool.h>
#include <ctype.h>

bool isNumeric(char* str ) {
    if (!str) { // 如果字符串为空,返回 false
        return false;
    }
    // 跳过前导空格
    while (isspace(*str)) { // 如果当前字符是空格,指针向后移动
        str++;
    }
    // 判断符号位
    if (*str == '+' || *str == '-') { // 如果当前字符是加号或减号,指针向后移动
        str++;
    }
    bool is_num = false; // 是否有数字
    bool is_dot = false; // 是否有小数点
    bool is_e = false; // 是否有指数符号
    while (*str != '\0') { // 当前字符不是字符串结尾
        if (isdigit(*str)) { // 如果当前字符是数字
            is_num = true; // 标记有数字
        } else if (*str == '.') { // 如果当前字符是小数点
            if (is_dot || is_e) { // 如果已经有小数点或指数符号,返回 false
                return false;
            }
            is_dot = true; // 标记有小数点
        } else if (*str == 'e' || *str == 'E') { // 如果当前字符是指数符号
            if (!is_num || is_e) { // 如果没有数字或已经有指数符号,返回 false
                return false;
            }
            is_e = true; // 标记有指数符号
            is_num = false; // 重置数字标记
        } else if (*str == '+' || *str == '-') { // 如果当前字符是加号或减号
            if (*(str - 1) != 'e' && *(str - 1) != 'E') { // 如果前一个字符不是指数符号,返回 false
                return false;
            }
        } else if (isspace(*str)) { // 如果当前字符是空格,跳出循环
            break;
        } else { // 如果当前字符不是数字、小数点、指数符号、加号或减号,返回 false
            return false;
        }
        str++; // 指针向后移动
    }
    // 跳过后续空格
    while (isspace(*str)) { // 如果当前字符是空格,指针向后移动
        str++;
    }
    return is_num && (*str == '\0'); // 如果有数字且当前字符是字符串结尾,返回 true,否则返回 false
}

  JZ49 丑数

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
    if(index<=0)
    {
        return 0;
    }
    int a=0,b=0,c=0;
    int dp[index];
    dp[0]=1;
    for(int i=1;i<index;i++)
    {
        dp[i]=min(min(dp[a]*2,dp[b]*3),dp[c]*5);
        if(dp[i]==dp[a]*2) a++;
        if(dp[i]==dp[b]*3) b++;
        if(dp[i]==dp[c]*5) c++;
    }
    return dp[index-1];
    }    
};

  https://uploadfiles.nowcoder.com/files/20210620/908787715_1624191012473/33.gif思路演示链接:  https://uploadfiles.nowcoder.com/files/20210620/908787715_1624191012473/33.gif

JZ48 最长不含重复字符的子字符串

int lengthOfLongestSubstring(char* s ) {
    int max=0;  // 定义最大子串长度变量max并初始化为0
    for(int i=0;s[i]!='\0';i++){  // 外层循环,枚举所有可能的子串起始点
        int sum=1;  // 定义当前子串长度sum并初始化为1
        for(int j=i+1;s[j]!='\0';j++){  // 内层循环,从起始点i开始枚举所有可能的子串
            int si=0;  // 定义是否重复标志si并初始化为0
            for(int k=j-1;k>=i;k--){  // 在s[i]-s[j]内部循环,检查子串中是否有重复的字符
                if(s[k]==s[j]) si=1;  // 如果发现重复的字符就把si设为1
            }
            if(si==1) break;  // 如果发现重复的字符就跳出内层循环
            else sum++;  // 如果没有发现重复的字符就把子串长度加1
        }
        if(sum>max) max=sum;  // 判断当前子串长度是否是最长的,并更新max
    }
    return max;  // 返回最长子串长度
}

礼物的最大值(动态规划)

int maxValue(int** grid, int gridRowLen, int* gridColLen ) {
    int i = 0, j = 0;
// 计算第一行的累加和
    for (i = 1; i < gridRowLen; i++) {
        grid[i][0] += grid[i - 1][0];
    }
// 计算第一列的累加和
    for (j = 1; j < *gridColLen; j++) {
        grid[0][j] += grid[0][j - 1];
    }
  // 计算到达当前格子时最大的数字之和
    for (i = 1; i < gridRowLen; i++) {
        for (j = 1; j < * gridColLen; j++) {
            if (grid[i - 1][j] > grid[i][j - 1]) {
                grid[i][j] += grid[i - 1][j];
            } else {
                grid[i][j] += grid[i][j - 1];
            }
        }
    }
    return grid[gridRowLen - 1][* gridColLen- 1];// 返回右下角格子的数字之和
}

                              

 JZ46 把数字翻译成字符串(动态规划)

 ①先定义dp数组的含义,dp[i]当数组长度为i时,一共有dp[i]个方案;②求关系式:10≤nums≤25时,dp[i]=dp[i-1]+dp[i-2];dp[i]=dp[i-1];③初始值:dp[0]=0,dp[1]=1。

int numDecodings(char * s){
    int n = strlen(s);
    // 创建数组 dp,长度为 n+1
    int dp[n+1];
    memset(dp, 0, sizeof(dp));  // 初始化为0

    dp[0] = 1;  // 将 dp[0] 设为 1,表示空字符串有一种译法
    for (int i = 1; i <= n; i++) {
        // 如果数字s[i-1]对应的字母可以被单独编码,dp[i] += dp[i-1]
        if (s[i - 1] != '0') {
            dp[i] += dp[i - 1];
        }
        // 如果数字s[i-2]和s[i-1]对应的字母可以被组合编码,dp[i] += dp[i-2]
        if (i > 1 && s[i - 2] != '0' && (s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26) {
            dp[i] += dp[i - 2];
        }
    }
    return dp[n];
}

我们使用 dp 数组记录当前位置可能的解码方法数目,在循环中不断地更新它。首先,我们将 dp[0] 设置为 1,以便当字符集长度为 0 或者第一个字符为 0 时得到正确的答案。接下来用循环依次处理每一个位置 i,计算 dp[i] 的值。

首先判断当前字符是否可被单独编码,在这种情况下,我们可以从之前的位置状态转移而来,因为我们可以将它当做一个完整的单一元素(一个字母 A 对应一个数字 1)。具体来说,可以转移到位置 (i-1) 上,那么方案数就是 dp[i-1]。此时直接用数组中的数据更新 dp[i] 的内容即可。

在第二个判断中,我们需要检查当前字符和前一个字符能否组成一个有效的编码。如果可以,我们同样可以从前面的位置转移而来——这个组合的两个字母对应着数字 26。在这种情况下,可以采取如下步骤:

  1. 检查前一个字符不能够是 '0'(否则无法与当前字符组成有效编码)。
  2. 利用两个字符将数组解析预期的整数后,确保其落在十进制 1 到 26 的范围内。如果是,那么就可以利用解析到的整数确定一个有效的编码,并将方案数累加到位置 (i-2) 上。

最终,我们返回 dp[n] 的值,其中 n 是给定字符串的长度,表示字符串中可能的解码方法数量。

JZ14 剪绳子 

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param number int整型 
 * @return int整型
 */
int cutRope(int number ) {
    // write code here

    // 如果绳子长度小于等于1,无法剪断,返回0
    if (number <= 1) {
        return 0;
    }
    // 如果绳子长度为2,只能剪成长度为1的两段,返回1
    if (number == 2) {
        return 1;
    }
    // 如果绳子长度为3,可以剪成长度为1和2的两段,返回2
    if (number == 3) {
        return 2;
    }
    // 创建数组存储子问题的最优解
    int products[number + 1];
    // 绳子长度为0时,最优解为0
    products[0] = 0;
    // 绳子长度为1时,最优解为1
    products[1] = 1;
    // 绳子长度为2时,最优解为2
    products[2] = 2;
    // 绳子长度为3时,最优解为3
    products[3] = 3;
    // 动态规划求解子问题的最优解
    for (int i = 4; i <= number; i++) {
        int max = 0;
        // 枚举剪断的位置,计算所有可能的最优解
        for (int j = 1; j <= i / 2; j++) {
            int product = products[j] * products[i - j];
            if (product > max) {
                max = product;
            }
        }
        // 将最优解存储到数组中
        products[i] = max;
    }
    // 返回绳子长度为number时的最优解
    return products[number];
}

 JZ83 剪绳子(进阶版) 动态规划

思路: * 1. 当绳子长度为 2 时,只能剪成长度为 1 的两段,乘积为 1。 * 2. 当绳子长度为 3 时,只能剪成长度为 1 和 2 的两段,乘积为 2。 * 3. 当绳子长度大于 3 时,尽可能多地剪长度为 3 的绳子段,如果剩下的长度为 1,则拿出一个长度为 3 的绳子段凑成两个长度为 2 的绳子段;如果剩下的长度为 2,则直接剪成长度为 2 的绳子段。最后将所有长度为 3 的绳子段和长度为 2 的绳子段的乘积相乘即可。 * * 注意: * 1. 需要使用快速幂算法计算幂次方,避免超时。 * 2. 需要对计算结果取模,避免溢出。 */ 

 /* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param number long长整型 
 * @return long长整型
 * C语言声明定义全局变量请加上static,防止重复定义
 */
long long mypow(int base,long long m){
    if(m==0)
        return 1;
    if(m%2==1){
        return base*mypow(base,m-1)%998244353;
    }
    else{
        long long temp=mypow(base,m/2);
        return temp*temp%998244353;
    }
    
}
long long cutRope(long long number ) {
    // write code here
    if(number==2)
        return 1;
    if(number==3)
        return 2;
    //其余的按照2 3 3 3 3的方式切割 不能有1
    long long count2=0;
    //含有3 的个数
    long long count3=number/3;
    //除3 余1 需要拿出来一个3 凑两个2
    if(number%3==1){
        count3--;
        count2=2;
        return (mypow(3,count3)*4%998244353);
    }
    //除3 余2 
    else if(number%3==2){
        count2=1;
        return (mypow(3,count3)*2%998244353);
    }
    return mypow(3,count3);
}

 JZ45 把数组排成最小的数

快速排序

3放在30后面(303)比 3放在30前面(330)小,就交换顺序

这里我们定义了一个比较函数 compare,并将其作为参数传入 C 语言中的标准库函数 qsort() 中,来对整数数组 nums 进行排序。在这个比较函数中,分别将两个数转换为字符串 s1s2,然后直接调用 strcmp 函数来比较这两个字符串的大小。当然,我们在将数字转换为字符串时,通过 sprintf 函数实现。

接下来,我们创建一个空字符数组 res,用来存储拼接起来的最小整数。利用 strcat() 函数,我们可以轻松地将每个数字按照顺序依次追加到这个结果中。最后返回 res 即可,这里需要注意函数的返回值类型是 char*,因为输出结果可能很大,所以不能用 int 类型来处理。

int compare(const void* v1, const void* v2) {
    char s1[22], s2[22];
    sprintf(s1, "%d%d", *(int*)v1, *(int*)v2);   // 将两个数字拼接到一起形成字符串
    sprintf(s2, "%d%d", *(int*)v2, *(int*)v1);   // 将两个数字拼接到一起形成字符串
    return strcmp(s1, s2);   // 比较两个字符串的大小关系
}
char* minNumber(int* nums, int numsSize){
    char* res = (char*)malloc(sizeof(char) * 1000);
    memset(res, '\0', sizeof(char) * 1000);
    qsort(nums, numsSize, sizeof(int), compare);   // 对数组进行排序
    for(int i = 0; i < numsSize; i++) {
        char temp[15];
        sprintf(temp, "%d", nums[i]);   // 将当前数字转换为字符串
        strcat(res, temp);   // 将字符串追加到结果中
    }
    return res;
}

JZ43 整数中1出现的次数(从1到n整数中1出现的次数)

int NumberOf1Between1AndN_Solution(int n ) {
    // write code here
      int ones = 0; // 统计数字1出现次数
    for (int m = 1; m <= n; m *= 10) { // m从1开始不断乘10,也就是枚举每一位
        int a = n/m, b = n%m; 
        ones += (a + 8) / 10 * m + (a % 10 == 1) * (b + 1); // 根据数学公式计算每一位出现次数
    }
    return ones;
}

JZ38 字符串的排列

 

1. 将字符串中的字符按照字典序排序,方便后续去重和输出;

2. 定义一个布尔数组used,用于标记当前字符是否已经被使用过;

3. 定义一个字符数组path,用于存储当前已经选择的字符;

4. 定义一个字符串数组res,用于存储所有的排列结果;

5. 定义一个递归函数,每次从未被使用过的字符中选择一个加入path中,并标记为已使用;

6. 当path中的字符数等于原字符串长度时,将path加入res中;

7. 回溯时需要将当前字符从path中删除,并标记为未使用。

JZ34 二叉树中和为某一值的路径(二)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
void dfs(struct TreeNode* root, int target, int** res, int* returnSize, int* path, int* pathSize, int** returnColumnSizes) {
    if (root == NULL) return; // 递归结束条件
    path[(*pathSize)++] = root->val; // 将当前节点加入路径中
    target -= root->val; // 目标值减去当前节点的值
    if (target == 0 && root->left == NULL && root->right == NULL) { // 如果已经到达叶子节点且满足路径和等于目标值,输出该路径
        (*returnSize)++;
        res[*returnSize-1] = (int*)malloc((*pathSize)*sizeof(int)); // 分配对应长度的空间
        memcpy(res[*returnSize-1], path, (*pathSize)*sizeof(int)); // 复制路径
        (*returnColumnSizes)[*returnSize-1] = *pathSize; // 记录该路径的长度
    } else { // 不是叶子节点或者路径和不等于目标值,继续递归
        dfs(root->left, target, res, returnSize, path, pathSize, returnColumnSizes);
        dfs(root->right, target, res, returnSize, path, pathSize, returnColumnSizes);
    }
    target += root->val; // 恢复目标值
    (*pathSize)--; // 减掉当前节点
}

int** FindPath(struct TreeNode* root, int target, int* returnSize, int** returnColumnSizes ) {
    int** res = (int**)malloc(1000*sizeof(int*)); // 最多有1000条路径
    *returnSize = 0;
    int* path = (int*)malloc(1000*sizeof(int));
    int pathSize = 0;
    *returnColumnSizes = (int*)malloc(1000*sizeof(int)); // 记录每条路径的长度
    dfs(root, target, res, returnSize, path, &pathSize, returnColumnSizes);
    return res;
}

JZ33 二叉搜索树的后序遍历序列

  1. 后序遍历的最后一个元素为树的根节点,将数组中最后一个元素取出作为根节点的值。

  2. 由于二叉搜索树的左子树节点的值都小于根节点,右子树节点的值都大于根节点,因此可以找到第一个大于等于根节点值的位置 i ,该位置前面的元素全部是左子树的,该位置以及之后的元素全部是右子树的。如果没有找到这样的位置,则说明整个数组都为左子树元素,没有右子树,不符合二叉搜索树的定义。

  3. 在找到上述的位置 i 后,需要判断该位置之后的所有元素是否都大于根节点的值,如果存在不符合要求的元素,则说明该数组不是某二叉搜索树的后序遍历结果。

  4. 然后递归地判断左子树和右子树是否分别是某二叉搜索树的后序遍历结果。

// 判断一个序列是否为二叉搜索树的后序遍历结果
bool check(int* seq, int l, int r){
    if(l >= r) return true; // 如果区间无效,返回真
    int root = seq[r];     // 取出子树的根节点
    int i = l;
    while(seq[i] < root) i++; // 找到左右子树的分界点
    int j = i;
    while(j < r){ // 检查右子树是否大于根节点
        if(seq[j++] < root) return false; // 如果任何元素小于根节点,那么该序列不是二叉搜索树的后序遍历序列,返回假
    }
    // 递归检查左子树和右子树
    return check(seq, l, i-1) && check(seq, i, r-1);
}

// 判断一个序列是否为二叉搜索树的后序遍历结果,并返回布尔值
bool VerifySquenceOfBST(int* sequence, int sequenceLen ) {
    // 检查输入是否无效,如果无效则返回假
    if(sequence == NULL || sequenceLen <= 0 ) return false;
    // 调用 check 函数递归检查序列
    return check(sequence, 0, sequenceLen-1);
}

JZ86 在二叉树中找到两个节点的最近公共祖先

 首先判断特殊情况。如果二叉树为空或者 o1 和 o2 中至少有一个节点为空,则无法进行查找,直接返回 NULL。如果 o1 或 o2 就是根节点,则根据最近公共祖先的定义,根节点就是 o1 和 o2 的最近公共祖先节点,直接返回根节点即可。

否则,对于任意一个节点 x,如果 o1 和 o2 分别在它的左右子树中,或者都在同一子树中,则节点 x 就是 o1 和 o2 的最近公共祖先节点。而如果只有 o1 或 o2 在节点 x 的左子树中(不含b 它等于 o1 或 o2),则继续在节点 x 的左子树中查找 o1 和 o2 的最近公共祖先节点;如果只有 o1 或 o2 在节点 x 的右子树中,则继续在节点 x 的右子树中查找 o1 和 o2 的最近公共祖先节点。为了判断节点 p 是否在二叉树中,可以递归找到 p 节点,并检查递归的结果是否为空指针,即可判断是否包含 p 节点。最终返回的节点就是 o1 和 o2 的最近公共祖先节点。

int lowestCommonAncestor(struct TreeNode* root, int o1, int o2 ) {
    // write code here
    if(root==NULL){
        return -1;
    }
    if(root->val==o1 || root->val==o2){
        return root->val;
    }
    int left=lowestCommonAncestor(root->left, o1, o2);
    int right=lowestCommonAncestor(root->right, o1, o2);
    if(left==-1){//左边没找到任何相同,那么一定在右边
        return right;
    }
    if(right==-1){//右边没有找到任何相同,则一定在左边
        return left;
    }
    return root->val;//左右都找到了,那说明当前root是最近公共祖先
}

JZ54 二叉搜索树的第k个节点

//中序遍历
void inorder(struct TreeNode* root, int* arr, int* idx) {
    if (root == NULL) return;
    inorder(root->left, arr, idx);
    arr[(*idx)++] = root->val;
    inorder(root->right, arr, idx);
}
int KthNode(struct TreeNode* proot, int k) {
    // 计算二叉搜索树的结点个数
    if(k<=0)  return -1;
    int n = 0;
    struct TreeNode* cur = proot;// 定义一个指向二叉树根节点的指针cur
    while (cur != NULL) {
        if (cur->left == NULL) {// 如果当前节点左子节点为空,则不需要考虑左子树,n+1并切换到右子节点
            n++;
            cur = cur->right;// 遍历右子树
        } else {// 如果当前节点左子节点非空,则先要处理左子树
            struct TreeNode* pre = cur->left;// 定义一个指向当前节点左子节点的指针pre
            while (pre->right != NULL && pre->right != cur) {// 找到当前结点的前驱结点
                pre = pre->right;
            }
            if (pre->right == NULL) {// 如果前驱结点的右子树为空
                pre->right = cur;// 将前驱结点的右子树连接到当前结点
                cur = cur->left;// 遍历左子树
            } else {// 如果前驱结点的右子树不为空
                pre->right = NULL;// 恢复树的形状
                n++;// 结点数加一
                cur = cur->right; // 遍历右子树
            }
        }
    }
    // 如果k大于n,返回-1
    if (k > n) return -1;
    // 中序遍历二叉搜索树,并将结果存储在数组中
    int* arr = (int*)malloc(sizeof(int) * n);
    int idx = 0;
    inorder(proot, arr, &idx);
    // 返回第k小的结点值
    return arr[k - 1];
}

JZ56 数组中只出现一次的两个数字

  1. 将数组中所有元素进行异或操作,得到的结果就是两个只出现一次的数字的异或结果;
  2. 找到异或结果中二进制表示的最低位 1 所在的位置,记为 k;
  3. 根据第 k 位是 0 还是 1 将原始数组划分为两个子数组,其中一个子数组中每个数字的第 k 位都是1,另一个子数组中每个数字的第 k 位都是 0;
  4. 对于这两个子数组分别进行异或操作,得到的结果就是两个只出现一次的数字。

注意要求输出按非降序排列,因此需要对最后的结果进行排序。

int* FindNumsAppearOnce(int* array, int arrayLen, int* returnSize) {
    if (array == NULL || arrayLen <= 1) {
        return NULL;
    }
    // 计算两个只出现一次的数字的异或结果
    int xor_result = array[0];
    for (int i = 1; i < arrayLen; i++) {
        xor_result ^= array[i];
    }
    // 找到异或结果中二进制表示的最低位 1 的位置
    int bit_index = 0;
    while ((xor_result & 1) == 0 && bit_index < sizeof(int) * 8) {
        bit_index++;
        xor_result >>= 1;
    }
    // 根据第 bit_index 位是 0 还是 1 将原始数组划分为两个子数组
    int num1_result = 0, num2_result = 0;
    for (int i = 0; i < arrayLen; i++) {
        if ((array[i] >> bit_index) & 1) { // 第 bit_index 位为 1
            num1_result ^= array[i]; // 第 bit_index 位为1的数异或得到第一个只出现一次的数字
        } else { // 第 bit_index 位为 0
            num2_result ^= array[i];// 第 bit_index 位为0的数异或得到第二个只出现一次的数字
        }
    }
    // 按非降序排列输出结果
    if (num1_result > num2_result) {
        int temp = num1_result;
        num1_result = num2_result;
        num2_result = temp;
    }
    // 保存结果并返回
    int* result = (int*)malloc(2 * sizeof(int));
    result[0] = num1_result;
    result[1] = num2_result;
    *returnSize = 2;
    return result;
}

JZ57 和为S的两个数字

这个问题可以使用双指针法来解决,具体思路如下:

  1. 初始化两个指针 i 和 j 分别指向数组的第一个元素和最后一个元素;
  2. 如果当前 i 和 j 指向的数字之和等于 sum,则直接返回它们;
  3. 如果当前 i 和 j 指向的数字之和小于 sum,则将 i 指向下一个元素;
  4. 如果当前 i 和 j 指向的数字之和大于 sum,则将 j 指向前一个元素;
  5. 重复步骤 2~4,直到找到一组符合条件的数或者 i >= j。 
int* FindNumbersWithSum(int* array, int arrayLen, int sum, int* returnSize) {
    if (array == NULL || arrayLen < 2) {
        return NULL;
    }
    int* result = (int*)malloc(2 * sizeof(int));
    int i = 0, j = arrayLen - 1;
    while (i < j) {
        int cur_sum = array[i] + array[j];
        if (cur_sum == sum) {
            result[0] = array[i];
            result[1] = array[j];
            *returnSize = 2;
            return result;
        } else if (cur_sum < sum) {
            i++;
        } else {
            j--;
        }
    }
    // 没有找到符合条件的数,返回空数组
    *returnSize = 0;
    return result;
}

JZ75 字符流中第一个不重复的字符 

使用哈希表记录每个字符出现的次数,然后遍历字符流找到第一个出现次数为1的字符。

int char_count[10000]; // 哈希表,记录每个字符出现的次数
char char_stream[10000]; // 字符流 
void Insert(char ch ) {
    // write code here
    char_count[ch]++; // 更新哈希表
    strcat(char_stream, &ch); // 更新字符流
}
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param 无 
 * @return char字符型
 */
char FirstAppearingOnce() {
    // write code here
    int len = strlen(char_stream);
    for (int i = 0; i < len; i++)
    {
        if (char_count[char_stream[i]] == 1) // 找到第一个出现次数为1的字符
        {
            return char_stream[i];
        }
    }
    return '#'; // 没有找到符合条件的字符,返回#
}

JZ74 和为S的连续正数序列 

思路:使用双指针法,定义两个指针left和right,分别指向序列的起始和结束位置。初始时,left=1,right=2。如果当前序列的和小于sum,则将right指针向右移动一位,扩大序列范围;如果当前序列的和大于sum,则将left指针向右移动一位,缩小序列范围;如果当前序列的和等于sum,则将序列加入结果集中,并将left指针向右移动一位,继续寻找下一个序列。

int** FindContinuousSequence(int sum, int* returnSize, int** returnColumnSizes ) {
    int left = 1, right = 2; // 初始化左右指针
    int curSum = 3; // 初始序列为1,2,所以curSum初始值为3
    int** res = (int**)malloc(sizeof(int*) * 1000); // 最多有1000个序列,动态分配内存
    *returnColumnSizes = (int*)malloc(sizeof(int) * 1000); // 动态分配内存
    *returnSize = 0; // 返回符合的序列的个数
    while (left < right) { // 左指针小于右指针时循环
        if (curSum == sum) { // 找到一个序列
            int* seq = (int*)malloc(sizeof(int) * (right - left + 1)); // 存放找到的合适的序列
            for (int i = left; i <= right; i++) { // 将找到的一串和为S的数存入序列中
                seq[i - left] = i;
            }
            res[*returnSize] = seq; // 将序列存入结果数组
            (*returnColumnSizes)[*returnSize] = right - left + 1; // 第returnSize个存储序列长度
            (*returnSize)++; // 序列数量加1
            curSum -= left; // 将left指针向右移动一位,继续寻找下一个序列,故需要先减去left的值,left指针再往后移动
            left++;
        } else if (curSum < sum) { // 序列和小于sum,将right指针向右移动一位,扩大序列范围
            right++;
            curSum += right;
        } else { // 序列和大于sum,将left指针向右移动一位,缩小序列范围
            curSum -= left;
            left++;
        }
    }
    return res; // 返回结果数组
}

JZ67 把字符串转换成整数(atoi)  

将一个字符串转换成整数。该函数不能使用现有的库函数,需要自己实现。传入的字符串可能包含空格、符号、数字、字母等,需要先去掉无用的前导空格,然后判断符号位,确定整数的正负性。接着,找到连续的数字部分,将其转换成整数。如果没有有效的整数部分,则返回0。最后,需要将整数截断到32位有符号整数范围内,并去掉无用的后导空格。

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param s string字符串 
 * @return int整型
 */
int StrToInt(char* s ) {
    // write code here
     if (s == NULL) return 0;
    // 去掉无用的前导空格
    while (*s == ' ') s++;
    // 判断符号位
    bool negative = false;//首先设置符号位为负时是false
    if (*s == '-') {
        negative = true;//若符号位为负则负符号位为true
        s++;
    } else if (*s == '+') {//若符号位为正则负符号位为false
        s++;
    }
    // 判断整数的有效部分
    long long num = 0;
    while (*s>= '0' && *s <= '9') {
        num = num * 10 + (*s- '0');
        s++;
        // 判断是否超过32位有符号整数范围
        if (num > 2147483647) {
            return negative ? -2147483648 : 2147483647;//超过2147483647且为负数的话返回-2147483648,超过2147483647且为正数的话返回2147483647
        }
    }
    // 去掉无用的后导空格
    while (*s == ' ') s++;
    return negative ? -num : num;//如果为负数的话返回刚才计算的num取反,否则直接输出
}

JZ76 删除链表中重复的结点 

首先判断头结点与头结点的下一个是否为空,创建一个虚拟头结点指向头结点,定义前驱节点和当前节点,遍历链表,判断当前节点和下一个节点值是否相同, 找到最后一个相同的节点,如果当前节点和下一个节点值不相同,继续遍历,返回去掉重复节点的链表。

/**
 * struct ListNode {
 *  int val;
 *  struct ListNode *next;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 *
 * @param pHead ListNode类
 * @return ListNode类
 */
struct ListNode *deleteDuplication(struct ListNode *pHead)
{
    if (pHead == NULL || pHead->next == NULL) {
        return pHead;
    }
    // 创建虚拟头节点
    struct ListNode *dummy = (struct ListNode *)malloc(sizeof(struct ListNode));
    dummy->next = pHead;
    // 定义前驱节点和当前节点
    struct ListNode *pre = dummy;
    struct ListNode *cur = pHead;
    // 遍历链表
    while (cur != NULL) {
        if (cur->next != NULL && cur->val == cur->next->val) {// 如果当前节点和下一个节点的值相同
            while (cur->next != NULL && cur->val == cur->next->val) {// 找到最后一个相同的节点
                cur = cur->next;
            }
            pre->next = cur->next;// 删除这些节点
        } else 
        {
            pre = cur; // 如果当前节点和下一个节点的值不同,继续遍历
        }
        cur = cur->next;
    }
    return dummy->next;// 返回去掉重复节点后的链表
}

JZ85 连续子数组的最大和(二)

定义当前和、最大和、当前序列起始位置和结束位置、最大序列起始和结束位置; 

如果当前和小于0,则当前子数组对后面数组没有贡献,重新选择序列; 

如果当前和大于0,当前子数组对后面的数组有贡献,继续加上下一个元素; 

若当前数组和大于最大和,或者等于最大和但长度比最大和序列大,更新最大和; 

返回最大和序列的大小; 

保存最大和序列。 

int* FindGreatestSumOfSubArray(int* array, int arrayLen, int* returnSize ) {
    int maxSum = array[0]; // 最大和
    int curSum = array[0]; // 当前和
    int start = 0; // 最大子数组的起始位置
    int end = 0; // 最大子数组的结束位置
    int curStart = 0; // 当前子数组的起始位置
    int i;
    for (i = 1; i < arrayLen; i++) {
        if (curSum < 0) { // 如果当前和小于0,说明当前子数组对后面的子数组没有贡献,需要重新开始计算
            curSum = array[i];
            curStart = i;
        } else { // 如果当前和大于等于0,说明当前子数组对后面的子数组有贡献,继续加上下一个元素
            curSum += array[i];
        }
        if (curSum > maxSum || (curSum == maxSum && i - curStart > end - start)) { // 如果当前子数组的和大于最大和,或者当前子数组的和等于最大和但是长度更长,更新最大子数组的起始位置和结束位置
            maxSum = curSum;
            start = curStart;
            end = i;
        }
    }
    *returnSize = end - start + 1;
    int* result = (int*)malloc(*returnSize * sizeof(int)); // 动态分配内存
    for (i = 0; i < *returnSize; i++) {
        result[i] = array[start + i];
    }
    return result;
}

JZ84 二叉树中和为某一值的路径(三) 

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */
// 定义一个深度优先搜索函数,用于在以root为根节点的树中查找和为sum的路径数量
int dfs(struct TreeNode* root, int sum) {
    // 如果当前节点为空,直接返回0
    if (root == NULL) return 0;
    // 定义一个变量res,用于记录以当前节点为起点的路径数量
    int res = 0;
    // 如果当前节点的值等于sum,说明找到了一条路径,res加1
    if (root->val == sum) res++;
    // 递归查找左子树中和为sum-root->val的路径数量,并加到res中
    res += dfs(root->left, sum - root->val);
    // 递归查找右子树中和为sum-root->val的路径数量,并加到res中
    res += dfs(root->right, sum - root->val);
    // 返回以当前节点为起点的路径数量
    return res;
}
// 定义一个函数,用于在整棵树中查找和为sum的路径数量
int FindPath(struct TreeNode* root, int sum ) {
    // 如果当前节点为空,直接返回0
    if (root == NULL) return 0;
    // 定义一个变量res,用于记录整棵树中和为sum的路径数量
    int res = dfs(root, sum); // 先查找以当前节点为根节点的路径数量
    // 递归查找左子树中和为sum的路径数量,并加到res中
    res += FindPath(root->left, sum);
    // 递归查找右子树中和为sum的路径数量,并加到res中
    res += FindPath(root->right, sum);
    // 返回整棵树中和为sum的路径数量
    return res;
}

JZ77 按之字形顺序打印二叉树

首先判断为空树的情况, 创建一个队列,创建一个二维数组,声明队列的头尾指针,根结点入队,尾指针后移一位,队列非空时,定义当前尾指针和当前层节点数,遍历当前层结点

//定义二叉树结点结构体
/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 *
 * C语言声明定义全局变量请加上static,防止重复定义
 */
//层次遍历二叉树
int** levelOrder(struct TreeNode* root, int* returnSize, int** returnColumnSizes) {
    *returnSize = 0;   //特殊情况,空树
    if (root == NULL)
        return NULL;
    struct TreeNode* queue[100001];  //新建队列
    int** res = (int**)malloc(sizeof(int*) * 100001);  //为res分配内存,res实际上是一个数组,数组内存的内容是指向int型数组的地址,即构成一个二维数组
    *returnColumnSizes = (int*)malloc(sizeof(int) * 100001);  //数组列数,即每层多少个元素    
    int head = 0, rear = 0;  //设队列的头尾指针
    queue[rear] = root;   //根结点入队,尾指针后移一位
    rear++;
    while (head != rear) {    //队列非空时
        int preRear = rear;  //记录当前层的尾结点
        int k = 0;           //当前层的结点数量
        res[*returnSize] = (int*)malloc(sizeof(int) * (preRear - head)); //为二维数组res的第*returnSize行分配空间
        while (head < preRear) {  //遍历当前层结点
            struct TreeNode* p = queue[head];   //队头元素出队
            res[*returnSize][k++] = p->val; //更新返回数组中的值
            if (p->left != NULL)   //将当前出队元素的左孩子结点入队
                queue[rear++] = p->left;
            if (p->right != NULL)  //将当前出队元素的右孩子结点入队
                queue[rear++] = p->right;
            head++;  //队头指针后移一位
        }
        (*returnColumnSizes)[*returnSize] = k;  //更新返回数组本层结点个数
        (*returnSize)++;  //指针指向下一层返回数组
    }
    return res;
}
//翻转
int** Print(struct TreeNode* pRoot, int* returnSize, int** returnColumnSizes ) {
    // write code here  
    int** res=levelOrder(pRoot, returnSize, &(* returnColumnSizes));  //调用层次遍历函数,返回一个二维数组res
    int row=0;
    while(row<=((*returnSize)-1)){  //遍历每一层
        if(row%2==1){  //如果是奇数层
            int k=0;
            int temp=0;
            int N=(*returnColumnSizes)[row];  //获取该层结点个数
            for(k=0;k<N/2;k++){  //将该层结点从中间位置翻转
                temp=res[row][k];
                res[row][k]=res[row][N-1-k];
                res[row][N-1-k]=temp;
            } 
        }
        row+=1;  //指向下一层
    }
    return res;  //返回翻转后的二维数组
}

 JZ59 滑动窗口的最大值

思路:使用双端队列来维护滑动窗口中的最大值。队列中存储的是数组元素的下标,队列头部存储的是当前窗口中的最大值。每次滑动窗口时,先判断队列头部的下标是否已经不在窗口中,如果不在,则弹出队列头部;然后将当前元素与队列里的元素比较,如果当前元素大于队列里的元素,则把比它小的元素全部弹出队列;若当前元素小于等于队列里最大的元素,然后将当前元素的下标加入队列尾部。因此最大元素总是在队头。最后将队列头部的元素加入结果数组中。 

/**
 * 
 * @param num int整型一维数组 
 * @param numLen int num数组长度
 * @param size int整型 
 * @return int整型一维数组
 * @return int* returnSize 返回数组行数
 */
int* maxInWindows(int* num, int numLen, int size, int* returnSize ) {
    // 如果数组长度为0或者窗口大小为0或者窗口大小大于数组长度,返回空数组
    if (numLen == 0 || size == 0 || size > numLen) {
        *returnSize = 0;
        return NULL;
    }
    // 创建结果数组
    int* res = (int*)malloc(sizeof(int) * (numLen - size + 1));//数组中存放的都是队头元素,即每个窗口的最大值
    int resIndex = 0; // 结果数组的下标
    int deque[size]; // 创建双端队列
    int dequeHead = 0, dequeTail = -1; // 队列头部和尾部的下标
    for (int i = 0; i < numLen; i++) {
        // 判断队列头部的下标是否已经不在窗口中
        if (dequeHead <= dequeTail && deque[dequeHead] <= i - size) {队头元素下标<当前元素下标-窗口大小
            dequeHead++; // 如果头部下标不在窗口中,头部下标向后移动一位
        }
        // 将当前元素与队列尾部的元素比较
        while (dequeHead <= dequeTail && num[deque[dequeTail]] < num[i]) {
            dequeTail--; // 如果队列尾部的元素小于当前元素,将尾部元素弹出
        }
        deque[++dequeTail] = i; // 将当前元素加入队列尾部
        // 将队列头部的元素加入结果数组中
        if (i >= size - 1) {
            res[resIndex++] = num[deque[dequeHead]];
        }
    }
    *returnSize = resIndex; // 返回结果数组的长度
    return res; // 返回结果数组
}

从前序与中序遍历序列构造二叉树 (分治)

// 根据前序遍历和中序遍历构造二叉树
struct TreeNode* buildTree(int* preorder, int preorderSize, int* inorder, int inorderSize) {
    // 如果前序遍历或中序遍历为空,则返回空指针
    if (preorderSize == 0 || inorderSize == 0) {
        return NULL;
    }
    // 创建根节点
    struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->val = preorder[0]; // 根节点的值为前序遍历的第一个元素
    root->left = NULL; // 左子树为空
    root->right = NULL; // 右子树为空
    // 在中序遍历中找到根节点的位置
    int rootIndex = 0;
    while (inorder[rootIndex] != root->val) { // 遍历中序遍历数组,找到根节点的位置
        rootIndex++;
    }
    // 递归构造左子树和右子树
    root->left = buildTree(preorder + 1, rootIndex, inorder, rootIndex); // 构造左子树,前序遍历从第二个元素开始,中序遍历从数组开头到根节点位置
    root->right = buildTree(preorder + rootIndex + 1, preorderSize - rootIndex - 1, inorder + rootIndex + 1, inorderSize - rootIndex - 1); // 构造右子树,前序遍历从根节点后一个元素开始,中序遍历从根节点位置后一个元素到数组结尾
    return root; // 返回根节点
}

// 中序遍历二叉树
void inorderTraversal(struct TreeNode* root) {
    if (root == NULL) { // 如果根节点为空,则返回
        return;
    }
    inorderTraversal(root->left); // 中序遍历左子树
    printf("%d ", root->val); // 输出根节点的值
    inorderTraversal(root->right); // 中序遍历右子树
}

排序链表(分治)

// 定义单链表结构体
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
// 合并两个有序链表
// 参数为两个有序链表的头指针
struct ListNode* merge(struct ListNode* l1, struct ListNode* l2) {
    // 如果其中一个链表为空,则直接返回另一个链表
    if (l1 == NULL) {
        return l2;
    }
    if (l2 == NULL) {
        return l1;
    }
    // 如果 l1 的值小于 l2 的值,则将 l1 的下一个节点和 l2 合并
    if (l1->val < l2->val) {
        l1->next = merge(l1->next, l2);
        return l1;
    } else {
        // 否则将 l2 的下一个节点和 l1 合并
        l2->next = merge(l1, l2->next);
        return l2;
    }
}
// 归并排序
// 参数为待排序链表的头指针
struct ListNode* sortList(struct ListNode* head) {
    // 如果链表为空或只有一个节点,则直接返回
    if (head == NULL || head->next == NULL) {
        return head;
    }
    // 使用快慢指针找到链表的中间节点
    struct ListNode* slow = head;
    struct ListNode* fast = head->next;
    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;
    }
    struct ListNode* mid = slow->next;
    slow->next = NULL;
    // 分别对左右两个子链表进行排序
    struct ListNode* l1 = sortList(head);
    struct ListNode* l2 = sortList(mid);
    // 合并左右两个有序链表
    return merge(l1, l2);
}

 1015. 可被 K 整除的最小整数

int smallestRepunitDivByK(int k){
  if (k % 2 == 0 || k % 5 == 0) {
        return -1;
    }
    int n = 1;
    int mod = 1 % k;
    while (mod != 0) {
        mod = (mod * 10 + 1) % k;
        n++;
    }
    return n;
}
if(k%2==0||k%5==0)return -1
int n=1;
int mod=1%k;
while(mod!=0)
{
    mod=mod*10+1%k;
    n++
}
retun n;

 深度优先搜索算法

1.恢复二叉搜索树

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
void recoverTree(struct TreeNode* root) {
    // 定义辅助指针和堆栈
    struct TreeNode *x = NULL, *y = NULL, *prev = NULL, *node = root;
    struct TreeNode *stack[100];  // 由于是递归算法可用数组代替堆栈
    int top = -1;  // 初始化堆栈顶

    // 中序遍历二叉搜索树,并将错误交换的节点保存到 x 和 y 指向的变量中
    while (node != NULL || top != -1) {
        while (node != NULL) {  // 将当前节点和左子树入栈
            stack[++top] = node;
            node = node->left;
        }
        node = stack[top--];  // 出栈时访问该节点
        if (prev != NULL && prev->val > node->val) {
            y = node;
            if (x == NULL) {  // 当前仅记录下第一个错误交换的节点为 x
                x = prev;
            } else {
                break;  // 已经找到两个错误交换的节点,退出循环
            }
        }
        prev = node;  // 记录下上一个节点作为前序节点
        node = node->right;  // 遍历右子树
    }

    // 交换 x 和 y 节点的值
    if (x != NULL && y != NULL) {
        int tmp = x->val;
        x->val = y->val;
        y->val = tmp;
    }
}

 剑指 Offer 29. 顺时针打印矩阵

// 该函数用于打印矩阵中一圈的元素
void printMatrixInCircle(int** matrix, int rows, int cols, int start, int* res, int* index) {
    // 计算当前圈的右下角坐标
    int endX = cols - 1 - start;
    int endY = rows - 1 - start;
    // 从左到右打印一行
    for (int i = start; i <= endX; i++) {
        res[(*index)++] = matrix[start][i]; // 将该元素加入结果数组中
    }
    // 从上到下打印一列
    if (start < endY) { // 如果该圈至少有两行
        for (int i = start + 1; i <= endY; i++) {
            res[(*index)++] = matrix[i][endX]; // 将该元素加入结果数组中
        }
    }
    // 从右到左打印一行
    if (start < endX && start < endY) { // 如果该圈至少有两行两列
        for (int i = endX - 1; i >= start; i--) {
            res[(*index)++] = matrix[endY][i]; // 将该元素加入结果数组中
        }
    }
    // 从下到上打印一列
    if (start < endX && start < endY - 1) { // 如果该圈至少有三行两列
        for (int i = endY - 1; i > start; i--) {
            res[(*index)++] = matrix[i][start]; // 将该元素加入结果数组中
        }
    }
}
// 该函数用于按照螺旋顺序遍历矩阵
int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize, int* returnSize) {
    // 如果矩阵为空,则返回空指针
    if (matrix == NULL || matrixSize == 0 || matrixColSize == NULL) {
        *returnSize = 0;
        return NULL;
    }
    int rows = matrixSize; // 矩阵的行数
    int cols = *matrixColSize; // 矩阵的列数
    int start = 0; // 当前圈的起始位置
    int* res = (int*)malloc(sizeof(int) * rows * cols); // 用于存储遍历结果的数组
    int index = 0; // 当前遍历到的位置

    // 按照螺旋顺序遍历矩阵
    while (cols > start * 2 && rows > start * 2) {
        printMatrixInCircle(matrix, rows, cols, start, res, &index); // 遍历当前圈
        start++; // 进入下一圈
    }
    *returnSize = index; // 将遍历结果的长度赋值给returnSize
    return res; // 返回遍历结果的数组
}

cols > start * 2 && rows > start * 2是为了保证还有未打印的元素。因为螺旋矩阵的打印是从外向内逐层打印的,每一层至少需要有两行两列,才能继续打印。如果剩下的行数或列数不足两行两列,那么就意味着已经打印完了所有元素,可以结束循环。因此,当剩下的行数或列数不足两行两列时,就不再需要继续打印了。 

剑指 Offer 58 - I. 翻转单词顺序

// 定义一个函数,输入一个字符串 s,输出将 s 中的单词顺序翻转后的字符串
char* reverseWords(char* s) {
    // 获取字符串 s 的长度
    int len = strlen(s);
    // 为结果字符串 res 分配内存空间,长度为 len+1,多出来的 1 个字节用于存储字符串结束符 '\0'
    char* res = (char*)malloc(sizeof(char) * (len + 1));
    // 定义变量 cur 和 index,用于记录当前单词的起始位置和结果字符串的下标
    int cur = 0, index = 0;
    // 定义变量 left 和 right,用于记录当前单词的左右边界
    int left = len - 1, right = len - 1;
    // 从字符串 s 的末尾开始遍历
    while (left >= 0) {
        // 如果当前字符是空格,则将 left 指针向左移动一位
        if (s[left] == ' ') {
            left--;
        } else {
            // 如果当前字符不是空格,则将 right 指针指向当前字符
            right = left;
            // 将 left 指针向左移动,直到指向当前单词的左边界
            while (left >= 0 && s[left] != ' ') {
                left--;
            }
            // 将当前单词从 s 中复制到 res 中
            cur = left + 1;
            while (cur <= right) {
                res[index++] = s[cur++];
            }
            // 在 res 中添加一个空格,用于分隔单词
            res[index++] = ' ';
        }
    }
    // 如果 res 中有字符,并且最后一个字符是空格,则将其替换为字符串结束符 '\0'
    if (index > 0 && res[index - 1] == ' ') {
        res[index - 1] = '\0';
    } else {
        // 如果 res 中没有字符或者最后一个字符不是空格,则在 res 的末尾添加字符串结束符 '\0'
        res[index] = '\0';
    }
    // 返回结果字符串 res
    return res;
}

剑指 Offer 35. 复杂链表的复制 

1.复制结点的值,并放到原结点的后面

2.复制random指针 

3. 拆分链表

// 定义一个结构体表示链表的节点
struct RandomListNode {
    int label; // 节点的值
    struct RandomListNode *next; // 指向下一个节点的指针
    struct RandomListNode *random; // 指向随机节点的指针
};

// 复制链表
struct RandomListNode* Clone(struct RandomListNode* pHead ) {
    if (pHead == NULL) { // 如果链表为空,直接返回NULL
        return NULL;
    }
    // 复制每个节点并插入到原节点后面
    struct RandomListNode* pNode = pHead; // 定义一个指针指向链表头节点
    while (pNode != NULL) { // 遍历链表
        struct RandomListNode* pClone = (struct RandomListNode*)malloc(sizeof(struct RandomListNode)); // 创建一个新节点
        pClone->label = pNode->label; // 复制原节点的值
        pClone->next = pNode->next; // 将新节点插入到原节点后面
        pClone->random = NULL; // 将新节点的随机节点指针初始化为NULL
        pNode->next = pClone; // 将新节点插入到原节点后面
        pNode = pClone->next; // 指向下一个原节点
    }
    // 复制random指针
    pNode = pHead; // 重新指向链表头节点
    while (pNode != NULL) { // 遍历链表
        struct RandomListNode* pClone = pNode->next; // 指向当前原节点的复制节点
        if (pNode->random != NULL) { // 如果当前原节点有随机节点
            pClone->random = pNode->random->next; // 将复制节点的随机节点指针指向原节点的随机节点的复制节点
        }
        pNode = pClone->next; // 指向下一个原节点
    }
    // 拆分链表
    pNode = pHead; // 重新指向链表头节点
    struct RandomListNode* pCloneHead = pHead->next; // 复制链表的头节点
    while (pNode != NULL) { // 遍历链表
        struct RandomListNode* pClone = pNode->next; // 指向当前原节点的复制节点
        pNode->next = pClone->next; // 将原节点的next指针指向下一个原节点
        if (pClone->next != NULL) { // 如果当前复制节点不是最后一个节点
            pClone->next = pClone->next->next; // 将复制节点的next指针指向下一个复制节点
        }
        pNode = pNode->next; // 指向下一个原节点
    }
    return pCloneHead; // 返回复制链表的头节点
}

BM83 字符串变形

//翻转函数
void reverse(char* s,int head,int tail){
    char temp;
    while(head<tail){
        temp=s[head];
        s[head]=s[tail];
        s[tail]=temp;
        head++;
        tail--;
    }
}
//主函数
char* trans(char* s, int n ) {
    //翻转整个字符串
    reverse(s,0,n-1);
    int i=0,j=0;
    while(i<n){
        j=i;
        //按照空格分段进行大小写切换,部分反转
        while(j<n && s[j] != ' '){
        	//切换大小写
            if(s[j]>='A' && s[j]<='Z'){
                s[j]+=32;
            }else if(s[j]>='a' && s[j]<='z'){
                s[j]-=32;
            }
            j++;
        }
        //把这一段翻转
        reverse(s,i,j-1);
        i=j+1;//i变为空格后一个
    }
    return s;
}

BM84 最长公共前缀

char* longestCommonPrefix(char** strs, int strsLen ) {
    //如果字符串长度为0,则返回原字符串
    if(strsLen==0){
        return strs;
    }
    //初始化行列i,j
    int i=0,j=0;
    //从第一行第一列开始对比
    char flag=strs[0][i];    
    while(flag){
        //对比其他行同一列字符是否相同
        for(j=0;j<strsLen;j++){
            if(strs[j][i]!=flag){
                break;//一旦有字符不同,则跳出此for循环
            }
        }
        //如果j小于字符串长度,则跳出大while循环
        if(j<strsLen){
            break;
        }
        //如果前面没有跳出循环,则++i,flag变为下一个字符
        flag=strs[0][++i];
    }
    //把i位置赋值为'\0',c语言通过'\0'判断一个字符串
    strs[0][i]='\0';
    return strs[0];
}

BM86 大数加法

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 计算两个数之和
 * @param s string字符串 表示第一个整数
 * @param t string字符串 表示第二个整数
 * @return string字符串
 */
char* solve(char* s, char* t ) {
    // write code here
    int len1=strlen(s); // 获取第一个整数的长度
    int len2=strlen(t); // 获取第二个整数的长度
    if(len1==0) // 如果第一个整数为空,则直接返回第二个整数
    return t;
    if(len2==0) // 如果第二个整数为空,则直接返回第一个整数
    return s;
    int maxlen=len1>len2?(len1+2):(len2+2); // 计算结果字符串的最大长度,加2是为了给结果字符串预留进位和结束符的位置
    char*res=(char *)calloc(maxlen, sizeof(char)); // 分配结果字符串的内存空间
    int sum=0; // 用于存储两个数相加的结果
    int slen=len1-1,tlen=len2-1; // 分别表示第一个整数和第二个整数的当前位数
    int carry=0; // 进位标志,初始为0
    int k=0; // 结果字符串的当前索引
    int i,j; // 循环变量
    while(slen>=0||tlen>=0||carry>0) // 当两个整数的位数都遍历完且没有进位时,结束循环
    {
       int n1=slen>=0?s[slen--]-'0':0; // 获取第一个整数当前位的数值,如果已经遍历完,则置为0
       int n2=tlen>=0?t[tlen--]-'0':0; // 获取第二个整数当前位的数值,如果已经遍历完,则置为0
       sum=n1+n2+carry; // 计算两个数相加的结果,加上进位
       if(sum>=10) // 如果结果大于等于10,则需要进位
       {
          carry=1; // 设置进位标志为1
          res[k++]=sum%10+'0'; // 将结果的个位数添加到结果字符串中
       }
       else {
          carry=0; // 设置进位标志为0
          res[k++]=sum+'0'; // 将结果添加到结果字符串中
       }
    }
    for(i=0,j=k-1;i<j;i++,j--) // 将结果字符串进行翻转,使得最高位在前,最低位在后
    {
        char tmp=res[j]; // 交换结果字符串的两个字符
        res[j]=res[i];
        res[i]=tmp;
    }
    return res; // 返回结果字符串
}

 BM19 寻找峰值

 

二分查找 

int findPeakElement(int* nums, int numsLen ) {
    // write code here
    int left=0,right=numsLen-1;
    while(left<right)
    {
        int mid=(left+right)>>1;
        if(nums[mid]<nums[mid+1])
        {
            left=mid+1;
        }
        else {
            right=mid;
        }
    }
    return left;
}

 BM93 盛水最多的容器

 双指针

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param height int整型一维数组 
 * @param heightLen int height数组长度
 * @return int整型
 */
int maxArea(int* height, int heightLen ) {
    // write code here
    if (heightLen < 2)
        return 0;
    int left = 0, right = heightLen - 1;
    int max = 0;
    while (left < right) {
        int cur = (right - left) * (height[left] < height[right] ? height[left] : height[right]);
        if (cur > max)
            max = cur;
        if (height[left] < height[right])
            left++;
        else
            right--;
    }
    return max;
}

BM89 合并区间

/**
 * struct Interval {
 *	int start;
 *	int end;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param intervals Interval类一维数组 
 * @param intervalsLen int intervals数组长度
 * @return Interval类一维数组
 * @return int* returnSize 返回数组行数
 */
int cmp(const void *a, const void *b) {
    return ((struct Interval *)a)->start - ((struct Interval *)b)->start;
}
struct Interval* merge(struct Interval* intervals, int intervalsLen, int* returnSize) {
    if (intervalsLen <= 0) {
        *returnSize = 0;
        return NULL;
    }
    // 按起点升序排列区间
    qsort(intervals, intervalsLen, sizeof(struct Interval), cmp);
   // 创建一个临时数组存储合并后的区间
    struct Interval* merged = (struct Interval *)malloc(sizeof(struct Interval) * intervalsLen);
    int i, j;
    merged[0] = intervals[0];
    int count = 1;
    for (i = 1; i < intervalsLen; i++) {
        if (intervals[i].start <= merged[count-1].end) {
            // 出现重叠,更新合并后的区间的终点
            merged[count-1].end = intervals[i].end > merged[count-1].end ? intervals[i].end : merged[count-1].end;
        } else {
            // 没有重叠,将当前区间添加到合并后的区间数组中
            merged[count++] = intervals[i];
        }
    }
    *returnSize = count;
    return merged;
}

BM50 两数之和

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param numbers int整型一维数组 
 * @param numbersLen int numbers数组长度
 * @param target int整型 
 * @return int整型一维数组
 * @return int* returnSize 返回数组行数
 */
int* twoSum(int* numbers, int numbersLen, int target, int* returnSize ) {
    // write code here
    int i=0,j=0;
    int*res=(int *)calloc(2,sizeof(int));
    for(i=0;i<numbersLen;i++)
    {
            if(numbers[i]>target)
            continue;        
        for(j=i+1;j<numbersLen;j++)
        {
            if(numbers[i]+numbers[j]==target)
            {
                res[0]=i+1;
                res[1]=j+1;
                * returnSize=2;
                return res;                
            }
        }
    }
* returnSize=0;
return 0; 
}

BM54 三数之和

 

先排序,设第一个数是nums[i],则将问题转换为求剩下的两个数和为-nums[i],剩下的两个数用双指针求解。为了防止重复,当当前的数和前一个数一样时,跳过。找到满足条件的三元组,跳过重复元素。返回结果。

int compare(const void* a, const void* b) {
    return *(int*)a - *(int*)b;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
    // 先对数组进行排序
    qsort(nums, numsSize, sizeof(int), compare);
    // 确定最大和最小索引的界限
    int left, right;
    int target;
    int i;
    int count = 0;
    // 创建存储结果的二维数组,并为返回结果的列宽分配内存
    int maxResultSize = numsSize * (numsSize - 1) * (numsSize - 2) / 6;  // C(numsSize, 3)
    int** result = (int**)malloc(maxResultSize * sizeof(int*));
    *returnColumnSizes = (int*)malloc(maxResultSize * sizeof(int));
    // 遍历数组,固定一个数并应用双指针法来搜索另外两个数
    for (i = 0; i < numsSize - 2; i++) {
        // 跳过重复元素
        if (i > 0 && nums[i] == nums[i - 1])
            continue;
        target = -nums[i];
        left = i + 1;
        right = numsSize - 1;
        while (left < right) {
            int sum = nums[left] + nums[right];
            if (sum == target) {
                // 找到满足条件的三元组
                result[count] = (int*)malloc(3 * sizeof(int));
                result[count][0] = nums[i];
                result[count][1] = nums[left];
                result[count][2] = nums[right];
                (*returnColumnSizes)[count] = 3;
                count++;
                // 跳过重复元素
                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--;
            }
        }
    }
    // 更新返回结果的大小
    *returnSize = count;
    return result;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值