欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報(bào)激活碼778899分享:使用rust學(xué)習(xí)基本算法(二)

柚子快報(bào)激活碼778899分享:使用rust學(xué)習(xí)基本算法(二)

http://yzkb.51969.com/

使用rust學(xué)習(xí)基本算法(二)

貪心算法

貪心算法是一種在每一步選擇中都采取當(dāng)前狀態(tài)下最優(yōu)的選擇,以期望通過一系列的局部最優(yōu)解來達(dá)到全局最優(yōu)解的算法策略。它的主要特點(diǎn)是局部最優(yōu)解的選擇,希望通過這種方式來解決某些優(yōu)化問題。但需要注意的是,貪心算法并不保證能夠得到全局最優(yōu)解,它的正確性需要根據(jù)具體問題來分析。

貪心算法的基本思路

建立數(shù)學(xué)模型:將問題抽象成數(shù)學(xué)問題。定義局部最優(yōu)解策略:確定在每一步選擇中應(yīng)該遵循的規(guī)則,以確保每一步都是當(dāng)前狀態(tài)下的最優(yōu)選擇。求解和構(gòu)建全局解:通過不斷地采取局部最優(yōu)解,累積最終求得全局最優(yōu)解或者接近全局最優(yōu)解的答案。

貪心算法的特點(diǎn)

簡(jiǎn)單高效:算法通常較為簡(jiǎn)單,且執(zhí)行效率高。局部最優(yōu)解:每一步都采取當(dāng)前看來最優(yōu)的選擇。不可回溯:一旦選擇,就不會(huì)改變。適用范圍有限:只適用于能夠通過局部最優(yōu)解確保能夠得到全局最優(yōu)解的問題。

適用場(chǎng)景

貪心算法適用于問題的最優(yōu)解可以通過一系列局部最優(yōu)解構(gòu)建的情況。

常見的適用于貪心算法的問題包括:

活動(dòng)選擇問題:如何安排活動(dòng)使得使用同一個(gè)會(huì)議室的活動(dòng)數(shù)目最多。 哈夫曼編碼:用于數(shù)據(jù)壓縮的編碼方法。 最小生成樹:如Kruskal算法和Prim算法。 單源最短路徑:如Dijkstra算法。

[!TIP]

點(diǎn)擊鏈接即可查看博客詳情介紹。

活動(dòng)選擇問題

活動(dòng)選擇問題是貪心算法中的一個(gè)經(jīng)典案例,它描述的是這樣一個(gè)問題:給定一個(gè)活動(dòng)集合,每個(gè)活動(dòng)都有一個(gè)開始時(shí)間和結(jié)束時(shí)間,我們需要選擇盡可能多的活動(dòng),使得這些活動(dòng)之間不相互沖突。

簡(jiǎn)而言之,目標(biāo)是在給定的時(shí)間內(nèi)安排盡可能多的活動(dòng)。

活動(dòng)選擇問題的貪心選擇策略

貪心算法解決活動(dòng)選擇問題的關(guān)鍵在于如何選擇活動(dòng)。一種有效的策略是總是選擇結(jié)束時(shí)間最早的活動(dòng),因?yàn)榻Y(jié)束得越早,留給其他活動(dòng)的時(shí)間就越多,從而有可能安排更多的活動(dòng)。當(dāng)然,前提是這個(gè)活動(dòng)與已經(jīng)選擇的活動(dòng)不沖突。

算法步驟

輸入:

活動(dòng)集合

(

S

=

{

a

1

,

a

2

,

.

.

.

,

a

n

}

)

,其中每個(gè)活動(dòng)

(

a

i

)

由一個(gè)開始時(shí)間

(

s

i

)

和結(jié)束時(shí)間

(

e

i

)

組成。

活動(dòng)集合 (S = \{a_1, a_2, ..., a_n\}),其中每個(gè)活動(dòng) (a_i) 由一個(gè)開始時(shí)間 (s_i) 和結(jié)束時(shí)間 (e_i) 組成。

