Xcode使用C++刷算法题

559 阅读5分钟

一、工程创建

  1. 打开Xcode新建project,选择macOS->Command Line Tool然后点击next

image.png

  1. 语言选择C++点击next,然后选择地方创建工程即可

image.png

  1. 在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返回-1
  • char * 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)区间内另一个数组的元素到vector2.增加函数
-   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();
    }
};

学会上面的这些数据结构基本够刷题用的了!!!