打卡leetcode第17天

本文深入探讨了多个计算机科学算法问题,包括寻找岛屿的最大面积、合并二叉树、填充树节点的下一个右侧指针、动态规划解决打家劫舍和三角形最小路径和问题。此外,还涉及数组加1的操作。文章提供了多种解决方案,包括深度优先搜索、递归和哈希表等数据结构和算法的应用。
摘要由CSDN通过智能技术生成

目录

1.岛屿的最大面积

2.合并二叉树

3.填充每一个节点的下一个右侧指针

4.打家劫舍(动态规划)

5.三角形最小路径和(动态规划)

6.组合

7.存在重复元素

8.移除元素

9.Pow(x,n )

10.数组加1


1.岛屿的最大面积

【题目】

 

 

【分析】dfs

//法1
int fun(int **grid, int gridSize, int *gridColSize, int r, int c){
    if ( r < 0 || r >= gridSize || c < 0 || c >= gridColSize[0] || !grid[r][c]) {
		return 0;
	}
        grid[r][c] = 0; //计算过即清零 减少计算次数
        return 1 + fun(grid,gridSize,gridColSize,r + 1,c) + 
        fun(grid,gridSize,gridColSize,r - 1,c) + 
        fun(grid,gridSize,gridColSize,r,c + 1) + 
        fun(grid,gridSize,gridColSize,r,c - 1);

}

int maxAreaOfIsland(int** grid, int gridSize, int* gridColSize){
    int ans, max = 0;
    int i = 0, j;
    for(i; i < gridSize; i++)
        for(j = 0; j < gridColSize[0]; j++){  //千万记得j = 0 写在里面!!
            ans = fun(grid,gridSize,gridColSize,i,j);  
            max = max > ans ? max : ans; 
        }
    return max;
}
---------
//法2
int dfs(int** grid, int gridSize, int* gridColSize, int i, int j) {
    if (i < 0 || j < 0 || i == gridSize || j == *gridColSize || grid[i][j] == 0) {
        return 0;
    }
    grid[i][j] = 0;
    int ans = 1;
    int x[4] = {0, -1, 0, 1};
    int y[4] = {-1, 0, 1, 0};
    for (int k = 0; k < 4; ++k) {
        ans += dfs(grid, gridSize, gridColSize, i + x[k], j + y[k]);
    }
    return ans;
}
int maxAreaOfIsland(int** grid, int gridSize, int* gridColSize){
    int ans = 0;
    for (int i = 0; i < gridSize; ++i) {
        for (int j = 0; j < *gridColSize; ++j) {
            int t = dfs(grid, gridSize, gridColSize, i, j);
            ans = ans > t ? ans : t;
        }
    }
    return ans;
}

2.合并二叉树

【题目】

【分析】深度优先搜索

struct TreeNode* mergeTrees(struct TreeNode* root1, struct TreeNode* root2){
      if(!root1){
          return root2;
      }
      if(!root2){
          return root1;
      }
      struct TreeNode* merge=malloc(sizeof(struct TreeNode));
      merge->val=root1->val+root2->val;
      merge->left=mergeTrees(root1->left,root2->left);
      merge->right=mergeTrees(root1->right,root2->right);
      return merge;;
}

3.填充每一个节点的下一个右侧指针

【题目】

 

【分析】

法1:递归

void connectTwoNode (struct Node* a, struct Node* b){
    if (a == NULL || b == NULL) return;
    a->next = b;
    connectTwoNode(a->left,a->right);  //连接a子树的左右结点
    connectTwoNode(b->left,b->right);  //连接b子树的左右结点
    connectTwoNode(a->right,b->left);  //连接相邻子树的相邻结点
}
struct Node* connect(struct Node* root) {
    if (root == NULL) return NULL;
    connectTwoNode(root->left,root->right);
    return root;
}

法2:dfs递归

void dfs(struct Node *root)
{
    if (root == NULL)
        return; // dfs退出条件
    if (root->left)
    {// 左子结点的next比较容易
        root->left->next = root->right;
    }

    if (root->right)
    {// 右子结点稍微多想一点,利用父节点的next
        if (root->next == NULL)
        {
            root->right->next = NULL;
        }
        else
        {
            root->right->next = root->next->left;
        }
    }
    dfs(root->left);
    dfs(root->right);
}

struct Node *connect(struct Node *root)
{
    if (root == NULL)
        return NULL;
    root->next = NULL;
    dfs(root);
    return root;
}

法3:直接next指针递归

struct Node* connect(struct Node* root) 
{
    if(root == NULL || root->left== NULL)
    {
        return root;
    }
    //直接的利用next指针
    root->left->next = root->right;
    if(root->next)
    {
        root->right->next = root->next->left;
    }
    connect(root->left);
    connect(root->right);
    return root;
}


4.打家劫舍(动态规划)

【题目】

【分析】

