1. 程式人生 > >小白專場-FileTransfer-c語言實現

小白專場-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]);
}

總之上述程式碼幹了這三件事:

  1. 先找到根;
  2. 把根變成X的父結點;
  3. 再返回根

因此,路徑壓縮第一次執行的時間比較長,但是如果頻繁使用查詢命令,第一次將路徑壓縮,大大減小樹的高度,後續查詢速度將大大增加

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語言實現掃雷遊戲

本文將從一行行程式碼中詳解掃雷小遊戲,對每一個模組都使用詳細的註釋,使這個掃雷小遊戲簡單易懂。 首先,簡單分析掃雷的玩法,掃雷就是在一個棋盤中佈置適當數量的雷數玩家通過對雷陣的排查,來找出雷的位置。如果玩家選擇的座標周圍無雷將自動展開這片區域,若有雷會顯示雷數。