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

这篇具有很好参考价值的文章主要介绍了算法基础模板 快排、快选、归并、二分、离散化、区间合并、链表、图搜索、最短路等。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

快速排序

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]<arr[i]的情况
        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]<arr[j]:res += mid - i + 1

二分

//查找左边界 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>
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表示边数文章来源地址https://www.toymoban.com/news/detail-541267.html

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<stdio.h>
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<fstream>
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];
}

日期计算


public:
    bool isLeapYear(int year) {
    return year%400 == 0 || year%4==0 && year%100!=0;
}

int daysBetweenDates(string date1,string date2){
        if (date1.compare(date2) > 0) return daysBetweenDates(date2, date1);
        int month_len[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
        int year1,year2,month1,month2,day1,day2;
        int ans = 0;
        stringstream ss(date1);
        ss >> year1;ss.ignore();ss >> month1;ss.ignore();ss >> day1;
        stringstream ss2(date2);
        ss2 >> year2;ss2.ignore();ss2 >> month2;ss2.ignore();ss2 >> day2;
        //计算年份
        for(int year = year1;year < year2;year++)
            ans += 365 + isLeapYear(year);
        //计算月份和天数
        for(int m = 1;m < month1;m++)
            day1 += month_len[m] + (isLeapYear(year1) && m==2);
        for(int m = 1;m < month2;m++)
            day2 += month_len[m] + (isLeapYear(year2) && m==2);

        return ans + day2 - day1;
}

到了这里,关于算法基础模板 快排、快选、归并、二分、离散化、区间合并、链表、图搜索、最短路等的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【基础算法练习】二分模板

    704. 二分查找,这道题目是最经典的二分查找,使用于任何模板(如果你学的模板连这道题都套不上,那大概是模板有问题) 34. 在排序数组中查找元素的第一个和最后一个位置,一个合格的二分模板,需要能够应对这道题目的两种二分情况,我待会儿也会以这道题作为例题

    2024年01月25日
    浏览(31)
  • 位运算 离散化 区间和算法

    首先知道一个概念: 一个正整数的负数等于其按位取反后+1 -x = ~x + 1 举个例子:3 而我们通过 (x ~x + 1)或(x -x) 就可以得到二进制数最后一位1的大小。 举个例子: 题目链接 题目描述 给定一个长度为 n 的数列,请你求出数列中每个数的二进制表示中 1 的个数。 输入格式 第一行

    2024年01月20日
    浏览(21)
  • 十大排序算法(冒泡排序、插入排序、选择排序、希尔排序、堆排序、快排、归并排序、桶排序、计数排序、基数排序)

    目录 一、冒泡排序: 二、插入排序: 三、选择排序: 四、希尔排序: 五、堆排序: 六、快速排序: 6.1挖坑法: 6.2左右指针法 6.3前后指针法: 七、归并排序: 八、桶排序: 九、计数排序: 9.1绝对映射: 9.2现对映射: 十、基数排序:  1、思路: 通过对待排序序列从前

    2024年03月11日
    浏览(45)
  • 快排+归并

    快排:首先选取一个参照物,然后使用两个指针分别与他进行比较,需要左边的比他小,右边的比他大,如果不符和就交换;知道两个指针相等或者交错;接着进行递归,直到最后只剩下1个元素才停止 (接下来的模版是需要记住的) 1.题目需要10^5的空间大小,我们为了方便多

    2024年02月07日
    浏览(26)
  • 数据结构——快排与归并

    重要的事说三遍! 学习!学习!学习! 努力!努力!努力! 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为: 任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列

    2024年02月08日
    浏览(34)
  • ①归并排序、快速排序 、堆排序、计数排序[算法、代码模板、面试题]

    个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ ⚪步骤 归并排序 : 归并排序是一种分治法(Divide and Conquer)的经典排序算法,它的基本思想是将原始数组

    2024年02月04日
    浏览(31)
  • 排序 | 冒泡插入希尔选择堆快排归并计数排序

    排序算法是一种将一组数据按照特定顺序排列的算法。数据结构排序算法的选择取决于数据的特征、规模和性能需求。 接下来我们就要实现排序~~ 我们需要实现的一些功能: 冒泡排序是一种基本的排序算法,其核心思想是通过多次交换相邻元素的位置,使得每一轮循环都将

    2024年02月04日
    浏览(32)
  • 排序 | 冒泡 插入 希尔 选择 堆 快排 归并 非递归 计数 基数 排序

    排序算法是一种将一组数据按照特定顺序排列的算法。数据结构排序算法的选择取决于数据的特征、规模和性能需求。 接下来我们就要实现排序~~ 我们需要实现的一些功能: 冒泡排序是一种基本的排序算法,其核心思想是通过多次交换相邻元素的位置,使得每一轮循环都将

    2024年02月04日
    浏览(39)
  • 七大排序(含快排+归并的递归版和非递归版)

    排序: 所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 稳定性: 假定在待排序的记录序列中,存在多个具有相同的的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而

    2024年01月20日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包