CD47.【C++ Dev】list的模擬實現(2)

目錄

1.const修飾的迭代器的實現

方法1:分成兩個類

完整代碼

方法2:STL庫的寫法

2.STL庫的第三個模版參數T*的解釋

->->的簡寫語法?

3.其他成員函數

insert

erase

push_back、push_front、pop_front、pop_back

size

clear

析構函數~list()

拷貝構造函數(★)

私有函數empty_initialize

swap

operator=

4.提交到leetcode題上測試成員函數的正確性

代碼

提交結果


承接CD46.【C++ Dev】list的模擬實現(1)文章

1.const修飾的迭代器的實現

回顧const修飾的迭代器的要求:1.自己可以修改 2.指向的數據不能修改

方法1:分成兩個類

const修飾的迭代器和非const修飾的迭代器分成兩個類,寫兩份代碼,這兩份代碼僅僅是operator*的返回的參數不同

//const修飾的迭代器
const T& operator*()
{return node->data;
}//非const修飾的迭代器
T& operator*()
{return node->data;
}

注意:const修飾的迭代器中的operator*不能寫成T& operator*() const,這個const修飾的是this指針指向的對象node不能被修改(在CD22.【C++ Dev】類和對象(13) 流提取運算符的重載和const成員文章講過),不是指node->data不能修改

完整代碼

#pragma once
namespace mystl
{template<class T>struct __list_node{typedef __list_node<T>* link_type;__list_node(const T& x = T()):next(nullptr), prev(nullptr), data(x){}link_type next;link_type prev;T data;};template<class T>struct __list_iterator{typedef __list_node<T>* link_type;typedef __list_iterator<T> iterator;__list_iterator(link_type x):node(x){}iterator& operator++(){node = node->next;return *this;}iterator operator++(int){iterator tmp(*this);node = node->next;return tmp;}iterator& operator--(){node = node->prev;return *this;}iterator operator--(int){iterator tmp(*this);node = node->prev;return tmp;}bool operator!=(const iterator& x) const{return node != x.node;}bool operator==(const iterator& x) const{return node == x.node;}T& operator*(){return node->data;}link_type node;};template<class T>struct __list_const_iterator{typedef __list_node<T>* link_type;typedef __list_const_iterator<T> const_iterator;__list_const_iterator(link_type x):node(x){}const_iterator& operator++(){node = node->next;return *this;}const_iterator operator++(int){const_iterator tmp(*this);node = node->next;return tmp;}const_iterator& operator--(){node = node->prev;return *this;}const_iterator operator--(int){const_iterator tmp(*this);node = node->prev;return tmp;}bool operator!=(const const_iterator& x) const{return node != x.node;}bool operator==(const const_iterator& x) const{return node == x.node;}const T& operator*(){return node->data;}link_type node;};template<class T>class list{typedef __list_node<T> list_node;typedef __list_node<T>* link_type;public:typedef __list_iterator<T> iterator;typedef __list_const_iterator<T> const_iterator;list(){node = new list_node;node->next = node;node->prev = node;}void push_back(const T& x){link_type tmp = new list_node(x);//先找尾link_type tail = node->prev;tail->next = tmp;tmp->prev = tail;tmp->next = node;node->prev = tmp;}iterator begin(){//返回哨兵位的下一個節點return node->next;}iterator end(){//返回哨兵位return node;}const_iterator begin() const{//返回哨兵位的下一個節點return node->next;}const_iterator end() const{//返回哨兵位return node;}private:link_type node;};
}

測試代碼:

#include <iostream>
#include "list.h"
void print_list(const mystl::list<int>& ls)//權限縮小
{mystl::list<int>::const_iterator it = ls.begin();while (it != ls.end()){std::cout << *it << " ";it++;}}
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);print_list(ls);return 0;
}

運行結果:

方法2:STL庫的寫法

STL的源碼:__list_iterator迭代器類有3個模版參數:T、Ref、Ptr

