1. 程式人生 > 其它 >Java的運算子

Java的運算子

前言

這篇部落格我自認為寫的非常清楚,不需要任何基礎,只要會 \(\text{C++}\) 語言基礎即可學懂。

任何一個地方我都沒有預設已經學過了,完全從 \(0\) 開始的 \(\text{FHQ−Treap}\) 教學!

好耶!我們開始吧!ヽ(✿゚▽゚)ノ

噠噠噠噠噠!

FHQ-Treap

\(\text{FHQ−Treap}\) 是一種平衡樹。名字裡帶有 \(\text{Treap}\) ,他的確與 \(\text{Treap}\) 有一些相同的性質。比如,他與 \(\text{Treap}\) 一樣,通過隨機權值來保證平衡。

可是,眾所周知,\(\text{Treap}\) 是一種通過旋轉來維護平衡的平衡樹。

範浩強(FHQ) 說過這樣一句話:

\(\text{Think functional.}\)

這句話是什麼意思呢?

如果不明白,可以看這篇文章以獲得一些啟發。

既然要 \(\text{Think functional}\),自然就不能使用旋轉操作了。

於是,通過拆開與合併來維護平衡的 \(\text{FHQ−Treap}\) 就誕生了。

主要操作

結構體

struct Node
{
    long long lch, rch; //左孩子,右孩子
    long long siz;//子樹大小
    long long val;//這個點的權值
    long long rnd;//隨機權值
}

生成隨機權值

srand(time(NULL));

但是這裡要注意的是,初始化種子只需要初始化一次,生成的數就是隨機的了,多次初始化反而會導致總是生成同一個數。

\(\text{Linux}\) 下生成的隨機數比較大,我們不需要那麼大的,可以取個模。

rnd = rand() % 114514;

拆分

拆分操作,就是把一棵樹拆成兩棵,以在兩棵樹中間進行一些操作。

設定一個 \(K\),然後把比 \(K\) 小或者等於 \(K\) 的放在左側,其餘在右側。

如圖:

void split(long long nowX, long long K, long long &Xtree, long long &Ytree)
//nowX是目前正在分裂的節點,K就是K,Xtree和Ytree分別是分裂後的兩棵樹。
{
    if (!nowX)
    {
        Xtree = Ytree = 0; //第一次分裂,先初始化
    }
    else
    {
        if (nodes[nowX].val <= K)
        {
            Xtree = nowX; //將nowX歸至X樹
            split(nodes[nowX].rch, K, nodes[nowX].rch, Ytree); //向nowX右子樹分裂
        }
        else
        {
            Ytree = nowX; //將nowX歸至Y樹
            split(nodes[nowX].lch, K, Xtree, nodes[nowX].lch); //向nowX左子樹分裂
        }
        update(nowX);
    }
}

合併

合併,就是拆分完操作完後再將樹合併回去。

long long merge(long long Xtree,long long Ytree)
{
    if(!Xtree||!Ytree)
    {
        return Xtree+Ytree; //邊界條件,返回有值的那一個
    }
    else if(nodes[Xtree].rnd<=nodes[Ytree].rnd)
    {
        nodes[Xtree].rch=merge(nodes[Xtree].rch,Ytree);
        update(Xtree);
        retrun Xtree;
    }
    else
    {
        nodes[Ytree].lch=merge(Ytree,nodes[Xtree].lch);
        update(Ytree);
        retrun Ytree;
    }
}

刪除

很顯然,將要刪除的數分裂出來之後不合並回去即可。

long long del(long long nowX)
{
    split(rot,nowX,X,Z); //先讓nowX作為X樹最大的,放在最後
    split(X,nowX-1,X,Y); //X樹中的所有nowX分到Y樹中
    Y=merge(nodes[nowX].lch,nodes[nowX].rch);//Y樹中全都是nowX,刪除Y樹根上那個nowX,其他nowX作為Y樹
    rot=merge(merge(X,Y),Z);//三部分恢復為一棵樹

}

排名第 k 的數

常規做法。

long long Kth(long long nowX, long long K) //nowX為目前尋找的樹根,K為要找的名次
{
    while (1)
    {
        if (K == nodes[nodes[nowX].lch].siz + 1)
        {
            return nowX; //好耶ヽ(✿゚▽゚)ノ!找到啦!(全~都可以炸完~)
        }
        else if (K > nodes[nodes[nowX].lch].siz + 1)
        {
            nowX = nodes[nowX].rch;                 //emm……這個數太小了,應該找他的右邊!走嘍!(飛,比跑快吧!)
            K -= nodes[nodes[nowX].lch].siz + 1; //前面已經有這麼多數了,應該減去!
        }
        else
        {
            nowX = nodes[nowX].lch; //看來這個數有點大!那就去左面!(我可是蒙德城的飛行冠軍!)
        }
    }
}

前驅

很顯然,讓要找的數成為 \(Y\) 樹的第一個,然後 \(X\) 樹最後一個即為前驅。

