線性表實訓(頭歌實踐平臺課程答案詳細解說)

C 和 C++ 支持 4 種基本數據類型(整型、浮點型、字符型、布爾型)和 3 種復合型數據類型(數組、指針、結構)。復合類型的數據對于數據結構至關重要,因為從某種程度上來說數據量的多少和數據結構的好壞決定了程序的復雜程度和程序結構的好壞。而數據結構基本可以抽象成數據對象之間的聯系,其中,結構適合描述數據對象,指針適合描述數據對象之間的聯系,數組適合描述同類型對象之間的特殊聯系(有序連續存放)。數據結構一般分為線性結構和非線性結構。
本實訓項目的主要目標是通過構建線性表來學習和掌握 C 和 C++ 中使用結構和指針構建復雜數據結構的方法。主要內容包括順序、逆序以及排序構建線性表,查找指定元素,刪除指定結點,求線性表的長度,棧,隊列和集合。

總共包括以下關卡。

第1關順序構建線性表

本關任務:按照數據輸入的順序構建一個線性表。即如果輸入的3個結點數據分別為1、2、3,則構建的線性表包含3個結點,且從前往后的結點數據分別為1、2、3。
為了完成本關卡任務,你需要掌握:

線性表的特性;
線性表的一般操作;
線性表的表示方法。
線性表(linear list)是一種數據結構,是由 n 個具有相同特性的數據元素構成的序列。

線性表中元素的個數 n 即為線性表的長度,當 n = 0 時稱為空表。線性表的相鄰元素之間存在著序偶關系。

如用( a[0] ,…… ,a[i-1] ,a[i] ,a[i+1] ,…… ,a[n-1] )表示一個線性表,則稱 a[i-1] 是 a[i] 的前驅,a[i+1] 是 a[i] 的后繼。

線性表的特性

線性表中必存在唯一的一個“第一元素”;

線性表中必存在唯一的一個“最后元素” ;

除最后一個元素之外,均有唯一的后繼;

除第一個元素之外,均有唯一的前驅。

線性表的一般操作

將線性表變為空表;

返回線性表的長度,即表中元素個數;

獲取線性表某位置的元素;

定位某個元素在線性表中的位置;

在線性表中插入一個元素;

刪除某個元素;

判斷線性表是否為空;

遍歷輸出線性表的所有元素;

線性表排序。
線性表可以用鏈表來實現。鏈表是通過指針鏈接在一起的一組數據項(結點)。

鏈表的每個結點都是同類型的結構,該結構中一般包含兩類信息:

數據域,存儲業務相關的數據(如財務系統的數據域為財務信息,車輛管理系統的業務信息為車輛信息);

指針域,用于構建結點的鏈接關系。單鏈表的實現只需要一個指針。

由于數據域跟線性表的構建無關(靠指針域就夠了),所以本關所有程序的實現中線性表的數據域都只有一個整數。

單鏈表的形式如下:
在這里插入圖片描述
如上圖所示,該單鏈表共包含了4個結點,4個結點的數據域分別是28、52、2、96。每個結點還包含一個指針,指向下一個結點。最后一個結點的指針域置為“NULL”,表示后面沒有結點了。

第一個結點的地址存放在一個指針變量 head 中( head 指向鏈表第一個結點, head 本身不是一個結點)。訪問該單鏈表只需要知道 head 的值就可以了,因為通過 head 可以訪問到第一個結點,通過第一個結點的指針域可以訪問第二個結點 …… 直到訪問鏈表中所有的結點。
要實現上述鏈表,只需要定義如下結構:

// 定義結點結構
struct node
{int data;     // 數據域node * next;     // 指針域,指向下一個結點
};

如下程序可以構建上述圖中鏈表(其中:每個結點都是 node 類型的一個結構變量,head 則是node*類型的指針):

node a, b, c, d, *head;
head = &a;     // 讓 head 指針指向結點 a
a.data = 28;     // 給 a 的數據域賦值
a.next = &b;     // 讓 a 的指針域指向結點 b
b.data = 52;  b.next = &c;     // 給 b 的數據域賦值,并讓 b 的指針域指向結點 c
c.data = 2;  c.next = &d;     // 給 c 的數據域賦值,并讓 c 的指針域指向結點 d
d.data = 96;  d.next = NULL;     // 給 d 的數據域賦值,并將 d 的指針域置為 NULL,表示后面沒有節點。