template<class T, class Ref, class Ptr>
struct __list_iterator {typedef __list_iterator<T, T&, T*>             iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;typedef __list_iterator<T, Ref, Ptr>           self;//......typedef Ref reference;//......reference operator*() const { return (*node).data; }
}

operator*的返回類型為reference,而reference為Ref,Ref是__list_iterator類的第二個模版參數

這樣可以為Ref指定:T&或者const T&,這樣可以簡寫const修飾和非const修飾的迭代器,沒有必要寫兩份代碼

之前返回類型為iterator要改成__list_iterator<T, Ref>,可以重定義為self?

其實是讓編譯器來代替我們寫iterator和const_iterator這兩個類

template<class T,class Ref>
struct __list_iterator
{typedef __list_node<T>* link_type;typedef __list_iterator<T,T&> iterator;typedef __list_iterator<T,const T&> const_iterator;typedef __list_iterator<T, Ref>	self;typedef Ref reference;__list_iterator(link_type x):node(x){}self& operator++(){node = node->next;return *this;}self operator++(int){self tmp(*this);node = node->next;return tmp;}self& operator--(){node = node->prev;return *this;}self operator--(int){self tmp(*this);node = node->prev;return tmp;}bool operator!=(const self& x) const{return node != x.node;}bool operator==(const self& x) const{return node == x.node;}reference operator*(){return node->data;}link_type node;
};

繼續測試之前的代碼,運行結果:

2.STL庫的第三個模版參數T*的解釋

上方代碼實現的__list_iterator只有2個模版參數,但是STL庫卻有3個模板參數:

template<class T, class Ref, class Ptr>
struct __list_iterator 
{//......
}

看看Ptr出現在什么函數中:

STL庫將Ptr重定義為pointer:?

typedef Ptr pointer;

那就找pointer還出現在哪里:

#ifndef __SGI_STL_NO_ARROW_OPERATORpointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */

發現:pointer是operator->的返回類型,原因:

迭代器的任務是模擬指針,而結構體指針是可以用operator->來訪問其指向對象的成員,那迭代器也要有operator->操作符

?那自制的operator->可以這樣寫:

template<class T,class Ref,class Ptr>
struct __list_iterator
{typedef __list_node<T>* link_type;typedef __list_iterator<T,T&,T*> iterator;typedef __list_iterator<T,const T&,const T*> const_iterator;typedef __list_iterator<T, Ref,Ptr>	self;typedef Ref reference;typedef Ptr pointer;//......reference operator*(){return node->data;}pointer operator->(){return &(node->data);}link_type node;
};

當然也可以復用operator*,寫成:

pointer operator->()
{return &(operator*());
}

測試代碼:

#include <iostream>
#include "list.h"
class  Myclass
{
public:Myclass(const int val1=0, const char val2='\0'):_val1(val1), _val2(val2){}int _val1;char _val2;
};int main()
{mystl::list<Myclass> ls;ls.push_back(Myclass(1,'a'));ls.push_back(Myclass(2, 'b'));ls.push_back(Myclass(3, 'c'));mystl::list<Myclass>::iterator it = ls.begin();while (it != ls.end()){std::cout << it->_val1 << " " << it->_val2 << std::endl;std::cout << (*it)._val1 << " " << (*it)._val2 << std::endl;it++;}return 0;
}

運行結果:

發現it->_val1和(*it)._val1的效果是等價的

->->的簡寫語法?

注意到operator->()返回的是Myclass*,嚴謹來說Myclass*是不能訪問_val1和_val2的,應該寫成

std::cout << it->->_val1 << " " << it->->_val2 << std::endl;

但編譯器在編譯時自動加上了第二個->,C++標準只允許寫一個->,提高運算符重載的可讀性?

3.其他成員函數

insert

和STL保持一致:

這里只實現第一個:?

先生成新節點,再在pos前插入:

