【贪玩巴斯】力扣 (LeetCode)刷题——算法入门 每日一练 【删除重复项、买卖股票的最好时机 II、旋转数组、存在重复元素、只出现一次的数字、两个数组的交集】2021-10-24

——转载自力扣 (LeetCode)
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/x2gy9m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

题目一:

题目:删除排序数组中的重复项

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

说明:
为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

示例 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 。不需要考虑数组中超出新长度后面的元素。

提示:

0 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums 已按升序排列

C++源程序:

class Solution {
public:
int removeDuplicates(vector<int>& nums){
    // 如果为空的判断
    if(nums.empty()){
        return 0;
    }

    int index = nums.size(); // 获取长度
    int sum = 1; // 为数组最后输出的个数数量
    int a = 1;   // 为构造最后输出数组的第一个下标

    for(int i = 0; i < index - 1; i++) // 遍历vector数组
    {
        if(nums[i+1] == nums[i]) // 如果右移动的值 == 左移动的值,则跳出本次循环,执行下一次循环
        {
            continue;
        }
        else{  // 如果右移动的值 不等于,则把右移动的
            nums[a] = nums[i+1];
            a++;
            sum++;
        }
    }
    return sum;
    }
};

因为数组第一个值本身存在,不管是否会和第二个值相等或不相等,第一个的值都不会改变,所以没有考虑第一个值的改变,直接就是 nums[a] = nums[i+1]; 第二个的值赋进去

个人心得:

题目讲解:

此题首先关键点——有序数组。
我们只需要前后两两比较,那么前后两两比较一个最简单的方法就是——
双指针法来解决:
使用两个指针,右指针始终往右移动,
如果右指针指向的值等于左指针指向的值,左指针不动。
如果右指针指向的值不等于左指针指向的值,那么左指针往右移一步,然后再把右指针指向的值赋给左指针。

涉及知识:

  1. empty()函数
    C++中empty()作为判断容器是否为空的函数;
    若变量已存在、非空字符串或者非零,则返回 false 值;若变量不存在则返回 TRUE。所以,当字符串的值为0时,也返回true,就是执行empty内部的语句。这就是陷阱。
    注意:
    若变量存在且其值为"“、0、 “0” 、NULL、FALSE、 array()、var $var; 以及没有任何属性的对象,则返回 TURE
    若变量存在且值不为”"、0、 “0” 、NULL、FALSE、 array()、var $var; 以及没有任何属性的对象,则返回 FALSE
string s;
if (s.empty())
{
cout << “字符串为空”;
}
else
{
    cout << "字符串不为空"}
  1. nums.size()函数
    获得长度:nums.size()

  2. continue
    continue语句的作用是跳过本次循环体中余下尚未执行的语句,立即进行下一次的循环条件判定,可以理解为仅结束本次循环。
    注意:continue语句并没有使整个循环终止。

题目二:

题目:买卖股票的最佳时机 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 。

示例 2:
输入: prices = [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:
输入: prices = [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

1 <= prices.length <= 3 * 104
0 <= prices[i] <= 104

相关标签:
贪心
数组
动态规划

C++源程序:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int num = prices.size(); // 获取有几天的天数
        int sumprofit = 0; // 利润总和
        for(int i = 1; i < prices.size(); i++){
            if(prices[i] > prices[i-1])            // 下标为0表示第一天
                {
                    int profit = prices[i] - prices[i-1];
                    sumprofit += profit;
                }
        
    }
    return sumprofit;
    }
};

个人心得:

此题关键在于一点,就是已经知道每一天股票的价格,那么我们只需要for循环所有天数,并前后两天的价格进行比较,如果后一天的大于,那么我们卖出去,相减得到利润,如果不大于,则继续往后,再次遇到后一天的大于前一天的价格,则相减得到利润。这样将利润加起来即是最大利润和。如果是连续的也无所谓,因为——比如 1234 这样的4天,2-1 + 3-2 + 4-3 是等于 4 - 1的。所以可以每次都比较相减。

涉及知识:

  1. prices.size()
    获取vector容器即数组的长度。

题目三:

题目:旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

示例 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]

提示:
1 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 105

相关标签:
数组
数学
双指针

