写leetcode关于数组的题之心得

leetCode题号:1,两数之和:

        给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。

思路:①暴力deal法:就是遍历每一个nums数组的元素,然后对于每一轮的这个元素,分别往后去拿一个元素做加法 看是否 == target。一旦找到满足这个条件的两个元素(也就把下标i和j都返回即可)

代码:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        //vector<int> tempVec;
        int numsSize = nums.size();
        for(int i = 0;i < numsSize -1;i++)
        {
            for(int j = i+1;j < numsSize;j++)
            {
                if((nums[i] + nums[j]) == target)
                {
                    // tempVec.push_back(i);
                    // tempVec.push_back(j);

                    return {i,j};
                }
            }
        }
        return {};
    }
};

运行结果:

②用哈希表map在遍历数组的元素时就把每个元素都push进去map中,然后再下一轮遍历数组的元素时就判断哈希表map中是否含有target - nums[i] 的值,若存在,也即找到满足 nums[i]+nums[j] ==target的i和j了,当然这里并不存在j这个下标,这里只是为了好说这么叙述而已!,这样就可以不用O(n^2)的时间复杂度来deal了,用O(n)即可deal这个问题了!

代码:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        map<int, int> mp;
        vector<int> vec;// (2, -1);//预留2个空间,且对应的值为-1
        vec.reserve(2);
        int numsSize = nums.size();
        for (int i = 0; i < numsSize; i++)
        {
            if (mp.count(target - nums[i]) > 0)//找到2个数的和 == target了!
            {
                int t1 = mp[target - nums[i]];
                vec.push_back(t1);
                cout <<"vec[0]:"<< vec[0] << endl;
                int t2 = i;
                vec.push_back(i);
                cout << "vec[1]:" << vec[1] << endl;
                break;
            }
            mp[nums[i]] = i;
            //mp.insert({ nums[i],i });
        }
        return  vec;
    }
};

运行结果:

 so,很明显地,用哈希表可以节省大量的遍历对比的操作!从而节省了大量的时间!!!

leetCode题号:350,两数之交集(二):

        给定两个数组,编写一个函数来计算它们的交集。

        提示说明:

  • 输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。
  • 我们可以不考虑输出结果的顺序。

思路:

        ①暴力deal法(其实还是用了哈希表map):用2个map分别存储数组nums1和nums2中的元素以及其在map中所出现的次数。然后对于nums2数组而言,只要其每一轮的元素出现在了nums1所对应的map中时,此时求解这个元素在nums1和nums2中出现的次数(用一个迭代器it1->second it2->second即可拿到这个数字!),然后按照这个次数对应的插入到result数组中即可!

请看以下代码:

class Solution {
private:
    typedef map<int, int>::iterator mpIt;
    map<int,int> vectorTwoMap(vector<int>& nums)
//这个函数负责将2个数组的元素以及其出现的次数对应为(弄成)哈希表map
    {
        map<int,int> mp;
		for (int i = 0; i < nums.size(); i++) {
			//0代表没有存在过这种元素
			int tempCnts = 0;
			if (!mp.count(nums[i])) {	
				tempCnts++;
				mp[nums[i]] = tempCnts;
			}
			else {
				++mp[nums[i]];//相当于把it->second ++了!
			}
		}
        return mp;
    }
public:
	vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
		map<int,int> mp,mp2;
		vector<int> newVec;
		int MinSize = min(nums1.size(), nums2.size());
		newVec.reserve(MinSize);
        mp = vectorTwoMap(nums1);
        mp2 = vectorTwoMap(nums2);
		for (mpIt it2 = mp2.begin(); it2 != mp2.end();it2++) {
			mpIt it1 = mp.find(it2->first);
			if (it1 != mp.end()) {
				int minCnts = min(it2->second, it1->second);
				while (minCnts) {
					newVec.push_back(it2->first);
					minCnts--;
				}
			}
		}
		return newVec;
	}
};

运行结果:

从中我们可以看出来,这个暴力解决算法的空间复杂度非常高,不尽如人意! 因此下面看了官方的解析之后我写出了第二种算法

②哈希表map法(用一个哈希表map即可deal问题了):

用一个哈希表map来存储nums1和nums2中数组长度较小的那个数组的元素以及其在此数组中所出现的次数!为的就是节省空间!(你当然可以随便让数组1or数组2的元素插入map中,当然如果某个数组长度过大的话do插入操作实在了太耗时了,因此不这么干就是为了节省时间也节省哈希表所占用的空间)

再对长度较大的那个数组do一次遍历,每一次的遍历都判断其是否出现在(用mp.count(x)来判断)长度较小的那个数组中,若出现了,再判断其出现次数是否减减到0 了,若不是就把这个元素push进result数组中去,再继续对下一轮的元素的判别,这样就可以

自动地满足这个条件让重复元素出现在result数组中的个数==在2个数组中的出现的最小次数

请看以下代码:

(若后期你coding时看不懂时可以自己画个图举几个小例子理解一下就很容易搞懂了!)

class Solution {
public:
	vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
		
		map<int, int> mp;
		int minSize = min(nums1.size(), nums2.size());
		if (nums1.size() > nums2.size()) {
			return intersect(nums2, nums1);
		}
		vector<int> insertion;
		insertion.reserve(minSize);

		for (int num : nums1)
		{
			int t = 0;
			if (!mp.count(num)) {
				t++;
				mp.insert({ num ,t });
			}
			else {
				mp[num] = mp[num] + 1;
			}
		}//把数组长度较小的那个数组的元素以及其咋map中出现的次数放入map中!

