單鏈表復習 (C語言版)

目錄

一.順序表與鏈表的區別

二.鏈表概念

三.單鏈表

1.單鏈表的開始與初始化

2.單鏈表的打印

3.單鏈表的尾插

重難點:單鏈表實現時的指針詳解

4.單鏈表的頭插

5.單鏈表的尾刪

6.單鏈表的頭刪

小結:

7.單鏈表的查找

8.在指定位置前插入數據

9.在指定位置后插入數據

10.刪除pos結點

11.刪除pos結點之后的結點

12.刪除鏈表

13.可能存在的疑惑解答

?14.全部用于測試的代碼

四.文章鏈接


?

一.順序表與鏈表的區別

順序表的問題及思考:

  1. 中間/頭部插入刪除,涉及到移動數據,時間復雜度為o(n)
  2. 增容需要申請新空間,拷貝數據,釋放舊空間,會有損耗。(realloc)
  3. 增容一般呈2倍的增長,當前容量為100,增加到200,只存放5字節,浪費了95字節
  4. 順序表中間頭部插入效率低下,增容造成運行效率降低
  5. 鏈表解決了上述問題

順序表相關文章鏈接:順序表復習(C語言版)

f6f0317216824cfe8941ab0aff0724b3.jpg

?上圖就形象地表示了線性表與鏈表的區別,鏈表存儲位置不連續,是用指針相連;線性表(SeqList)在內存中是連續存放的

?

二.鏈表概念

線性表是一類相同元素的集合,例如蘋果和香蕉(都是水果)

邏輯結構:一定線性

物理結構:不一定線性(因為在內存中的存放位置是不連續的)

int a =10;float f =0.1 變量a和f的物理空間不一定連續,即存放位置不一定連續

0c1f39d1dc964dc4b3c867789eca40af.jpg

鏈表和火車很相似

通過一個鉤子連在一起,地址空間是鏈接在一起的,車頭也是車廂,因為它也可以裝人

旺季:增加車廂

淡季:減少車廂

鏈表是由一個一個節點組成,結點可以看成車廂

c048c7bf10a9408a860546cd6ab64c9a.jpg

?plist位置稱之為頭結點,頭結點指向的結點叫做首元結點,最后一個指針域指向NULL的結點叫做尾元結點

結點和節點:同一個東西,隨便用哪個

結點由什么組成的呢?

有兩個組成部分

1.數據域? --->在該域內存儲了數據

2.指針域? --->存儲了指向下一個結點的指針

三.單鏈表

1.單鏈表的開始與初始化

鏈表就是在定義鏈表的結點結構

struct?SListNode single list node{int?data; ?????????????????????????//int a =10;//int* pa =&astruct?SListNode* next; ???????????//指向下一個節點的指針}SLTNode;typedef?int?SLTTDataType;

上述代碼就是在對鏈表進行初始化,定義了一個結構體,并在里面定義了一個data還有一個指針next(next指向下一個結點)

void?SListTest01(){//創建節點SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));node1->data = 1;SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));node2->data = 2;SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));node3->data = 3;SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));node4->data = 4;//將四個節點連接起來node1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;//調用鏈表的打印SLTNode* plist = node1;void?SLTPrint(plist); //為了讓代碼邏輯更加清晰}

上述代碼依舊是對鏈表的初始化,將四個結點進行創建,然后相連接;在初始化代碼寫完以后,直接跟了一個鏈表打印函數

2.單鏈表的打印

920ec114d1554024aa1a8dbc538c0809.jpg

緊接上文代碼,上文中 SLTNode* plist = node1就是在說明,plist是個指針,且與node1一樣都是一級指針;因此它指向node1所在的這塊空間,這就可以看成讓node1和plist兩個指針指向同一塊空間,且這塊空間在早些時候已經通過node1指針初始化完畢了;因此如果創建一個pcur,讓他等于plist,那么pcur依舊指向node1所指向的空間,這就可以看出三個指針指向同一塊空間

7fd278659e714d43aed2a9cc3fdf06e8.jpg

先是打印pcur所指向空間的data,然后讓pcur里指向下一個結點的指針覆蓋pcur這個結點,pcur就自然而然向后移動了

e99983d4b84f497582bb0098d17178bf.jpg

重復上述操作,先打印所指向空間的data值,然后再讓其指向的下一個結點地址將現在這個結點地址覆蓋

084955dd4e1d4191829f3f40f46e67b7.jpg

由上圖可知,當pcur指向的空間為NULL時,打印操作完成;由上四圖,不難得出下述代碼:

void?SLTPrint(SLTNode* phead) //傳的是首元結點{SLTNode* pcur = phead;while?(pcur) ?//pcur != NULL{printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");}

?

3.單鏈表的尾插

因為在此以后我們需要多次進行單鏈表的增刪查改操作,因此我們可以專門寫一個函數,這個函數是專門用來創建新結點的,在順序表那篇文章中已經講解過,故在此省略

	SLTNode* SLTBuyNode(SLTDataType x){SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode*));if (newnode == NULL){perror("malloc fail!");exit(-1);}newnode->data = x;newnode->next = NULL; //此處指向空指針是為了方便后續操作}

接下來就要開始進行尾插操作

89f24ff48fd94fe1a9a10b62150ffa27.jpg

77fc5ec222b34612b9b24215ac2a42b7.jpg