C++源程序

class Solution {
public:
    void rotate(vector<int>& nums, int k) {     
        int length = nums.size();
        vector<int> newArr(length); // 构建一个新数组 这里涉及到vector用法可以看后面的 涉及知识有详细讲解。
        for(int i = 0; i < length; i++){
            newArr[(i+k) % length] = nums[i]; // 这里的意思就是说,当 移动 不超过长度时候,取余结果直接为移动到达的下标位置。如果超过了,可以通过取余得到位移后的下标位置。
    }
     nums.assign(newArr.begin(), newArr.end()); // assign函数
}
};

个人心得:

这道题我做过两次了,其中最关键的地方莫过于(i+k) % length 这个算法点,利用取余来进行得到下标位置。
另一个重要点在于——考虑构建一个新的vector数组。

涉及知识:

1. vector newArr(length)

  • Vector<类型>标识符(最大容量,初始所有值)

以及vector< vector > dp(m, vector(n) )

常见定义方法:

(1) vector a(5);
//定义了5个整型元素的向量(<>中为元素类型名,它可以是任何合法的数据类型),但没有给出初值,其值是不确定的。
(2)vector a(5,1); //定义了5个整型元素的向量,且给出每个元素的初值为1
(3)vector
a(b); //用b向量来创建a向量,整体复制性赋值
(4)vector a(b.begin(),b.begin+3);
//定义了a值为b中第0个到第2个(共3个)元素 (5)int b[7]={1,2,3,4,5,9,8};
vector a(b,b+7); //从数组中获得初值

vector< vector > v(m, vector(n) ); 定义了一个vector容器,元素类型为vector,初始化为包含m个vector对象,每个对象都是一个新创立的vector对象的拷贝,而这个新创立的vector对象被初始化为包含n个0。

2. nums.size()

  • 获得数组长度

3. num.assign()函数

  • assign赋值方法,释放了原来的数据空间,并分配了新的数据空间
  • assign操作不适用与关联容器和array;

assign的3种赋值操作;

  • seq.assign(b, e);
  • 将seq中的元素替换为迭代器b和e所表示的范围中的元素,迭代器不能指向seq中的元素;assign操作后可将l1内容赋值给l2;

list<char *> l1 = {“a”, “an”, “the”};
vector l2;
l2.assign(l1.begin(), l1.end()); b和e也可表示子范围的元素

list<char *> l1 = {“a”, “an”, “the”};
vector l2;
l2.assign(l1.begin(), l1.begin()+1);

  • seq.assign(i1);
  • 将seq中元素替换为初始化列表i1中的元素;

string l1 = (“a”, “an”, “the”);
vector l2;
l2.assign(l1);

  • seq.assign(n, t)
  • 将seq中元素替换为n个值为t的元素

vector l2;
l2.assign(10,“hi”);

题目四:

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/x248f5/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

题目:存在重复元素

给定一个整数数组,判断是否存在重复元素
如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。

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

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

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

相关标签
数组
哈希表
排序

C++源程序:

// #include<unorderd_set> 声明头文件
class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
            // 构建哈希表
        unordered_set<int> un;
        for (int x: nums){
        // 如果find()找到哈希表中有相等元素,那么返回一个迭代器。那么if语句条件成立,执行return语句。 即true 说明有重复元素
            if(un.find(x) != un.end()){
                return true; // 有相同元素
            }
            un.insert(x);
        }
        return false; // 无重复元素
    }
};
// 构建了哈希表来和原数组对比,核心思想就是在于两个数组的对比,这在数组类的算法题中很常见。

个人心得:

首先,这道题读起来很简单,意思就是数组内不能有重复的数值,那么我们就可以考虑一个——哈希表来存储遍历的数组,再使用哈希中的find函数来进行判断!
构建了哈希表来和原数组对比,核心思想就是在于两个数组的对比,这在数组类的算法题中很常见。

涉及知识:

