【C++筆記】紅黑樹封裝map和set深度剖析

【C++筆記】紅黑樹封裝map和set深度剖析

在這里插入圖片描述

🔥個人主頁大白的編程日記

🔥專欄C++筆記


文章目錄

  • 【C++筆記】紅黑樹封裝map和set深度剖析
    • 前言
    • 一. 源碼及框架分析
      • 1.1 源碼框架分析
    • 二. 模擬實現map和set
      • 2.1封裝map和set
    • 三.迭代器
      • 3.1思路分析
      • 3.2 代碼實現
    • 四.operator[]
      • 4.1思路分析
    • 五.源碼
    • 后言

前言

哈嘍,各位小伙伴大家好!上期我們講了紅黑樹。今天我們來講一下用紅黑樹封裝map和set。話不多說,我們進入正題!向大廠沖鋒
在這里插入圖片描述

一. 源碼及框架分析

我們看一下源碼是如何實現紅黑樹封裝map和set。

參考SGI-STL30版本源代碼,map和set的源代碼在map/set/stl_map.h/stl_set.h/stl_tree.h等幾個頭文件中。

// set
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_set.h>
#include <stl_multiset.h>
// map
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_map.h>
#include <stl_multimap.h>
// stl_set.h
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:// typedefs:typedef Key key_type;typedef Key value_type;
private:typedef rb_tree<key_type, value_type,identity<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing set
};
// stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:// typedefs:typedef Key key_type;typedef T mapped_type;typedef pair<const Key, T> value_type;
private:
typedef rb_tree<key_type, value_type,
select1st<value_type>, key_compare, Alloc> rep_type;
rep_type t; // red-black tree representing map
};
// stl_tree.h
struct __rb_tree_node_base
{
typedef __rb_tree_color_type color_type;
typedef __rb_tree_node_base* base_ptr;
color_type color;
base_ptr parent;
base_ptr left;
base_ptr right;
};
// stl_tree.h
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc= alloc>
class rb_tree {
protected:typedef void* void_pointer;typedef __rb_tree_node_base* base_ptr;typedef __rb_tree_node<Value> rb_tree_node;typedef rb_tree_node* link_type;typedef Key key_type;typedef Value value_type;
public:// insert?的是第?個模板參數左形參pair<iterator, bool> insert_unique(const value_type& x);// erase和find?第?個模板參數做形參size_type erase(const key_type& x);iterator find(const key_type& x);
protected:size_type node_count; // keeps track of size of treelink_type header;
};
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
typedef __rb_tree_node<Value>* link_type;
Value value_field;
};

1.1 源碼框架分析

  • 通過下圖對框架的分析,我們可以看到源碼中rb_tree用了?個巧妙的泛型思想實現,rb_tree是實現key的搜索場景,還是key/value的搜索場景不是直接寫死的,而是由第?個模板參數Value決定_rb_tree_node中存儲的數據類型。
  • set實例化rb_tree時第二個模板參數給的是key,map實例化rb_tree時第二個模板參數給的是pair<const key, T>,這樣?顆紅黑樹既可以實現key搜索場景的set,也可以實現key/value搜索場景的map。
  • 要注意一下,源碼里面模板參數是用T代表value,而內部寫的value_type不是我們我們日常key/value場景中說的value,源碼中的value_type反而是紅黑樹結點中存儲的真實的數據的類型。
  • rb_tree第二個模板參數Value已經控制了紅黑樹結點中存儲的數據類型,為什么還要傳第一個模板參數Key呢?尤其是set,兩個模板參數是?樣的,這是很多同學這時的?個疑問。要注意的是對于map和set,find/erase時的函數參數都是Key,所以第?個模板參數是傳給find/erase等函數做形參的類型的。對于set而言兩個參數是?樣的,但是對于map而言就完全不一樣了,map insert的是pair對象,但是find和ease的是Key對象。

所以我們可以通過模版參數來控制紅黑樹底層的結構。
所以不用實現兩顆紅黑樹,但是本質還是編譯器根據模版參數生成兩顆不同的紅黑樹。

二. 模擬實現map和set