	void SLTPushBack(SLTNode** pphead, SLTDataType x){SLTNode* newnode = SLTBuyNode(x);//空鏈表的情況if (*pphead == NULL){*pphead = newnode; //newnode的data有了,next指向空指針,將pphead地址覆蓋,指向新的newnode空間,完成尾插}//非空鏈表的情況else{//找尾SLTNode* ptail = *pphead; //不想讓*pphead變動位置(變動了就需要使用二級指針了),因此又定義了一個變量ptail指針while (ptail->next){ptail = ptail->next;}//退出循環就說明已經找到尾了,此時ptail指向空指針ptail->next = newnode; //讓ptail指向新結點}
}

上述代碼指針部分較難理解,因此筆者將在下文進行詳細解釋

重難點:單鏈表實現時的指針詳解

上述代碼中,為什么使用二級指針?
在函數中,想要通過形參改變實參的值(即對其進行某些操作,且這些操作是針對該參數而言的,而不是該參數針對別的變量而言的),就必須在傳參時傳地址。

即使實際參數是一個指針,我們也需要通過指針的地址來改變該指針本身。(如下圖所示)

所以,我們在函數傳參時,傳的是指針的地址,因此需要函數用二級指針來接收。

但請注意,只有二級指針類型的形式參數,在進行了一次解引用操作狀態下的改變,才需要用到二級指針類型的形式參數。

此處我們可以類比一級指針和整型變量。要通過形式參數改變實際參數,那么需要形式參數是一個指向需改變變量的指針,然后才可以通過形式參數改變該變量;而想要改變一級指針變量,那么就需要通過二級變量來改變。(前者是結點里的指針域、數據域的內容的改變,后者是結點本身的改變)

所以,如果需要改變結點,就用二級指針;如果只需要改變某個結點指針域、數據域內容,那就傳一級指針,此時就沒有必要傳二級指針了。

上述代碼中,為什么有些地方需要解引用,而有些地方不需要呢?

解引用(即*操作符)可以看作是把指針降級(文章鏈接部分有指針其他內容復習),把二級指針變成一級指針,把一級指針變成指針指向的內容;所以上述代碼中,部分地方使用了一次解引用操作,即從指向結點的指針地址變為了指向結點的指針(也可以看成是結點本身)。

為什么單鏈表的打印不需要二級指針,而單鏈表的尾插就需要呢?

因為單鏈表的打印并沒有改變鏈表的首元結點本身,只是完成了打印操作,所以不需要通過形式參數來改變實際參數;而尾插操作需要改變鏈表的首元結點本身(從NULL變成了newnode),因此需要通過形參來改變實參。

SLTNode* 和 * 的區別是?

SLTNode* 是在告訴編譯器,這個變量是個指針,指向了SLTNode這個類型的數據,而并不是在對變量進行解引用操作;*是解引用操作符,在上文已經講解完畢;而在函數當中,對二級指針進行解引用,其本質上還是個二級指針,因為形式參數依然還是二級指針。

045a54bf69824b03a12d409368acadfe.jpg

?實參形參(前面的*是指函數中使用的解引用操作符個數)
第一個結點的內容*plist(即xxx)

ptail?-> xxx

(即一般情況下的**pphead,此處只是因為是結構體指針,所以需要用結構體指針的解引用方式)

指向第一個結點的指針plist*pphead
指向第一個結點的指針的地址&plistpphead
	SLTNode* plist = NULL; //代碼1SLTNode* plist1 = node1; //代碼2SLTPushBack(&plist1, 1); //代碼3SLTPrint(plist1); //代碼4plist1->next = NULL; //代碼5

代碼1:結點為空

代碼2:一個有效結點

代碼3:傳輸有效結點的地址

代碼4:傳輸有效結點

代碼5:對有效結點解引用,結構體里套了一個指向結構體類型(即為其本身)的指針變量,讓該變量指向空,這一操作即是讓該結點的下一個結點為空

4.單鏈表的頭插

3abd1fb12b9347c194e420fdf3028572.jpg?4f25ddd568754c5a8f5324e4e3ae0bf0.jpg