活動(dòng)集合(S={a1?,a2?,...,an?}),其中每個(gè)活動(dòng)(ai?)由一個(gè)開始時(shí)間(si?)和結(jié)束時(shí)間(ei?)組成。 排序:按照活動(dòng)的結(jié)束時(shí)間從小到大對(duì)活動(dòng)進(jìn)行排序。 選擇活動(dòng): $$ 選擇結(jié)束時(shí)間最早的活動(dòng)(假設(shè)為 a_1將其加入到結(jié)果集合中。 從剩下的活動(dòng)中繼續(xù)選擇結(jié)束時(shí)間最早且與已選擇的活動(dòng)不沖突的活動(dòng),將其加入到結(jié)果集合中。

重復(fù)上述步驟,直到?jīng)]有更多的活動(dòng)可以被選擇。 $$

算法實(shí)現(xiàn)實(shí)例

activities = [(1, 4), (3, 5), (0, 6), (5, 7), (3, 9), (5, 9), (6, 10), (8, 11), (8, 12), (2, 14), (12, 16)]

# 每個(gè)元組代表一個(gè)活動(dòng),元組的第一個(gè)元素是開始時(shí)間,第二個(gè)元素是結(jié)束時(shí)間

# 按照結(jié)束時(shí)間對(duì)活動(dòng)進(jìn)行排序

activities.sort(key=lambda x: x[1])

def select_activities(activities):

selected_activities = [activities[0]] # 選擇結(jié)束時(shí)間最早的活動(dòng)

last_finish_time = activities[0][1]

for start_time, finish_time in activities[1:]:

if start_time >= last_finish_time: # 如果當(dāng)前活動(dòng)的開始時(shí)間不早于上一個(gè)選中活動(dòng)的結(jié)束時(shí)間

selected_activities.append((start_time, finish_time))

last_finish_time = finish_time

return selected_activities

# 調(diào)用函數(shù)

selected_activities = select_activities(activities)

print("Selected activities:", selected_activities)

/*

步驟 1: 定義活動(dòng)結(jié)構(gòu)體

首先,我們定義一個(gè) Activity 結(jié)構(gòu)體來表示一個(gè)活動(dòng),包括它的開始時(shí)間和結(jié)束時(shí)間。

*/

#[derive(Debug, Clone)]

struct Activity {

start: i32,

end: i32,

}

/*

步驟 2: 實(shí)現(xiàn)活動(dòng)選擇函數(shù)

接下來,我們實(shí)現(xiàn)一個(gè)函數(shù)來解決活動(dòng)選擇問題。這個(gè)函數(shù)接收一個(gè)活動(dòng)列表作為輸入,返回一個(gè)選中活動(dòng)的列表。

*/

fn activity_selection(mut activities: Vec) -> Vec {

// 按照活動(dòng)的結(jié)束時(shí)間進(jìn)行排序

activities.sort_by(|a, b| a.end.cmp(&b.end));

let mut selected_activities = Vec::new();

// 選擇第一個(gè)活動(dòng)

if let Some(first_activity) = activities.first() {

selected_activities.push(first_activity.clone());

let mut last_end = first_activity.end;

// 遍歷其余的活動(dòng)

for activity in activities.iter().skip(1) {

if activity.start >= last_end {

// 如果當(dāng)前活動(dòng)的開始時(shí)間晚于或等于上一個(gè)選中活動(dòng)的結(jié)束時(shí)間,則選擇這個(gè)活動(dòng)

selected_activities.push(activity.clone());

last_end = activity.end;

}

}

}

selected_activities

}

fn activity_selection(mut activities: Vec) -> Vec {

// 按照活動(dòng)的結(jié)束時(shí)間進(jìn)行排序

activities.sort_by(|a, b| a.end.cmp(&b.end));

let mut selected_activities = Vec::new();

// 選擇第一個(gè)活動(dòng)

if let Some(first_activity) = activities.first() {

selected_activities.push(first_activity.clone());

let mut last_end = first_activity.end;

// 遍歷其余的活動(dòng)

for activity in activities.iter().skip(1) {

if activity.start >= last_end {

// 如果當(dāng)前活動(dòng)的開始時(shí)間晚于或等于上一個(gè)選中活動(dòng)的結(jié)束時(shí)間,則選擇這個(gè)活動(dòng)

selected_activities.push(activity.clone());

last_end = activity.end;

}

}

}

selected_activities

}

