扫描线问题

这篇具有很好参考价值的文章主要介绍了扫描线问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

观前提醒:「文章仅供学习和参考,如有问题请在评论区提出」

目录
  • 引入
  • 矩形面积并(Atlantis 问题)
    • P5490 【模板】扫描线 - 洛谷
    • Robot - Virtual Judge
    • 4350. 覆盖的面积 - AcWing
  • 矩形并集的周长
    • 262. 海报 - AcWing
  • 二维数点
    • P2163 园丁的烦恼 - 洛谷
    • Pair Sum and Perfect Square - Virtual Judge
  • 参考资料

一些扫描线问题的整理。


引入


扫描线一般被用来解决一些图形问题,像图形面积、周长,以及二维数点问题等。


矩形面积并(Atlantis 问题)


扫描线问题

在二维坐标系上,给出多个矩形的左下和右上坐标 \((x1, y1), (x2, y2)\) ,求出所有矩形构成的图形的面积。

观察上面的图形,我们可以通过扫描线来把它分成不同的小矩形,如下图所示

扫描线问题

那么这样的话我们就可以从小到大枚举每一个 \(x\) 坐标,那么就能得到每一个小矩形的长度,然后再乘以每个矩形的高度就可以了。而每个小矩阵高度的维护就需要用到线段树

我们要先给原先的每个矩形的左右边进行标记,矩形左边界标记为 \(1\) ,矩形右边界标记为 \(-1\)

那么我们之后在从左到右扫描的时候,对于标记为 \(1\) 的边,我们就加进来一条矩形的高,对于标记为 \(-1\) 的边,我们就删去这一条矩形的高。总体就是一个不断增删高度值的过程,增删一个矩阵对于此时高度的贡献值。

线段树所维护的是一个 \(y\) 坐标的区间值,而且对于坐标数据很大的情况下,需要离散化后再进行操作。

这样枚举每一个 \(x\) 坐标对有效高度更新,线段树每次更新是 \(O(logN)\) ,所以总的求解时间复杂度为 \(O(NlogN)\)


P5490 【模板】扫描线 - 洛谷


\(n\) 个四边平行于坐标轴的矩形的面积并。

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 10;

struct Tree {  // 线段树
    int l, r;  // 所维护的区间
    int len;   // 区间的有效高度
    int cnt;   // 区间被覆盖的次数
} tr[N * 8];   // 开8倍防止越界

struct Line {  // 扫描线
    int x, y1, y2;  // 每个 x 边界所对应的矩形的 下边界 y1, 上边界 y2
    int op;         // 左边界 1, 右边界 1

    bool operator<(const Line &w) {  // 重载, 用来排序
        return x < w.x;
    }
} X[N * 2];

int Y[N * 2];  // 所有 y 坐标
int n;

// 建树
void build(int u, int l, int r) {
    tr[u] = {Y[l], Y[r]};    // 区间赋值
    if (l + 1 == r) return;  // 子区间, 返回

    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid, r);
}

// 更新节点的区间有效高度
void pushup(int u) {
    // 此节点的区间依旧处于覆盖状态,
    // 区间有效高度就是区间宽度
    if (tr[u].cnt) tr[u].len = tr[u].r - tr[u].l;
    // 此区间并不处于完全覆盖状态
    // 区间有效高度取决于左右节点的区间有效高度
    else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
}

// 更新操作
void update(int u, int l, int r, int op) {
    if (tr[u].r <= l || tr[u].l >= r) return;  // 越界区间, 返回

    // 区间被完全包含, 更新覆盖次数
    if (tr[u].l >= l && tr[u].r <= r) tr[u].cnt += op;
    else update(u << 1, l, r, op), update(u << 1 | 1, l, r, op);  // 向下拆分

    pushup(u);  // 回溯更新, 更新实际区间长度
}

int main() {
    cin >> n;

    for (int i = 1; i <= n; i++) {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        X[i] = {x1, y1, y2, 1};       // 左边界, op = 1
        X[n + i] = {x2, y1, y2, -1};  // 右边界, op = -1
        Y[i] = y1, Y[n + i] = y2;     // 存储所有的 y 坐标
    }

    n *= 2;
    // 排序
    sort(X + 1, X + n + 1);
    sort(Y + 1, Y + n + 1);

    build(1, 1, n);  // 建树

    LL res = 0;
    for (int i = 1; i < n; i++) {
        // 每次更新高度, 每次更新完的新高度就是根节点的区间有效高度
        update(1, X[i].y1, X[i].y2, X[i].op);
        // 计算小矩形的面积: 宽 * 高
        res += (LL)(X[i + 1].x - X[i].x) * tr[1].len;
    }

    cout << res << endl;

    return 0;
}

