C++相关练习及详细讲解

面试榜单

NC65 斐波那契数列

题目描述:
在这里插入图片描述

分析:
使用递归求解,当输入参数等于1、2时返回整数1

class Solution {
public:
    int Fibonacci(int n) {
        if(n == 1 || n == 2)
        {
            return 1;
        }
        return Fibonacci(n-1) + Fibonacci(n-2);
    }
};

NC103 反转字符串

题目描述:
在这里插入图片描述

分析:
使用string迭代器逆向打印存入字符串返回

#include<string>
class Solution {
public:
    string solve(string str) {
        string s;
        string::reverse_iterator rit = str.rbegin();
        while(rit != str.rend())
        {
            s.push_back(*rit);
            ++rit;
        }
        return s;
    }
};

NC4 判断链表中是否有环

在这里插入图片描述

分析:
定义两个指针一开始都指向头节点,区别在于步长不同,一快一慢。
slow 指针每次向后移动一个位置,而fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与 slow 指针在环中相遇。
如果不存在环,则fast指针会指向nullptr,则返回false

class Solution {
public:
    int Fibonacci(int n) {
        if(n == 1 || n == 2)
        {
            return 1;
        }
        return Fibonacci(n-1) + Fibonacci(n-2);
    }
};
————————————————
版权声明:本文为CSDN博主「halooy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_49619206/article/details/134290089

NC141 判断是否为回文字符串

题目描述:
在这里插入图片描述

分析:
先找到首字符strat和最后一个字符end的位置
然后进行比较,如果不相等直接返回false
如果相等,则++start,–end
当start大于等于end时,则正常结束,跳出循环,返回true

class Solution {
public:
    bool judge(string str) {
        //首字符索引
        char start = 0;
        //最后一个字符索引
        char end = str.size()-1;
        while(start < end)
        {
            if(str[start] != str[end])
            {
                return false;
            }
            ++start;
            --end;
        }
        return true;
    }
};

NC151 最大公约数

题目描述:
在这里插入图片描述

分析:
定义一个变量tmp用来存储a,b中较小的数。
然后用a,b对tmp进行取余,如果有余数,则对tmp进行–操作。
直到被a,b两数整除则为a,b的最大公约数。返回该值。

class Solution {
public:
    int gcd(int a, int b) {
        int tmp = (a>b)? b:a;
        while(tmp)
        {
            if(a%tmp == 0 && b%tmp == 0)
            {
                return tmp;
            }
            --tmp;
        }
        return 1;
    }
};

NC7 买卖股票的最好时机(一)

题目描述:
在这里插入图片描述

分析1(动态规划):

思路:对于每一天都有两个状态:到此为止的最大收益和是否持股。
因此用:
dp[i][0]表示第i天不持股到该天为止的最大收益,
dp[i][1]表示第i天持股,到该天为止的最大收益。
然后对于之后的每一天,都根据前一天的情况进行赋值。
如果当天不持股,有可能是前面的若干天中卖掉了或是还没买,因此到此为止的总收益和前一天相同,也有可能是当天才卖掉;
如果当天持股,有可能是前面若干天中买了股票,当天还没卖,因此收益与前一天相同,也有可能是当天买入,此时收益为负的股价。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(2, 0));
        //第一天不持股收益
        dp[0][0] = 0;
        //第一天持股收益
        dp[0][1] = -prices[0];

        for(int i = 1; i < n; ++i)
        {
            //第i天不持股最大收益:
            //1.前一天也不持股,前几天卖掉或者没买,收益和前一天相同
            //2.前一天持股,第i天当天抛出,收益为前一天持股加上当天抛出
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);
            //第i天持股最大收益:
            //1.前一天不持股,第i天当天买入,收益为当天买入价格的负数
            //2.前一天持股,收益和前一天相同
            dp[i][1] = max(-prices[i], dp[i-1][1]);
        }
        //最后一天不持股,到改天为止的最大收益
        return dp[n-1][0];
    }
};

分析2(贪心思想):
思路:如果在某一天卖出了股票,那么要想收益最高,一定是它前面价格最低的那天买入的股票才可以。因此可以利用贪心思想解决,每次都将每日收入与最低价格相减维护最大值。
将第一天看成价格最低,后续遍历的时候遇到价格更低则更新价格最低
每次都比较最大收益与当日价格减去价格最低的值,选取最大值作为最大收益

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param prices int整型vector 
     * @return int整型
     */
    int maxProfit(vector<int>& prices) {
        // write code here
        int n = prices.size();
        if(n == 0)
            return 0;
        //最大收益
        int maxProfit;
        //当天最大收益
        int max_td;
        //截至当天最低价格,将第一天看作价格最低
        int lessPrice = prices[0];
        //买入必须是卖出前一天
        maxProfit = 0;
        for(int i = 1; i < n; ++i)
        {
            //第i天的最大收益,当天价格减去先前最低价格
            max_td = prices[i]-lessPrice;
            //如果第i天的最大收益比之前的大,则更新最大收益
            maxProfit = max(maxProfit, max_td);
            //如果先前最低价格大于第i天的价格,则更新最低价格
            //由于买入必须是卖出前一天,因此先计算第i天的最大收益后再更新最低价格
            lessPrice = min(lessPrice, prices[i]);
        }
        return maxProfit;
    }
};

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