/*

代碼測(cè)試

*/

fn main() {

let activities = vec![

Activity { start: 1, end: 4 },

Activity { start: 3, end: 5 },

Activity { start: 0, end: 6 },

Activity { start: 5, end: 7 },

Activity { start: 8, end: 9 },

Activity { start: 5, end: 9 },

];

let selected_activities = activity_selection(activities);

for activity in selected_activities {

println!("Activity starts at {}, ends at {}", activity.start, activity.end);

}

}

哈夫曼樹

哈夫曼樹(Huffman Tree),又稱最優(yōu)二叉樹,是一種應(yīng)用廣泛的用于數(shù)據(jù)壓縮的樹形結(jié)構(gòu)。它是基于字符出現(xiàn)頻率來構(gòu)造的二叉樹,其中頻率高的字符離根較近,而頻率低的字符離根較遠(yuǎn),從而實(shí)現(xiàn)數(shù)據(jù)的有效壓縮。構(gòu)造哈夫曼樹的過程是一種貪心算法。

構(gòu)造過程

初始化:根據(jù)待編碼的字符及其頻率構(gòu)造一個(gè)森林,每個(gè)字符都是一個(gè)單節(jié)點(diǎn)的二叉樹,節(jié)點(diǎn)的權(quán)值為字符出現(xiàn)的頻率。構(gòu)造過程:在森林中選出兩個(gè)根節(jié)點(diǎn)的權(quán)值最小的樹合并,作為一個(gè)新的二叉樹的左、右子樹,新二叉樹的根節(jié)點(diǎn)權(quán)值為兩個(gè)子樹根節(jié)點(diǎn)權(quán)值之和。將選中的兩棵樹從森林中刪除,同時(shí)將新形成的二叉樹加入森林。重復(fù)上述過程,直到森林中只剩下一棵樹,這棵樹就是哈夫曼樹。結(jié)果:得到的哈夫曼樹中,每個(gè)字符都對(duì)應(yīng)一條從根到該字符節(jié)點(diǎn)的路徑,路徑上的左右分支分別代表編碼中的0和1,從而得到每個(gè)字符的哈夫曼編碼。

特點(diǎn)

哈夫曼編碼是一種變長(zhǎng)編碼方法,不同字符的編碼長(zhǎng)度不同,頻率高的字符編碼短,頻率低的字符編碼長(zhǎng)。哈夫曼編碼是前綴編碼,任何字符的編碼都不是其他字符編碼的前綴,這保證了編碼的唯一解碼性。

應(yīng)用

哈夫曼編碼廣泛應(yīng)用于數(shù)據(jù)壓縮領(lǐng)域,如ZIP文件壓縮、JPEG圖像壓縮等。通過哈夫曼編碼,可以有效減少存儲(chǔ)空間或傳輸帶寬的需求,提高數(shù)據(jù)處理效率。

/*

步驟 1: 定義節(jié)點(diǎn)和樹的結(jié)構(gòu)

首先,定義哈夫曼樹的節(jié)點(diǎn)結(jié)構(gòu),以及一個(gè)枚舉來表示節(jié)點(diǎn)可以是葉子節(jié)點(diǎn)或者有兩個(gè)子節(jié)點(diǎn)的中間節(jié)點(diǎn)。

*/

use std::rc::Rc;

use std::cell::RefCell;

use std::collections::BinaryHeap;

use std::cmp::Ordering;

#[derive(Debug, Clone)]

enum Node {

Leaf { character: char, frequency: usize },

Internal { frequency: usize, left: Rc>, right: Rc> },

}

impl Node {

fn frequency(&self) -> usize {

match *self {

Node::Leaf { frequency, .. } | Node::Internal { frequency, .. } => frequency,

}

}

}

impl PartialEq for Node {

fn eq(&self, other: &Self) -> bool {

self.frequency() == other.frequency()

}

}

impl Eq for Node {}

impl PartialOrd for Node {

fn partial_cmp(&self, other: &Self) -> Option {

other.frequency().partial_cmp(&self.frequency())

}

}