iterator insert(iterator pos,const T& val)
{link_type newnode = new list_node(val);newnode->prev = pos.node->prev;newnode->next = pos.node;pos.node->prev->next = newnode;pos.node->prev = newnode;return newnode;
}

測試代碼:

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);auto new_it1=ls.insert(++ls.begin(), 4);//在2的前面插入4for (auto a : ls)std::cout << a << " ";return 0;
}

運行結果:

erase

和STL保持一致:

這里只實現第一個:?

注意:erase不能刪哨兵位,因此先斷言

erase函數在刪除元素時,會?使當前迭代器失效?n并返回指向?下一個元素?的迭代器,因此不能返回void

iterator erase(iterator pos)
{assert(pos != end());iterator ret = pos.node->next;pos.node->prev->next = pos.node->next;pos.node->next->prev = pos.node->prev;delete pos.node;return ret;
}

測試代碼:

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.erase(++ls.begin());//刪除2for (auto a : ls)std::cout << a << " ";return 0;
}

運行結果:

push_back、push_front、pop_front、pop_back

可以復用insert和erase的代碼

void push_back(const T& x)
{insert(end(), x);
}void push_front(const T& x)
{insert(begin(), x);
}
void pop_front()
{erase(begin());
}
void pop_back()
{erase(--end());
}

?測試代碼:

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.pop_back();ls.pop_front();ls.push_back(5);ls.push_front(6);for (auto a : ls)std::cout << a << " ";return 0;
}

運行結果:

size

STL庫的實現:

size_type size() const {size_type result = 0;distance(begin(), end(), result);return result;
}

distance是算法庫<algorithm>中的函數,用于計算兩個迭代器之間的距離,并將結果存儲到result中,這里我們實現的簡單一些:

void distance(const_iterator begin, const_iterator end, size_type& result) const
{const_iterator it = begin;while (it != end){it++;result++;}
}
size_type size() const 
{size_type result = 0;distance(begin(), end(), result);return result;
}

測試代碼:

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);std::cout << ls.size();return 0;
}

(當然這樣遍歷一遍是有缺點的,比較耗時,可以為list類引入第二個成員變量size來存儲鏈表的長度,這里省略不寫)?

運行結果:

clear

和STL保持一致:

刪除多余節點,只保留哨兵位

void clear()
{iterator it = begin();while (it != end())it=erase(it);//應對迭代器失效,接收返回值
}

測試代碼:

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);for (auto a : ls)std::cout << a << " ";ls.clear();ls.push_back(4);ls.push_back(5);ls.push_back(6);for (auto b : ls)std::cout << b << " ";return 0;
}

運行結果:

析構函數~list()

先調用clear(),再將node釋放,最后置為空指針

~list()
{clear();delete node;node = nullptr;
}

拷貝構造函數(★)

注意是深拷貝,淺拷貝會出問題(在CD44.【C++ Dev】vector模擬實現(3)文章的深拷貝問題解決提到過)

方法:通過遍歷的方式一個個拷貝

//const修飾,防止權限放大
list(const list<T>& ls)//必須是引用,這個&如果不寫,會無限調用拷貝構造函數
{//準備哨兵位node = new list_node;node->next = node;node->prev = node;//添加節點for (auto& a : ls)//使用引用,節省時間{push_back(a);}
}

測試代碼:

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);mystl::list<int> ls_copy(ls);for (auto a : ls_copy)std::cout << a<<" ";return 0;
}

運行結果:

?

私有函數empty_initialize

兩種構造函數的部分代碼有些冗余

可以封裝成一個私有函數empty_initialize(),當然庫里面也是這樣實現的:

swap

和STL保持一致:

注:上面swap的參數:list& x的list是類名,不是類型,卻也可以做參數,C++的語法支持這樣做,但不建議這樣寫,代碼不直觀

交換鏈表其實是交換頭節點