long long pre(long long nowX)
{
    split(rot, nowX-1, X, Y);
    return Kth(X, nodes[X].siz);
}

後繼

同樣思路,讓要找的數成為 \(X\) 樹的最後一個,然後 \(Y\) 樹第一個即為前驅。

long long nxt(long long nowX)
{
    split(rot, nowX, X, Y);
    return Kth(Y, 1);
}

注意

我們的前驅,字尾操作,都把樹拆開了,呼叫後需要把 \(X\) 樹和 \(Y\) 樹復原回去。

#define mrg() merge(X,Y)

Code

好了,這就講完了!是不是很輕鬆!

你可別說不輕鬆哦 (鍾城 曉)

#include <bits/stdc++.h>
#define INF 999999999
#define arand() rand() % 114514
#define mrg() rot = merge(X, Y)

using namespace std;

inline long long read()
{
    long long x = 0;
    int f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
void write(const long long &x)
{
    if (!x)
    {
        putchar('0');
        return;
    }
    char f[100];
    long long tmp = x;
    if (tmp < 0)
    {
        tmp = -tmp;
        putchar('-');
    }
    long long s = 0;
    while (tmp > 0)
    {
        f[s++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (s > 0)
    {
        putchar(f[--s]);
    }
}

const long long maxN = 100090;

struct Node
{
    long long lch, rch;
    long long val, siz;
    long long rnd;
} nodes[maxN];

long long X, Y, Z;
long long rot;
long long cnt;
long long totN;

void update(long long nowX)
{
    nodes[nowX].siz = nodes[nodes[nowX].lch].siz + nodes[nodes[nowX].rch].siz + 1;
}

long long merge(long long Atree, long long Btree)
{
    if ((!Atree) || (!Btree))
    {
        return Atree + Btree;
    }
    else if (nodes[Atree].rnd < nodes[Btree].rnd)
    {
        nodes[Atree].rch = merge(nodes[Atree].rch, Btree);
        update(Atree);
        return Atree;
    }
    else
    {
        nodes[Btree].lch = merge(Atree, nodes[Btree].lch);
        update(Btree);
        return Btree;
    }
}

void split(long long splitX, long long K, long long &Xtree, long long &Ytree)
{
    if (!splitX)
    {
        Xtree = Ytree = 0;
        return ;
    }
    else
    {
        if (nodes[splitX].val <= K)
        {
            Xtree = splitX;
            split(nodes[splitX].rch, K, nodes[splitX].rch, Ytree);
        }
        else
        {
            Ytree = splitX;
            split(nodes[splitX].lch, K, Xtree, nodes[splitX].lch);
        }
        update(splitX);
    }
}
long long Kth(long long KX, long long K)
{
    while (1)
    {
        if (K <= nodes[nodes[KX].lch].siz)
        {
            KX = nodes[KX].lch;
        }
        else if (K == nodes[nodes[KX].lch].siz + 1)
        {
            return KX;
        }
        else
        {
            K -= nodes[nodes[KX].lch].siz + 1;
            KX = nodes[KX].rch;
        }
    }
}

void del(long long delX)
{
    split(rot, delX, X, Z);
    split(X, delX - 1, X, Y);
    Y = merge(nodes[Y].lch, nodes[Y].rch);
    rot = merge(merge(X, Y), Z);
}

void insert(long long nowX)
{
    split(rot, nowX, X, Y);
    ++cnt;
    nodes[cnt].lch = nodes[cnt].rch = 0;
    nodes[cnt].val = nowX;
    nodes[cnt].siz = 1;
    nodes[cnt].rnd = arand();
    rot = merge(merge(X, cnt), Y);
}

long long pre(long long nowX)
{
    split(rot, nowX - 1, X, Y);
    return Kth(X, nodes[X].siz);
}
long long nxt(long long nowX)
{
    split(rot, nowX, X, Y);
    return Kth(Y, 1);
}
long long thK(long long nowX)
{
    split(rot, nowX - 1, X, Y);
    return nodes[X].siz + 1;
}

int main()
{
    totN = read();
    int readX, readY;
    while (totN--)
    {
        readX = read();
        readY = read();
        if (readX == 1)
        {
            insert(readY);
        }
        else if (readX == 2)
        {
            del(readY);
        }
        else if (readX == 3)
        {
            write(thK(readY));
            putchar('\n');
            mrg();
        }
        else if (readX == 4)
        {
            write(nodes[Kth(rot, readY)].val);
            putchar('\n');
        }
        else if (readX == 5)
        {
            write(nodes[pre(readY)].val);
            putchar('\n');
            mrg();
        }
        else if (readX == 6)
        {
            write(nodes[nxt(readY)].val);
            putchar('\n');
            mrg();
        }
    }
    return 0;
}

文章作者: Thomitics
文章連結: https://blog.foxex.cn/2021/06/27/FHQ-Treap/
版權宣告: 本部落格所有文章除特別宣告外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 Thomitics