impl Ord for Node {

fn cmp(&self, other: &Self) -> Ordering {

other.frequency().cmp(&self.frequency())

}

}

/*

步驟 2: 構(gòu)建哈夫曼樹

接下來,實(shí)現(xiàn)構(gòu)建哈夫曼樹的函數(shù)。這個(gè)函數(shù)將接受一個(gè)字符及其頻率的映射,然后構(gòu)建并返回哈夫曼樹。

*/

fn build_huffman_tree(frequencies: &[(char, usize)]) -> Option>> {

if frequencies.is_empty() {

return None;

}

let mut heap = BinaryHeap::new();

for &(character, frequency) in frequencies {

heap.push(Rc::new(RefCell::new(Node::Leaf { character, frequency })));

}

while heap.len() > 1 {

let left = heap.pop().unwrap();

let right = heap.pop().unwrap();

let new_node = Node::Internal {

frequency: left.borrow().frequency() + right.borrow().frequency(),

left,

right,

};

heap.push(Rc::new(RefCell::new(new_node)));

}

heap.pop()

}

/*

步驟 3: 生成哈夫曼編碼

最后,我們需要從哈夫曼樹中生成每個(gè)字符的編碼。這需要遍歷樹,并在遍歷過程中記錄路徑。

*/

fn generate_codes(node: &Rc>, prefix: String, codes: &mut Vec<(char, String)>) {

match *node.borrow() {

Node::Leaf { character, .. } => {

codes.push((character, prefix));

},

Node::Internal { ref left, ref right, .. } => {

generate_codes(left, format!("{}0", prefix), codes);

generate_codes(right, format!("{}1", prefix), codes);

},

}

}

fn main() {

let frequencies = [('a', 45), ('b', 13), ('c', 12), ('d', 16), ('e', 9), ('f', 5)];

let tree = build_huffman_tree(&frequencies);

let mut codes = Vec::new();

generate_codes(&tree, "".to_string(), &mut codes);

for (character, code) in codes {

println!("Character: {}, Code: {}", character, code);

}

}

/*

單元測(cè)試

*/

use std::rc::Rc;

use std::cell::RefCell;

use std::collections::BinaryHeap;

use std::cmp::Ordering;

#[cfg(test)]

mod tests {

use super::*;

#[test]

fn test_empty_input() {

let frequencies = [];

let tree = build_huffman_tree(&frequencies);

assert!(tree.is_none());

}

#[test]

fn test_single_element_input() {

let frequencies = [('a', 1)];

let tree = build_huffman_tree(&frequencies).expect("Tree should be created with single element");

let mut codes = Vec::new();

generate_codes(&tree, "".to_string(), &mut codes);

assert_eq!(codes.len(), 1);

assert_eq!(codes[0], ('a', "".to_string()));

}

}

最小生成樹

最小生成樹(Minimum Spanning Tree,MST)是在一個(gè)加權(quán)無向圖中尋找一個(gè)邊的子集,使得這個(gè)子集構(gòu)成的樹包括圖中的所有頂點(diǎn),并且樹的所有邊的權(quán)值之和最小。最小生成樹在很多領(lǐng)域都有應(yīng)用,比如網(wǎng)絡(luò)設(shè)計(jì)、電路設(shè)計(jì)、交通網(wǎng)絡(luò)規(guī)劃等。

Prim算法(Prim’s Algorithm)

從圖中的任意一個(gè)頂點(diǎn)開始構(gòu)建最小生成樹。 在每一步中,選擇連接樹與非樹頂點(diǎn)且權(quán)值最小的邊,并將其加入到樹中,直到所有頂點(diǎn)都被包含在樹中。 時(shí)間復(fù)雜度:使用優(yōu)先隊(duì)列時(shí)為O(E+VlogV),其中 V 是頂點(diǎn)數(shù),E 是邊數(shù)。

