算法基礎 第3章 數據結構

1.單調棧

1.什么是單調棧
單調棧,即具有單調性的棧。
實現

#include <iostream>
#include <stack>
using namespace std;
const int N = 3e6 + 10;
int a[N], n;
void test1()
{stack<int> st; // 維護?個單調遞增的棧for(int i = 1; i <= n; i++){// 棧???于等于 a[i] 的元素全部出棧while(st.size() && st.top() >= a[i]) st.pop();st.push(a[i]);}
}

2.單調棧解決的問題

  • 尋找當前元素左側,離它最近,且比它大的元素在哪
  • 尋找當前元素左側,離它最近,且比它小的元素在哪
  • 尋找當前元素右側,離它最近,且比它大的元素在哪
  • 尋找當前元素右側,離它最近,且比它小的元素在哪

雖然是四個問題,但是原理是?致的。因此,只要解決?個,舉?反三就可以解決剩下的幾個。

3.尋找當前元素左側,離它最近,并且比它大的元素在哪
從左往右遍歷元素,構造?個單調遞減的棧。插入當前位置的元素的時:
? 如果棧為空,則左側不存在比當前元素大的元素;
? 如果棧非空,若棧頂元素比當前位置大則棧頂元素為目標元素;若棧頂元素比當前元素小,出棧維護單調遞減棧。
注意,因為我們要找的是最終結果的位置。因此,棧里面存的是每個元素的下標。

1.1【模板】單調棧

P5788 【模板】單調棧

2.單調隊列

1.什么是單調隊列?
單調隊列,即存儲的元素要么單調遞增要么單調遞減的隊列,這里的隊列是個雙端隊列。
2.單調隊列解決的問題
一般用于解決滑動窗口內最大值最小值問題,以及優化動態規劃。

2.1 滑動窗口 /【模板】單調隊列

P1886 滑動窗口 /【模板】單調隊列

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<deque>
//https://www.luogu.com.cn/problem/P1886
using namespace std;
const int N = 1e6 + 10;
int n, k;
int a[N];
//單調隊列
int main()
{cin >> n >> k;for (int i = 1; i <= n; ++i)cin >> a[i];//存儲下標的雙端隊列deque<int> q;//找窗口內最小值for (int i = 1; i <= n; ++i){//單調遞增的隊列while (q.size() && a[i] <= a[q.back()])q.pop_back();q.push_back(i);//判斷是否在窗口內,并更新隊列if (q.size() && i - q.front() >= k)q.pop_front();//當窗口大小達到k時,取出窗口內的最值if (i >= k)cout << a[q.front()] << ' ';}cout << endl;q.clear();//找窗口內最大值for (int i = 1; i <= n; ++i){//單調遞減的隊列while (q.size() && a[i] >= a[q.back()])q.pop_back();q.push_back(i);//判斷是否在窗口內,并更新隊列if (q.size() && i - q.front() >= k)q.pop_front();//當窗口大小達到k時,取出窗口內的最值if (i >= k)cout << a[q.front()] << ' ';}cout << endl;return 0;
}

3.并查集

3.1并查集的概念及實現

3.1.2并查集的概念

并查集(Union Find):是一種用于維護元素所屬集合的數據結構,實現為一個森林,其中每棵樹表示一個集合,樹中的節點表示對應集合中的元素,根節點來代表整個集合。

實現并查集的時,我們?般讓根節點自己指向自己。
在這里插入圖片描述

我們需要維護若干個集合

  • 查詢操作:查找x屬于哪一個集合
  • 合并操作:將x所在集合和y所在集合合并成一個集合
  • 判斷操作:判斷xy是否在同一個集合

3.1.2并查集的實現

1.初始化:讓元素自己指向自己

for(int i=1;i<=n;i++)fa[i]=i;

2.查詢

// 返回父親節點
int find(int x)
{if(fa[x] == x) return x;//     路徑壓縮			把被查詢的節點到根節點的路徑上的所有節點的?節點設置為根節點return fa[x] = find(fa[x]);// ??實現return fa[x] == x ? x : fa[x] = find(fa[x]);
}