	void SLTPushFront(SLTNode** pphead, SLTDataType x){assert(pphead);SLTNode* newnode = SLTBuyNode(x); newnode->next = *pphead; //newnode指向首元結點*pphead = newnode; //將頭指針指向新創建的結點}

?

5.單鏈表的尾刪

88de588083eb4ca795eb5a760e86e716.jpg

	void SLTPopBack(SLTNode** pphead) {assert(pphead && *pphead) //指向鏈表結點的指針不能為空,鏈表也不能為空SLTNode* prev = *pphead; //為防止野指針,要讓尾元結點的next指針指向空,因此需要再創建一個指針,來完成這一操作SLTNode* ptail = *pphead;while (ptail->next){prev = ptail; //prev需要指向尾元結點的前一個結點,因此需要跟著ptail變動ptail = ptail->next; //ptail指向尾元結點}free(ptail); //釋放ptail所指向的空間,就能達到尾刪的目的ptail = NULL; //動態內存開辟內容,下文有鏈接prev->next = NULL; //要讓prev里的next指針從指向野指針到指向空指針}

?

4b7639d7a4f14bb59bc72213fb92393f.jpg

當只有一個結點的時候,循環直接跳過,ptail和prev指向同一個結點,在將ptail空間釋放并變成空指針以后,又一次對prev(即ptail的同一結點)進行了解引用操作,這樣代碼會報錯(對空指針解引用)

因此當鏈表只有一個節點時,代碼如下所示:

	//鏈表只有一個結點if ((*pphead)->next = NULL) //加括號是因為 -> 優先級高于 *{free(*pphead);*pphead = NULL;}

尾刪的全部代碼:

	void SLTPopBack(SLTNode** pphead) {assert(pphead && *pphead) //鏈表不能為空,指向鏈表結點的指針也不能為空//鏈表只有一個結點if ((*pphead)->next = NULL) //加括號是因為 -> 優先級高于 *{free(*pphead);*pphead = NULL;}else{SLTNode* prev = *pphead; //為防止野指針,要讓尾元結點的next指針指向空,因此需要再創建一個指針,來完成這一操作SLTNode* ptail = *pphead;while (ptail->next){prev = ptail; //prev需要指向尾元結點的前一個結點,因此需要跟著ptail變動ptail = ptail->next; //ptail指向尾元結點}free(ptail); //釋放ptail所指向的空間,就能達到尾刪的目的ptail = NULL; //動態內存開辟內容,下文有鏈接prev->next = NULL; //要讓prev里的next指針從指向野指針到指向空指針}}

?

6.單鏈表的頭刪

dde786103c6c45bcad2d18e0b3569991.jpg?cc8648526b6647a383ed9598cf4afc93.jpg

先要保存好頭刪前的鏈表第二個節點,頭刪以后把*pphead指針指向新的節點

void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}
//只有一個結點的情況上述代碼也能夠解決

小結:

上述的插入、刪除代碼中,因為四種代碼所以必須使用二級指針:
1.free(*pphead);

2.*pphead = NULL;

3.*pphead = newnode;

4.*pphead = next;

即對一級指針類型的參數本身進行了某些操作

7.單鏈表的查找

在使用查找函數以后,要分為找到了和沒找到兩種情況,那么我們可以通過如下代碼來區分(此處的3是指數據域為3的結點):

SLTNode* find = SLTFind(plist, 3);
if (find == NULL)
{printf("沒有找到");
}
else
{printf("找到了");
}

上述代碼中,plist即為首元結點的指針

不需要傳輸首元結點指針的地址,這是因為并不需要通過查找函數來改變鏈表的首元結點。?

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pcur = phead; //不想形式參數是個二級指針,因此可以在函數中定義一個指針變量while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

?

8.在指定位置前插入數據

該函數必須要有三個參數,分別是鏈表的首元結點(SLTNode** pphead)、在鏈表的哪個結點前插入(SLTNode* pos)、所需要插入的數據(x)。如下所示:

void SLTInsert(SLTNode** pphead,SLTNode* pos,SLTDataType x)

一個參數使用二級指針,一個參數使用一級指針的原因:

前一個二級指針類型是鏈表的首元結點地址,是需要通過形式參數來改變首元結點的;下一個一級指針類型是鏈表指定位置的結點,可以直接通過解引用,對結點的指針域、數據域進行操作。

6fac2faf81f9429c8c9982e47ca39e66.jpg?579a96948e964c49be9432242decbd8f.jpg

?

如果要在第3個結點前插入數據,就需要先創建一個結點,然后把這個結點和第3個結點相連接,然后斷開第2個結點和第3個結點的連接,讓第2個結點和第3個結點相連。而第2個結點就需要通過遍歷獲得,循環語句的退出條件是:prev->next != pos (prev為函數中定義的指針變量,遍歷以前,prev == *pphead)

aa755b4a649c4be7ad1be126a1474017.jpg

如果是在鏈表的首元結點之前插入數據,那么prev的next就不可能會等于pos,因此我們在代碼實現里要考慮到一般情況和在首元結點之前插入數據兩張情況。并且在首元結點前插入數據可以通過頭插的函數來實現(這種情況也是函數需要使用二級指針的原因,因為函數需要傳一個二級指針)。

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(*pphead && pphead); //鏈表不能為空,因為為空了,就無法確定“某一位置”了assert(pos);SLTNode* newnode = SLTBuyNode(x);if (pos == *pphead) //在首元結點之前插入{SLTPushFront(pphead, x);}else //一般情況{//找到pos結點的前一個結點SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next; //沒有找到就找下一個}newnode->next = pos; //完成上文的操作prev->next = newnode;}
}//pos指針即為上文查找部分出現的find

?

9.在指定位置后插入數據

在指定位置后插入數據,不需要再創建一個指針,直接通過"指定位置"結點的next以及該結點next的next來插入即可

91182df3ec8647e4b229c4a88a65f201.jpg

第一種:先紅色,再綠色

第二種:先綠色,再紅色

以上兩種方式是否相同?

//第一種方式的代碼
newnode->next = pos->next;
pos->next = newnode;

//第二種方式的代碼
pos->next = newnode;
newnode->next = pos->next;

9d37c24988ea4d60811acab0693d4e2d.jpg

第一種方式可以完成我們需要的操作,即先讓新節點指向鏈表的指定節點的下一個結點(next),然后再讓指定節點指向新結點

第二種方式讓指定節點指向新節點,此時指定結點的下一個結點即為新節點,因此讓新節點指向指定的下一個結點時,即為新節點本身,無法完成需要的操作(如上圖所示)

void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);newnode->next = pos->next;pos->next = newnode;
}//pos指針即為上文查找部分出現的find