int rob(int* nums, int numsSize){
    if(numsSize==1){
        return nums[0];
    }//如果只有一个元素,就可以直接输出

    int dp[numsSize];
//定义一个数组dp来记录走到当前时可以拿的最大数之和
    dp[0]=nums[0];
    dp[1]=fmax(nums[0],nums[1]);
//在dp[1]时就应该考虑nums[0]和nums[1]拿哪一个的问题了,因为dp记录的是最大值,
//nums[0]和nums[1]只能拿一个所以要进行一次比较

    for(int i=2;i<numsSize;i++){
    dp[i]=fmax(dp[i-2]+nums[i],dp[i-1]);
//由于在dp[i]时,你不能拿(dp[i-1]+nums[i])因为dp[i-1]中可能记录了nums[i-1]
//所以只有拿(dp[n-2]+nums[i])和继承dp[i-1]这两种选择,取大值即可
}
return fmax(dp[numsSize-2],dp[numsSize-1]);
//输出时不能忘了比较一下dp[numsSize-2]和dp[numsSize-1]因为这两个数是没有比较过的
}

5.三角形最小路径和(动态规划)

【题目】

 

【分析】

 

int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
          int dp[triangleSize][triangleSize];
//创建一个可以存放所有路径的空间
          memset(dp,0,sizeof(dp));
//初始化该空间,使初值都为0
          dp[0][0]=triangle[0][0];
//给dp空间的第一个空间赋值
          for(int i=1;i<triangleSize;i++){
              dp[i][0]=dp[i-1][0]+triangle[i][0];
//因为最左边和别的点不一样,最左边只能通过一个点赋值,所以就当前路径大小等于上面的点加当前的点
              for(int j=1;j<i;j++){
                  dp[i][j]=fmin(dp[i-1][j-1],dp[i-1][j])+triangle[i][j];
//由于不是最左的点,所以有两条路可以选,比较一下那一条路更小,再加上当前路
                }
              dp[i][i]=dp[i-1][i-1]+triangle[i][i];
//出循环后最右边的和最左边的点一样,都只有一个点能够给它赋值,所以不需要比大小,
//且如果把最右的点加入会越界
            }
            int ans=dp[triangleSize-1][0];
//创建一个变量用来存放答案,由于所有尽可能小的路径都在最后一排了所以从最后一排找就可以
//所以ans的初始值为最后一排的最左边
           for(int i=1;i<triangleSize;i++){
               ans=fmin(ans,dp[triangleSize-1][i]);
//通过比较得出最小的路径
            }
           return ans;
}

6.组合

【题目】给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

【分析】C++

回溯法

在这里要定义两个全局变量,一个用来存放符合条件单一结果,一个用来存放符合条件结果的集合。还需要一个参数,为int型变量startIndex,这个参数用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,...,n] )

class Solution {
private:
    vector<vector<int>> result; // 存放符合条件结果的集合
    vector<int> path; // 用来存放符合条件结果
    void backtracking(int n, int k, int startIndex) {
        if (path.size() == k) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i <= n; i++) {
            path.push_back(i); // 处理节点 
            backtracking(n, k, i + 1); // 递归
            path.pop_back(); // 回溯,撤销处理的节点
        }
    }
public:
    vector<vector<int>> combine(int n, int k) {
        result.clear(); // 可以不写
        path.clear();   // 可以不写
        backtracking(n, k, 1);
        return result;
    }
};

剪枝优化:

for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) // i为本次搜索的起始位置

7.存在重复元素

【题目】

【分析】

1.先排序在比较

int cmp(const void* _a, const void* _b) {
    int a = *(int*)_a, b = *(int*)_b;
    return a - b;
}

bool containsDuplicate(int* nums, int numsSize) {
    qsort(nums, numsSize, sizeof(int), cmp);
    for (int i = 0; i < numsSize - 1; i++) {
        if (nums[i] == nums[i + 1]) {
            return true;
        }
    }
    return false;
}

法2:哈希表

直接 set 记录是否出现过

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_set<int> s;
        for(int i:nums){
            if(s.count(i)){
                return true;
            }
            s.insert(i);
        }
        return false;
    }
};

8.移除元素

【题目】

【分析】

int removeElement(int* nums, int numsSize, int val) {
    int left = 0;
    for (int right = 0; right < numsSize; right++) {
        if (nums[right] != val) {
            nums[left] = nums[right];
            left++;
        }
    }
    return left;
}

9.Pow(x,n )

【题目】

【分析】

double myPow(double x, int n){
      if(n==0){
          return 1;
      }
      if(n==1){
          return x;
      }
      if(n==-1){
          return 1/x;
      }
      if(n%2!=0){
          return x*myPow(x,n-1);
      }else{
          return myPow(x*x,n/2);
      }

}

10.数组加1

【题目】

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头

【分析】

从后向前对数组进行遍历,若该元素为9,则继续向后遍历;否则,将该元素加一,返回数组长度不变返回数组,
若遍历到最后一个元素依然需要进位,创建一个新的数组返回最后的值,将新数组的第一位置为1

int* plusOne(int* digits, int digitsSize, int* returnSize){
    for(int i = digitsSize - 1;i >= 0;i--){
        if(digits[i] == 9)
            digits[i] = 0;
        else{
            digits[i]++;
            *returnSize = digitsSize;
            return digits;
       }
   }
    *returnSize = digitsSize + 1;
    int *ret = (int*)malloc(sizeof(int)*(digitsSize + 1));
    memset(ret,0,sizeof(int)*(digitsSize + 1));
    ret[0] = 1;
    return ret;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值