很顯然,這樣寫程序比較麻煩,而且在不知道有多少結點的情況下也根本無法處理。怎么可以解決這個問題呢?

C 和 C++ 的內存動態分配方法可以很好的解決這個問題。C++ 提供了兩種動態分配內存的方法:

C 語言提供的 malloc 和 free 函數,代碼如下:

node *t = (node *)malloc(sizeof(node));
t->data = 2;
free(t);

sizeof 是一個運算符,sizeof(node)用于計算并返回一個 node 類型變量所占內存空間的大小(字節數);

malloc(int n)用于申請動態分配 n 個字節大小的數據空間,并返回該空間的首地址;

(node )是類型轉換運算符,malloc 函數返回的地址是void類型,轉換成node類型后賦值給指針變量 t( t 的類型是node)。

所以上面第一條語句執行后,指針 t 指向一塊動態分配的內存空間,該空間大小為一個 node 類型變量的大小。

第二條語句通過指針 t 訪問該空間(當作 node 變量)的數據域 data,給它賦值為2。free 函數用于釋放 t 指向的動態分配空間(不需要該空間后再釋放)。
C++ 擴展的 new 和 delete 運算符,代碼如下:

node *t = new node;
t->data = 2;
delete t;

那么使用動態內存分配的方法實現上述的單鏈表的代碼如下:

node *head, *t, *p;
t = new node;     // 動態分配一個 node 結點的空間,并讓 t 指向它
head = t;     // 該結點是第一個結點,讓 head 指向它
t->data = 28;     // 給第一個結點的數據域賦值
p = new node;     // 動態分配第二個 node 結點的空間,并讓 p 指向它
t->next = p;     // 修改第一個結點的指針域為第二個結點的地址(第一個結點的指針域指向第二個結點)
p->data = 52;     // 給第二個結點的數據域賦值
t=p;     // 讓 t 指向最后一個結點
p = new node;     // 動態分配第三個 node 結點的空間,并讓 p 指向它( p 不再指向第二個結點了)
t->next = p;     // 修改第二個結點的指針域為第三個結點的地址(第二個結點的指針域指向第三個結點)
p->data = 2;     // 給第三個結點的數據域賦值
t = p;     // 讓 t 指向最后一個結點
p = new node;     // 動態分配第四個 node 結點的空間,并讓 p 指向它( p 不再指向第三個結點了)
t->next = p;     // 修改第三個結點的指針域為第四個結點的地址(第三個結點的指針域指向第四個結點)
p->data = 96;     // 給第四個結點的數據域賦值
p->next = NULL;     // 給第四個結點的指針域置為 NULL,表示后面沒有結點了

上述程序語句雖然好像變多了,程序也好像變復雜了,但細讀會發現這段程序可以很方便地改成循環,這樣處理后其實是使得程序更簡單了,并且適用于任意多個結點的情況。

改成循環后,單鏈表里面可能包含0個或多個結點,如果包含多個結點,則下面程序中的語句可以讓指針 t 指向鏈表的最后一個結點,具體代碼如下:

node *t = head; // t 指向第一個結點
// 如果 t->next 不為 NULL,即后面還有結點
while(t->next != NULL)
{
t = t->next; // 把 t 指向結點的指針域(下一個結點地址)賦值給 t,t 指向下一個結點
}
// 循環結束時,t->next 為 NULL,即 t 指向的結點后面沒有結點了。

完整代碼解析

#include "linearList.h"node *insertTail(node *h, node *t)
{// 請在此添加代碼,補全函數insertTail/********** Begin *********/if(h == NULL) // 空鏈表單獨處理{t->next = NULL; // 鏈表尾指針置為NULLreturn t; // 返回第一個結點的地址(即鏈表頭指針)}// 非空鏈表的情況node *p = h;// 讓p指向最后一個結點while(p->next){p = p->next;}p->next = t; // 讓最后一個結點的指針域指向結點tt->next = NULL; // 鏈表尾指針置為NULLreturn h;  // 返回第一個結點的地址(即鏈表頭指針)/********** End **********/
}

第2關逆序構建線性表

相關知識

在介紹如何將一個結點插入到一個鏈表頭部之前,我們先假設該鏈表頭指針為 head,則 head 中存放著鏈表當前頭結點的地址。