?

10.刪除pos結點

刪除pos結點需要我們將pos結點的前一個結點和pos的后一個結點相接,因此還需要有個prev指針去保存pos結點的前一個結點,如下圖所示?

首元結點、尾元結點的刪除請讀者自行判斷

?ea52e351378b48f48101055f5421b69a.jpg

void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);assert(pos);//pos是首元結點if (pos == *pphead){//頭刪SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;}
}//pos指針即為上文查找部分出現的find
//在使用完find指針以后,需要在函數外進行free、置空操作

?

11.刪除pos結點之后的結點

刪除pos->next,讓pos結點和pos->next->next相連即能完成操作,如下圖所示:?

ad6dcc92da534b8699185df2fbbbfc13.jpg

void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);SLTNode* del = pos->next; //保存pos的下一個結點pos->next = del->next;  //讓pos的下一個結點(即del)和pos的下一個結點(即del)的下一個節點相鏈接free(del);del = NULL;
}

?

12.刪除鏈表

d1a63226464048a1821ef0bdcdb021d5.jpg

?ed7295ad506d4d47a02a840a64af2344.jpg

傳入首元結點,然后保存好首元結點的下一個結點(next指針),然后free掉pcur以后,讓next指針和pcur指針都往后走一個結點,循環退出條件為pcur為空指針。(如上圖所示)

