赞
踩
题目描述
在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]]
给定 target = 7,返回 true。
给定 target = 3,返回 false。
数据范围:矩阵的长宽满足 0<=n, m<=500 , 矩阵中的值满足 0≤val≤10^9
进阶:空间复杂度 O(1) ,时间复杂度 O(n+m)示例1
输入:
7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]返回值:
true说明:
存在7,返回true示例2
输入:
1,[[2]]返回值:
false示例3
输入:
3,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]返回值:
false说明:
不存在3,返回false
这题有个解法:
暴力搜索
解题思路: 逐行逐列的搜索二维数组,判断是否存在目标值。复杂度分析:
时间复杂度:O(MN)
空间复杂度:O(1)
class Solution { public: bool Find(int target, vector<vector<int> > array) { for(auto i : array)//c++11语法 { for(auto j : i) { if(j == target) return true;//找到目标值 } } return false; } };
其中使用了auto
auto : 类型推导. 在使用c++的时候会经常使用, 就像在考虑STL时迭代器类型, 写模板的时候使用auto能少写代码, 也能帮助我们避免一些隐患的细节.
auto
型别推导要求必须在定义时初始化, 毕竟需要根据对象的类型推导左值对象的型别.auto
型别推导会忽略引用和顶层const, 所以要对对象加上想要的修饰.()
和=
对变量初始化, C++11增加了对定义的对象初始化的方法,可以使用{}
对变量初始化早在C++ 98标准中就存在了auto关键字,那时的auto用于声明变量为自动变量(自动变量在控制流进入变量作用域时系统自动为其分配存储空间,并在离开作用域时释放空间),拥有自动的生命周期。但是该作用是多余的,变量默认拥有自动的生命周期。
- int a = 10; // 自动生命周期
- auto int a = 10; // 自动生命周期
在C++ 11中,已经删除了该用法,取而代之的作用是:自动推断变量的类型。
自从C++ 11开始,编译器可以使用auto关键字自动推断被初始化的变量的类型。auto关键字也被称为占位类型说明符(placeholder type specifier)。
auto
从初始值的类型推断变量本身的类型,变量得到一个初始值的副本。
- int i;
- auto j = i; // j is int contains a copy of i
auto
会将引用类型推断为值类型。为了确保是引用类型,使用auto&
。
- int m = 0;
- int& i = m; // i is an alias to m
- auto j = i; // j is int contains a copy of i
- auto& k = m; // k is int& and alias to m
auto
不能推断出const
修饰符,为了确保可以推断出,使用const auto
。
- int i =0;
- const int m = 0;
- auto j = m; // j is int (non-const)
- const auto k = m; // k is const int
- const auto l = i; // l is const int, holds a copy of i
auto&
可以推断出const
修饰符。
- const int i =0;
- auto& j = i; // const int&, alias for i
auto
可以识别出以下类型:
- auto i = 1; // int
- auto j= 7ul; // unsigned long
- auto x = 2.0; // double
- auto y = 2.0f; // float
- auto c = 'A'; // char
- auto s = "hi"; // char const*
- auto b = true; // bool
从C++ 14开始,可以识别出字符串常量:
- using namespace std; // This is necessary
- auto s = "hellow"s // std::string, note the s operator.
从C++ 23开始,可以识别出size_t
和有符号的size_t
类型。
- auto k = 1uz; // size_t
- auto m = 1z; // signed size_t
可以识别出自定义的类:
- class LongNameClass {};
- auto a = new LongNameClass(); // a is LongNameClass*
可以识别出list类型:
auto l = {1,2,3}; // l is std::initializer_list<int>
类型名字比较长的类型也可以被auto
轻松代替:
- std::map<std::string, std::string> m;
- auto n = m;
-
- auto p = std::make_unique<int>(); //unique pointer
-
- std::vector<double> vec(100);
- auto& r = vec;
vector的iterator类型也可以被代替:
- // type of i is std::vector<double>::iterator
- for (auto i = vec.begin(); i != vec.end(); i++){
- cout<< *i;
- }
也可以很容易获得vector中的元素:
- std::vector<unsigned long long int> vec = {1, 2, 3};
-
- for (auto& x : vec)
- cout << x ;
auto
最常见的就是与for
联用, 特别是类型特别复杂的时候. 但是auto又有多种选择, 如 : auto, auto &等, 不同的选择其效率也不一样.
auto
, 即 for(auto i:range) . 这使range中的每一个元素都会产生一个副本, 所以即使修改了 i 也不会实际影响到range.const auto
, 及for(const auto i : range). 这也会是range的每一个元素产生一个副本, 但是这个副本竟不能被修改.auto &
, 即for(auto &i : range). 引用, 因为i 直接引用range里面的元素, 所以并不会产生一个副本, 但是 i 的修改也会影响range里元素的值. 通常我们需要修改range是会考虑用到.const auto&
, 即for(const auto &&i : range). i 直接引用range里面的元素, 所以并不会产生一个副本, 并且i 也不能修改. 一般初始化的是一个左值时而且是读取range里的元素时都是用const auto&
而不用auto
, 因为前者不会产生副本, 效率要高. 当然一般初始化的是一个左值时效率低, 但是如果是右值还是使用const auto
效率高, 因为const auto &
需要把 i 存储在内存中的一个位置,间接访问会更消耗时间auto&&
, 即for(auto &&i : range). 如果初始化是左值, 那么 i 就是左值引用, 如果初始化是右值, 那么 i 就是右值引用,还有const auto &
, 当然具体的选择还是看具体的情况而定.
最后, 当用auto
推导多维数组的时, 保证除最内层循环外, 其他的外层循环都应该是引用类型, 否则很容易出错, 即 :
- int a[10][10][10];
- for (const auto &i : a)
- for(const auto &j : i)
- for(const auto k : j)
- ;
1. 初始化
auto
初始化在平台上还有一点好处, 比如 :- vector<int> v;
- unsigned size = v.size(); // size()返回size_t型别
- auto sizet = v.size();
在32的平台unsigned
代表的是32位, size_t
是32位, 在64的平台unsigned
代表的也是23位, 但是size_t
却是64位, 这样平台差异可能就会带来问题, 使用auto
代替就没有这样的问题.
不过只有这几点可能不会让人心动, 下面我们还有auto的好处.
2. STL使用型别推导
还记得在前言中个说过调用STL最好使用auto
推导型别, 如果你还记得map
与pair
吗? 是这样 map<pair<key, type>>
? 还是map<pair<const key, type>>
? 答案是最后一种, 那么现在我们就来分析的使用auto
推导还是显示型别比较好.
看到上面的例子毫无问题, 但是深究起来显示型别还是些不完美. 我们知道map的key
不能被改变, 所以显示型别的string
与map的const string
不是匹配, 编译器就会将map对象都会产生一个临时对象再隐式的转为string
, 等等. 是不是注意到有一点了, 为了型别匹配赋值会产生临时变量, 那岂不是每一循环都会产生一个临时变量, 但是auto
型别推导就是精确匹配的, 不会产生临时变量.
可能觉得将显示型别的key改为const string
就能解决这个问题了, 确实是这样, 但是如果没有注意到这一点细节, 那就会损失效率了, 使用auto可以完全不想这些问题啊.
本节对C11的auto用法做了一个浅显的分析, 分别对使用auto
的好处, 定义时注意{}
对象也必须初始化, auto
在与for连用的时候要根据实际参数确定选择哪种实现, 这样效率才会达到最大, 当然一般都使用const auto&
和auto&&
. 最后还对auto
与函数返回值关联, 可以将返回型别放在函数名尾也可以, 这样的做法一般在模板中将模板参数作为返回值才考虑用, 平时也不必这样定义函数.
参考:
C++ auto keyword makes your life easier
C++ keyword: auto - cppreference.com
Placeholder type specifiers (since C++11) - cppreference.com
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。