/*

定義圖的數(shù)據(jù)結(jié)構(gòu):首先,我們需要定義一個(gè)適合表示圖的數(shù)據(jù)結(jié)構(gòu)。這里我們使用Vec>來表示圖,其中每個(gè)頂點(diǎn)都是一個(gè)索引,每個(gè)頂點(diǎn)存儲(chǔ)一個(gè)向量,向量中的元素是(頂點(diǎn)索引, 邊的權(quán)重)的元組。

*/

use std::collections::BinaryHeap;

use std::cmp::Ordering;

#[derive(Debug, Clone, Eq)]

struct Edge {

node: usize,

cost: i32,

}

impl PartialEq for Edge {

fn eq(&self, other: &Self) -> bool {

self.cost == other.cost

}

}

impl PartialOrd for Edge {

fn partial_cmp(&self, other: &Self) -> Option {

other.cost.partial_cmp(&self.cost)

}

}

impl Ord for Edge {

fn cmp(&self, other: &Self) -> Ordering {

other.cost.cmp(&self.cost)

}

}

struct Graph {

adj_list: Vec>,

}

impl Graph {

fn new(size: usize) -> Self {

Graph {

adj_list: vec![vec![]; size],

}

}

fn add_edge(&mut self, src: usize, dest: usize, cost: i32) {

self.adj_list[src].push((dest, cost));

self.adj_list[dest].push((src, cost)); // For undirected graph

}

}

/*

步驟2: 實(shí)現(xiàn)普里姆算法

接下來,我們實(shí)現(xiàn)普里姆算法來找到最小生成樹:

*/

impl Graph {

fn prim_mst(&self) -> i32 {

let mut total_cost = 0;

let mut edge_heap = BinaryHeap::new();

let mut visited = vec![false; self.adj_list.len()];

// Start from the first node

visited[0] = true;

for &(node, cost) in &self.adj_list[0] {

edge_heap.push(Edge { node, cost });

}

while let Some(Edge { node, cost }) = edge_heap.pop() {

if visited[node] {

continue;

}

visited[node] = true;

total_cost += cost;

for &(next_node, next_cost) in &self.adj_list[node] {

if !visited[next_node] {

edge_heap.push(Edge { node: next_node, cost: next_cost });

}

}

}

total_cost

}

}

/*

步驟3: 添加單元測(cè)試

最后,我們?yōu)槠绽锬匪惴ㄌ砑訂卧獪y(cè)試:

*/

#[cfg(test)]

mod tests {

use super::*;

#[test]

fn test_prim_mst() {

let mut graph = Graph::new(4);

graph.add_edge(0, 1, 10);

graph.add_edge(0, 2, 6);

graph.add_edge(0, 3, 5);

graph.add_edge(1, 3, 15);

graph.add_edge(2, 3, 4);

assert_eq!(graph.prim_mst(), 19);

}

}

Kruskal算法(Kruskal’s Algorithm)

? 將圖中的所有邊按權(quán)值從小到大排序。 ? 依次考慮每條邊,如果這條邊不會(huì)與已選擇的邊形成環(huán),則將其加入到最小生成樹中。 ? 使用并查集(Disjoint Set Union,DSU)可以高效地檢測(cè)環(huán)。 ? 時(shí)間復(fù)雜度:O(ElogE) 或 O(ElogV)(因?yàn)樾枰獙?duì)邊進(jìn)行排序)。

/*

定義邊和并查集的數(shù)據(jù)結(jié)構(gòu):克魯斯卡爾算法需要對(duì)所有的邊進(jìn)行排序,并使用并查集(Disjoint Set Union,DSU)來檢測(cè)環(huán)。

*/

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]

struct Edge {

src: usize,

dest: usize,

weight: i32,

}

struct DisjointSet {

parent: Vec,

rank: Vec,

}

impl DisjointSet {

fn new(size: usize) -> Self {

DisjointSet {

parent: (0..size).collect(),

rank: vec![0; size],

}

}

fn find(&mut self, node: usize) -> usize {

if self.parent[node] != node {

self.parent[node] = self.find(self.parent[node]);

}

self.parent[node]

}

fn union(&mut self, node1: usize, node2: usize) {

let root1 = self.find(node1);

let root2 = self.find(node2);

if self.rank[root1] < self.rank[root2] {

self.parent[root1] = root2;

} else if self.rank[root1] > self.rank[root2] {

self.parent[root2] = root1;

} else {

self.parent[root2] = root1;

self.rank[root1] += 1;

}

}

}