题目描述:
在这里插入图片描述

分析1(递归):
从根节点遍历到叶子,在根节点每次往下一层的时候,将sum减去节点值,最后检查是否完整等于0。

终止条件: 每当遇到节点为空,意味着过了叶子节点,返回。每当检查到某个节点没有子节点,它就是叶子节点,此时sum减去叶子节点值刚好为0,说明找到了路径。
返回值: 将子问题中是否有符合新目标值的路径层层往上返回。
本级任务: 每一级需要检查是否到了叶子节点,如果没有则递归地进入子节点,同时更新sum值减掉本层的节点值。

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 *	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param root TreeNode类 
     * @param sum int整型 
     * @return bool布尔型
     */
    bool hasPathSum(TreeNode* root, int sum) {
        // write code here
        if(root == nullptr)
            return false;
        if(root->left == nullptr && root->right == nullptr && sum-root->val == 0)
            return true;

        return hasPathSum(root->left,sum-root->val) || hasPathSum(root->right,sum-root->val);
    }
};

分析2(深度优先搜索dfs):
首先检查空间点,空树没有路径;
栈每次保存一个pair类型数据,包含两个值,一个值记录节点,一个值记录到该节点位置的路径和;
遍历过程中,每次弹出栈顶元素,判断是否是叶子节点并且路径和是否等于目标值;
没有到叶子节点就将左右子节点(如果有)加入栈中,并跟随加入路径和;
如果遍历结束也没有找到路径和,则该二叉树中没有。

/**
 * struct TreeNode {
 *  int val;
 *  struct TreeNode *left;
 *  struct TreeNode *right;
 *  TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 * };
 */
#include <cstdio>
class Solution {
  public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param root TreeNode类
     * @param sum int整型
     * @return bool布尔型
     */
    bool hasPathSum(TreeNode* root, int sum) {
        // write code here
        if (root == nullptr)
            return false;
        //建立栈
        stack<pair<TreeNode*, int>> s;
        //将根节点及根节点的值压入
        s.push({root, root->val});
        while (!s.empty()) 
        {
            //给栈顶元素赋值
            auto temp = s.top();
            //去除栈顶元素
            s.pop();
            //如果栈顶元素的左右子树都空,并且累加值等于sum则返回true
            if (temp.first->left == nullptr && temp.first->right == nullptr && temp.second == sum) 
            {
                return true;
            }
            //如果左子树非空,则将左子树压入,并将左子树的值进行累加
            if (temp.first->left != nullptr) 
            {
                s.push({temp.first->left, temp.second + temp.first->left->val});
            }
            //如果右子树非空,则将右子树压入,并将右子树的值进行累加
            if (temp.first->right != nullptr) 
            {
                s.push({temp.first->right, temp.second + temp.first->right->val});
            }
        }
        return false;
    }
};

NC11 将升序数组转化为平衡二叉搜索树

题目描述:
在这里插入图片描述

分析(递归):
返回值为当前节点为根节点的平衡二叉树;
由于给定一个升序数组,递归函数的参数应为数据的左右边界;
根节点数值应为给定边界的中点的值;
递归左右子树建树。

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 *	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型vector 
     * @return TreeNode类
     */
     TreeNode* dfs(vector<int>& nums, int left, int right)
     {
        //结束条件:
        if(left > right)
            return nullptr;
        //定义数据中间位置:即根节点
        int mid = (left + right + 1)/2;
        //根据mid值创建根节点
        TreeNode* root = new TreeNode(nums[mid]);
        //遍历左子树
        root->left = dfs(nums, left, mid-1);
        //遍历右子树
        root->right = dfs(nums, mid+1, right);

        return root;
     }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        // write code here
        int len = nums.size();
        return dfs(nums, 0, len-1);
    }
};

others

输出数组中第k小的数

题目描述:
给定一个数组arr 输出数组中第k小的数
如果不存在 输出-1
输入格式:
第一行输入一个数字n 代表数组arr大小
第二行依次输入n个数
第三行输入一个数字k
输出格式:
输出数组中第k小的数 如果不存在 输出-1
测试样例:
在这里插入图片描述

分析:
根据set容器去重的特性以及set容器内部按照从小到大的顺序排列
将数组内所有元素输入到set容器内,然后对set迭代器进行遍历,如果遍历到第k个元素存在则存在第k小的数,进行输出;如果不存在,也就是迭代器到end的位置,则输出-1。

举例:
数组元素:3 3 2
set容器:2 3
如果k=2,则输出3
如果k=3,此时set迭代器遍历到end()的位置,不存在该数据,则输出-1

#include<iostream>
#include<set>