void swap(list<T>& ls)//寫成void swap(list& ls)也可以
{std::swap(node, ls.node);
}

測試代碼:

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls1; ls1.push_back(1); ls1.push_back(2); ls1.push_back(3);mystl::list<int> ls2; ls2.push_back(4); ls2.push_back(5); ls2.push_back(6);std::cout << "ls1:";for (auto a : ls1) std::cout << a << " ";std::cout << std::endl << "ls2:";for (auto b : ls2) std::cout << b << " ";ls1.swap(ls2);std::cout << std::endl << "ls1:";for (auto a : ls1) std::cout << a<<" ";std::cout << std::endl << "ls2:";for (auto b : ls2) std::cout << b<<" ";return 0;
}

運行結果:

operator=

使用CD40.【C++ Dev】string類的模擬實現(4)(operator=、拷貝構造函數的現代寫法、寫時拷貝的簡單了解)文章提到的現代寫法:1.operator=的參數需要調用拷貝構造 2.調用swap

list<T>& operator=(list<T> tmp)//寫成list& operator=(list tmp)也可以
{swap(tmp);return *this;
}

測試代碼:

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls1; ls1.push_back(1); ls1.push_back(2); ls1.push_back(3);mystl::list<int> ls2 = ls1;std::cout << "ls2:";for (auto a : ls2) std::cout << a << " ";return 0;
}

4.提交到leetcode題上測試成員函數的正確性

題:https://leetcode.cn/problems/design-linked-list/

你可以選擇使用單鏈表或者雙鏈表,設計并實現自己的鏈表。

單鏈表中的節點應該具備兩個屬性:valnextval 是當前節點的值,next 是指向下一個節點的指針/引用。

如果是雙向鏈表,則還需要屬性?prev?以指示鏈表中的上一個節點。假設鏈表中的所有節點下標從 0 開始。

實現 MyLinkedList 類:

  • MyLinkedList() 初始化 MyLinkedList 對象。
  • int get(int index) 獲取鏈表中下標為 index 的節點的值。如果下標無效,則返回 -1
  • void addAtHead(int val) 將一個值為 val 的節點插入到鏈表中第一個元素之前。在插入完成后,新節點會成為鏈表的第一個節點。
  • void addAtTail(int val) 將一個值為 val 的節點追加到鏈表中作為鏈表的最后一個元素。
  • void addAtIndex(int index, int val) 將一個值為 val 的節點插入到鏈表中下標為 index 的節點之前。如果 index 等于鏈表的長度,那么該節點會被追加到鏈表的末尾。如果 index 比長度更大,該節點將 不會插入 到鏈表中。
  • void deleteAtIndex(int index) 如果下標有效,則刪除鏈表中下標為 index 的節點。

示例:

輸入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
輸出
[null, null, null, null, 2, null, 3]解釋
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 鏈表變為 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 現在,鏈表變為 1->3
myLinkedList.get(1);              // 返回 3

提示:

  • 0 <= index, val <= 1000
  • 請不要使用內置的 LinkedList 庫。
  • 調用 getaddAtHeadaddAtTailaddAtIndexdeleteAtIndex 的次數不超過 2000

代碼