/*

實(shí)現(xiàn)克魯斯卡爾算法:對(duì)邊進(jìn)行排序,并逐一檢查每條邊以構(gòu)建最小生成樹,同時(shí)使用并查集來避免環(huán)的形成。

*/

fn kruskal(edges: &mut Vec, num_nodes: usize) -> Vec {

edges.sort(); // Sort edges based on weight

let mut dsu = DisjointSet::new(num_nodes);

let mut mst = Vec::new();

for edge in edges.iter() {

let root1 = dsu.find(edge.src);

let root2 = dsu.find(edge.dest);

if root1 != root2 {

mst.push(edge.clone());

dsu.union(root1, root2);

}

}

mst

}

/*

添加單元測(cè)試:確保算法的正確性。

*/

#[cfg(test)]

mod tests {

use super::*;

#[test]

fn test_kruskal() {

let mut edges = vec![

Edge { src: 0, dest: 1, weight: 10 },

Edge { src: 0, dest: 2, weight: 6 },

Edge { src: 0, dest: 3, weight: 5 },

Edge { src: 1, dest: 3, weight: 15 },

Edge { src: 2, dest: 3, weight: 4 }

];

let mst = kruskal(&mut edges, 4);

assert_eq!(mst.len(), 3);

assert_eq!(mst.iter().map(|e| e.weight).sum::(), 19);

}

}

選擇算法

[!NOTE]

在圖論中,判斷一個(gè)圖是稠密(Dense)還是稀疏(Sparse)通常依賴于圖中邊的數(shù)量與頂點(diǎn)數(shù)量的關(guān)系。

稀疏圖:如果邊的數(shù)量接近頂點(diǎn)的數(shù)量,即 (E ≈V) 或 (E = O(V)),則圖被認(rèn)為是稀疏的。在實(shí)際應(yīng)用中,如果邊的數(shù)量少于頂點(diǎn)數(shù)量的兩倍,即 (E < 2V),圖通常也被認(rèn)為是稀疏的。 稠密圖:如果邊的數(shù)量接近頂點(diǎn)數(shù)量的平方,即 (E ≈ V^2) 或 (E = O(V^2)),則圖被認(rèn)為是稠密的。在實(shí)際應(yīng)用中,如果邊的數(shù)量大于或等于頂點(diǎn)數(shù)量的對(duì)數(shù)倍,即 (E ≥Vlog V),圖也可能被認(rèn)為是較為稠密的。

快速判斷方法

計(jì)算邊和頂點(diǎn)的比率:計(jì)算 (E/V) 的值,這個(gè)比率可以提供圖是稠密還是稀疏的直觀感受。如果這個(gè)比率接近1或者更小,圖很可能是稀疏的;如果這個(gè)比率接近頂點(diǎn)數(shù) (V) 或更高,圖很可能是稠密的。 觀察實(shí)際應(yīng)用場(chǎng)景:在某些情況下,圖的應(yīng)用背景也能提供是否稠密或稀疏的線索。例如,在社交網(wǎng)絡(luò)中,用戶(頂點(diǎn))之間的連接(邊)可能非常豐富,使得這類圖傾向于更稠密。而在某些交通網(wǎng)絡(luò)中,地點(diǎn)(頂點(diǎn))之間的直接路線(邊)可能相對(duì)較少,使得這類圖可能更傾向于稀疏。

注意事項(xiàng)

如果圖是稠密的,即邊數(shù)接近頂點(diǎn)數(shù)的平方,通常使用普里姆算法更高效。如果圖是稀疏的,即邊數(shù)與頂點(diǎn)數(shù)相近,克魯斯卡爾算法可能更合適。

柚子快報(bào)激活碼778899分享:使用rust學(xué)習(xí)基本算法(二)

http://yzkb.51969.com/

參考文章

評(píng)論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。

轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/18876537.html

發(fā)布評(píng)論

您暫未設(shè)置收款碼

請(qǐng)?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