		for (int j = 0; j < nums2.size(); j++)
		{
			if( mp.count(nums2[j]) && mp[nums2[j]] != 0)
			{
				insertion.push_back(nums2[j]);
				mp[nums2[j]]--;
			}
		}
		return insertion;
	}
};

运行结果:

从结果我们可以看出来空间复杂度没有刚才那么高了!这是良好的! 

leetCode题号:121,买卖股票的最佳时机:

        给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

思路:

        ①暴力deal法:就是遍历每一个prices价格数组中的每一天的价格,然后将其与其后的每一个元素do差,并返回这一轮这个价格下买卖股票的最大差值(也即是最大利润了!),一共用2层循环即可deal问题了!但是就是空间复杂度太高了,会超出leetcode编译器的时间限制!

请看以下代码:

class Solution {
public:
	int maxProfit(vector<int>& prices) {
		//对于每一个price 我都计算能获得的profits
		//1-先找到最小的那天的股票价格
		//2-然后再去找从这个最小股票的天数之后去找最大的股票价格的那一天
		//然后将1 和 2 的结果do差,也即用result2 - result1 == maxprofits!
		//遍历prices的每一个数组头元素,然后对于这一天买入股票求其能获得的maxprofits!
		//然后将all的profits都求出来对比一下,把最大的利润弄出来,并判断是否>0,if大于0就可以return
		//了,否则没法return
		multimap<int, int> mmp;
		for (auto i = 0; i < prices.size(); i++)
		{
			int tempMinIndex = i;
			int tempMaxIndex = tempMinIndex;
			if (tempMinIndex < prices.size())
			{
				for (auto i = tempMinIndex; i < prices.size(); i++)
				{
					if (prices[i] > prices[tempMaxIndex]) {
						tempMaxIndex = i;
					}
				}
			}
			int tempProfits = prices[tempMaxIndex] - prices[tempMinIndex];
			//cout << "第一天买入股票价格:" << prices[i] << "\t所能够获得的最大利润为:" << tempProfits << endl;
			mmp.insert({ tempMinIndex ,tempProfits });
		}

		multimap<int, int>::const_iterator it = mmp.begin();
		int tempMax = INT_MIN;
		while (it != mmp.end())
		{
			if (tempMax < it->second) {
				tempMax = it->second;
			}
			it++;
		}
		if (tempMax > 0)
			return tempMax;
		else
			return 0;
	}
};

运行结果:

 即便你用lc官方的暴力deal方法也是会超出时间的限制的!

思路:

       ②快慢指针思想法:定义,fast_i_index和slow_j_index,然后求其,但凡快指针所指向的数据比慢指针所指向的数据(也即prices[快or满指针下标])要大(此时也即买卖股票有利润赚的case),就可以记录这个ans,若快指针所指向的数据比满指针所指向的数据要小的话,就更新慢指针,不进行记录最大利润给ans的操作

注意(快慢指针的思想真的很有用!!!)

请看以下代码:

class Solution {
public:
	int maxProfit(vector<int>& prices) {
		if (prices.empty()) return 0;
		int ans = 0;
		int Size = (int)prices.size();
		int fast_i_index = 1;
		int slow_j_index = 0;
		while(fast_i_index < Size)
		{
			if (prices[fast_i_index] < prices[slow_j_index]) {
				slow_j_index = fast_i_index;//更新慢指针下标
			}
			else {
				ans = max(ans, prices[fast_i_index] - prices[slow_j_index]);
			}
			fast_i_index++;
		}
		return ans;
	}
};

运行结果:

leetCode题号:283,移动零元素:

        给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

思路:①临时栈deal法:就是来一个副助的栈,存储nums数组中的all非0元素,然后利用栈stack的后进先出的特点,把非0的元素数据覆盖回nums数组中去!(利用栈的后进先出的特点就不会改变原来nums数组当中非0元素的次序了)然后把剩下的没有覆盖到的index都赋值位0即可!

代码:

class Solution {
public:
	void moveZeroes(vector<int>& nums) {
		stack<int> myStk1;
		int vecSize = (int)nums.size();
		for (int i = vecSize - 1; i >=0; i--)
		{
			myStk1.push(nums[i]);
			if (myStk1.top() == 0)
			{
				myStk1.pop();
			}
		}
		int index = 0;
		while (index < vecSize && !myStk1.empty())
		{
			nums[index] = myStk1.top();
			myStk1.pop();
			index++;
		}
		while (index < vecSize)
			nums[index++] = 0;
	}
};

运行结果:

 

 

思路:②双指针思路法:就是来2个index,一个left(慢指针下标),一个right(快指针下标),让right专门用于指向nums数组中的非0数据(通过if语句判断一下即可),left指针下标用于交换元素。

(这个双指针思路的deal方法很巧妙的,需要你通过画图多试验几次才能够想出来!没事的,现在想不出来这些很正常,才入门嘛,多写多练多看leetcode的题解代码,迟早会熟练的!代码写千万遍,其意自现!)

代码:

class Solution {
public:
	void moveZeroes(vector<int>& nums) {
		//使用双指针法:
		//定义一个left 一个 right指针
		//只有当right是指向非0元素时,就让left的元素和right的元素交换,并让left++
		int vecSize = nums.size(); int left = 0, right = 0;
		while (right < vecSize)//当然,right是快指针,left是慢指针
		{
			if (nums[right]) {
				//注意啊!标准的算法头文件#include<algorithm>中已经包含了
				//标准的交换swap的头文件了!
				swap(nums[left], nums[right]);
				left++;
			}
			right++;
		}
	}
};

运行结果:

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fanfan21ya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值