Robot - Virtual Judge

二维坐标系中,一个正方形的左下角坐标为 \((0, 0)\) ,边长为 \(k\)

给出 \(n\) 个指令,分别是向上、向下、向左或向右移动 \(d\) 的距离,正方形最后扫过的面积是多少(多次覆盖算一次)。

对于正方形的每一次移动都当作一个矩形,然后用扫描线求这些矩形的面积并。

#include <bits/stdc++.h>
using namespace std;

#define int long long
typedef long long LL;
const int N = 2e5 + 10;

struct Node {
    LL l, r;
    int len;
    int cnt;
} tr[N << 3];

struct Line {
    int x, y1, y2;
    int op;
    bool operator<(const Line &W) const { return x < W.x; }
} X[N << 1];
int num[N << 1];

void pushup(int u) {
    if (tr[u].cnt) tr[u].len = tr[u].r - tr[u].l;
    else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
}

void build(int u, int l, int r) {
    tr[u] = {num[l], num[r]};
    if (l + 1 == r) return;
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid, r);
}

void modify(int u, int l, int r, int op) {
    if (tr[u].r <= l || tr[u].l >= r) return;
    if (tr[u].l >= l && tr[u].r <= r) tr[u].cnt += op;
    else {
        modify(u << 1, l, r, op);
        modify(u << 1 | 1, l, r, op);
    }
    pushup(u);
}

signed main() {
    int k, n;
    cin >> k >> n;

    int x = 0, y = 0;
    for (int i = 1; i <= n; i++) {
        char op[2];
        int d;
        scanf("%s%lld", op, &d);
        int x1 = x, y1 = y, x2 = x + k, y2 = y + k;
        if (*op == 'N') y2 += d, y += d;
        else if (*op == 'S') y1 -= d, y -= d;
        else if (*op == 'W') x1 -= d, x -= d;
        else if (*op == 'E') x2 += d, x += d;

        X[i] = {x1, y1, y2, 1};
        X[i + n] = {x2, y1, y2, -1};
        num[i] = y1, num[i + n] = y2;
    }

    n *= 2;
    sort(X + 1, X + n + 1);
    sort(num + 1, num + n + 1);
    build(1, 1, n);

    LL res = 0;
    for (int i = 1; i < n; i++) {
        modify(1, X[i].y1, X[i].y2, X[i].op);
        res += (X[i + 1].x - X[i].x) * (LL)tr[1].len;
    }
    cout << res << "\n";
    return 0;
}

4350. 覆盖的面积 - AcWing

给定 \(n\) 个四边平行于坐标轴的矩形,求被这些矩形覆盖过至少两次的区域的面积。

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;

struct Node {
    double l, r;
    double len1, len2;  // 分别是覆盖一次的长度, 覆盖两次的长度
    int cnt;
} tr[N << 4];

struct Seg {
    double y, x1, x2;
    int op;
    bool operator<(const Seg &W) const { return y < W.y; }
} Y[N << 1];
double x[N << 1];

void pushup(int u) {
    Node &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
    // 更新覆盖一次的长度
    if (root.cnt) root.len1 = root.r - root.l;
    else root.len1 = left.len1 + right.len1;
    // 更新覆盖两次的长度
    if (root.cnt >= 2) root.len2 = root.r - root.l;
    else if (tr[u].cnt == 1) root.len2 = left.len1 + right.len1;
    else root.len2 = left.len2 + right.len2;
}

void build(int u, int l, int r) {
    tr[u] = {x[l], x[r], 0, 0, 0};
    if (l + 1 == r) return;
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid, r);
}

void modify(int u, double l, double r, int op) {
    if (tr[u].l >= r || tr[u].r <= l) return;
    if (tr[u].l >= l && tr[u].r <= r) tr[u].cnt += op;
    else {
        modify(u << 1, l, r, op);
        modify(u << 1 | 1, l, r, op);
    }
    pushup(u);
}

int main() {
    int T;
    cin >> T;

    while (T--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++) {
            double x1, y1, x2, y2;
            scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
            x[i] = x1, x[i + n] = x2;
            Y[i] = {y1, x1, x2, 1};
            Y[i + n] = {y2, x1, x2, -1};
        }
        n *= 2;
        sort(Y + 1, Y + n + 1);
        sort(x + 1, x + n + 1);
        build(1, 1, n);

        double res = 0;
        for (int i = 1; i < n; i++) {
            modify(1, Y[i].x1, Y[i].x2, Y[i].op);
            res += (Y[i + 1].y - Y[i].y) * tr[1].len2;
        }
        printf("%.2lf\n", res);
    }
    return 0;
}