using namespace std;
int main()
{
	//数组大小
	int n;
	//定义第k小的数
	int kmin;
	cin >> n;
	int *arr = new int[n];
	//给数组元素传值
	for (int i = 0; i < n; ++i)
	{
		cin >> arr[i];
	}
	//数组传入set容器
	set<int> s(arr, arr + n);
	cin >> kmin;
	//用来记录遍历位置
	int count = 1;
	set<int>::iterator it = s.begin();

	while (it != s.end())
	{
		//如果第k小的数存在则输出该值,跳出循环
		if (count == kmin)
		{
			cout << *it;
			break;
		}
		++it;
		++count;
	}
	//如果循环正常结束,则第k个值不存在,输出-1
	if (it == s.end())
	{
		cout << -1 << endl;
	}
	delete[] arr;

	return 0;
}

在数组内找出查找数字在该数组第一次出现的索引

题目描述
给定一个长度为N的单调不递减整数数组 arr,进行Q次询问。对于每次询问,给出一个整数X,要求找出X在arr中第一次出现的下标,如果X未在数组中出现过,则输出-1。
输入格式
第一行输入两个整数N,Q 其中N 代表数组 arr 长度,Q代表询问次数
第二行输入N个整数 用空格分开
第三行输入Q个整数 用空格分开
输出格式
在一行中输出Q次查询的结果 每个整数用空格分开

分析:
用三个数组分别记录要查询的值,被查询的值,值在给定数组的索引。
首先给出最简单遍历的方法,该方法时间复杂度较高

int main()
{

    int n,q;
    cin >> n >> q;
    //数组元素放入arr
    int* arr = new int[n];
    for(int i = 0; i < n; ++i)
    {
        cin >> arr[i];
    }
    
    //查询元素放入arr_q
    int* arr_q = new int[q];
    for(int i = 0; i < q; ++i)
    {
        cin >> arr_q[i];
    }
	//查询结果放入result
    int* result = new int[q];
    for(int i = 0; i < q; ++i)
    {
        int Q = arr_q[i];
        int idx = -1;
        for(int j = 0; j < n; ++i)
        {
            if(arr[j] == Q)
            {
                idx = j;
                break;
            }
        }
        result[i] = idx;
    }

    for(int i = 0; i < q; ++i)
    {
        cout << result[i] << " ";
    }
    cout << endl;
    
	delete[] arr;
    delete[] arr_q;
    delete[] result;
	return 0;
}

方法二:
上述方法在面对数据量大时时间复杂度过高,可以使用二分查找降低查询次数。

#include<iostream>

using namespace std;

int binarySearch(int* arr, int n, int Q)
{
    int left = 0;
    int right = n - 1;
    int idx = -1;

    while(left <= right)
    {
        int mid = (right + left)/2;
        if(arr[mid] == Q)
        {
            idx = mid;
            right = mid - 1;
        }
        else if(arr[mid] > Q)
        {
            right = mid - 1;
        }
        else if(arr[mid] < Q)
        {
            left = mid + 1;
        }
    }
    return idx;
}

int main()
{

    int n,q;
    cin >> n >> q;
    //数组元素放入arr
    int* arr = new int[n];
    for(int i = 0; i < n; ++i)
    {
        cin >> arr[i];
    }
    
    //查询元素放入arr_q
    int* arr_q = new int[q];
    for(int i = 0; i < q; ++i)
    {
        cin >> arr_q[i];
    }

    int* result = new int[q];
    for(int i = 0; i < q; ++i)
    {
        int Q = arr_q[i];
        int idx = binarySearch(arr, n, Q);
        result[i] = idx;
    }

    for(int i = 0; i < q; ++i)
    {
        cout << result[i] << " ";
    }
    cout << endl;
    
    delete[] arr;
    delete[] arr_q;
    delete[] result;

    return 0;
}

计算每个节点的字数包含节点数

题目描述
给定一棵以1为根,包含N个节点的树,每个节点都有一个唯一的编号。请计算每个节点的子树中包含的节点数,包括节点本身。
输入格式
第一行包含一个正整数n,表示树的节点个数。
接下来的n-1行,每行包含一个正整数p,表示节点i的父节点的值。
输出格式
n个整数,每个整数用空格分开
第i个数为节点i的子节点个数(包括他自己)
在这里插入图片描述
在这里插入图片描述

分析:
通过建立二维数组
第一维存储树的每个节点
第二维存放每个节点的子节点
通过遍历树的节点来解决,统计每个节点的子节点数(包括自身)。使用深度优先搜索(DFS)算法。

#include<iostream>
#include<vector>

using namespace std;
//存储每个节点
vector<vector<int>> tree;
//存储每个节点及其子树的个数
vector<int> subtree_size;

void dfs(int node)
{
    subtree_size[node] = 1;

    for(int child : tree[node])
    {
        dfs(child);
        subtree_size[node] += subtree_size[child];
    }
}

int main()
{
    //树的节点个数
    int n;
    cin >> n;
    //存储每个节点
    tree.resize(n+1);
    //存储每个节点及其子树的个数
    subtree_size.resize(n+1);

    int parent;
    for(int i = 2; i <= n; ++i)
    {
        cin >> parent;
        tree[parent].push_back(i);
    }

    dfs(1);

    for(int i = 1; i <= n; ++i)
    {
        cout << subtree_size[i] << " "; 
    }
    cout << endl;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值