void SListDesTroy(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next; free(pcur);pcur = next;}*pphead = NULL; //已經釋放完所有的內容了,將pphead置為空
}

?

13.可能存在的疑惑解答

**pphead作為形參出現那么多次,那么他到底對應哪個結點呢?

他既可以對應空指針(鏈表完全沒有結點,對應了插入等的情況),同時也可以對應首元結點(頭刪和尾刪等的情況)

為什么在部分函數里有時候free不需要二級指針?

例如free(ptail),在尾刪板塊出現。這是因為ptail是在函數里創建、使用的,無需通過函數來改變任何一個實參(它也沒有對應的實參、因為它并不是一個形式參數)。在出了函數以后,ptail指針就沒有作用了,如果在函數內沒有進行free、置空操作,就會變成一個野指針。?

單鏈表的開始和初始化必須要像1那樣寫嗎?
并不是,其實完全可以通過尾插、頭插等等方式進行鏈表的初始化的。

所有函數都必須要傳二級指針嗎?

有些一定需要傳,例如*pphead=newnode(在頭插板塊),因為這條代碼如果不寫在函數里,就完成不了頭插操作;在某些情況下不一定需要傳,例如free(*pphead),放到函數實現外的部分,可以通過free(plist)完成操作,不寫在函數里也不會影響函數的功能實現。

如果打印函數里使用形參進行打印,不創建變量了,是否可以?
其實是可以的,因為形參是一級指針,不會影響實參。

在函數中,定義一個變量pcur = *pphead了以后,為什么影響了pcur就能影響整個鏈表了?

在函數當中,實際上pcur能直接看成pphead解引用了一次,即可以把所有的pcur看成是(*pphead),這就像是定義宏一樣;這就像是傳入了一個一級指針? int *a,然后在函數了假設變量int b = *a ,然后通過b的改變來改變傳入進來的*a一樣。而我們會在函數里使用到pcur = *pphead有兩個原因,其一就是為了代碼的可讀性,其二就是為了讓pphead指針一直指向首元結點。

?14.全部用于測試的代碼

請注意,以下代碼有些是不兼容的,就比如plist指針一會是NULL,一會是首元結點;同時有些功能,例如單鏈表的查找,不能鏈表是空的,需要有前提條件。因此請讀者對函數進行一一嘗試,需嘗試的功能直接取消注釋即可。

int main()
{//SListTest01(); //對于開始和初始化//SLTNode* plist = NULL; //從沒有鏈表開始對于尾插//SLTPushBack(&plist, 1);//SLTPrintf(plist); //此處plist又變成了首元結點,在使用完該操作以后,plist由于//SLTPushBack(&plist, 2);//SLTPrintf(plist);//SLTPushBack(&plist, 3);//SLTPrintf(plist);//SLTPushBack(&plist, 4);//SLTPrintf(plist);對于頭插(尾插、頭插寫一塊了)//SLTPushFront(&plist, 6);//SLTPrintf(plist);//SLTPushFront(&plist, 2);//SLTPrintf(plist);//SLTPushFront(&plist, 3);//SLTPrintf(plist);//SLTPushFront(&plist, 4);//SLTPrintf(plist);尾刪、頭刪(先決條件:不為空)//SLTPopBack(&plist);//SLTPrint(plist);//SLTFrontBack(&plist);//SLTPrint(plist);查找(先決條件:不為空)//SLTNode* find = SLTFind(plist, 3);//if (find == NULL)//{//	printf("沒有找到");//}//else//{//	printf("找到了");//}指定位置的插入刪除(先決條件:不為空+有具體指定位置)//SLTNode* find = SLTFind(plist, 3); //find即為具體指定位置//SLTInsert(&plist, find, 10);//SLTPrint(plist);//SLTInsertAfter(find, 10);//SLTPrint(plist);//SLTErase(&plist, find);//SLTPrintf(plist);//SLTEraseAfter(find);//SLTPrintf(plist);//銷毀//SListDesTroy(&plist);//SLTPrintf(plist);
}
//以上代碼是在vscode2022中進行的

