算法基础模板 快排、快选、归并、二分、离散化、区间合并、链表、图搜索、最短路等

模板

  • 快速排序
  • 快速选择
  • 归并排序
  • 二分
  • 离散化
  • 区间合并
  • 单链表
  • STL栈
  • 队列
  • 单调队列
  • 并查集
  • DFS
  • 邻接表
  • DFS遍历图
  • BFS遍历图
  • 朴素Dijkstra
  • 堆优化Dijkstra
  • 文件读写
    • C读写
    • C++读写
  • 快速幂
  • 进制转换

快速排序

void quick_sort(int arr[],int l, int r){
    if(l >= r) return;
    
    int i = l-1, j=r+1, x = arr[l+r >> 1];
    
    while(i < j){
        do i++; while(arr[i]<x);
        do j--; while(arr[j]>x);
        if(i<j) swap(arr[i],arr[j]);
    }
    quick_sort(arr,l,j);
    quick_sort(arr,j+1,r);
}

快速选择

vector<int> arr;
int quick_sort(int l,int r,int k){
    if(l>=r)//只剩一个元素,可以理解为快速排序结束,arr是一个有序数组
        return arr[k];//k一定在该区间内  直接返回该元素
    int x = arr[l], i = l - 1, j = r + 1;//取最左元素为基准
    while(i<j){
        do i++; while(arr[i]<x);//指针i右移,左边找到一个大于基准的便跳出循环
        do j--; while(arr[j]>x);//指针j左移,右边找到一个小于基准的便跳出循环
        if(i<j) swap(arr[i],arr[j]);//让左边都是小于基准的,右边都是大于基准的
    }

    if(k <= j)//第k个数在左半部分
        return quick_sort(l,j,k);//去左边区间找
    else
        return quick_sort(j+1,r,k);//去右边区间找
}

归并排序

void merge_sort(vector<int>& arr,int l,int r){
    if(l>=r) return;
    
    int mid = l+r>>1;
    
    merge_sort(arr,l,mid);
    merge_sort(arr,mid+1,r);
    
    int k = 0, i = l, j = mid + 1;
    
    while(i<=mid && j<=r){
            //因为先递归,所以最小区间是两个数进行对比,所以更大的区间内,左右两个区间是分别有序的
            //所以这里直接顺序比较就可以了,不用担心左边区间会出现arr[i+1]
        if(arr[i] < arr[j]) 
            temp[k++] = arr[i++];//把小的放到前面,如果左边的数更小选择左边
        else 
            temp[k++] = arr[j++];
    }
    
    //上面的while循环结束后可能 i 还没达到 mid  或者 j 没达到r,但肯定有一个已经达到了
    //那么剩余的数一定都是在左区间或者右区间更大的数

    while(i <= mid)
        temp[k++] = arr[i++];
    while(j <= r)
        temp[k++] = arr[j++];

    for(i = l,k = 0;i <= r;i++, k++)//temp数组的长度只是部分数组l到r
        arr[i] = temp[k];//把temp的k个元素赋给arr
    
}

解决逆序对数量问题,即if arr[i]

二分

//查找左边界 SearchLeft 简写SL
int SL(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid; 
        else l = mid + 1; 
    }   
    return l;
}
//查找右边界 SearchRight 简写SR 
int SR(int l, int r) 
{
    while (l < r)
    {                   
        int mid = l + r + 1 >> 1; //需要+1 防止死循环
        if (check(mid)) l = mid;
        else r = mid - 1; 
    }
    return r; 
}

离散化

vector<int> alls;//待离散化的值
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(),alls.end()), alls.end());

int find(int x){//找到第一个大于等于x的位置
	int l = 0;
	int r = alls.size()-1;
	int mid = l+r>>1;
	while(l<r){
	if(alls[mid] >= x)
		r = mid;
	else
		l = mid + 1;
	}
	return r+1;//映射到1,2...n 不+1则是0..n
}

区间合并

void merge(vector<PII>&segs)
{
    vector<PII> res;
    sort(segs.begin(),segs.end());
    int l = -2e9,r = -2e9;
    for(auto item:segs)
        if(r < item.first)
        {
            if(l != -2e9) res.push_back({l,r});
            l = item.first;
            r = item.second;
        }
        else r = max(r,item.second);
    if(l != -2e9) res.push_back({l,r});
    segs = res;
}


单链表

int e[N],ne[N];//当前节点值和当前节点指向的下一个节点的指针

int head,idx;//头节点指针和当前节点指针

void init(){
    head = -1;//空链表时头指针指向末尾,末尾指针用-1表示
    idx = 1;//初始化从下标1开始
}

void add_to_head(int x){//将值x插入头节点
    e[idx] = x;//当前指针插入一个值x
    ne[idx] = head;//当前指针的下一个节点指向原本的头节点
    head = idx;//头节点更新为当前指针
    idx++;//当前指针向前移动
}

void add(int k,int x){//在第k个节点后插入一个节点
    e[idx] = x;
    ne[idx] = ne[k];//当前指针的下一个节点指向k的指向的节点
    ne[k]  = idx;//k指向的节点更新为插入的节点
    idx++;//当前指针向前移动
}

void del(int k){//删除第k个节点指向的节点,即k后面的一个节点
    ne[k] = ne[ne[k]];
}

        if(op == "push"){
            int num;
            cin>>num;
            a[++top] = num;
        }
        if(op == "pop"){
            top--;
        }
        if(op == "query"){
            cout<<a[top]<<endl;
        }
        if(op == "empty"){
            cout<<(top == -1?"YES":"NO")<<endl;
        }

