小白專場-FileTransfer-c語言實現
目錄
- 一、集合的簡化表示
- 二、題意理解
- 三、程式框架搭建
- 3.1 Input_connection
- 3.2 Check_connection
- 3.3 Check_network
- 四、pta測試
- 五、按秩歸併
- 5.1 方法一:樹高替代
- 5.2 方法二:規模替代
- 六、路徑壓縮
- 6.1 路徑壓縮時間複雜度計算
更新、更全的《資料結構與演算法》的更新網站,更有python、go、人工智慧教學等著你:https://www.cnblogs.com/nickchen121/p/11407287.html
一、集合的簡化表示
在上一節 集合及運算中,我們對集合使用二叉樹表示,如下圖所示:
為了使用二叉樹,我們在上一節中使用以下程式碼,構造二叉樹:
/* c語言實現 */ typedef struct{ ElementType Data; int Parent; } SetType; int Find(SetType S[], ElementType X) { // 在陣列S中查詢值為X的元素所屬的集合 // MaxSize是全域性變數,為陣列S的最大長度 int i; for (i = 0; i < MaxSize && S[i].Data != X; i++); if (i >= MaxSize) return -1; // 未找到X,返回-1 for (; S[i].Parent >= 0; i = S[i].Parent); return i; // 找到X所屬集合,返回樹根結點在陣列S中的下標 }
使用二叉樹構造集合,Find操作在差的情況下時間複雜度可能為\(O(n^2)\)
因此對於任何有限集合的(N個)元素都可以被一一對映為整數 0~N-1。即對於集合 {2, 5, 4, 3} 和 {6, 0, 1} 我們可以使用如下圖所示的陣列表示:
對於上述的陣列,我們可以使用如下程式碼構造:
/* c語言實現 */ typedef int ElementType; // 預設元素可以用非負整數表示 typedef int SetName; //預設用根結點的下標作為集合名稱 typedef ElementType SetType[MaxSize]; SetName Find(SetType S, ElementType X) { // 預設集合元素全部初始化為-1 for (; S[X] >= 0; X = S[X]); return X; } void Union(SetType S, SetName Root1, SetName Root2) { // 這裡預設Root1和Root2是不同集合的根節點 S[Root2] = Root1; }
二、題意理解
根據輸入樣例,以此來判斷計算機之間有多少個組成,如下圖所示
上圖動態變化如下圖所示:
下圖為五臺計算機之間形成全連線狀態,因此看成一個整體:
三、程式框架搭建
/* c語言實現 */
int main()
{
初始化集合;
do {
讀入一條指令;
處理指令;
} while (沒結束);
return 0;
}
int main()
{
SetType S;
int n;
char in;
scanf("%d\n", &n);
Initialization(S, n);
do {
scanf("%c", &in);
switch (in) {
case 'I': Input_connection(S); break; // Union(Find)
case 'C': Check_connection(S); break; // Find
case 'S': Check_network(S, n); break; // 數集合的根,判斷計算機網路的組成個數
}
} while (in != 'S');
return 0;
}
3.1 Input_connection
/* c語言實現 */
void Input_connection(SetType S)
{
ElementType u, v;
SetName Root1, Root2;
scanf("%d %d\n", &u, &v);
Root1 = Find(S, u-1);
Root2 = Find(S, v-1);
if (Root1 != Root2)
Union(S, Root1, Root2);
}
3.2 Check_connection
/* c語言實現 */
void Check_connection(SetType S)
{
ElementType u, v;
scnaf("%d %d\n", &u, &v);
Root1 = Find(S, u-1);
Root2 = Find(S, v-1);
if (Root1 == Root2)
printf("yes\n");
else printf("no\n");
}
3.3 Check_network
/* c語言實現 */
void Check_network(SetType S, int n)
{
int i, counter = 0;
for (i = 0; i < n; i++){
if (S[i] < 0) counter++;
}
if (counter == 1)
printf("The network is connected.\n");
else
printf("There are %d components.\n", counter);
}
四、pta測試
/* c語言實現 */
typedef int ElementType; // 預設元素可以用非負整數表示
typedef int SetName; //預設用根結點的下標作為集合名稱
typedef ElementType SetType[MaxSize];
SetName Find(SetType S, ElementType X)
{
// 預設集合元素全部初始化為-1
for (; S[X] >= 0; X = S[X]);
return X;
}
void Union(SetType S, SetName Root1, SetName Root2)
{
// 這裡預設Root1和Root2是不同集合的根節點
S[Root2] = Root1;
}
對於上述的程式碼,如果我們放入pta中測試,會發現測試點6執行超時,如下圖所示:
因此,我們會考慮是不是因為出現了某種情況,導致Root2為根結點的樹過大了,因此我們修改程式碼為:
/* c語言實現 */
typedef int ElementType; // 預設元素可以用非負整數表示
typedef int SetName; //預設用根結點的下標作為集合名稱
typedef ElementType SetType[MaxSize];
SetName Find(SetType S, ElementType X)
{
// 預設集合元素全部初始化為-1
for (; S[X] >= 0; X = S[X]);
return X;
}
void Union(SetType S, SetName Root1, SetName Root2)
{
// 這裡預設Root1和Root2是不同集合的根節點
// S[Root2] = Root1;
S[Root1] = Root2;
}
發現更換程式碼後,測試點5卻執行超時了,為了解決上述問題,我們可以使用下面將要講到了按秩歸併的思想修改程式碼。
五、按秩歸併
為什麼需要按秩歸併呢?因為我們使用pta測試程式,發現程式碼總是超時,因此我們可以考慮是否出現這種情況——我們再不斷地往一顆樹上累加子樹,如下圖所示:
/* c語言實現 */
Union(Find(2), Find(1));
Union(Find(3), Find(1));
……;
Union(Find(n), Find(1));
從上圖可以看出,此過程的時間複雜度為:\(T(n) = O(n^2)\)
除了上述這種情況,會導致樹的高度越來越高,如果我們把高樹貼在矮樹上,那麼樹高也會快速增長,因此我們應該考慮把矮樹貼在高數上。
對於上述問題的解決,我們給出以下兩個解決方法,這兩種方法統稱為按秩歸併。
5.1 方法一:樹高替代
為了解決上述問題,我們可以把根結點從-1替代為-樹高,程式碼如下:
/* c語言實現 */
if ( Root2高度 > Root1高度 )
S[Root1] = Root2;
else {
if ( 兩者等高 ) 樹高++;
S[Root2] = Root1;
}
if ( S[Root2] < S[Root1] )
S[Root1] = Root2;
else {
if ( S[Root1]==S[Root2] ) S[Root1]--;
S[Root2] = Root1;
}
5.2 方法二:規模替代
為了解決上述問題,我們也可以把根結點從**-1替代為-元素個數(把小樹貼到大樹上),程式碼如下:
/* c語言實現 */
void Union( SetType S, SetName Root1, SetName Root2 )
{
if ( S[Root2]<S[Root1] ){
S[Root2] += S[Root1];
S[Root1] = Root2;
} else {
S[Root1] += S[Root2];
S[Root2] = Root1;
}
}
六、路徑壓縮
對於上述程式碼超時的問題,我們也可以使用路徑壓縮的方法優化代,即壓縮給定元素到集合根元素路徑中的所有元素,詳細情況如下圖所示:
上圖程式碼可表示為:
/* c語言實現 */
SetName Find(SetType S, ElementType X)
{
// 找到集合的根
if (S[X] < 0)
return X;
else
return S[X] = Find(S, S[X]);
}
總之上述程式碼幹了這三件事:
- 先找到根;
- 把根變成X的父結點;
- 再返回根
因此,路徑壓縮第一次執行的時間比較長,但是如果頻繁使用查詢命令,第一次將路徑壓縮,大大減小樹的高度,後續查詢速度將大大增加
6.1 路徑壓縮時間複雜度計算
由於pta並沒有嚴格控制時間限制,使用java這種語言,不使用路徑壓縮,問題不大,我寫這個也只是為了回顧演算法,來放鬆放鬆,不是為了折騰自己,因此。
給你一個眼神自己體會,給你一個網址親自體會https://www.icourse163.org/learn/ZJU-93001?tid=1206471203#/learn/content?type=detail&id=1211167097&sm=1,我是懶得研究下圖所示了。
相關推薦
小白專場-FileTransfer-c語言實現
目錄 一、集合的簡化表示 二、題意理解 三、程式框架搭建 3.1 Input_connection 3.2 Check_connection 3.3 Check_network
小遊戲---掃雷(C語言實現)
一、分析遊戲步驟: 具體步驟如圖: 二、程式碼實現: 遊戲步驟想好之後,就是用程式碼把步驟一步一步的實現。具體程式碼如下: 1、遊戲主要實現: game.c #define _CRT_SECURE_NO_WARNINGS
今天定個小目標,用C語言實現三子棋的玩法。裡面有精彩情景故事幫助你更快理解程式碼內容,不進來了解一下嗎?(內附程式碼)
如標題所示,今天我們要用C語言來實現三子棋的遊戲。相信大家都玩過這個遊戲。我們來回憶一下游戲步驟。 一、今天你在家裡看書,你的朋友小紅邀請你和她一起玩三子棋。這時你有兩個選擇。 1.接受她的邀請,在玩遊戲的同手,促進你們的感情。 0.殘忍
最小二乘法的C語言實現
1. 前言 最近斷斷續續重溫了一些數學書,有高等數學,也有初等數學。 有時候,覺得數學才是世界上最美的東西,但有時候又覺得數學很高冷,不接地氣。 不過,前段時間工作中用到了最小二乘法,下面記錄一些用法。 2. 最小二乘法 根據維基百科的說明: 最小二乘法(又稱最小平
小白專場-多項式乘法與加法運算-c語言實現
目錄 一、題意理解 二、求解思路 三、多項式的表示 3.1 陣列 3.2 連結串列 四、程式框架搭建 五、如何讀入多項式 六、
小白專場-是否同一顆二叉搜尋樹-python語言實現
目錄 一、二叉搜尋樹的相同判斷 二、問題引入 三、舉例分析 四、方法探討 4.1 中序遍歷 4.2 層序遍歷 4.3 先序遍歷 4.4 後序遍歷
用C語言實現:將三個數按從大到小輸出。
temp clas 實現 ima 編程 程序 c語言實現 從大到小 code 這個題目用編程來實現非常簡單,由於我在上一篇博客中已經介紹過使用“冒泡排序”的方法。 所以我在這裏直接給出使用“冒泡排序”寫出的代碼: #include<stdio.h> int m
C語言實現簡易2048小遊戲
blog 而在 而是 null 移動 是我 我們 空指針 主體 一直很喜歡玩這個小遊戲,簡單的遊戲中包含運氣與思考與策略,喜歡這種簡約又不失內涵的遊戲風格。於是萌生了用C語言實現一下的想法。 具體代碼是模仿這個:https://www.cnblogs.com/judgeyo
C語言實現經典小遊戲貪吃蛇
純c語言寫的小遊戲,本人才疏學淺,程式碼有很多不足,僅供給初學者參考。 實現功能: ↑ ↓ ← →分別用來控制上下左右四個方向 空格暫停 esc退出遊戲 f1加速, f2 減速 蛇的長度越長,速度越快(速度有上限) 食物顏色隨機
C語言實現簡單的三子棋小遊戲
函式實現的基本功能: 1.初始化棋盤:用空字元初始化 2.列印棋盤:控制列印九宮格來表示棋盤 3.玩家下棋:用P表示玩家的棋子 4.電腦隨機下棋:隨機生成座標,用0表示玩家的棋子 5.判斷輸贏:玩家贏,電腦贏,平局,繼續遊戲四種情況 多檔案實現: 測試部分test.
用C語言實現最小二乘法演算法
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
小遊戲——三子棋(C語言實現)
一、遊戲步驟 三子棋的實現其實很簡單,只要思路理清,然後按照步驟一步一步的用程式碼實現就可以了。具體步驟如下: 1、遊戲開始。(列印一個遊戲選單) 2、列印遊戲棋盤。 3、玩家走棋,判斷該座標是否有效,無效,提示重新輸入。有效,列印棋盤;然後判贏,贏,遊戲結束; 4、電腦走棋,列
三子棋小遊戲(C語言實現)
C語言打造簡單的三子棋小遊戲 簡單三子棋是指棋盤為3*3,玩家與電腦之間對決的遊戲。 話不多說,先上圖:其中‘0’代表電腦落子,‘X’:玩家落子 基本思路: 1.列印地圖(列印一個“#”字狀的棋盤) 2.電腦落子(隨機落子) 3.玩家落子(通過輸入座標的方式) 4判斷遊戲結果 程式碼及註釋
C語言實現一條龍小遊戲
一條龍小遊戲 一、程式碼構思 全部程式碼分為三個檔案:game.h, game.c, text.c(game.h用來申明所有用到的函式,game.c用來寫實現遊戲的程式碼,text.c用來除錯程式碼) 第一步將棋盤初始化為空格;第二步列印棋盤;第三步設計由玩家先走(在棋盤中放入X),然後電腦
用C語言實現簡單 三子棋(井字棋)小遊戲
函式頭 放在標頭檔案裡 #ifndef __GAME_H__ #define __GAME_H__ #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #inc
使用C語言實現隨機小遊戲原始碼
#include"stdio.h" #include"stdlib.h" #include"windows.h" int print() { printf("\n\n+++++++++++++你會看見的數字和運算子+++++++++++++\n"); &
C語言實現井字棋小遊戲
#include <stdio.h> #include <stdlib.h> int store[]={'_','_','_','_','_','_','_','_','_'}, shunt=1, count, i; void pr
c語言實現 **三子棋小遊戲**
| 備註都寫在程式碼中 標頭檔案 game.h #ifndef __GAME_H_ #define __GAME_H_ #define ROW 3 #define COL 3 #define _CRT_SECURE_NO_WARNINGS #include&
C語言實現最簡單的2048小遊戲
網上解釋很多了,直接上程式碼吧,這個功能很簡單,易於學習,後期有時間會完善功能 #include<stdio.h> #include<stdlib.h> #include<string.h> #define Key_Up 0x4800
C語言實現掃雷小遊戲
本文將從一行行程式碼中詳解掃雷小遊戲,對每一個模組都使用詳細的註釋,使這個掃雷小遊戲簡單易懂。 首先,簡單分析掃雷的玩法,掃雷就是在一個棋盤中佈置適當數量的雷數玩家通過對雷陣的排查,來找出雷的位置。如果玩家選擇的座標周圍無雷將自動展開這片區域,若有雷會顯示雷數。