可以边学边做的LeetCode题目:数组_C++

1.两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

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

只会存在一个有效答案

进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?

// 方法一 暴力枚举
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
		int len = (int)nums.size();
    	for(int i = 0; i < len ; i++){
            for(int j = i + 1; j < len; j++){
                if(target == nums[i] + nums[j]){
                    return{i, j}
                }
            }
            
        }
    return {};
	}
};
// 方法二 哈希表
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> hashtable;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = hashtable.find(target - nums[i]);	
            // 如果执行hashtable.find(x),哈希表中无x,就会返回hashtable.end()
            if (it != hashtable.end()) {
                return {it->second, i};
            }
            hashtable[nums[i]] = i; // 将数和下标存进哈希表
        }
        return {};
    }
};

补充:

C++中哈希表的操作

(1)创建哈希表

unordered_map<int,int> m; //<string,string>,<char,char>

(2)添加元素

// 1. insert函数
m.insert(pair<int,int>(1, 10));
m.insert(pair<int,int>(2, 20));

// 2. 数组的方法添加
m[3]=30;
m[4]=40;

(3)成员函数

// 1. begin(), end()
m.begin() // 指向哈希表的第一个容器
m.end()  //	 指向哈希表的最后一个容器,实则超出了哈希表的范围,为空
    
// 2. find()
m.find(2)  // 查找key为2的键值对是否存在 ,若没找到则返回m.end()
if(m.find(2)!=m.end()) // 判断找到了key为2的键值对,用 auto 关键字定义的变量来接收
    
// 3. count() 查找函数
// 查找哈希表中key为3和5的键值对,找到返回1,找不到返回0
m.count(3)  //返回 1
m.count(5)   //返回 0

// 4. size()
m.size() // 返回哈希表的大小
    
// 5. empty()
m.empty() // 判断哈希表是否为空,返回值为 true / false
    
// 6. swap()
// 交换两个哈希表中的元素,整个哈希表的键值对全部都交换过去
unordered_map<int,int> m1;
unordered_map<int,int> m2;
m1.swap(m2);
swap(m1,m2);

(4)遍历哈希表 

// 第一种遍历
unordered_map<int, int> count;
for (auto p : count) {
	int front = p.first;   // key
    int end = p.second;   // value
}

// 第二种遍历
unordered_map<int, int> count;
for(auto it=m.begin();it!=m.end();it++)
{
    int front = it->first;   // key
    int end = it->second;   // value
}

2.最接近的三数之和

给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。

返回这三个数的和。

假定每组输入只存在恰好一个解。

// 方法 排序 + 双指针
class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        // 对数组进行排序
        sort(nums.begin(), nums.end());
        int n = nums.size();
        // 1e7 == 一乘十的七次方,是一个整型
        int best = 1e7;
		
        // 这里是定义了一个匿名函数
        // 根据差值的绝对值来更新答案
        auto update = [&](int cur) {
            if (abs(cur - target) < abs(best - target)) {
                best = cur;
            }
        };

        // 枚举 a
        for (int i = 0; i < n; ++i) {
            // 保证和上一次枚举的元素不相等
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            // 使用双指针枚举 b 和 c
            int j = i + 1, k = n - 1;
            while (j < k) {
                int sum = nums[i] + nums[j] + nums[k];
                // 如果和为 target 直接返回答案
                if (sum == target) {
                    return target;
                }
                update(sum);
                if (sum > target) {
                    // 如果和大于 target,移动 c 对应的指针
                    int k0 = k - 1;
                    // 移动到下一个不相等的元素
                    while (j < k0 && nums[k0] == nums[k]) {
                        --k0;
                    }
                    k = k0;
                } else {
                    // 如果和小于 target,移动 b 对应的指针
                    int j0 = j + 1;
                    // 移动到下一个不相等的元素
                    while (j0 < k && nums[j0] == nums[j]) {
                        ++j0;
                    }
                    j = j0;
                }
            }
        }
        return best;
    }
};

补充:

C++中的 sort 函数

函数:sort()