STL栈

 vector<int> stack;
    int m,x;
    string op;
    cin >> m;
    while (m--) {
        cin >> op;
        if (op == "push") {
            cin >> x;
            stack.push_back(x);
        }
        else if (op == "pop") {
            stack.pop_back();
        }
        else if (op == "empty") {
            if (stack.size() == 0) {
                cout << "YES" << endl;
            }
            else {
                cout << "NO" << endl;
            }
        }
        else if (op == "query") {
            cout << stack.back() << endl;
        }
    }

队列

    int head = 0;
    int tail = -1;
    while(m--){
        string op;
        cin>>op;
        if(op == "push"){
            int x;
            cin>>x;
            a[++tail] = x; 
        }
        if(op == "pop"){
            head++;
        }
        if(op == "empty"){
            cout<<(head>tail?"YES":"NO")<<endl;
        }
        if(op == "query"){
            cout<<a[head]<<endl;
        }
    }

单调队列

#include
deque<int> q;
 for(int i = 1;i<=n;i++){
        cin>>a[i];
        //求最大值
        while(!q.empty() && a[i] > q.back()){//插入的值比队尾的值更大
            q.pop_back();//弹出队尾
        }
        q.push_back(a[i]);//入队
        if(i-k>=1 && q.front() == a[i-k]){//a[i-k]是上一个窗口的元素,如果还在队列内作为队头则要删去
            q.pop_front();//弹出队头
        }
        if(i >= k){//当窗口大小达到k时,以后每次移动,队头元素都是要求的元素
            cout<<q.front()<<" ";
        }
    }

并查集

int Find(int x){//寻找祖宗节点+路径压缩
    if(x != pre[x])
        pre[x] = Find(pre[x]);//把父结点设置为父结点的父结点,直到父结点已经是根结点
    return pre[x];
}

void Union(int i,int j){//将i和j并入一个集合
    pre[Find(i)] = Find(j);//把i的父结点的父结点设置为j的父结点
}

DFS

void dfs(int u){
    if(u > n){
        for(int i = 1;i<=n;i++)
            cout<<path[i]<<" ";
                cout<<endl;
        return;
    }

    for(int i = 1;i<=n;i++){
        if(!st[i]){
            path[u] = i;
            st[i] = 1;
            dfs(u + 1);
            st[i] = 0;
        }
    }
} 

邻接表

// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;

// 添加一条边a->b
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

// 初始化
idx = 0;
memset(h, -1, sizeof h);

DFS遍历图

int dfs(int u)
{
    st[u] = true; // st[u] 表示点u已经被遍历过

    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j]) dfs(j);
    }
}

BFS遍历图

queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);

while (q.size())
{
    int t = q.front();
    q.pop();

    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true; // 表示点j已经被遍历过
            q.push(j);
        }
    }
}

朴素Dijkstra

时间复杂是 O ( n 2 + m ) O(n2+m) O(n2+m) n n n表示点数, m m m表示边数

int g[N][N];  // 存储每条边
int dist[N];  // 存储1号点到每个点的最短距离
bool st[N];   // 存储每个点的最短路是否已经确定

// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;     // 在还未确定最短路的点中,寻找距离最小的点
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        // 用t更新其他点的距离
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        st[t] = true;
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

堆优化Dijkstra

时间复杂度 O ( m l o g n ) O(mlogn) O(mlogn) n n n表示点数, m m m表示边数

typedef pair<int, int> PII;

int n;      // 点的数量
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储所有点到1号点的距离
bool st[N];     // 存储每个点的最短距离是否已确定

// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});      // first存储距离,second存储节点编号

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, distance = t.first;

        if (st[ver]) continue;
        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

文件读写

C读写

#include
int n,m;
FILE* inputFile = fopen("filepath","r");
FILE* outputFile = fopen("filepath","w");
while(fscanf(inputFile,"%d%d",&n,&m) != EOF){
fprintf(outputFile,"%d %d\n",n,m);
fclose(inputFile);
fclose(outputFile);
} 

C++读写

#include
ifstream inputFile("inputpath");
ofstream outputFile("outputpath");
int n,m,a[100];
inputFile>>n>>m;
for(int i = 0;i<n;i++){
int x,y;
inputFile>>x>>y;
a[i] = x+y;
}
for(int i = 0;i<n;i++){
outputFile<<a[i];
}
string line;
while(getline(inputFile,line)){
outputFile<<line<<endl;
}

快速幂

int quick_mi(ll a,ll b,ll p){
    ll res = 1;
    
    while(b){
        if(b&1) res = res*a%p;
        
        b = b>>2;
        
        a = a*a%p;
    }
    return res;
}

进制转换


ll getNum(char c){
    if(c >='0' && c<='9')
        return c - '0';
    if(c >='A' && c<='Z')
        return c - 'A' + 10;
    if(c >= 'a' && c<='z')
    	return c - 'a' + 10;
}

void m2n(string x,int m,int n){
  	reverse(x.begin(),x.end());
  	ll num = 0;
  	/*m进制转10进制*/
  	for(int i = 0;i<x.size();i++)
  	    if(i > 0) num += getNum(x[i])*pow(m,i);
  	    else num += getNum(x[i]);
  	/*10进制转n进制*/
  	while(num){
  	    ll t = num%n;
  	    num /= n;
  	    if(t>=0 && t<=9) ans.push_back(t+'0');
  	    else ans.push_back(t - 10 + 'a');
  	}
  	for(int i = ans.size()-1; i>=0 ;i--)
  	    cout<<ans[i];
}

你可能感兴趣的:(算法,数据结构,笔记,算法,链表,图搜索算法)