矩形并集的周长

262. 海报 - AcWing

\(n\) 个四边平行于坐标轴矩形的并集的周长。

我们可以分别对 \(x\) 轴和 \(y\) 轴进行扫描线。

在扫描时,对于每一条边的加入,对于答案的贡献就是 \(abs(上一次边的和 - 这一次边的和)\)

#include <bits/stdc++.h>
using namespace std;
const int N = 5010;

struct Node {
    int l, r;
    int len;
    int cnt;
} tr[N << 4];

struct Seg_x {
    int x, y1, y2;
    int op;
    bool operator<(const Seg_x &W) const { return x < W.x; }
} X[N * 2];

struct Seg_y {
    int y, x1, x2;
    int op;
    bool operator<(const Seg_y &W) const { return y < W.y; }
} Y[N * 2];

int x[N * 2], y[N * 2];

void pushup(int u) {
    if (tr[u].cnt) tr[u].len = tr[u].r - tr[u].l;
    else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
}

void build(int u, int l, int r, int op) {
    if (op == 1) tr[u] = {x[l], x[r], 0, 0};
    else tr[u] = {y[l], y[r], 0, 0};
    if (l + 1 == r) return;
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid, op);
        build(u << 1 | 1, mid, r, op);
    }
}

void modify(int u, int l, int r, int op) {
    if (tr[u].r <= l || tr[u].l >= r) return;
    if (tr[u].l >= l && tr[u].r <= r) tr[u].cnt += op;
    else {
        modify(u << 1, l, r, op);
        modify(u << 1 | 1, l, r, op);
    }
    pushup(u);
}

int main() {
    int n;
    cin >> n;

    for (int i = 1; i <= n; i++) {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        x[i] = x1, x[i + n] = x2;	// 存所有 x 坐标
        y[i] = y1, y[i + n] = y2;	// 存所有 y 坐标
        X[i] = {x1, y1, y2, 1};
        X[i + n] = {x2, y1, y2, -1};
        Y[i] = {y1, x1, x2, 1};
        Y[i + n] = {y2, x1, x2, -1};
    }

    n *= 2;
    sort(X + 1, X + n + 1);
    sort(Y + 1, Y + n + 1);
    sort(x + 1, x + n + 1);
    sort(y + 1, y + n + 1);

    int res = 0;
    build(1, 1, n, 2);	// 扫 x 轴, 取纵向长度
    for (int i = 1; i <= n; i++) {
        int last = tr[1].len;
        modify(1, X[i].y1, X[i].y2, X[i].op);
        res += abs(tr[1].len - last);
    }
    
    build(1, 1, n, 1);	// 扫 y 轴, 取横向长度
    for (int i = 1; i <= n; i++) {
        int last = tr[1].len;
        modify(1, Y[i].x1, Y[i].x2, Y[i].op);
        res += abs(tr[1].len - last);
    }

    cout << res << endl;
    return 0;
}

二维数点


给一个长度为 \(n\) 的序列,有 \(m\) 次查询,每次查询区间 \([l, r]\) 中值在 \([x, y]\) 内的元素个数。

这种问题可以等价为求一个二维平面上矩形内的点的个数,而主要做法就是扫描线+树状数组

扫描线问题

这里的主要思路其实也要用到二维前缀和。我们知道,对于一个矩阵,左下角为 \((x_{1}, y_{1})\) ,右上角为 \((x_{2}, y_{2})\) ,那么通过二维前缀和,矩阵和就为 \(S = s[x_{2}][y_{2}] - s[x_{2}][y_{1} - 1] - s[x_{1} - 1][y_{2}] + s[x_{1} - 1][y_{1} - 1]\)

那么我们就可以把一个矩阵和分成四部分 \(s[x_{2}][y_{2}], s[x_{2}][y_{1} - 1], s[x_{1} - 1][y_{2}], s[x_{1} - 1][y_{1} - 1]\)

我们就可以离线把这些询问矩阵都存储下来,离散化处理,然后对于每个询问都拆解成四部分。然后通过扫描线从左到右扫 \(x\) 坐标,并逐一处理所拆解出来的询问,最后就能合出每一个完成的询问结果。

每次通过树状数组来求解前缀和,时间复杂度为 \(O(logN)\) ,所以处理完所有请求的时间复杂度为 \(O(mlogn)\)


P2163 园丁的烦恼 - 洛谷


\(n\) 个点 \((x, y)\)\(m\) 次查询,对于每次查询要输出以 \((a,b)\) 为左下角, \((c, d)\) 为右上角的矩形内部有多少点(包括边界)。

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 5e5 + 10, M = 1e7 + 10;