namespace mystl
{template<class T>struct __list_node{typedef __list_node<T>* link_type;__list_node(const T& x = T()):next(nullptr), prev(nullptr), data(x){}link_type next;link_type prev;T data;};template<class T,class Ref,class Ptr>struct __list_iterator{typedef __list_node<T>* link_type;typedef __list_iterator<T,T&,T*> iterator;typedef __list_iterator<T,const T&,const T*> const_iterator;typedef __list_iterator<T, Ref,Ptr>	self;typedef Ref reference;typedef Ptr pointer;__list_iterator(link_type x):node(x){}self& operator++(){node = node->next;return *this;}self operator++(int){self tmp(*this);node = node->next;return tmp;}self& operator--(){node = node->prev;return *this;}self operator--(int){self tmp(*this);node = node->prev;return tmp;}bool operator!=(const self& x) const{return node != x.node;}bool operator==(const self& x) const{return node == x.node;}reference operator*(){return node->data;}pointer operator->(){return &(node->data);}link_type node;};template<class T>class list{typedef __list_node<T> list_node;typedef __list_node<T>* link_type;typedef size_t size_type;public:typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;list(){empty_initialize();}list(const list<T>& ls){empty_initialize();for (auto& a : ls){push_back(a);}}void push_back(const T& x){insert(end(), x);}iterator begin(){return node->next;}iterator end(){//返回哨兵位return node;}const_iterator begin() const{return node->next;}const_iterator end() const{return node;}iterator insert(iterator pos,const T& val){link_type newnode = new list_node(val);newnode->prev = pos.node->prev;newnode->next = pos.node;pos.node->prev->next = newnode;pos.node->prev = newnode;return newnode;}iterator erase(iterator pos){assert(pos != end());iterator ret = pos.node->next;pos.node->prev->next = pos.node->next;pos.node->next->prev = pos.node->prev;delete pos.node;return ret;}void push_front(const T& x){insert(begin(), x);}void pop_front(){erase(begin());}void pop_back(){erase(--end());}void distance(const_iterator begin, const_iterator end, size_type& result) const{const_iterator it = begin;while (it != end){it++;result++;}}size_type size() const {size_type result = 0;distance(begin(), end(), result);return result;}void clear(){iterator it = begin();while (it != end())it=erase(it);}void swap(list<T>& ls){std::swap(node, ls.node);}list<T>& operator=(list<T> tmp){swap(tmp);return *this;}~list(){clear();delete node;node = nullptr;}private:void empty_initialize(){node = new list_node;node->next = node;node->prev = node;}link_type node;};
}class MyLinkedList {
public:MyLinkedList() {}mystl::list<int>::iterator getiterator(int index){mystl::list<int>::iterator it=ls.begin();while(index--)it++;return it;}int get(int index) {if (index<ls.size())return getiterator(index).node->data; return -1;       }void addAtHead(int val) {ls.push_front(val);}void addAtTail(int val) {ls.push_back(val);}void addAtIndex(int index, int val) {if (index<=ls.size())//取等是尾插ls.insert(getiterator(index),val);}void deleteAtIndex(int index) {if (index<ls.size())ls.erase(getiterator(index));}mystl::list<int> ls;
};

提交結果

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

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

相關文章

UI前端與數字孿生融合新領域拓展:智慧教育的虛擬實驗室建設

hello寶子們...我們是艾斯視覺擅長ui設計、前端開發、數字孿生、大數據、三維建模、三維動畫10年經驗!希望我的分享能幫助到您!如需幫助可以評論關注私信我們一起探討!致敬感謝感恩!一、引言&#xff1a;虛擬實驗室 —— 打破教育邊界的技術革命傳統實驗教學正面臨 “設備昂貴、…

7. TCP 和 UDP 的區別

總結 TCP 面向連接&#xff0c;需要三次握手建立連接&#xff0c;UDP 無連接&#xff0c;不需要握手&#xff0c;直接發送數據。UDP 有較好的實時性&#xff0c;效率比 TCP 高。TCP 面向字節流&#xff0c;實際上是 TCP 把數據看成一連串無結構的字節流&#xff0c;UDP 是面向報…

iOS Widget 開發-7:TimelineProvider 機制全解析:構建未來時間線

在 WidgetKit 中&#xff0c;TimelineProvider 是小組件生命周期的核心機制之一。它控制著 數據獲取時機、展示內容 與 刷新策略&#xff0c;是實現時間驅動內容更新的基礎。 本文將介紹 TimelineProvider 的工作原理、設計模式、常見場景與高級用法&#xff0c;幫助大家構建智…

基于PHP/MySQL的企業培訓考試系統源碼,高并發、穩定運行,源碼開源可二開

溫馨提示&#xff1a;文末有資源獲取方式這是一款專為企業設計的開源培訓考試系統&#xff0c;采用PHPMySQL技術棧開發&#xff0c;具有高并發處理能力和穩定運行特性。系統源碼完全開放&#xff0c;支持二次開發&#xff0c;可滿足各類企業的培訓考核需求。核心功能特點1. 高性…

時序數據庫InfluxDB

一.定義 時序數據庫 是一種專門用于高效存儲和查詢帶有時間戳的數據的數據庫。如果你的數據是隨著時間變化而不斷產生&#xff0c;并且你想知道過去某一時刻發生了什么&#xff0c;那么你應該用時序數據庫。 這類數據通常具有以下特征&#xff1a; 數據點按時間順序不斷寫入…

2025.07.09華為機考真題解析-第三題300分

?? 點擊直達筆試專欄 ??《大廠筆試突圍》 ?? 春秋招筆試突圍在線OJ ?? 筆試突圍OJ 03. 博物館安保攝像頭配置 問題描述 A先生負責為一家新開的博物館設計安保監控系統。博物館有多個展廳需要監控,每個展廳都有不同的面積。現在有多種型號的監控攝像頭可供選擇,每…

存儲過程封裝:復雜業務邏輯的性能優化

存儲過程作為數據庫層面的重要功能&#xff0c;能夠顯著提升復雜業務邏輯的執行效率。以下是存儲過程在性能優化中的核心優勢、實現策略和實際應用場景。一、存儲過程的核心優勢?網絡傳輸壓縮?存儲過程將多條SQL語句封裝為單次調用&#xff0c;相比應用層多次請求可減少60%-8…

逗號分隔字段統計秘籍:一條SQL實現逗號分割字段的數量分析

一、問題場景與痛點 在數據庫設計中&#xff0c;經常會遇到統計某一些數據的最大數量最小數量等&#xff0c;特別是**逗號分隔字段 **的統計會顯得非常困難 下面以我生產上遇到的一個問題講解&#xff1a; 有個需求是在o_work_order表中統計sn字段中哪個工單號的數量最多&#…

數據庫性能優化指南:解決ORDER BY導致的查詢性能問題( SQL Server )

數據庫性能優化指南&#xff1a;解決ORDER BY導致的查詢性能問題 問題描述 在300萬行的INTERFACE_INTERACTION_LOG表中執行以下查詢&#xff1a; SELECT TOP 1 * FROM INTERFACE_INTERACTION_LOG WHERE 1 1AND (SENDSTATUS 0 OR SENDSTATUS -1)AND SENDMETHOD POSTAND ERRO…

Centos 7下使用C++使用Rdkafka庫實現生產者消費者

1. 了解 Kafka Apache Kafka 是一個分布式流處理平臺&#xff0c;核心功能包括&#xff1a; 發布/訂閱消息系統&#xff1a;解耦生產者和消費者 分布式存儲&#xff1a;持久化、容錯的消息存儲 流處理&#xff1a;實時處理數據流 核心概念&#xff1a; 概念說明BrokerKaf…

UE5多人MOBA+GAS 13、添加死亡、復活邏輯以及布娃娃含物理資產的修改調整

文章目錄使用GE為角色添加定時的Tag控制死亡時間1、添加死亡Tag2、創建死亡GE&#xff0c;并完成相關配置3、在AbilitySystemComponent中監聽屬性的變化&#xff0c;調用GE來添加Tag到角色上4、在角色中監聽ASC傳入的Tag以及Tag的層數&#xff0c;來響應不同的函數添加死亡、復…

Jiasou TideFlow重塑AI SEO全鏈路自動化新標桿

引言 在Google日均處理85億次搜索請求的數字化浪潮中&#xff0c;傳統SEO工作流面臨三大致命瓶頸&#xff1a;人工拓詞效率低下、跨部門協作成本高企、數據監控鏈路斷裂。因此諸如Jiasou AI SEO這樣專門為AI SEO而生的Agent就應運而生了。 背景 Jiasou AIGC不僅僅可以批量生成…

CentOs 7 MySql8.0.23之前的版本主從復制

準備倆臺虛擬機并啟動倆臺虛擬機都開啟mysql后查看二進制日志是否開啟先登錄mysqlmysql -u root -r輸入sql命令show variables like %log_bin%;如果log_bin 的value為OFF則是沒有開啟&#xff0c;跟著下面步驟開啟二進制日志退出mysqlexitvim /etc/my.cnf在最底下添加log_binmy…

Leetcode 3607. Power Grid Maintenance

Leetcode 3607. Power Grid Maintenance 1. 解題思路2. 代碼實現 題目鏈接&#xff1a;3607. Power Grid Maintenance 1. 解題思路 這一題思路上首先是一個DSU的思路&#xff0c;將所有的連通網絡計算出來&#xff0c;并對每一個網絡的節點進行歸類。然后我們需要對每一個網…

開源 python 應用 開發(三)python語法介紹

最近有個項目需要做視覺自動化處理的工具&#xff0c;最后選用的軟件為python&#xff0c;剛好這個機會進行系統學習。短時間學習&#xff0c;需要快速開發&#xff0c;所以記錄要點步驟&#xff0c;防止忘記。 鏈接&#xff1a; 開源 python 應用 開發&#xff08;一&#xf…

1-Kafka介紹及常見應用場景

Kafka 介紹 Apache Kafka 是一個開源的 分布式流處理平臺&#xff0c;最初由 LinkedIn 開發&#xff0c;后捐贈給 Apache 軟件基金會。它被設計用于高吞吐量、低延遲、可水平擴展地處理實時數據流。官網地址是&#xff1a;https://kafka.apache.org/ 以下是 Kafka 的核心介紹…

CH9121T電路及配置詳解

目錄1. CH9121T簡介2. 原理圖及接口2.1 參考電路2.2 CH9121T評估板2.3 差分端口2.4 網口燈顯示2.5 晶振2.6 其他接口3. 使用手冊及說明3.1 配置介紹3.2 默認參數3.3 串口波特率3.4 配置指令3.5 應用示例1. CH9121T簡介 CH9121 是一款網絡串口透傳芯片&#xff0c;自帶 10/100M…

科研數據可視化核心技術:基于 AI 與 R 語言的熱圖、火山圖及網絡圖繪制實踐指南

在學術研究競爭日趨激烈的背景下&#xff0c;高質量的數據可視化已成為科研成果呈現與學術傳播的關鍵要素。據統計&#xff0c;超過 60% 的學術稿件拒稿原因與圖表質量存在直接關聯&#xff0c;而傳統繪圖工具在處理組學數據、復雜關聯數據時&#xff0c;普遍存在效率低下、規范…

Windows體驗macOS完整指南

一、虛擬機安裝macOS專業方案1. 環境準備階段硬件檢測&#xff1a;進入BIOS&#xff08;開機時按Del/F2鍵&#xff09;確認開啟VT-x/AMD-V虛擬化選項建議配置&#xff1a;i5十代以上CPU/16GB內存/256GB SSD軟件準備&#xff1a;官網下載VMware Workstation 17 Pro獲取Unlocker補…

【普及/提高?】洛谷P1577 ——切繩子

見&#xff1a;P1577 切繩子 - 洛谷 題目描述 有 N 條繩子&#xff0c;它們的長度分別為 Li?。如果從它們中切割出 K 條長度相同的繩子&#xff0c;這 K 條繩子每條最長能有多長&#xff1f;答案保留到小數點后 2 位(直接舍掉 2 位后的小數)。 輸入格式 第一行兩個整數 N …