头文件:#include<algorithm>

功能:对给定范围内的序列进行升序排序。sort(begin,end,cmp),cmp参数默认升序

示例:

#include<iostream>
#include<algorithm>
#include<string>

using namespace std;

int main(){
// 数组排序  
    int a[4] = {4, 2, 5, 1};
    // 升序
    sort(a, a + 4);
    // 降序
    sort(a, a + 4, greater<int>());
    
// 字符串排序
    string s("hello world");
    // 升序
    sort(s.begin(), s.end());
    // 降序,利用反向迭代器
    sort(s.rbegin(), s.rend());
    
// vector 进行排序
    sort(nums.begin(), nums.end());
        
    return 0;
}

C++中的 auto 关键字

auto:用来声明自动变量。它是存储类型标识符,表明变量(自动)具有本地范围,块范围的变量声明(如for循环体内的变量声明)默认为auto存储类型。

// 更新变量cur与target差值
        auto update = [&](int cur) {
            if (abs(cur - target) < abs(best - target)) {
                best = cur;
            }
        };

C++中的匿名函数

定义:

auto fp = [捕获参数列表](函数参数列表) mutable throw(异常类型)->返回值类型 {函数体语句};
[捕获列表] (形参列表) ->返回值类型
{
	函数体;
}
[](){};		// 匿名函数声明
[](){} ();	// 匿名函数的调用
// 注:在使用匿名函数时主要有以上两种方式:
// 第一种称之为匿名函数的声明,是在匿名函数作为参数时使用,类比普通函数的函数名
// 第二种称之为匿名函数的调用,是在直接调用匿名函数的方法。
捕获形式说明
[ ]不捕获任何外部变量(注意,可以使用全局变量和静态变量
[a, b…]默认以值得形式捕获指定的多个外部变量(用逗号分隔),如果引用捕获,需要显示声明(使用&说明符)
[this]以值的形式捕获this指针
[=]值的形式捕获所有外部变量(包括lambda所在类的this)
[&]引用形式捕获所有外部变量(同上)
[=, &x]变量x以引用形式捕获,其余变量以传值形式捕获
[&, x]变量x以值的形式捕获,其余变量以引用形式捕获

按引用捕获相当于按引用传递。这里需要说明的是,按值捕获相当于实参传递形参的过程,捕获的变量在匿名函数内部生成一份等值的拷贝。

示例:

//	按引用捕获外部变量
	int a = 10;
	[&]() { cout << a << endl; }();	
	[&]()        { a = 20; cout << a << endl; }();
	[&]() mutable{ a = 30; cout << a << endl; }();	
	cout << a << endl;
	// 输出 10 20 30 30
// 1. mutable 说明符只针对按值捕获的外部变量,按引用捕获的外部变量可以直接修改。 // 2. 修改按引用捕获的外部变量,原来的外部变量也会改变。

3.删除有序数组中的重复项

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

说明:

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

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

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

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

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}
// 方法 双指针
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
     int n = nums.size();
        if (n == 0) {
            return 0;
        }
        int fast = 1, slow = 1;
        while (fast < n) {
            if (nums[fast] != nums[fast - 1]) {
                nums[slow] = nums[fast];
                ++slow;
            }
            ++fast;
        }
        return slow;
    }
};

4.移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明:

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

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

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

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

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

// 方法一 双指针
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int n = nums.size();
        int left = 0;
        for (int right = 0; right < n; right++) {
            if (nums[right] != val) {
                nums[left] = nums[right];
                left++;
            }
        }
        return left;
    }
};
// 方法二 双指针优化
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int left = 0, right = nums.size();
        while (left < right) {
            if (nums[left] == val) {
                nums[left] = nums[right - 1];
                right--;
            } else {
                left++;
            }
        }
        return left;
    }
};

5.三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

// 方法 排序 + 双指针
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
                if (second == third) {
                    break;
                }
                if (nums[second] + nums[third] == target) {
                    ans.push_back({nums[first], nums[second], nums[third]});
                }
            }
        }
        return ans;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值