這里借鑒源碼的底層實現。
我們也自己模擬實現出我們的mymap和myset.

2.1封裝map和set

  • 參考源碼框架,map和set復用之前我們實現的紅黑樹。
  • 我們這里相比源碼調整?下,key參數就用K,value參數就用V,紅黑樹中的數據類型,我們使用T。
  • 其次因為RBTree實現了泛型不知道T參數導致是K,還是pair<K, V>,那么insert內部進行插入邏輯比較時,就沒辦法進行比較,因為pair的默認支持的是key和value?起參與比較,我們需要時的任何時候只比較key,所以我們在map和set層分別實現?個MapKeyOfT和SetKeyOfT的仿函數傳給RBTree的KeyOfT,然后RBTree中通過KeyOfT仿函數取出T類型對象中的key,再進行比較,具體細節參考如下代碼實現。

set.h:

template<class k>
class set
{
public:struct SetKeyofT{const k& operator()(const k& key){return key;}};bool insert(const k& kv){return _t.Insert(kv);}
private:qcj::RBTree<k,  const k, SetKeyofT> _t;
};

map.h:

template<class k,class v>
class map
{
public:struct MapKeyofT{const k& operator()(const pair<k, v>& key){return key.first;}};bool insert(const pair<k, v>& kv) {return _t.Insert(kv);}
private:qcj::RBTree<k, pair< const k, v>, MapKeyofT> _t;
};

insert:

bool Insert(const T& x) 
{if (_root == nullptr)//插入根節點{_root = new node(x);_root->col = BLACK;return true;};node* cur = _root;node* parent = nullptr;//保留父親節點KeyofT kot;while (cur){/*介質不冗余*/if (kot(x) < kot(cur->_date)){parent = cur;cur = cur->left;}else if (kot(x) > kot(cur->_date)){parent = cur;cur = cur->right;}else{return false;}}cur = new node(x);cur->col = RED;if (kot(x) > kot(parent->_date)){parent->right = cur;}else//相等插入左子樹{parent->left = cur;}cur->parent = parent;while (parent&&parent->parent&&parent->col == RED){node* grandfather = parent->parent;if (parent == grandfather->left){node* uncle = grandfather->right;//         g//       p    u//     c  c//u存在且為紅if (uncle&&uncle->col==RED){parent->col = uncle->col=BLACK;grandfather->col = RED;cur = grandfather;parent = cur->parent;}//u不存在或存在為黑else{//         g//       p    //     cif (cur == parent->left){RoRateR(grandfather);parent->col = BLACK;grandfather->col = RED;}//         g//       p    //         celse{RoRateL(parent);RoRateR(grandfather);cur->col = BLACK;grandfather->col = RED;}break;}}else{node* uncle = grandfather->left;//         g//       u   p    //          c  c//u存在且為紅if (uncle && uncle->col == RED){parent->col = uncle->col = BLACK;grandfather->col = RED;cur = grandfather;parent = cur->parent;}//u不存在或存在為黑else{//         g//            p    //              cif (cur == parent->right){RoRateL(grandfather);parent->col = BLACK;grandfather->col = RED;}//         g//            p    //          celse{RoRateR(parent);RoRateL(grandfather);cur->col = BLACK;grandfather->col = RED;}break;}}}_root->col = BLACK;//循環結束把根變黑return true;
}

find:

node* Find(const k& x)
{node* ret = nullptr;node* cur = _root;while (cur){if (x < kot(cur)){cur = cur->left;}else if (x > kot(cur)){cur = cur->right;}else{ret = cur;//保留當前節點cur = cur->left;//繼續向左子樹查找中序的第一個}}return ret;
}

這里通過仿函數就取出key控制了比較邏輯。

三.迭代器

3.1思路分析

  • operator++
  • operator- -
  • 迭代器的key修改問題

    如果我們這樣實現的迭代器是可以修改的key的。
    但是紅黑樹的key是不能被修改的。那怎么辦呢?
    我們只需要把紅黑樹的第二個模版參數的key改為
    const key即可這樣迭代器的模版參數T的key也是const的了
    set的iterator也不支持修改,我們把set的第?個模板參數改成const K即可,
qcj::RBTree<k, pair< const k, v>, MapKeyofT> _t;
qcj::RBTree<k,  const k, SetKeyofT> _t;
  • 對比庫里的迭代器
    為了實現反向迭代器我們可以在operator- -多加一個判斷。
    同時還需要在迭代器里面加一個_root節點
    在這里插入圖片描述

  • 運算符重載
    運算符重載比較簡單具體參考下面的代碼

3.2 代碼實現

template<class T,class Ref,class Ptr>
struct RBTreeIteraor
{using node= RBNode<T>;using self= RBTreeIteraor< T, Ref, Ptr >;node* _node;node* _root;RBTreeIteraor(node* Node,node* root):_node(Node),_root(root){}self& operator++(){if (_node == nullptr){node* cur = _root;while (cur->right){cur = cur->right;}}else if (_node->right!=nullptr){node* cur = _node->right;while (cur->left){cur = cur->left;}_node = cur;}else{node* parent = _node->parent;while (parent&&_node != parent->left){_node = parent;parent = _node->parent;}_node = parent;}return *this;}self& operator--(){if (_node == nullptr){node* cur = _root;while (cur->right){cur = cur->right;}_node = cur;}else if (_node->left != nullptr){node* cur = _node->left;while (cur->right){cur = cur->right;}_node = cur;}else{node* parent = _node->parent;while (parent && _node != parent->right){_node = parent;parent = _node->parent;}_node = parent;}return *this;}Ref operator*(){return _node->_date;}Ptr operator->(){return &_node->_date;}bool operator==(const self& tmp) const{return _node==tmp._node;}bool operator!=(const self& tmp) const{return _node != tmp._node;}
};

四.operator[]

4.1思路分析

對于map我們還需要支持operator[]。
對于operator[]我們前面已經講過底層實現了。
參考博文:map的operator[]底層剖析
主要就是insert來支持的。改一下返回值即可。
讓insert返回迭代器和bool的pair即可。
返回insert迭代器的second也就是value即可。

v& operator[](const k& key)
{pair<iterator, bool> ret = _t.Insert(make_pair(key, v()));return ret.first->second;
}

五.源碼

  • set.h
#pragma once
#include"RBTree.h"
namespace qcj
{template<class k>class set{public:struct SetKeyofT{const k& operator()(const k& key){return key;}};public:typedef typename RBTree<k,  const k, SetKeyofT>::Iteratoriterator;typedef typename RBTree<k, const k, SetKeyofT>::Const_Iteratorconst_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}pair<iterator, bool> insert(const k& kv){return _t.Insert(kv);}iterator find(const k& key){return _t.find(key);}private:qcj::RBTree<k,  const k, SetKeyofT> _t;};void Print(const set<int>& s){set<int>::iterator it = s.end();while (it != s.begin()){--it;// 不?持修改//*it += 2;cout << *it << " ";}cout << endl;}void test_set(){set<int> s;int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){s.insert(e);}for (auto e : s){e += 1;cout << e << " ";}cout << endl;}}
  • map.h
#pragma once
#include"RBTree.h"
namespace qcj 
{template<class k,class v>class map{public:struct MapKeyofT{const k& operator()(const pair<k, v>& key){return key.first;}};public:typedef typename RBTree<k, pair<const  k, v>, MapKeyofT>::Iteratoriterator;typedef typename RBTree<k, pair<const  k, v>, MapKeyofT>::Const_Iteratorconst_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}pair<iterator, bool> insert(const pair<k, v>& kv) {return _t.Insert(kv);}iterator find(const k& key){return _t.find(key);}v& operator[](const k& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, v()));return ret.first->second;}private:qcj::RBTree<k, pair< const k, v>, MapKeyofT> _t;};void test_map(){map<int,int> dict;dict.insert({1,1});dict.insert({2,2 });dict.insert({3,3 });dict.insert({ 4,4 });dict.insert({ 5,5 });dict.insert({ 6,6 });map<int,int>::iterator it = dict.end();while (it != dict.begin()){--it;// 不能修改first,可以修改second//it->first += 'x';it->second += 1;cout << it->first << ":" << it->second << endl;}cout << endl;}
}
  • RBTree.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace qcj
{enum coloros{RED,BLACK};template<class T>struct RBNode{using node= RBNode<T>;T _date;node* left;//左節點node* right;//右節點node* parent;//父親節點coloros col;//顏色RBNode(const T& date):_date(date), left(nullptr), right(nullptr), parent(nullptr){}};template<class T,class Ref,class Ptr>struct RBTreeIteraor{using node= RBNode<T>;using self= RBTreeIteraor< T, Ref, Ptr >;node* _node;node* _root;RBTreeIteraor(node* Node,node* root):_node(Node),_root(root){}self& operator++(){if (_node == nullptr){node* cur = _root;while (cur->right){cur = cur->right;}}else if (_node->right!=nullptr){node* cur = _node->right;while (cur->left){cur = cur->left;}_node = cur;}else{node* parent = _node->parent;while (parent&&_node != parent->left){_node = parent;parent = _node->parent;}_node = parent;}return *this;}self& operator--(){if (_node == nullptr){node* cur = _root;while (cur->right){cur = cur->right;}_node = cur;}else if (_node->left != nullptr){node* cur = _node->left;while (cur->right){cur = cur->right;}_node = cur;}else{node* parent = _node->parent;while (parent && _node != parent->right){_node = parent;parent = _node->parent;}_node = parent;}return *this;}Ref operator*(){return _node->_date;}Ptr operator->(){return &_node->_date;}bool operator==(const self& tmp) const{return _node==tmp._node;}bool operator!=(const self& tmp) const{return _node != tmp._node;}};template<class k, class T, class KeyofT>class RBTree{using node = RBNode<T>;public:RBTree() = default;using Iterator = RBTreeIteraor<T, T&, T*>;using Const_Iterator = RBTreeIteraor<T, const T&, const T*>;Iterator Begin(){node* cur = _root;while (cur&&cur->left){cur = cur->left;}return Iterator(cur,_root );}Iterator End(){return Iterator(nullptr,_root);}Const_Iterator Begin() const{node* cur = _root;while (cur&&cur->left){cur = cur->left;}return { cur,_root };}Const_Iterator End() const{return { nullptr,_root };}void Destory(const node* root){if (root == nullptr){return;}Destory(root->left);Destory(root->right);delete root;}~RBTree(){Destory(_root);_root = nullptr;}RBTree<k,T,KeyofT>& operator = (RBTree<k, T, KeyofT> tmp){swap(_root, tmp._root);return *this;}RBTree(const RBTree<k, T, KeyofT>& x){_root = Copy(x._root,nullptr);}node* Copy(node* x,node* parent){if (x == nullptr){return x;}node* root = new node(x->_date);root->parent = parent;root->left = Copy(x->left,root);root->right = Copy(x->right,root);return root;}void RoRateR(node* parent)//右單旋{node* subL = parent->left;node* subLR = subL->right;node* pparnet = parent->parent;parent->left = subLR;if (subLR){subLR->parent = parent;//修改父節點}subL->right = parent;parent->parent = subL;if (pparnet == nullptr)//parent就是根節點{_root = subL;subL->parent = nullptr;}else{if (pparnet->left == parent)//確定parent節點是左還是右{pparnet->left = subL;}else{pparnet->right = subL;}subL->parent = pparnet;//修改父節點}}void RoRateL(node* parent)//左單旋{node* subR = parent->right;node* subRL = subR->left;node* pparnet = parent->parent;parent->right = subRL;if (subRL)//防止subRL為空{subRL->parent = parent;//修改父節點}subR->left = parent;parent->parent = subR;if (pparnet == nullptr)//parent就是根節點{_root = subR;subR->parent = nullptr;}else{if (pparnet->left == parent)//確定parent節點是左還是右{pparnet->left = subR;}else{pparnet->right = subR;}subR->parent = pparnet;//修改父節點}}pair<Iterator,bool> Insert(const T& x) {if (_root == nullptr)//插入根節點{_root = new node(x);_root->col = BLACK;return make_pair(Iterator(_root, _root), true);};node* cur = _root;node* parent = nullptr;//保留父親節點KeyofT kot;while (cur){/*介質不冗余*/if (kot(x) < kot(cur->_date)){parent = cur;cur = cur->left;}else if (kot(x) > kot(cur->_date)){parent = cur;cur = cur->right;}else{return make_pair(Iterator(cur, _root), true);}}cur = new node(x);cur->col = RED;if (kot(x) > kot(parent->_date)){parent->right = cur;}else//相等插入左子樹{parent->left = cur;}cur->parent = parent;while (parent&&parent->parent&&parent->col == RED){node* grandfather = parent->parent;if (parent == grandfather->left){node* uncle = grandfather->right;//         g//       p    u//     c  c//u存在且為紅if (uncle&&uncle->col==RED){parent->col = uncle->col=BLACK;grandfather->col = RED;cur = grandfather;parent = cur->parent;}//u不存在或存在為黑else{//         g//       p    //     cif (cur == parent->left){RoRateR(grandfather);parent->col = BLACK;grandfather->col = RED;}//         g//       p    //         celse{RoRateL(parent);RoRateR(grandfather);cur->col = BLACK;grandfather->col = RED;}break;}}else{node* uncle = grandfather->left;//         g//       u   p    //          c  c//u存在且為紅if (uncle && uncle->col == RED){parent->col = uncle->col = BLACK;grandfather->col = RED;cur = grandfather;parent = cur->parent;}//u不存在或存在為黑else{//         g//            p    //              cif (cur == parent->right){RoRateL(grandfather);parent->col = BLACK;grandfather->col = RED;}//         g//            p    //          celse{RoRateR(parent);RoRateL(grandfather);cur->col = BLACK;grandfather->col = RED;}break;}}}_root->col = BLACK;//循環結束把根變黑return make_pair(Iterator(cur, _root), true);}bool check(node* cur,size_t tmp,size_t sum){if (cur == nullptr){if (tmp != sum){cout << "存在黑色結點的數量不相等的路徑" << endl;return false;}return true;}if (cur->col == RED){if (cur->parent->col == RED){cout << cur->_key << ":" << "存在連續紅節點" << endl;return false;}}else{sum++;}return check(cur->left, tmp, sum) && check(cur->right, tmp, sum);}bool ISRBTree(){return _ISRBTree(_root);}bool _ISRBTree(node* cur){if (cur == nullptr){return true;}if (cur->col == RED){return false;}size_t t = 0;while (cur){if (cur->col == BLACK){t++;}cur = cur->left;}return check(_root,t,0);}node* Find(const k& x){node* ret = nullptr;node* cur = _root;while (cur){if (x < kot(cur)){cur = cur->left;}else if (x > kot(cur)){cur = cur->right;}else{ret = cur;//保留當前節點cur = cur->left;//繼續向左子樹查找中序的第一個}}return ret;}void Inorder(){_Inorder(_root);cout << endl;}bool IsBalanceTree(){return _IsBalanceTree(_root);}void _Inorder(const node* root){if (root == nullptr){return;}_Inorder(root->left);cout << root->_key << ":" << root->_value << endl;_Inorder(root->right);}size_t Size(){return _Size(_root);}size_t _Size(const node* parent){if (parent){return 1 + _Size(parent->left) + _Size(parent->right);}else{return 0;}}size_t Height(){return _Height(_root);}size_t _Height(const node* parent){if (parent){return 1 + max(_Height(parent->left), _Height(parent->right));}else{return 0;}}bool Empty(){return _root == nullptr;}private:node* _root = nullptr;};
}

在這里插入圖片描述

后言

這就是紅黑樹封裝map和set深度剖析。大家自己好好消化!今天就分享到這!感謝各位的耐心垂閱!咱們下期見!拜拜~

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

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

相關文章

win32匯編環境,怎么得到磁盤的盤符

;運行效果 ;win32匯編環境,怎么得到磁盤的盤符 ;以下代碼主要為了展示一下原理&#xff0c;應用GetLogicalDrives、GetLogicalDriveStrings函數、屏蔽某些二進制位、按雙字節復制內容等。以下代碼最多查8個盤&#xff0c;即返回值中的1個字節的信息 ;直接抄進RadAsm可編譯運行。…

MongoDB vs Redis:相似與區別

前言 在當今的數據庫領域&#xff0c;MongoDB 和 Redis 都是備受關注的非關系型數據庫&#xff08;NoSQL&#xff09;&#xff0c;它們各自具有獨特的優勢和適用場景。本文將深入探討 MongoDB 和 Redis 的特點&#xff0c;并詳細對比它們之間的相似之處和區別&#xff0c;幫助…

mybatis(19/134)

大致了解了一下工具類&#xff0c;自己手敲了一邊&#xff0c;java的封裝還是真的省去了很多麻煩&#xff0c;封裝成一個工具類就可以不用寫很多重復的步驟&#xff0c;一個工廠對應一個數據庫一個environment就好了。 mybatis中調用sql中的delete占位符里面需要有字符&#xf…

重學SpringBoot3-WebClient配置與使用詳解

更多SpringBoot3內容請關注我的專欄&#xff1a;《SpringBoot3》 期待您的點贊??收藏評論 重學SpringBoot3-WebClient配置與使用詳解 1. 簡介2. 環境準備 2.1 依賴配置 3. WebClient配置 3.1 基礎配置3.2 高級配置3.3 retrieve()和exchange()區別 4. 使用示例 4.1 基本請求操…

.Net Core微服務入門全紀錄(二)——Consul-服務注冊與發現(上)

系列文章目錄 1、.Net Core微服務入門系列&#xff08;一&#xff09;——項目搭建 2、.Net Core微服務入門全紀錄&#xff08;二&#xff09;——Consul-服務注冊與發現&#xff08;上&#xff09; 3、.Net Core微服務入門全紀錄&#xff08;三&#xff09;——Consul-服務注…

Spark Streaming的核心功能及其示例PySpark代碼

Spark Streaming是Apache Spark中用于實時流數據處理的模塊。以下是一些常見功能的實用PySpark代碼示例&#xff1a; 基礎流處理&#xff1a;從TCP套接字讀取數據并統計單詞數量 from pyspark import SparkContext from pyspark.streaming import StreamingContext# 創建Spar…

深度學習系列75:sql大模型工具vanna

1. 概述 vanna是一個可以將自然語言轉為sql的工具。簡單的demo如下&#xff1a; !pip install vanna import vanna from vanna.remote import VannaDefault vn VannaDefault(modelchinook, api_keyvanna.get_api_key(my-emailexample.com)) vn.connect_to_sqlite(https://va…

【線性代數】列主元法求矩陣的逆

列主元方法是一種用于求解矩陣逆的數值方法&#xff0c;特別適用于在計算機上實現。其基本思想是通過高斯消元法將矩陣轉換為上三角矩陣&#xff0c;然后通過回代求解矩陣的逆。以下是列主元方法求解矩陣 A A A 的逆的步驟&#xff1a; [精確算法] 列主元高斯消元法 步驟 1&am…

[0242-06].第06節:SpringBoot對SpringMVC的自動配置

SpringBoot學習大綱 一、基于SpringBoot搭建Web工程&#xff1a; 1.1.編碼實現步驟&#xff1a; a.創建SpringBoot項目 b.選中依賴&#xff1a;選中我們所需要的模塊 1.2.SSM中的WEB開發配置與SpringBoot中WEB開發自動配置對比&#xff1a; a.SSM中的WEB開發&#xff1a; 1…

【21】Word:德國旅游業務?

目錄 題目 NO1.2.3 NO4 NO5.6 NO7 NO8.9.10.11 題目 NO1.2.3 F12&#xff1a;另存為布局→頁面設置→頁邊距&#xff1a;上下左右選中“德國主要城市”→開始→字體對話框→字體/字號→文本效果&#xff1a;段落對話框→對齊方式/字符間距/段落間距 NO4 布局→表對話框…

什么是軟件架構

什么是軟件架構 程序員說&#xff0c;軟件架構是要決定編寫哪些C程序或OO類、使用哪些庫和框架 程序經理說&#xff0c;軟件架構就是模塊的劃分和接口的定義 系統分析員說&#xff0c;軟件架構就是為業務領域對象的關系建模 配置管理員說&#xff0c;軟件架構就是開發出來的…

1/20賽后總結

1/20賽后總結 T1『討論區管理員』的旅行 - BBC編程訓練營 算法&#xff1a;IDA* 分數&#xff1a;0 damn it! Ac_code走丟了~~&#xff08;主要是沒有寫出來&#xff09;~~ T2華強買瓜 - BBC編程訓練營 算法&#xff1a;雙向DFS或者DFS剪枝 分數&#xff1a;0 Ac_code…

大數據與AI驅動的商業查詢平臺:企業市場拓展的變革引擎?

在競爭白熱化的商業環境里&#xff0c;企業對準確市場信息的高效獲取能力&#xff0c;直接關系到業務拓展的成敗。商業查詢平臺借助大數據和人工智能技術&#xff0c;為企業提供精準客戶篩選、市場拓展分析以及風險評估服務&#xff0c;正逐漸成為企業市場開拓的得力助手。本文…

redis 各個模式的安裝

一、Redis單機安裝 1、安裝gcc依賴 Redis是C語言編寫的&#xff0c;編譯需要GCC。 Redis6.x.x版本支持了多線程&#xff0c;需要gcc的版本大于4.9&#xff0c;但是CentOS7的默認版本是4.8.5。 升級gcc版本&#xff1a; yum -y install centos-release-scl yum -y install d…

TiDB 的優勢與劣勢

TiDB 的優勢與劣勢 TiDB 作為一款新興的分布式數據庫&#xff0c;在業界逐漸嶄露頭角。它兼具傳統關系型數據庫的特性&#xff0c;又充分利用分布式架構的優勢。那么&#xff0c;TiDB 究竟有怎樣的優缺點呢&#xff1f;今天我們來聊聊 TiDB 的優勢與劣勢&#xff0c;幫你全面了…

藍橋杯算法日常|c\c++常用競賽函數總結備用

一、字符處理相關函數 大小寫判斷函數 islower和isupper&#xff1a;是C標準庫中的字符分類函數&#xff0c;用于檢查一個字符是否為小寫字母或大寫字母&#xff0c;需包含頭文件cctype.h&#xff08;也可用萬能頭文件包含&#xff09;。返回布爾類型值。例如&#xff1a; #…

微服務知識——4大主流微服務架構方案

文章目錄 1、微服務聚合模式2、微服務共享模式3、微服務代理模式4、微服務異步消息模式 微服務是大型架構的必經之路&#xff0c;也是大廠重點考察對象&#xff0c;下面我就重點詳解4大主流微服務架構方案。 1、微服務聚合模式 微服務聚合設計模式&#xff0c;解決了如何從多個…

【HTML+CSS】使用HTML與后端技術連接數據庫

目錄 一、概述 1.1 HTML前端 1.2 后端技術 1.3 數據庫 二、HTML表單示例 三、PHP后端示例 3.1 連接數據庫 3.2 接收數據并插入數據庫 四、安全性 4.1 防止SQL注入 4.2 數據驗證與清洗 五、優化 5.1 索引優化 5.2 查詢優化 六、現代Web開發中的最佳實踐 6.1 使用…

T-SQL語言的數據庫編程

T-SQL語言的數據庫編程 1. 引言 在信息化迅速發展的今天&#xff0c;數據庫已經成為數據管理和使用的重要工具。其中&#xff0c;T-SQL&#xff08;Transact-SQL&#xff09;作為微軟SQL Server的擴展SQL語言&#xff0c;不僅用于數據查詢和管理&#xff0c;還能夠進行復雜的…

通信協議—WebSocket

一、WebSocket編程概念 1.1 什么是WebSocket WebSocket 是一種全雙工通信協議&#xff0c;允許在客戶端&#xff08;通常是瀏覽器&#xff09;和服務器之間建立持久連接&#xff0c;以實現實時的雙向通信。它是 HTML5 標準的一部分&#xff0c;相比傳統的 HTTP 請求&#xff…