3.合并
將元素 x 所在的集合與元素 y 所在的集合合并成?個集合:

  • 讓元素 x 所在樹的根節點指向元素 y 所在樹的根節點。
// 合并操作
void un(int x, int y) // 注意,函數名字不能? union,因為它是 C++ 的關鍵字
{int fx = find(x);int fy = find(y);// 讓元素 x 所在樹的根節點指向元素 y 所在樹的根節點。fa[fx] = fy;
}

4.判斷
判斷元素 x 和元素 y 是否在同?集合:

  • 看看兩者所在樹的根節點是否相同。
// 合并操作
// 判斷是否在同?集合
bool issame(int x, int y)
{return find(x) == find(y);
}

3.2普通并查集

P3367 【模板】并查集

3.3拓展并查集

普通的并查集只能解決各元素之間僅存在?種相互關系,比如《親戚》題目中:
? a 和 b 是親戚關系, b 和 c 是親戚關系,這時就可以查找出 a 和 c 也存在親戚關系。

但如果存在各元素之間存在多種相互關系,普通并查集就無法解決。比如下面的案例:
? a和 b是敵?關系, b和 c是敵人關系,但是 a和 c其實不是敵人關系,而是另?種朋友關系。
此時,就不僅僅是簡單的敵?關系,還是出現?種朋友關系。
解決這類問題就需要對并查集進行擴展:將每個元素拆分成多個域,每個域代表?種狀態或者關系。
通過維護這些域之間的關系,來處理復雜的約束條件。