如果要將指針變量 t 指向的新結點插入到鏈表頭部,其實只需要將原來的頭結點的地址存入 t 指向結點的指針域(也就是讓 t 指向結點的指針域指向原來的頭結點),然后 t 指向的新結點就成了新的頭結點了。那么,如何將原來的頭結點的地址存入 t 指向結點的指針域?

t 指向結點的指針域可以用t->next訪問,原來頭結點的地址在 head 中,所以下面的語句可以實現這個功能:

t->next = head;

又由于新結點( t 指向的結點)是新的頭結點,其地址必須存入頭指針 head 中,而 t 指向結點的地址存放在指針變量 t 中,所以下面的語句可以實現這個功能:
head = t;

完整代碼解析

#include "linearList.h"node * insertHead(node *h, node *t)
{// 請在此添加代碼,補全函數insertHead/********** Begin *********/t->next = h->next;h->next=t;return t;/********** End **********/
}

第3關排序構建線性表

本關任務:補全 insertSort 函數,實現將一個結點按結點數據 data 從小到大的順序插入到一個有序鏈表中。根據插入結點 data 的大小,插入點可能是鏈表頭、尾或者中間某個位置。
為了完成本關卡,你需要掌握:

如何找到插入點;
如何插入結點。
以上兩點也是實現本關任務的兩個關鍵步驟。
插入點的定位可以使用兩個指針( p 和 q ),定位步驟如下圖所示:
在這里插入圖片描述
上圖中鏈表有4個結點,則共有5個可能的插入點。

用兩個指針首先定位第一個插入點( step1 ,p 為 NULL ,q 指向第一個結點),如果插入結點的 data 小于 q 指向結點的 data ,則就是該插入點(兩個指針指向的結點之間),否則兩個指針一起往后移動( step2 );定位第二個插入點,判斷條件依然是插入結點的 data 是否小于 q 指向的結點的 data,是則就是該插入點,否則兩個指針往后平移( step3 );定位第三個插入點,…… ,直到最后當 q 指針為“NULL”時,說明插入結點的 data 比鏈表中所有數據都大,則插入點應該是鏈尾(第五個插入點)。插入點的定位操作可以很容易地用循環實現。

定位好插入點后,當 p 為“NULL”時,插入點是鏈表頭部;當 q 為“NULL”時,插入點是鏈表尾部;否則,插入點為 p 和 q 指向的兩個結點之間。
上述定位好插入點后,接下來是插入結點。對于頭部插入和尾部插入的內容前兩關已做過介紹,本關只介紹中間插入的情況,即將 t 指向的結點插入到 p 和 q 指向的兩個結點之間。

這種情況只需要讓 p 指向結點的指針域指向 t 指向的結點, t 指向結點的指針域指向 q 指向的結點即可。具體參見下面代碼:

p->next = t;
t->next = q;

完整代碼解析

#include "linearList.h"node * insertSort(node *h, node *t)
{// 請在此添加代碼,補全函數insertSort/********** Begin *********/node *p = NULL, *q = h;  // 定位第一個插入點:鏈首// 查找插入點while(q && q->data < t->data){// 兩個指針并行后移p = q;q = q->next;}// 插入鏈首if(p == NULL){t->next = h;return t;}// 插入鏈尾if(q == NULL){p->next = t;t->next = NULL;}// 插入p、q之間t->next = q;p->next = t;return h;/********** End **********/
}

第4關查找元素

本關任務:補全 search 函數,實現在一個鏈表中搜索包含指定數據的結點。如果鏈表中有多個結點包含該數據,則返回第一個。

相關知識
遍歷列表元素
在線性表中查找特定元素是線性表的常用操作之一。由于鏈表結點都是動態內存分配得到的,在內存中不是連續存儲,沒法使用二分法之類的算法來實現信息檢索,但可以使用順序查找的方法。

順序查找需要遍歷整個鏈表,逐個檢查每個結點是否滿足條件。下面 printList 函數則可以遍歷鏈表元素。

