初级算法题(上)

概述

这是由 LeetCode 官方推出的经典面试题目清单,我们将题目重新整理规划,从而为大家提供更好的练习体验和帮助大家找到理想的工作。 我们将题目分为以下三个部分:

  • 初级算法 - 帮助入门
  • 中级算法 - 巩固训练
  • 高级算法 - 提升进阶

这一系列 LeetBook 将帮助您掌握算法及数据结构,并提高您的编程能力。

编程能力就像任何其他技能一样,也是一个可以通过刻意练习大大提高的。

大多数经典面试题目都有多种解决方案。 为了达到最佳的练习效果,我们强烈建议您至少将此清单里的题目练习两遍,如果可以的话,三遍会更好。

在第二遍练习时,你可能会发现一些新的技巧或新的方法。 到第三遍的时候,你会发现你的代码要比第一次提交时更加简洁。 如果你达到了这样的效果,那么恭喜你,你已经掌握了正确的练习方法!

数组

数组问题在面试中出现频率很高,你极有可能在面试中遇到。

我们推荐以下题目:只出现一次的数字,旋转数组,两个数组的交集 II 和 两数之和。

1.01 删除有序数组的重复项

给你一个升序排列的数组 nums ,请你原地删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。元素的相对顺序应该保持 一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。
更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。
将最终结果插入 nums 的前 k 个位置后返回 k 。
不要使用额外的空间,你必须在原地修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:
输入:nums = [1, 1, 2]
输出:2, nums = [1, 2, _]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例 2:
输入:nums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4]
输出:5, nums = [0, 1, 2, 3, 4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

//方法一调用系统库函数,测试通过
int removeDuplicates(vector<int>& nums) {
   
	int length = nums.size();
	if (length == 0 || length == 1) return length;
	auto x = unique(nums.begin(), nums.end());
	vector<int>::iterator it = nums.begin();
	int count = 0;
	while (it != x)
	{
   
		++count;
		it++;
	}
	return count;
}
// 方法二快慢指针,测试通过
int removeDuplicates(vector<int>& nums) {
   
	int length = nums.size();
	if (length == 0 || length == 1) return length;
	int slow = 1;
	int fast = 1;
	while (fast < length)
	{
   
		if (nums[fast] != nums[fast - 1])
		{
   
			nums[slow++] = nums[fast++];
		}
		else
		{
   
			++fast;
		}
	}
	return slow;
}

1.02 买卖股票的最佳时机II

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和 / 或出售股票。你在任何时候最多只能持有一股股票。你也可以先购买,然后在同一天出售。

返回你能获得的最大利润 。

示例 1:
输入:prices = [7, 1, 5, 3, 6, 4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
总利润为 4 + 3 = 7 。

示例 2:
输入:prices = [1, 2, 3, 4, 5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
总利润为 4 。

示例 3:
输入:prices = [7, 6, 4, 3, 1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。

// 方法一动态规划,测试通过
// 考虑两种情况,nohold当天交易结束未持有股票,可能是前一天没有持股今天也没有购买或者前一天持股今天抛售;
// hold当天交易结束持有股票,可能是前一天持股今天没有抛售或者前一天没有持股今天购买股票。
int maxProfit_II(vector<int>& prices) {
   
	int length = prices.size();
	if (length == 1) return 0;
	vector<vector<int> > dp(length, vector<int>(2));
	dp[0][0] = 0;
	dp[0][1] = -prices[0];
	for (int i = 1; i < length; ++i)
	{
   
		dp[i][0] = std::max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
		dp[i][1] = std::max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
	}
	return dp[length - 1][0];
}
// 方法二动态规划优化,测试通过
// 使用变量代替数组,思路同方法一完全相同
int maxProfit_II(vector<int>& prices) {
   
	if (prices.size() < 2) return 0;
	int length = prices.size();
	int hold = -prices[0];
	int nohold = 0;
	for (int i = 1; i < length; ++i)
	{
   
		nohold = std::max(nohold, hold + prices[i]);
		hold = std::max(hold, nohold - prices[i]);
	}
	return nohold;
}
// 方法二贪心算法,测试通过
// 在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。
int maxProfit_II(vector<int>& prices) {
   
	if (prices.size() < 2) return 0;
	int length = prices.size();
	int total = 0;
	for (int i = 0; i < length - 1; ++i)
	{
   
		total += std::max(0, prices[i + 1] - prices[i]);
	}
	return total;
}

1.03 旋转数组

给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:
输入: nums = [1, 2, 3, 4, 5, 6, 7], k = 3
输出 : [5, 6, 7, 1, 2, 3, 4]
解释 :
向右轮转 1 步 : [7, 1, 2, 3, 4, 5, 6]
向右轮转 2 步 : [6, 7, 1, 2, 3, 4, 5]
向右轮转 3 步 : [5, 6, 7, 1, 2, 3, 4]

示例 2 :
输入:nums = [-1, -100, 3, 99], k = 2
输出:[3, 99, -1, -100]
解释 :
向右轮转 1 步 : [99, -1, -100, 3]
向右轮转 2 步 : [3, 99, -1, -100]

// 方法一普通旋转,超时
// 计算余数可以大幅提高效率,但时间复杂度仍然高
void move_right(vector<int>& nums)
{
   
	int length = nums.size();
	int tmp = nums[length - 1];
	for (int i = length - 1; i > 0; --i)
	{
   
		nums[i] = nums[i - 1];
	}
	nums[0] = tmp;
}
void rotate(vector<int>& nums, int k) {
   
	int length = nums.size();
	if (length < 2) return;
	k = k % length;
	if (k >= 0)
	{
   
		while (k--)
		{
   
			move_right(nums);
		}
	}
}
// 方法二反转数列,测试通过
// On级别的时间复杂度
void ReverseArray(vector<int>& nums, int left, int right) {
   
	while (left < right) {
   
		std::swap(nums[left++], nums[right--]);
	}
}
void rotate(vector<int>& nums, int k) {
   
	int n = nums.size();
	k = n - (k % n);
	ReverseArray(nums, 0, k - 1);
	ReverseArray(nums, k, n - 1);
	ReverseArray(nums, 0, n - 1);
}

1.04 存在重复元素

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

示例 1:
输入:nums = [1, 2, 3, 1]
输出:true

示例 2:
输入:nums = [1, 2, 3, 4]
输出:false

示例 3:
输入:nums = [1, 1, 1, 3, 3, 4, 3, 2, 4, 2]
输出:true

// 方法一排序,测试通过
// 排完序两两比较
bool containsDuplicate(vector<int>& nums) {
   
	std::sort(nums.begin(), nums.end());
	for (int i = 0; i < nums.size() - 1; ++i)
	{
   
		if (nums[i] == nums[i + 1]) return true;
	}
	return false;
}
// 方法二哈希表,测试通过
// 时间复杂度较低
bool containsDuplicate(vector<int>& nums) {
   
	unordered_set<int> myset;
	bool res = false;
	for (auto x : nums)
	{
   
		auto flag = myset.find(x);
		if (flag == myset.end())
		{
   
			myset.insert(x);
		}
		else
		{
   
			res = true;
			break;
		}
	}
	return res;
}

1.05 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:
输入: [2, 2, 1]
输出 : 1

示例 2 :
输入 : [4, 1, 2, 1, 2]
输出 : 4

// 方法一哈希表,测试通过
// 记录每个数字出现的次数,然后查找只出现一次的数字
int singleNumber(vector<int>& nums) {
   
	unordered_map<int, int> mymap;
	for (auto x : nums)
	{
   
		auto res = mymap.find(x);
		if (res != mymap.end())
		{
   
			res->second += 1;
		}
		else
		{
   
			mymap.insert(std::pair<int, int>(x, 0));
		}
	}
	unordered_map<int, int>::iterator it = mymap.begin();
	for (; it != mymap.end(); ++it)
	{
   
		if (it->second == 0) break;
	}
	return it-&g
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值