vector<PII> v;	// 存所有的点坐标

struct Query {	// 存所有的被拆分出来询问
    // opt = 1, 加值; op5 = -1, 减值
    int x, y, id, opt;

    bool operator<(const Query &w) {	// 重载, x 坐标递增排序
        return x < w.x;
    }
} Q[N * 4];

int tr[M];	// 树状数组
int ans[N];	// 答案
int n, m;

int lowbit(int x) { return x & -x; }

void add(int x, int c) {
    for (int i = x; i < M; i += lowbit(i)) tr[i] += c;
}

int query(int x) {
    int res = 0;
    for (int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

int main() {
    cin >> n >> m;

    for (int i = 1; i <= n; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        x++, y++;	// 让下标从 (1, 1) 开始, 题目是 (0, 0)
        v.push_back({x, y});
    }
    
    sort(v.begin(), v.end());	// 坐标排序
	
    int cnt = 0;
    
    for (int i = 1; i <= m; i++) {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        x1++, y1++, x2++, y2++;
		
        // 根据二维前缀和来拆分请求, 并存储
        Q[++cnt] = {x1 - 1, y1 - 1, i, 1};
        Q[++cnt] = {x2, y2, i, 1};
        Q[++cnt] = {x2, y1 - 1, i, - 1};
        Q[++cnt] = {x1 - 1, y2, i, -1};
    }

    sort(Q + 1, Q + cnt + 1); // 请求排序

    int idx = 0;

    for (int i = 1; i <= cnt; i++) {
        Query q = Q[i];
        
        while (idx < n && v[idx].first <= q.x)
            add(v[idx].second, 1), idx++;
        
        ans[q.id] += q.opt * query(q.y);	// 单独计算每一部分
    }

    for (int i = 1; i <= m; i++) printf("%d\n", ans[i]);

    return 0;
}

Pair Sum and Perfect Square - Virtual Judge


给定一个 \(1 \sim n\) 的排列,有 \(m\) 次询问操作。

对于每次询问要输出 \([L, R]\) 区间里,对于 \(L \le i < j \le R\) ,有多少 \(p_{i} + p_{j}\) 是平方数。

先预处理出所有能成对构成平方数的 \((i, j)\) ,然后将问题转化为求二维矩阵内点的个数。

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> PII;
const int N = 1e5 + 10;

struct Query {
    int x, y, id, opt;
    bool operator<(const Query &w) const { return x < w.x; }
} Q[N * 4];

vector<int> Sqrt;
vector<PII> v;
int p[N];
int st[N];  // 位置
int ans[N]; // 答案
int tr[N];  // 树状数组
int n, m;

int lowbit(int x) { return x & -x; }

void add(int x, int c) {
    for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

int query(int x) {
    int res = 0;
    for (int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

void init() {
    for (int i = 2; i * i < N * 2; i++) Sqrt.push_back(i * i);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    init();
    int T;
    cin >> T;

    while (T--) {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> p[i];
            st[p[i]] = i;
            tr[i] = ans[i] = 0;
        }

        v.clear();
        for (int i = 1; i <= n; i++) {
            int x = p[i];
            for (auto y : Sqrt) {
                if (y - x > n) break;
                if (y <= x) continue;
                if (st[y - x] > i) v.push_back({i, st[y - x]});
            }
        }

        cin >> m;
        int cnt = 0;
        for (int i = 1; i <= m; i++) {
            int l, r;
            cin >> l >> r;
            Q[++cnt] = {r, r, i, 1};
            Q[++cnt] = {l - 1, l - 1, i, 1};
            Q[++cnt] = {l - 1, r, i, -1};
            Q[++cnt] = {r, l - 1, i, -1};
        }

        sort(Q + 1, Q + cnt + 1);
        int idx = 0;
        int len = v.size();

        for (int i = 1; i <= cnt; i++) {
            Query q = Q[i];
            while (idx < len && v[idx].first <= q.x)
                add(v[idx].second, 1), idx++;
            ans[q.id] += q.opt * query(q.y);
        }
        for (int i = 1; i <= m; i++) cout << ans[i] << "\n";
    }

    return 0;
}

参考资料

扫描线 - OI Wiki (oi-wiki.org)

576 扫描线算法 线段树【计算几何】

【数据结构】二维数点/二维偏序_NoobDream_的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-626772.html

到了这里,关于扫描线问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【计算机图形学 】扫描线多边形填充算法 | OpenGL+鼠标交互

    传送门 实现多边形扫描线填充算法,并和鼠标进行交互。 具体原理略过,会贴上完整代码,可直接运行。 环境: vs2019,OpenGL的库(可以搜索如何用vs使用OpenGL的库,可以使用vs自带的插件或者其他方法,很方便) 要点: 1.NET和AET的创建,改动 2.改变鼠标点击和鼠标拖拽的响应

    2023年04月08日
    浏览(39)
  • 计算机图形学实验——利用MFC对话框实现多边形绘制与填充(扫描线填充算法)附源码

    内容概括: 利用基于对话框的MFC项目 实现鼠标点击绘制多边形 实现扫描线算法填充多边形 源码见Yushan-Ji/ComputerGraphics: ECNU2023秋 计算机图形学课程实验代码 (github.com) 通过鼠标交互输入多边形 对各种多边形进行填充,包括边界自交的情况 利用 OnLButtonDown 和 OnRButtonDown 函数,

    2024年02月04日
    浏览(45)
  • hive学习(仅供参考)

    将结构化的数据文件映射为数据库表 提供类sql的查询语言HQL(Hive Query Language) Hive让更多的人使用Hadoop 提供了一个简单的优化模型 HQL类SQL语法,简化MR开发 支持在不同的计算框架上运行 支持在HDFS和HBase上临时查询数据 支持用户自定义函数、格式 常用于ETL操作和BI 稳定可靠(

    2023年04月08日
    浏览(31)
  • Python爬取MidJourney历史图片【仅供参考学习使用】

    使用MidJourney时, 在https://www.midjourney.com/app/这里有接口https://www.midjourney.com/api/app/recent-jobs/?amount=35dedupe=truejobStatus=completedjobType=upscaleorderBy=newpage=3prompt=undefinedrefreshApi=0searchType=advancedservice=nulltoDate=2023-06-16+09%3A50%3A17.379092type=alluserId=b12e169c-f609-4fd6-b917-11c2deaa8cffuser_id_ranked_score=n

    2024年02月13日
    浏览(39)
  • 【JS逆向一】逆向某站的 加密参数算法--仅供学习参考

    逆向日期:2024.02.06 使用工具:Node.js 文章全程已做去敏处理!!!  【需要做的可联系我】 可使用AES进行解密处理(直接解密即可):在线AES加解密工具 1、打开某某网站(请使用文章开头的AES在线工具解密):T9mpG48zIdYFB40hkjsQS4+N5rr4x4mSPHlx5EoT/+s= 2、点击右上角的登录按钮,账号

    2024年02月22日
    浏览(43)
  • 深度学习:从入门到精通课后习题解答本答案仅供参考

    第一章: 1、通过本章的学习,你认为深度学习崛起的原因有哪些? 答:(1) 计算能力的发展。深度学习的起源并不晚,但是在发展初期遭遇瓶颈的最主要原因是:当时的计算资源无法支持我们实现深度学习如此庞大复杂的计算。直到我们开始使用GPU进行计算后,深度学习才终

    2024年02月07日
    浏览(41)
  • OpenCV学习笔记之Overload报错的处理(仅供参考)

    今天在练习一个文档识别的小项目时,运行后一直提示报错,可是我还不知道问题出在哪里,源代码如下: 报错内容如下: 可是错误并不在报错所提示的那一行,而是上面的findContours()函数,新版本中这个函数要有两个返回值(以前好像是有三个),猛然反应过来以后,加上

    2024年02月12日
    浏览(27)
  • Unity单机手游逆向破解思路(仅供学习参考,禁止用于非法行为)

    一、安卓逆向常用工具 针对安卓单机游戏逆向,尤其是逆向使用Unity引擎开发的安卓游戏,只需了解下面的工具即可。 (1)Android Killer        Android Killer是安卓通用逆向工具,其可以对apk进行反向编译,得到smail代码,用户可以更改smail代码后,对apk重新打包,以实现破解

    2024年01月19日
    浏览(28)
  • 一篇搞定Ubuntu 22.04 下联网问题、 ifconfig、net-tools不能用的问题(亲测可行_仅供参考)

    刚下载的Ubuntu 联不上网、找不到ifconfig (告诉你要安装net-tools)但是输入 sudo apt install net-tools 又发现 E:无法定位软件包 net-tools 鼠标跟着“—”走 step1:打开虚拟网络编辑器 编辑—虚拟网络编辑器 —更改设置 —选择“NAT”模式—移除网络 —添加网络(哪一个都行问题不大

    2023年04月26日
    浏览(36)
  • restful接口设计规范[仅供参考]

    应该尽量将API部署在专用域名之下。 如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。 应该将API的版本号放入URL。 另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github就采用了这种做法。 因为不同的版本,可以理解成同一种资源的不

    2024年02月15日
    浏览(34)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包