下例中,若1與2是敵人2與3是敵人那么1和3就在同一個集合中,則1與3是朋友
在這里插入圖片描述
以下題為例實現:關鍵在于將敵人的敵人合并
P1892 [BalticOI 2003] 團伙

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
//https://www.luogu.com.cn/problem/P1892
using namespace std;
const int N = 1e3 + 10;
// 拓展并查集int n, m, p, q;
char opt;
int fa[N*2];int find(int x)
{if (fa[x] == x)return x;return fa[x] = find(fa[x]);
}
// 使朋友域元素為父節點
void un(int x, int y)
{int fx = find(x), fy = find(y);fa[fy] = fx;
}
int main()
{cin >> n >> m;for (int i = 1; i <= 2 * n; ++i)fa[i] = i;while (m--){cin >> opt >> p >> q;if (opt == 'F'){un(p, q);}else{//敵人的敵人是朋友un(p, q + n);un(q, p + n);}}int res = 0;for (int i = 1; i <=  n; ++i){if (fa[i] == i)res++;}cout << res << endl;return 0;
}

3.4帶權并查集

1.帶權并查集的概念
帶權并查集在普通并查集的基礎上,為每個結點增加了?個權值。這個權值可以表示當前結點與父結點之間的關系、距離或其他信息(注意,由于我們有路徑壓縮操作,所以最終這個權值表示的是當前結點相對于根結點的信息)。有了這樣?個權值,就可以推斷出集合中各個元素之間的相互關系
2.帶權并查集的實現
實現?個能夠查詢任意兩點之間距離的并查集。實現帶權并查集的核心是在進行 FindUnion 操作時,不僅要維護集合的結構,還要維護結點的權值

基本操作:

const int N = 1e5 + 10, INF = 0x3f3f3f3f;
int n;
int fa[N], d[N];
// 初始化
void init()
{for(int i = 1; i <= n; i++){fa[i] = i;d[i] = 0; // 根據題?要求來初始化,這里表示到父節點距離}
}// 查詢操作
int find(int x)
{if(fa[x] == x) return x;int t = find(fa[x]); // 這句代碼?定要先執?,先縮短路徑// 更新到根節點的距離d[x] += d[fa[x]]; // 會根據權值的意義有所改變return fa[x] = t;
}// 合并操作
// x 所在集合與 y 所在集合合并,x 與 y 之間的權值是 w
void un(int x, int y, int w)
{int fx = find(x), fy = find(y);if(fx != fy) // 不在同?個集合中{fa[fx] = fy;// 更新到根節點的距離d[fx] = d[y] + w - d[x]; // 會根據權值的意義有所改變}
}

4.字符串哈希

  • 什么是字符串哈希:
    定義?個把字符串映射到整數的函數 ,這就是字符串哈希。即將?個字符串用一個整數表示。

  • 字符串哈希中的哈希函數:
    在字符串哈希中,有?種沖突概率較小的哈希函數,將字符串映射成 p 進制數字:
    hash(s)=∑i=0n?1s[i]×pn?i?1(modM)hash(s) = \sum_{i=0}^{n-1} s[i] \times p^{n-i-1} \pmod{M}hash(s)=i=0n?1?s[i]×pn?i?1(modM)

  • 前綴哈希數組:
    一般使用前綴和存儲每個前綴的哈希值,這樣的話每次就能快速求出子串的哈希了。

// 處理前綴哈希數組以及 p 的 i 次?數組
void init_hash()
{f[0] = 0; p[0] = 1;for(int i = 1; i <= len; i++){f[i] = f[i - 1] * P + s[i];p[i] = p[i - 1] * P;}
}// 快速求得任意區間的哈希值
ULL get_hash(int l, int r)
{return f[r] - f[l - 1] * p[r - l + 1];
}

將f[i]映射成p進制數,則f[i] = f[i - 1] * P + s[i];
若要求出中間某一段的哈希值,將目標字符串、f[r]、f[l-1]表示出來找出他們之間的關系,表示如下
在這里插入圖片描述

例子:
P3370 【模板】字符串哈希

5.Trie樹

  1. Trie 樹又叫字典樹或前綴樹,是?種能夠快速插入查詢字符串的數據結構。它利用字符串的公共前綴,將字符串組織成?棵樹形結構。
    我們可以把字典樹想象成?棵多叉樹,每一條邊代表?個字符,從根節點到某個節點的路徑就代表了一個字符串。例如,要存儲 “abc” 、 “abd” 、 “acde” 以及 “cd” 時,構建的字典樹如下:
    在這里插入圖片描述
  2. 字典樹的作用
    查詢某個單詞是否出現過,并且出現幾次;
    查詢有多少個單詞是以某個字符串為前綴
    查詢所有以某個前綴開頭的單詞;(這個作用可以用到輸入法中,輸入拼音的時候,可以提示可能的單詞)
  3. 字典樹的實現
    實現一個能夠查詢單詞出現次數以及查詢有多少個單詞是以某個字符串為前綴的字典樹,默認全是小寫字母。
// 字典樹        經過這個節點的字符串
int tr[N][62], p[N];
// 新插入節點的編號
int idx;
int t, n, q;
// 查詢是哪個字符
int get_num(char ch)
{if (ch >= 'a' && ch <= 'z')return ch - 'a';else if (ch >= 'A' && ch <= 'Z')return ch - 'A' + 26;else return ch - '0' + 52;
}// 插入字典樹
void insert(string& s)
{int cur = 0;// 從根結點開始p[cur]++;// 這個格?經過?次for (auto ch : s){int path = get_num(ch);if (tr[cur][path] == 0)tr[cur][path] = ++idx;// 迭代到下一個路徑cur = tr[cur][path];p[cur]++;}e[cur]++;
}
// 查詢特定前綴字符串個數
int find_pre(string& s)
{int cur = 0;for (auto ch : s){int path = get_num(ch);if (tr[cur][path] == 0)return 0;cur = tr[cur][path];}// 查詢從這個格子經過的所有字符串return p[cur];
}// 查詢字符串出現次數
int find(string& s)
{int cur = 0;for(auto ch : s){int path = ch - 'a';if(tree[cur][path] == 0) return 0;cur = tree[cur][path];}return e[cur];
}

例子:
P8306 【模板】字典樹

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93322.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93322.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93322.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[機器學習]08-基于邏輯回歸模型的鳶尾花數據集分類

使用sklearn的LogisticRegression多分類模型程序代碼&#xff1a;import numpy as np from sklearn.linear_model import LogisticRegression import matplotlib.pyplot as plt import matplotlib as mpl from sklearn import datasets from sklearn import preprocessing impo…

【STM32入門教程】stm32簡介

一、STM32簡介二、ARM三、stm32f103c8t6四、命名規則五、系統結構六、引腳定義七、啟動配置一般情況下&#xff0c;都是在flash開始程序&#xff0c;而啟動程序也可以進行配置在其他地方啟動程序&#xff0c;通過配置boot0和boot1來進行配置八、最小系統電路

SAE J2716多協議網關的硬件架構與實時協議轉換機制解析

本文解析符合SAE J2716標準的工業級協議轉換設備技術架構&#xff0c;通過拆解其四路雙向SENT通道與多總線&#xff08;CANFD/Ethernet/USB&#xff09;的實時交互機制、MicroSD獨立日志系統設計及模擬量動態映射方案&#xff0c;為汽車電子與工業通信開發者提供可復用的技術參…

VS2022+QT5.15.2+OCCT7.9.1的開發環境搭建流程

以下是VS2022 QT5.15.2 OCCT7.9.1開發環境搭建的完整流程&#xff1a; 一、安裝Visual Studio 2022 下載安裝程序 訪問VS官網下載Community版安裝組件 選擇"使用C的桌面開發"工作負載勾選&#xff1a; MSVC v143 - VS 2022 C x64/x86生成工具Windows 10 SDK (建議…

數據庫訪問模式詳解

數據庫訪問模式詳解數據庫訪問模式是軟件架構中數據訪問層&#xff08;Data Access Layer&#xff09;設計的核心&#xff0c;它定義了應用程序如何與數據庫進行交互的策略和方法。選擇合適的訪問模式對于系統的性能、可維護性、可擴展性、事務一致性和開發效率至關重要。不同的…

BGE向量算法

一、是什么 什么是BGE向量算法&#xff1f;先說說網上的概念吧。本文不講解太深的算法知識&#xff0c;主要講解如何用&#xff01; BGE&#xff08;BAAI General Embedding&#xff09;是北京智源研究院開源的“通用語義向量模型”。一句話&#xff1a;把中文或英文句子變成…

AI數據倉庫的核心優勢解析

內容概要本文旨在全面解析AI數據倉庫的核心優勢&#xff0c;為讀者提供清晰的框架。文章首先從基礎定義出發&#xff0c;探討其如何高效整合多源數據&#xff0c;并支持人工智能與機器學習應用。隨后&#xff0c;將詳細闡述處理TB級數據的能力&#xff0c;包括兼容結構化和非結…

具身智能Scaling Law缺失:機器人界的“摩爾定律“何時誕生?

8月9日&#xff0c;在世界機器人大會的演講臺上&#xff0c;宇樹科技創始人王興興談論到目前機器人運動控制領域存在的RL Scaling Law問題&#xff0c;他認為現在的機器人在學習一項新的技能時&#xff0c;往往都是需要從頭開始研究以及教學。而在未來更加希望的是能夠在原有的…

【跨越 6G 安全、防御與智能協作:從APT檢測到多模態通信再到AI代理語言革命】

跨越 6G 安全、防御與智能協作&#xff1a;從APT檢測到多模態通信再到AI代理語言革命引言單篇總結**2. Integrated Multimodal Sensing and Communication: Challenges, Technologies, and Architectures****3. Why do AI agents communicate in human language?**引言 在邁向…

微前端-解決MicroApp微前端內存泄露問題

前言 之前使用京東微前端框架MicroApp集成10個微前端的頁面到AngularJs的后臺管理系統中&#xff0c;每個微前端做成一個菜單&#xff0c;一共10個&#xff0c;每次打開都是一個新的微前端&#xff0c;但是發現打開的微前端越多&#xff0c;容易造成內存泄露&#xff0c;下面講…

線性代數 · 向量運算 | 叉乘 / 幾何意義 / 推導

注&#xff1a;本文為 “線性代數 向量運算” 相關合輯。 圖片清晰度受引文原圖所限。 略作重排&#xff0c;未整理去重。 如有內容異常&#xff0c;請看原文。 數學基礎 —— 向量運算&#xff08;叉乘&#xff09; keng_s 于 2016-08-05 17:17:57 發布 1_ 向量的叉乘 向量…

方法中只包含查詢操作需要添加事務嗎?

方法中只包含查詢操作需要添加事務嗎?絕大部分情況都不需要 是否需要為包含數據庫查詢操作的方法添加 @Transactional 注解,取決于業務需求和查詢操作的特性,不能一概而論。以下是具體分析: 一、不需要添加 @Transactional 的常見場景 如果查詢操作滿足以下條件,通常不需…

MTK平臺Wi-Fi學習--wifi channel 通過國家碼進行功率限制和wifi eFEM 基本配置和wifi Tx SEM問題

一. 國家碼可以用來限制功率上限,可以針對各國家實現By channel降功率的能力 可以通過country code來設置不同channel的power limit,操作方法如下: 在rlm_txpwr_init.h文件中g_rRlmPowerLimitConfiguration[]下添加需要限制功率的channel, 例如:國家碼CN,信道:CH1,po…

MedGemma: 多模態醫學文本與圖像處理的創新模型

MedGemma: 多模態醫學文本與圖像處理的創新模型 今天&#xff0c;我有幸參加了在上海舉行的Google 2025 I/O大會&#xff0c;這是一場充滿創新與突破的技術盛宴。作為全球最具影響力的科技大會之一&#xff0c;Google I/O每年都會吸引來自世界各地的開發者、企業領袖以及科技愛…

深入剖析 C++ STL 中的 std::list 容器

基本介紹在 C 標準庫&#xff08;STL&#xff09;中&#xff0c;std::list 是一個基于雙向鏈表實現的序列容器。它與 std::vector、std::deque 等連續存儲容器不同&#xff0c;提供了在序列中高效插入和刪除元素的能力&#xff0c;尤其是在序列中間位置操作時優勢明顯。1. std:…

大規模調用淘寶商品詳情 API 的分布式請求調度實踐

在電商數據分析、比價系統、選品工具等業務場景中&#xff0c;往往需要大規模調用淘寶商品詳情 API 以獲取商品標題、價格、銷量、評價等核心數據。然而&#xff0c;面對淘寶開放平臺的嚴格限流策略、海量商品 ID 的處理需求以及系統高可用要求&#xff0c;傳統的單節點調用方式…

在 Windows 系統中解決 Git 推送時出現的 Permission denied (publickey) 錯誤,請按照以下詳細步驟操作:

完整解決方案步驟&#xff1a; 1. 檢查并生成 SSH 密鑰 # 打開 Git Bash ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 全程按回車&#xff08;使用默認路徑&#xff0c;不設密碼&#xff09; 密鑰將生成在&#xff1a;C:\Users\<用戶名>\.ssh\ 目…

【入門級-算法-2、入門算法:枚舉法】

枚舉法&#xff08;Brute Force&#xff09;&#xff1a;是一種直接遍歷所有可能情況的算法思想&#xff0c;適合解決數據范圍較小的問題。它的核心是窮舉所有可能性&#xff0c;并檢查哪些情況符合要求。 枚舉法的基本思想&#xff1a;計算機主要功能&#xff0c;或者說它的優…

Python/Node.js 調用taobao API:構建實時商品詳情數據采集服務

在電商數據分析、價格監控、競品分析等場景中&#xff0c;實時獲取商品詳情數據至關重要。淘寶提供了豐富的 API 接口&#xff0c;允許開發者合法合規地獲取商品信息。本文將介紹如何使用 Python 和 Node.js 兩種主流語言調用淘寶 API&#xff0c;構建一個實時商品詳情數據采集…

【OpenCV】Mat詳解

在OpenCV中&#xff0c;cv::Mat是用于存儲圖像、矩陣等多維數據的核心數據結構&#xff0c;替代了早期的IplImage&#xff08;需手動管理內存&#xff09;&#xff0c;其設計的核心目標是自動內存管理和高效數據操作。下面詳細介紹其組成原理及使用方法。 一、cv::Mat的組成原理…