1. unordered_set
使用前要先#include<unordered_set>
声明变量:unordered_set myset
开始:myset.begin()
结束:myset.end()
插入:myset.insert()
查询位置:myset.find()
删除:myset.erase()
对特定元素进行基数:myset.count(),如果存在就返回1,否则返回0
2.unordered_set中的find()函数详解
find()是会返回一个迭代器,这个迭代器指向和传入参数哈希值一样匹配的元素,如果没有匹配元素,那么会返回这个容器的结束迭代器——myset.end()。
所以在这道题中,我们使用了un.find(x) != un.end() 来进行判断。
3. int x: nums
for (auto x : nums)的作用:相当于:
for (vector< int >::iterator iter = nums.begin(); iter != nums.end(); iter++)

for(auto a:b)中b为一个容器,效果是利用a遍历并获得b容器中的每一个值,但是a无法影响到b容器中的元素。
for(auto &a:b)中加了引用符号,可以对容器中的内容进行赋值,即可通过对a赋值来做到容器b的内容填充。

题目五:

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/x21ib6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

题目:只出现一次的数字

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

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

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

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

相关标签
位运算
数组

C++源程序:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for(int x : nums)
        {
            ret ^= x;
        }
        
        return ret;
        }    
        
};

个人心得:

本题涉及到一个可以运用到的知识点——就是异或。
异或的意思是:
一个数和相同的数的异或 = 0;
一个数和0的异或 = 这个数本身。

涉及知识:

  1. 异或的定义

1 ⊕ 1 = 0

0 ⊕ 0 = 0

1 ⊕ 0 = 1

0 ⊕ 1 = 1

  1. 异或的两个特征

根据定义我们很容易获得异或两个特性:

恒等律:X ⊕ 0 = X 归零律:X ⊕ X = 0

题目六:

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/x2y0c2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
C++源程序:

题目:两个数组的交集 II

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

示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]

示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]

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

进阶:
如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果 nums2 的元素存储在磁盘上,内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

相关标签
数组
哈希表
双指针
二分查找
排序

C++源程序:

#include <algorithm> // 使用算法
class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int> target; // 定义一个新的数组容器来存储
        sort(nums1.begin(), nums1.end());  // 进行排序 从小到大
        sort(nums2.begin(), nums2.end());  // sort默认升序
        for(vector<int>::iterator it1 = nums1.begin(), it2 = nums2.begin(); it1 != nums1.end() && it2 != nums2.end();)
        {
            if( *it1 < *it2 )
                it1++;   // 解引用 如果第一个数字小于了
            else if( *it1 == *it2)
            {
                target.push_back(*it1);  // 如果相等,那么说明是交集元素,则存入创建的数组中。
                it1++;
                it2++;
            }
            else if (*it1 > *it2)
                it2++; // 永远是小的数向后移,因为我们使用了sort来排序!
        }
        return target;
    }
};

个人心得:

  • 这是一道数组的题,那么我们可以很清楚地想到数组的两种常用方法——构建新数组和使用双指针的方法。
  • 构建新数组,即是用一个新数组来存储我们需要满足条件的数据。
  • 双指针方法,即是用两个指针分别指向不同的数组的数值,并进行移动。

涉及知识:

1.algorithm 头文件

  • algorithm意为"算法",是C++的标准模版库(STL)中最重要的头文件之一,提供了大量基于迭代器的非成员模板函数。

类 别 C++标准库
头文件 #include
命名空间 using namespace std

-其中包括以下部分函数:

max()、
min()和abs()
swap()
reverse()
next_permutation()
fill()
sort()
lower_bound()和upper_bound()

2.sort()

  • sort 函数

  • sort函数可以三个参数也可以两个参数,必须包含头文件
    #include < algorithm>
    using namespace std;

  • 它使用的排序方法是类似于快排的方法,时间复杂度为o(nlog(n))

  • Sort函数有三个参数:(第三个参数可不写)
    (1)第一个是要排序的数组的起始地址
    (2)第二个是结束的地址(最后一位要排序的地址)
    (3)第三个参数是排序的方法,可以是「降序」也可是「升序」,
    第三个参数可省略,此时默认的排序方法是「升序」排序。

3.*it
解引用 把迭代器地址指向的值解出来

4.push_back
push_back()函数的用法
函数将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素
push_back() 在Vector最后添加一个元素(参数为要插入的值)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

贪玩巴斯

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

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

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

打赏作者

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

抵扣说明:

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

余额充值