?

四.文章鏈接

指針講解:指針復習

動態內存開辟講解:動態內存開辟復習

?

?

?

?

?

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

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

相關文章

mingw64,clang,gcc

MinGW-w64、Clang 和 GCC 都是編譯器,但它們在功能和設計上有一些區別: MinGW-w64 是一個在 Windows 平臺上的開發工具集,它提供了一組 GNU 工具和庫,可以用于編譯和構建本地的 Windows 應用程序。MinGW-w64 的主要目標是提供類似…

GitHub工程獲取第三方PR操作

GitHub工程獲取第三方PR操作 1. 源由2. 獲取第三方PRStep 1:安裝ghStep 2:獲取個人TokenStep 3:通過git協議獲取代碼Step 4:獲取第三方PR分支 3. 總結 1. 源由 通常來說,GitHub上通常有三種場景: 工程管理…

STM32 UART 錯誤代碼 HAL_UART_ERROR_PE

HAL_UART_ERROR_PE 是STM32 HAL庫(硬件抽象層庫)中UART(通用異步收發傳輸器)模塊定義的一個錯誤碼,表示“Parity Error”(奇偶校驗錯誤)。當UART通信過程中檢測到奇偶校驗不匹配時,會…

自動化噴涂生產線方案三

本文介紹自動化噴涂生產線的另外一種方案,單工作站方案,單工作站配置一臺機械臂和一套除塵柜,代替一個工位完成噴涂工藝,其工作原理簡要介紹如下。 一套單工作站的主要組成設備有,10kg負載機械臂一臺主要負責噴涂工藝&…

源碼講解kafka 如何使用零拷貝技術(zero-copy)

前言 kafka 作為一個高吞吐量的分布式消息系統,廣泛應用與實時應用場景中。為了實現高效的數據傳輸,kafka使用了零拷貝技術(zero-copy)顯著提高了性能。本文將詳細講解 Kafka 如何利用零拷貝技術優化數據傳輸。 什么是零拷貝 零拷貝技術目的是減少數據傳輸的效率。在傳統…

馮喜運:6.8下周倫敦金行情怎么看?黃金原油下周操作建議

【黃金消息面分析】:黃金不是由通脹驅動的。它也不是由通貨緊縮驅動的。它也不是由美元驅動的。當股市反彈時,它也不會下跌,反之亦然。黃金是由市場情緒驅動的。而且,它是黃金市場上唯一的常數,使人們能夠對黃金價格趨…

vue elementui el-input 正則驗證,限制只能輸入數字和小數

vue elementui el-input 正則驗證 限制只能輸入數字和小數,以下兩種方法都可以: 1、οninput“value value.replace(/[^0-9.]/g,‘’)” 2、οninput“value value.replace(/[^\d.]/g, ‘’)” 限制只能輸入數字: 1、oninput “valuevalu…

c++使用_beginthreadex創建線程

