一、工程创建
- 打开Xcode新建project,选择macOS->Command Line Tool然后点击next
- 语言选择C++点击next,然后选择地方创建工程即可
- 在main文件导入一批常用头文件
//C++头文件:
#include<vector>
#include <algorithm> //通用算法
#include <deque> //双端队列容器
#include <list> //线性列表容器
#include <map> //映射容器
#include <iostream> //基本输入输出流
#include <queue> //队列容器
#include <set> //集合容器
#include <stack> //堆栈容器
#include <string> //字符串类
#include <vector> //动态数组容器
//C头文件:
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
下面就可以开始刷题了!!
二、常用数据结构和函数
2.1 单链表
//链表节点
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
下面看一道leetcode题目 206. 反转链表
class Solution{
public:
ListNode * reverseList(ListNode *head){
ListNode *pre = nullptr;
ListNode *cur = head;
while(cur){
ListNode *temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
需要注意的是通过指针操作链表节点中的变量时使用->
,直接通过链表节点操作变量的时候使用.
,例如:
ListNode *node = head;
node->val = 3;
ListNode pre = ListNode();
pre.val = 3;
2.2 字符串(c语言)
说到字符串要区分c和c++,在c语言中是使用字符数组来表示的,字符串实际上就是一个以null('\0')
字符结尾的字符数组,例如
char s[10] = "abc";
看一道leetcode真题 344. 反转字符串的c语言解法
void reverseString(char* s, int sSize){
for(int i=0;i<sSize/2;i++){
char temp = s[i];
s[i] = s[sSize-1-i];
s[sSize-1-i] = temp;
}
}
在函数模版中直接给出了字符串长度sSize,如果需要我们自己获取长度可以使用strlen(char *)
函数。
这道题目很简单,就是交换字符数组的开头位置和结尾位置的字符,然后向内收缩直到完成字符串反转操作。
c语言中对于字符数组的常用函数有:
int strlen(char *a)
获取字符数组长度char* strcpy(char *a,char *b)
将b中的字符赋值到a中,如果b的长度比a可容纳长度大那么会出现越界问题。char * strcat(char *str1,char *str2)
串连接int strcmp(char* str1,char* str2)
str1>str2返回1,str1==str2返回0,str1<str2返回-1char * strchar(char* str1,char ch)
获取字符在字符数组中出现的第一个位置,否则返回-1 更多内容请看:# C字符串和C++字符串的区别
2.3 字符串(c++)
在c++中有字符串这种数据结构string,但是刷题过程中很少见到string,出现字符串的地方基本都是用字符数组的形式来表示的,我们同样看一下leetcode真题 344. 反转字符串的c++解法
class Solution {
public:
void reverseString(vector<char>& s) {
int left = 0;
int right = s.size()-1;
while(left<right){
char temp = s[left];
s[left++] = s[right];
s[right--] = temp;
}
}
};
此时的逻辑和c语言的解法并没有什么不同,都是对字符数组的操作
2.3 vector
据我的经验vector
是刷题过程中最常用的数据结构了,没有之一。
向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。
1.构造函数
- vector():创建一个空vector
- vector(int nSize):创建一个vector,元素个数为nSize
- vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t
- vector(const vector&):复制构造函数
- vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中
2.增加函数
- void push_back(const T& x):向量尾部增加一个元素X
3.删除元素
- void pop_back():删除向量中最后一个元素
- void clear():清空向量中所有元素
4.访问元素(也可以通过[index]方式访问)
- reference front():返回首元素的引用
- reference back():返回尾元素的引用
5.判空
- bool empty() const:判断向量是否为空,若为空,则向量中无元素
6.获取大小
- int size() const:返回向量中元素的个数
更多内容请看 C++ vector 容器浅析
绝大部份情况使用数组下标直接访问和修改vector的值就可以解决问题了,看一道真题#### 1109. 航班预订统计
class Solution {
public:
vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
//创建长度为n初始值都为0的容器res
vector<int> res(n);
//后面都是通过下标来操作容器中的值
for(vector<int> ve : bookings){
res[ve[0]-1] += ve[2];
if(ve[1] < n){
res[ve[1]] -= ve[2];
}
}
for(int i=1;i<n;i++){
res[i] = res[i-1]+res[i];
}
return res;
}
};
2.4 queue
此数据结构适用于FIFO技术,其中FIFO表示先进先出,首先插入的元素将首先被提取,队列有前/后
的概念,在前
的元素表示先进入队列的元素,在后
的元素表示后进入队列的元素。
1.常用函数
push(xxx):在队列末尾插入一个元素,底层通过push_back函数来实现的
pop():队列前面的元素出队列
back():返回队列最后一个元素(不出队列)
front():返回队列最前面的元素(不出队列)
size():返回队列中元素个数
empty():队列是否为空
更多内容请看# C++ queue(STL queue)用法详解
看一道leetcode真题 102. 二叉树的层序遍历
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ret;
//1、边界条件
if(!root){
return ret;
}
//2、根节点存储到队列
queue<TreeNode *> q;
q.push(root);
//3、取出队列元素并将队列元素的左右节点加入队列
while(!q.empty()){
int num = q.size();
ret.push_back(vector<int>());
for(int i=0;i<num;i++){
//获取最前面元素的引用
TreeNode *node = q.front();
//最前面元素出队列
q.pop();
//在队列最后边插入元素,push和push_back效果一样
ret.back().push_back(node->val);
if(node->left)q.push(node->left);
if(node->right)q.push(node->right);
}
}
//4、最终出队列的顺序就是层序遍历的结果
return ret;
}
};
2.4 priority_queue
priority_queue中的每一个元素都和某个优先级相关联,优先级越高就排到越靠前的位置,优先级相同则按照该元素在队列中的顺序排列。queue可以认为是一种特殊的priority_queue,其优先级是进入队列的时间,所以queue严格按照FIFO的顺序出队列。priority_queue的函数和queue有很多相同的
常用函数
push():将新元素插入优先队列
pop():将优先级最高的元素出队列
top():获取优先级最高元素的引用(不出队列)
size():返回队列中元素数量
empty():优先队列是否为空
定义:
priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆(既大数据先出队列,小数据后出队列)
//升序队列(从前往后为升序队列,既小的先出队列,大的后出队列)
priority_queue <int,vector<int>,greater<int> > q;
//降序队列( 等同于priority_queue<int> a; )
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。
//其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
使用pair
的比较,先比较第一个元素,后比较第二个元素
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main()
{
priority_queue<pair<int, int> > a;
pair<int, int> b(1, 2);
pair<int, int> c(1, 3);
pair<int, int> d(2, 5);
a.push(d);
a.push(c);
a.push(b);
while (!a.empty())
{
cout << a.top().first << ' ' << a.top().second << '\n';
a.pop();
}
}
//输出
2 5
1 3
1 2
当优先队列中存储的是自定义数据类型的时候我们需要对运算符<
重载
#include <iostream>
#include <queue>
using namespace std;
//方法1
struct tmp1 //运算符重载<
{
int x;
tmp1(int a) {x = a;}
bool operator<(const tmp1& a) const
{
return x < a.x; //大顶堆
}
};
//方法2
struct tmp2 //重写仿函数
{
bool operator() (tmp1 a, tmp1 b)
{
return a.x < b.x; //大顶堆
}
};
int main()
{
tmp1 a(1);
tmp1 b(2);
tmp1 c(3);
priority_queue<tmp1> d;
d.push(b);
d.push(c);
d.push(a);
while (!d.empty())
{
cout << d.top().x << '\n';
d.pop();
}
cout << endl;
priority_queue<tmp1, vector<tmp1>, tmp2> f;
f.push(c);
f.push(b);
f.push(a);
while (!f.empty())
{
cout << f.top().x << '\n';
f.pop();
}
}
//输出为
3
2
1
3
2
1
参考文章 c++优先队列(priority_queue)用法详解
看一道leetcode真题 23. 合并K个升序链表
class Solution {
public:
struct temp {
int val;
ListNode *ptr;
//运算符重载为小顶堆,既小数据先出队列
bool operator < ( const temp &t) const{
return val > t.val;
}
};
priority_queue<temp> q;
ListNode* mergeKLists(vector<ListNode*>& lists) {
for(ListNode *node:lists){
//push入优先队列
if(node)q.push({node->val,node});
}
ListNode head, *tail = &head;
while(!q.empty()){
//top获取优先队列最前面元素,pop将最前面元素出队列
auto f = q.top();q.pop();
tail->next = f.ptr;
tail = tail->next;
if(f.ptr->next){
q.push({f.ptr->next->val,f.ptr->next});
}
}
return head.next;
}
};
2.4 stack
栈是一种后进先出的数据结构
常用操作
size():栈内有多少个元素
top():栈顶元素
push():压栈
pop():出栈
empty():判空
更多内容请看# C++ Stack(栈)
看一道leetcode真题 20. 有效的括号
class Solution {
public:
bool isValid(string s) {
stack<char> st;
char t;
for (char &c: s) {
if (c == '(' || c == '[' || c == '{') st.push(c);
else {
if (st.empty()) return false;
t = st.top(); st.pop();
if (c == ')' && t != '(') return false;
if (c == ']' && t != '[') return false;
if (c == '}' && t != '{') return false;
}
}
return st.empty();
}
};
用到了push\pop\empty
函数
2.4 unordered_map
是一个无序的哈希表结构,查找速度很快,时间复杂度为O(1)
常用操作
count(key):获取key的个数,因为c++中不允许有重复的key,所以当存在key时为1,不存在时为0
unordered_map<int, double> m:创建
m[0] = 1.11:没有对应key时自动创建,访问元素也是同样的方式
erase(key):删除元素
clear(key):清空元素
size(key):有效元素个数
empty():判空
会用以上函数基本够刷题用了,还是看上一道leetcode真题 20. 有效的括号借助unordered_map如何实现
class Solution {
public:
bool isValid(string s) {
stack<char> st;
unordered_map<char,char> m = {
{')','('},
{'}','{'},
{']','['}
};
for (char c: s) {
//如果便利到')'、'}'、']'则判断是否和栈顶元素配对
if(m.count(c)){
if(st.empty() || m[c]!=st.top()){
//不能配对直接返回false
return false;
}
//能配对则将栈顶元素出栈
st.pop();
}else{
//如果遍历到'('、'{'、'['直接入栈
st.push(c);
}
}
return st.empty();
}
};
学会上面的这些数据结构基本够刷题用的了!!!