// 函數printList:輸出鏈表,每個數據之間用一個空格隔開
// 參數:h-鏈表頭指針
void printList(node *h)
{cout << "List:";while(h){    // h 為真,即 h 指向的結點存在,則輸出該結點的數據cout << " " << h->data;     // 輸出結點數據h=h->next;     // 將該結點的指針域賦值給 h,h 就指向了下一個結點}cout << endl; // 輸出換行符
}

完整代碼解析

#include "linearList.h"node * search(node * h, int num)
{// 請在此添加代碼,補全函數search/********** Begin *********/while(h){// h為真,即h指向的結點存在if(h->data == num){return h;} h = h->next;  // 將該結點的指針域賦值給h,h就指向了下一個結點}return NULL; // 沒找到包含num的結點/********** End **********/
}

第5關刪除指定位置的結點

本關任務:補全 delAt 函數,實現根據指定位置刪除鏈表中對應的結點。
線性表的結點是有序號的,包含 n 個結點的線性表從鏈頭到鏈尾的結點序號分別為0、1、…… 、n?1。刪除線性表指定位置的操作也是常用的功能。

結點的刪除可以根據結點位置的不同分為三種情況:刪除鏈首結點、刪除鏈尾結點、刪除中間結點。

回答以下幾個問題,解決本關問題就不難了:

刪除鏈首結點后,原來序號為1的結點成為新的鏈首結點,鏈表頭指針也應該存儲該結點的地址,該結點的地址原來存放在哪?

刪除鏈尾結點后,原來序號為n?2的結點成為新的鏈尾結點,該結點的指針域也應該置為“NULL”,表示鏈表的結束。怎么訪問序號為n?2的結點的指針域?

刪除中間結點只需要將其前面結點的指針域修改為其后面結點的地址即可。例如刪除序號為i的結點,需要將序號為i?1的結點的指針域修改為序號為i+1的結點地址。怎么訪問序號為i?1的結點的指針域?序號為i+1的結點地址存放在哪?

完整代碼解析

#include "linearList.h"node * delAt(node * h, int i)
{// 請在此添加代碼,補全函數delAt/********** Begin *********/
if(h==NULL){return h;
}
if(i==0){node *temp=h;h=h->next;free(temp);return h;
}
node *current=h;
int count=0;
while(current->next!=NULL&&count<i-1){current=current->next;count++;
}
if(current->next==NULL){return h;
}
node *temp=current->next;
current->next=current->next->next;
if(temp->next==NULL){current->next=NULL;
}
free(temp);
return h;/********** End **********/
}

第6關刪除包含特定數據的結點

本關任務:補全 delHas 函數,實現刪除鏈表中包含特定數據的結點,如果有多個這樣的結點,只刪除第一個。

刪除鏈表的結點時,有時候不知道結點在哪,只知道結點數據的特征,如刪除成績低于60的學生結點、刪除學號為20160903的學生等。

提示程序是可以湊出來的,之前已經完成了一些函數,有些函數的功能這里可以借鑒。例如 delAt 可以刪除某個序號的結點,本關可以先搜索鏈表,查找包含數據 n 的第一個結點的序號,找到了則調用 delAt 函數刪除它。

也可以用另外一種方法,結合 search 函數和 delAt 函數的部分功能,使用指針對要刪除的結點進行定位,定位后根據結點位置(指針信息可以反映出要刪除的結點在鏈首、鏈中或者鏈尾),然后直接刪除。

完整代碼解析

#include "linearList.h"node * delHas(node * h, int n)
{// 請在此添加代碼,補全函數delHas/********** Begin *********/
node *p=NULL;
node *c=h;
while(c!=NULL){if(c->data==n){if(p==NULL){h=c->next;}else{p->next=c->next;}free(c);break;}p=c;c=c->next;
}
return h;/********** End **********/
}

第7關線性表長度

本關任務:編寫 listLength 函數來求線性表的長度。

溫馨提示:建議在開始你的任務前,先仔細閱讀右側“文件目錄”下提供的 step7/linearList.h 和 step7/test.cpp 兩個文件,對整個程序有一個全面的了解,再動手完成任務!

完整代碼解析

#include "linearList.h"int listLength(node * h)
{// 請在此添加代碼,補全函數listLength/********** Begin *********/
int c=0;
node *p=h;
while(p!=NULL){c++;p=p->next;
}
return c;/********** End **********/
}

第8關線性表應用一:棧

本關任務:用前面已經實現的線性表來實現一個整數棧(棧里的數據是整數)。共需要補全三個函數(也是棧的基本功能):判斷棧空的 empty 函數、壓棧的 push 函數和彈棧的 pop 函數。

棧( stack )又名堆棧,是一種功能受限的線性表,具有先進后出的特性。

其限制為僅允許在線性表的一端進行插入和刪除運算。這一端被稱為棧頂,相對地,把另一端稱為棧底。

向一個棧插入新元素又稱作進棧、入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成為新的棧頂元素;

從一個棧刪除元素又稱作出棧、彈棧或退棧,它是把棧頂元素刪除掉,使其下面的元素成為新的棧頂元素。

接下來首先聲明結構類型:

// 定義結點結構
struct node
{int data;     // 數據域node * next;     // 指針域,指向下一個結點
};
typedef node * intStack;  // 定義類型別名,intStack 即相當于 node*

定義 intStack 是為了使程序的可讀性更好一些。因為本關是用線性表實現棧,而訪問一個線性表其實只需要一個鏈表頭指針就可以了,intStack 實際上就是node*類型,所以后面可以這樣聲明棧 sk :

intStack sk = NULL;     // 聲明棧 sk,并初始化為空棧(空線性表)

實際上 sk 就是一個node*類型的指針,用它可以訪問一個線性表,也就可以看成一個棧了。

完整代碼解析

#include "mstack.h"
// 函數empty:判斷棧sk是否為空
// 參數:sk-棧
// 返回值:true-sk為空,false-sk不為空
bool empty(intStack sk)
{// 請在此添加代碼,補全函數empty/********** Begin *********/return sk == NULL;/********** End **********/
}
// 函數pop:彈棧
// 參數:sk-棧,傳引用,彈棧可能會改變sk的值
// 返回值:彈棧的彈出的整數,如果棧空,返回-1
int pop(intStack &sk)
{// 請在此添加代碼,補全函數pop/********** Begin *********/if (sk == NULL) {return -1;}int value = sk->data;intStack temp = sk;sk = sk->next;delete temp;return value;/********** End **********/
}
// 函數push:壓棧,將整數n壓入棧sk中
// 參數:sk-棧,傳引用,壓棧會改變sk的值,n-要壓棧的整數
// 返回值:無,采用鏈表實現棧,只要還有內存,壓棧都會成功
void push(intStack &sk, int n)
{// 請在此添加代碼,補全函數push/********** Begin *********/intStack newNode = new node;newNode->data = n;newNode->next = sk;sk = newNode;/********** End **********/
}

第9關線性表應用二:隊列

本關任務:用前面已經實現的線性表來實現一個整數隊列(隊列里的數據是整數)。共需要補全三個函數(也是隊列的基本功能):判斷隊列空的 queueEmpty 函數、入列的 enQueue 函數和出列的 deQueue 函數。
隊列是一種功能受限的線性表,具有先進先出的特性。隊列僅允許從一頭出列(這一頭稱為隊列頭),從另外一頭入列(隊列尾)。

入列操作是將結點插入到隊列尾(該結點稱為新的隊列尾);

出列操作是從隊列頭刪除頭結點,并獲取刪除節點的數據(原頭結點的后繼結點為新的頭結點)。

接下來首先聲明結構類型:

// 定義結點結構
struct node
{int data;     // 數據域node * next;     // 指針域,指向下一個結點
};
typedef node * intQueue;  // 定義類型別名,intQueue 即相當于 node*

定義 intQueue 是為了使程序的可讀性更好一些。因為本關是用線性表實現隊列,而訪問一個線性表其實只需要一個鏈表頭指針就可以了,intQueue 實際上就是node*類型,所以后面可以這樣聲明隊列 iq :

intQueue iq = NULL;  // 聲明隊列 iq,并初始化為空隊列(空線性表)

實際上 iq 就是一個node*類型的指針,用它可以訪問一個線性表,也就可以看成一個隊列。

完整代碼解析

#include "mqueue.h"// 函數queueEmpty:判斷隊列iq是否為空
// 參數:iq-整數隊列
// 返回值:true-隊列iq為空,false-iq不為空
bool queueEmpty(intQueue iq)
{// 請在此添加代碼,補全函數queueEmpty/********** Begin *********/ return iq == NULL;/********** End **********/
}
// 函數enQueue:將整數num入列到iq
// 參數:iq-整數隊列,傳引用,入列有可能改變隊列頭指針,num-入列的整數
// 返回值:無,只要還有內存,入列總會成功
void enQueue(intQueue &iq, int num)
{// 請在此添加代碼,補全函數enQueue/********** Begin *********/intQueue newNode = new node;newNode->data = num;newNode->next = NULL;if (iq == NULL) {iq = newNode;} else {intQueue tail = iq;while (tail->next != NULL) {tail = tail->next;}tail->next = newNode;}/********** End **********/
}
// 函數deQueue:出列
// 參數:iq-整數隊列,傳引用,出列有可能改變隊列頭指針
// 返回值:出列結點的數據,如果隊列為空,返回-1
int deQueue(intQueue &iq)
{// 請在此添加代碼,補全函數deQueue/********** Begin *********/if (iq == NULL) {return -1;}int value = iq->data;intQueue temp = iq;iq = iq->next;delete temp;return value;/********** End **********/
}

第10關線性表應用三:集合

本關任務:使用線性表實現集合的表示及操作。具體需要補全三個函數:計算集合并集的 unionSet 函數、計算集合交集的 intersection 函數和向集合中添加元素的 addElement 函數。

集合是數學中一個基本概念,它是集合論的研究對象。樸素集合論中,集合的定義就是“一堆東西”,集合里的“東西”,稱為元素。

下面介紹幾個集合的知識點:

集合中的元素是無序的,對于任意的集合S1和S2,S1=S2當且僅當對于任意的對象a,都有若a∈S1,則a∈S2;若a∈S2,則a∈S1;

集合中的元素互不相同,空集合是沒有任何元素的集合;

集合的并集定義為:A∪B=x∣x∈A或x∈B。例如,A={1,3,5},B={1,2,5} ,則A∪B= {1,2,3,5} ;

集合的交集定義為:A∩B=x∣x∈A且x∈B。例如, A= {1,3,5},B={1,2,5} ,則A∩B= {1,5} 。

接下來首先聲明結構類型:

// 定義結點結構
struct node
{int data;     // 數據域node * next;     // 指針域,指向下一個結點
};
typedef node * intSet;     // 定義類型別名,intSet 即相當于 node*

上述結構類型的聲明中定義 intSet 是為了使程序的可讀性更好一些。因為本關是用線性表實現集合,而訪問一個線性表其實只需要一個鏈表頭指針就可以了, intSet 實際上就是node*類型,所以后面可以這樣聲明集合 a :

intSet a=NULL;     // 聲明集合 a,并初始化為空集合(空線性表)

在右側編輯器中的Begin-End之間補充代碼,完成 unionSet、intersection 、addElement 三個函數,以實現集合的三個功能。具體要求如下:

函數 unionSet 計算并返回集合 a 和 b 的并集。參數 a 和 b 是兩個集合,返回值為 a 和 b 的并集。

函數 intersection 計算并返回集合 a 和 b 的交集。參數 a 和 b 是兩個集合,返回值為 a 和 b 的交集。

函數 addElement 將元素 num 加入到集合 is 中,如果 is 中已經存在 num 了,則不需要加入,不存在則加入。參數 is 是一個集合,num 是要加入到 is 中的元素。

溫馨提示:可以使用線性表已有的功能來實現這些函數。具體提供的函數請從“文件目錄”中查看step10/LinearList.cpp、step10/LinearList.h、step10/mset.h和step10/test.cpp。

完整代碼解析

#include "mset.h"// 函數unionSet:求集合a和b的并集
// 參數:a-集合,b-集合
// 返回值:集合(集合a和b的并集)
intSet unionSet(intSet a, intSet b)
{// 請在此添加代碼,補全函數unionSet/********** Begin *********/intSet result = NULL;intSet current = a;// 將集合a中的元素添加到結果集合中while (current != NULL) {addElement(result, current->data);current = current->next;}current = b;// 將集合b中的元素添加到結果集合中while (current != NULL) {addElement(result, current->data);current = current->next;}return result;/********** End **********/
}
// 函數intersection:求集合a和b的交集
// 參數:a-集合,b-集合
// 返回值:集合(集合a和b的交集)
intSet intersection(intSet a, intSet b)
{// 請在此添加代碼,補全函數intersection/********** Begin *********/
intSet result = NULL;intSet currentA = a;// 遍歷集合a,檢查元素是否也在集合b中while (currentA != NULL) {intSet currentB = b;bool found = false;while (currentB != NULL) {if (currentA->data == currentB->data) {found = true;break;}currentB = currentB->next;}if (found) {addElement(result, currentA->data);}currentA = currentA->next;}return result;/********** End **********/
}
// 函數addElement:在集合is中增加元素num
// 參數:is-集合,num-要增加的元素
// 返回值:無
void addElement(intSet &is, int num)
{// 請在此添加代碼,補全函數addElement/********** Begin *********/intSet current = is;bool exists = false;// 檢查元素是否已存在于集合中while (current != NULL) {if (current->data == num) {exists = true;break;}current = current->next;}// 如果元素不存在,則添加到集合中if (!exists) {intSet newNode = new node;newNode->data = num;newNode->next = is;is = newNode;}/********** End **********/
}

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

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

相關文章

【前端】threeJS學習(長期更新)

簡介 Three.js是用JavaScript編寫的第三方庫&#xff0c;用于實現3D功能&#xff0c;基于WebGL進行封裝。 一個3D模型的建立主要由以下幾個部分組成&#xff08;基本版&#xff09;&#xff1a; * 創建場景scene--相機camera--渲染器renderer--(燈光light)&#xff1b; *…

Linux系統--權限

大家好&#xff0c;上一次我們學習了關于Linux中的基礎指令&#xff0c;那么我們今天來繼續學習Linux的新的內容&#xff1a;權限。那么話不多說&#xff0c;我們開始今天的學習&#xff1a; 目錄 Linux權限 1. Linux權限的概念 2. Linux權限管理 3. ?件權限值的表??法…

論文筆記 <交通燈> <多智能體>DERLight雙重經驗回放燈機制

今天看的論文是這篇 主要提出了傳統優先級經驗回放&#xff08;PER&#xff09;在復雜交通場景中效率低下&#xff0c;使用二叉樹存儲樣本&#xff0c;導致大規模樣本時計算復雜度高。而且不丟棄樣本&#xff0c;造成存儲空間浪費。 雙重經驗池&#xff1a; 為了解決以上問題…

Chromium 136 編譯指南 macOS篇:環境準備與系統配置(一)

1. 引言 在瀏覽器技術的星空中&#xff0c;Chromium 猶如一顆最亮的明星&#xff0c;照亮了整個互聯網的發展軌跡。作為推動現代 Web 技術革命的核心引擎&#xff0c;Chromium 不僅是 Google Chrome 的技術基石&#xff0c;更是 Microsoft Edge、Opera、以及眾多定制瀏覽器的共…

linux機器間無密碼如何傳輸文件

1. scp傳輸時的問題 $ scp deepseek_r1_distill_qwen1.5b_content_audit_fp16_20250613_2_Q4_K_M.gguf xxx192.168.xxx:/home/xxx/pretrained_model/output The authenticity of host 192.168.xxx (192.168.xxx) cant be established. ED25519 key fingerprint is SHA256:deOs…

PySpark 使用pyarrow指定版本

背景說明 在 PySpark 3.1.3 環境中&#xff0c;當需要使用與集群環境不同版本的 PyArrow (如 1.0.0 版本)時&#xff0c;可以通過以下方法實現&#xff0c;而無需更改集群環境配置 完整操作說明 去pyarrowPyPI下載對應版本的whl文件后綴whl直接改成zip解壓后有兩個文件夾&am…

安卓APP投屏調試工具使用教程

安卓APP投屏調試工具使用教程 一、準備工作&#xff08;一&#xff09;下載ADB工具&#xff08;二&#xff09;配置ADB的環境變量&#xff08;三&#xff09;檢查是否成功安裝&#xff08;四&#xff09;adb核心命令說明 二、無線調試流程&#xff08;一&#xff09;環境要求&a…

huggingface網站里的模型和數據集

直接下載肯定是不太行&#xff0c;平時訪問都不容易&#xff0c;更別提下載東西了&#xff0c;但是我們可以通過國內鏡像進行快速下載。 鏡像網址&#xff1a; hf-mirror地址&#xff1a;HF-Mirror 進入網站之后&#xff0c;在搜索框里搜索你想下載的內容&#xff0c;接下來…

Node.js 路由請求方式大全解:深度剖析與工程實踐

文章目錄 &#x1f310; Node.js 路由請求方式大全解&#xff1a;深度剖析與工程實踐一、&#x1f4dc; HTTP 請求方法全景圖&#x1f3c6; 核心方法深度對比HTTP 請求方法概念對比表&#x1f6e0;? 特殊方法應用場景 二、&#x1f3a8; 各方法深度解析1. GET - 數據查看器&am…

JS-實現一個鏈式調用工具庫

要求&#xff1a; 支持鏈式調用&#xff0c;如&#xff1a;_chain(data).map().filter().value()實現map、filter、等常用方法支持惰性求值&#xff08;延遲執行、直到用到value()時才真正計算&#xff09;。 鏈式調用的實現原理的關鍵點是&#xff1a;函數執行完以后&#x…

【人工智能數學基礎】實變函數與泛函分析

數學分析、解析幾何、高等代數、實變函數、常微分方程、近世代數、微分幾何、復變函數、點集拓撲、概率論、數理統計、數理邏輯、偏微分方程、泛函分析、動力系統、數學物理方程、數論導引、群與代數表示、微分流形、代數拓撲、代數幾何、金融數學、多元統計分析、應用隨機過程…

css3 背景色漸變

在 CSS 中&#xff0c;使用漸變色需要用到 gradient 屬性&#xff0c;而 gradient 屬性分為 線性漸變 linear-gradient 與 徑向漸變 radial-gradient。今天主要是說一下 linear-gradient 線性漸變屬性。 例如&#xff1a;background: linear-gradient(90deg, #e7f1fc, #f5f9fb…

將圖片合成為視頻(基于 OpenCV)

本文將介紹如何使用 Python 和 OpenCV 將一組圖像文件合成為一個視頻文件。你將學會&#xff1a; 使用 os 模塊遍歷文件夾中的圖像 使用 cv2.VideoWriter 寫入視頻 設置分辨率與幀率參數 對圖像尺寸進行統一處理 簡單的視頻生成應用開發 1. 所需模塊與安裝 本章需要以下 …

HanLP 使用教程:從安裝到實戰應用

HanLP 使用教程&#xff1a;從安裝到實戰應用 HanLP 是由hankcs開發的一款高效、多功能的中文自然語言處理&#xff08;NLP&#xff09;工具包&#xff0c;支持分詞、詞性標注、命名實體識別&#xff08;NER&#xff09;、依存句法分析、關鍵詞提取、文本摘要等任務。本教程將…

MySQL 分組函數全面詳解與最佳實踐

MySQL 分組函數全面詳解與最佳實踐 MySQL 分組函數&#xff08;聚合函數&#xff09;的核心知識、注意事項和高級應用技巧&#xff1a; &#x1f4ca; 分組函數核心列表 函數描述示例COUNT()計算行數COUNT(*)SUM()計算數值總和SUM(salary)AVG()計算平均值AVG(score)MAX()獲取…

華為OD 最小循環子數組

1. 題意 給定一個由若干整數組成的數組 nums&#xff0c;請檢查數組是否是由某個子數組重復循環拼接而成&#xff0c;請輸出這個最小的子數組。 2. 題解 利用 k m p kmp kmp中的 n e x t next next數組性質&#xff0c;我們可以求出 n u m s nums nums中的最長公共 前綴后綴…

FreeCAD創作參數化凹形和水波紋式雨水箅子

這種非常流行的美觀的雨水篦子是都市的寵愛&#xff0c;大家要多多去用。 用FC來創建參數化后&#xff0c;設計人員可以隨意修改參數&#xff0c;滿足自身的要求&#xff0c;調整各部件的位置&#xff0c;達到滿意的布局&#xff0c;非常快捷。 水波紋雨水篦子 凹形雨水篦子

如何用一臺服務器用dify私有部署通用的大模型應用?

dify是什么&#xff1f;如何用一臺服務器用dify私有部署通用的大模型應用&#xff1f; Dify 是一款開源的大語言模型(LLM) 應用開發平臺。它融合了后端即服務&#xff08;Backend as Service&#xff09;和LLMOps的理念&#xff0c;使開發者可以快速搭建生產級的生成式 AI 應用…

海洋捕食算法優化BP神經網絡

引言BP神經網絡因梯度下降法的固有缺陷,常出現訓練震蕩和早熟收斂。海洋捕食算法(MPA)受海洋生物覓食行為啟發,其分階段搜索策略(高速游動→自適應步長→局部開發)能有效平衡全局探索與局部開發。本文通過MPA優化BP初始權值及學習率,構建混合優化模型。 方法論2.1 MPA算…

C++/OpenCV 圖像預處理與 PaddleOCR 結合進行高效字符識別

C/OpenCV 圖像預處理與 PaddleOCR 結合進行高效字符識別 在許多實際應用場景中&#xff0c;直接從原始圖片中提取文字的準確率可能不盡人意。圖像中的噪聲、光照不均、角度傾斜等問題都會嚴重干擾 OCR (Optical Character Recognition) 引擎的識別效果。本文將詳細介紹如何利用…