記錄使用_beginthreadex(),來創建線程。方便后期的使用。 創建一個線程 相關函數介紹 unsigned long _beginthreadex( void *security, // 安全屬性, 為NULL時表示默認安全性 unsigned stack_size, // 線程的堆棧大小, 一般默認為0 u…

3D打印隨形透氣鋼:技術革新引領模具制造新潮流

在模具制造領域,透氣鋼一直扮演著重要角色,它能夠有效解決模具困氣問題,提高注塑成型的效率和質量。然而,傳統的透氣鋼制造方法受限于工藝和材料,難以滿足復雜模具的需求。隨著3D打印技術的飛速發展,3D打印…

Go微服務: 分布式之通過本地消息實現最終一致性

概述 我們的業務場景是可以允許我們一段時間有不一致的消息的狀態的,并沒有說必須特別高的這個消息的一致性比如說在TCC這個架構中,如果采用了消息的最終一致性,整體架構設計要輕松好多即便我們庫存服務掛了,或者我們積分服務掛了…

【2024PythonPycharm詳細安裝教程】

1.打開官網 https://www.python.org/ downloads——>Windows 2.找到 Download Windows installer (64-bit) 下載 3.下載完成雙擊安裝包 勾選Add python.exe to PATH(自動配置系統變量) 點擊Install Now(默認安裝) 然后看到安裝成功&#xff0…

LeetCode-day05-3038. 相同分數的最大操作數目 I

LeetCode-day05-3038. 相同分數的最大操作數目 I 題目描述示例示例1:示例2: 思路代碼 題目描述 給你一個整數數組 nums ,如果 nums 至少 包含 2 個元素,你可以執行以下操作: 選擇 nums 中的前兩個元素并將它們刪除。…

深度學習中2D檢測

深度學習中的2D目標檢測 2D目標檢測是深度學習中的一個關鍵任務,旨在識別圖像中的目標對象,并在每個目標對象周圍生成一個邊界框。該任務在自動駕駛、視頻監控、機器人視覺等領域具有廣泛應用。以下是對深度學習中2D目標檢測的詳細介紹,包括…

通過windbg查看dump文件中靜態變量的值

可以使用 WinDbg 和 SOS 擴展來查看它們。下面是如何使用 WinDbg 和 SOS 的步驟: 打開 WinDbg,并用它打開你的 dump 文件。你可以使用 File → Open Crash Dump 來實現。 在命令行中,加載 SOS 擴展。一般來說這可以通過 .loadby sos clr 來完…

Oracle EBS AP發票創建會計科目錯誤:子分類帳日記帳分錄未按輸入幣種進行平衡

系統版本 RDBMS : 12.1.0.2.0 Oracle Applications : 12.2.6 問題癥狀: 提交“創建會計科目”請求提示錯誤信息如下: 中文報錯: 該子分類帳日記帳分錄未按輸入幣種進行平衡。請檢查日記帳分錄行中輸入的金額。 英文報錯:The subledger journal entry does not balance i…

excel vlookup

Excel中的VLOOKUP函數用于在數據表中查找特定值,并返回該值所在行的另一個單元格中的值。這個函數非常有用,特別是在需要從一張表中查找信息并將其填充到另一張表中的情況下。VLOOKUP函數的基本語法是:VLOOKUP(查找值, 數據范圍, 返回值的…

PS的stable diffusion插件安裝指南

PS的stable diffusion插件安裝指南 1.首先要安裝stable diffusion,具體安裝方法,參考https://blog.csdn.net/sheji888/article/details/139196688 stable diffusion要求要啟用API功能 2.安裝ps2023以上版本,低于這個版本不能使用stable diff…

17- Redis 中的 quicklist 數據結構

在 Redis 3.0 之前,List 對象的底層數據結構是雙向鏈表或者壓縮列表,然后在 Redis 3.2 的時候,List 對象的底層改由 quicklist 數據結構實現。 其實 quicklist 就是【雙向鏈表 壓縮列表】組合,因為一個 quicklist 就是一個鏈表&…

什么是ESG?

什么是ESG? ESG的實施和發展是企業應對全球和國內環境、社會和治理挑戰的關鍵路徑。《ESG入門一本通》詳細闡述了ESG的概念、發展歷程和評價體系,并結合中國的實際情況,強調了ESG的重要性和必要性。企業需重視ESG管理和信息披露,…

詳細分析Mysql中的JSON_OBJECT() 基本知識(附Demo)

目錄 前言1. 基本知識2. Demo 前言 對于基本的命令行以及優化推薦閱讀: 數據庫中增刪改常用語法語句(全)Mysql優化高級篇(全)命令行登錄Mysql的詳細講解 1. 基本知識 JSON_OBJECT() 是 MySQL 中用于生成 JSON 對象…