【數據結構與算法】數據結構初階:詳解順序表和鏈表(四)——單鏈表(下)

?


?🔥個人主頁:艾莉絲努力練劍

?專欄傳送門:《C語言》、《數據結構與算法》、C語言刷題12天IO強訓、LeetCode代碼強化刷題

🍉學習方向:C/C++方向

??人生格言:為天地立心,為生民立命,為往圣繼絕學,為萬世開太平


前言:本篇文章,我們復盤順序表和鏈表相關的知識點,在初階的數據結構與算法階段,我們把知識點分成三部分,復雜度作為第一部分,順序表和鏈表、棧和隊列、二叉樹為第二部分,排序為第二部分,我們之前已經介紹完了第一部分:算法復雜度,本文我們繼續學習第二部分中的順序表和鏈表部分內容啦。

? ? ? ? 半個多月前,博主更新了頭插、尾刪、頭刪、隨機位置插入、隨機位置刪除、查找、修改、菜單等內容,本篇文章,我們就來復盤一下動態順序表的內容,博主會添加很多新內容,希望對大家的順序表學習有所幫助。


??


目錄

正文

三、單鏈表

(二)實現單鏈表

3、增刪查改

(1)尾插

(2)頭插

(3)在指定位置之前插入數據

(4)在指定位置之后插入數據

(1)尾刪

(2)頭刪

(3)刪除pos節點

(4)刪除pos之后的節點

(1)查找

改?

(1)修改

?銷毀鏈表

(1)銷毀鏈表

4、完整代碼?

(1)SList.h:

(2)SList.c:

(3)test.c:

結尾


正文

提醒:為什么我們要學那么多的數據結構?這是因為沒有一種數據結構能夠去應對所有場景。我們在不同的場景需要選擇不同的數據結構,所以數據結構沒有誰好誰壞之分,而評估數據結構的好壞要針對場景,如果在一種場景下我們需要頻繁地對頭部進行插入刪除操作,那么這個時候我們用鏈表;但是如果對尾部進行插入刪除操作比較頻繁,那我們用順序表比較好。

????????因此,不同的場景我們選擇不同的數據結構。

三、單鏈表

(二)實現單鏈表

3、增刪查改
(1)尾插

我們要申請新的節點(需要malloc),我們單獨封裝一個函數。

?

現在新節點就申請好了,我們要讓5和4節點連起來:

?

這就是為什么我們明明已經有phead這個指針,還要額外再定義一個指針pcur——

?

這樣一來pcur在不斷變化,phead保持不變,phead始終保存的是第一個節點的地址。在這里我不想改變phead,phead始終指向第一個節點,方便我們后面遍歷完了如果還要再從頭開始遍歷的時候我們能夠找到第一個節點的地址。

我們定義pcur,只要pcur不為空,我們就進入循環,pcur為空我們就跳出循環。

?

我們這邊調用test02:?

?

?這是SList.c尾插的代碼:

//尾插
void SLTPushBack(?SLTNode* phead, SLTDatatype x)
{//申請新節點?SLTNode* newnode = SLTBuyNode(x);//鏈表為空——要特殊處理if (phead == NULL){phead = newnode;}?SLTNode* ptail = phead;while (ptail->next != NULL){ptail = ptail->next;}//找到了尾節點 ptail newnodeptail->next = newnode;
}

這邊其實代碼還是有問題的,我們先運行一下看看:

?

尾插:?

SList.c:

//尾插
void SLTPushBack(?SLTNode** pphead, SLTDatatype x)
{//申請新節點?SLTNode* newnode = SLTBuyNode(x);//鏈表為空——要特殊處理if (*pphead == NULL){*pphead = newnode;}else{?SLTNode* ptail = *pphead;while (ptail->next != NULL){ptail = ptail->next;}//找到了尾節點 ptail newnodeptail->next = newnode;}
}

test.c:?

void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);
}int main()
{/*test01();*/test02();return 0;
}

?

尾插的時間復雜度:O(N) 。

(2)頭插

?

頭插:

SList.c:

//頭插
void SLTPushFront(?SLTNode** pphead, SLTDatatype x)
{assert(pphead);?SLTNode* newnode = SLTBuyNode(x);//newnode *ppheadnewnode->next = *pphead;*pphead = newnode;
}

test.c:?

void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushFront(&plist, 1);SLTPrint(plist);SLTPushFront(&plist, 2);SLTPrint(plist);SLTPushFront(&plist, 3);SLTPrint(plist);SLTPushFront(&plist, 4);SLTPrint(plist);
}int main()
{/*test01();*/test02();return 0;
}

?

頭插的時間復雜度:O(1) 。?

(3)在指定位置之前插入數據

函數形參中同樣需要用二級指針傳入鏈表地址,還要傳入指定位置的地址和需要插入的數據。

在函數中,需要先找到指定位置的前一個節點,然后把需要添加的數據插入到這兩個節點之間:

SList.c:

void SLTPushBefore(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && pos);//當pos為頭節點時 相當于頭插if(*pphead == pos){SLTPushFront(pphead, x);}else{SLTNode* newNode = SLTBuyNode(x);//找到pos前一個節點SLTNode* pre = *pphead;while(pre->next != pos){pre = pre->next;}//把新節點放在pre和pos之間newNode->next = pre->next;//等價于newNode->next = pospre->next = newNode;}}

test.c:?

void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);?SLTNode* pos = SLTFind(plist, 4);SLTInsert(&plist, pos, 100);SLTPrint(plist);
}int main()
{/*test01();*/test02();return 0;
}

在指定位置之前插入數據時間復雜度:O(N)。?

(4)在指定位置之后插入數據

和類似,我們找到指定位置的后一個節點,把新節點放在這兩個節點之間——

4后面插入一個100:?

1后面插入一個100:

SList.c:

void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && pos);if(pos->next == NULL){//相當于尾插SLTPushBack(pphead, x);}else{SLTNode* newNode = SLTBuyNode(x);newNode->next = pos->next;pos->next = newNode;//順序不能顛倒 否則會先修改pos->next}
}

test.c:?

void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);?SLTNode* pos = SLTFind(plist, 1);//SLTInsert(&plist, pos, 100);SLTInsertAfter(pos, 100);SLTPrint(plist);
}int main()
{/*test01();*/test02();return 0;
}

在指定位置之后插入數據時間復雜度:O(1)。??

(1)尾刪

pphead和phead的關系:

本文中的pphead是形參,phead是畫圖時定義的一個指針。

phead是指向第一個結點的指針;

在程序里面,指向第一個節點的指針在形參里面是*pphead。

函數形參中二級指針存放表頭地址。

如果鏈表只有一個元素需要釋放頭節點的空間,并把鏈表指針置為空;

如果有多個元素需要找到倒數第二個節點和最后一個節點,釋放最后一個節點,并把倒數第二個節點的next指針置為空。

?出問題了:?

?

萬一鏈表只有一個節點,我們要注意這種情況——

尾刪:

SList.c:

//尾刪
void SLTPopBack(?SLTNode** pphead)
{//鏈表為空不能刪除assert(pphead && *pphead);//pphead是*pphead的地址//pphead是一個二級指針,我們對pphead解引用一次,*pphead就是指向第一個節點的地址//*pphead為空說明鏈表為空//鏈表只有一個節點的情況if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{?SLTNode* prev = NULL;?SLTNode* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}//prev  ptailprev->next = NULL;free(ptail);ptail = NULL;}
}

test.c:

void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);//SLTPushFront(&plist, 1);//SLTPrint(plist);//SLTPushFront(&plist, 2);//SLTPrint(plist);//SLTPushFront(&plist, 3);//SLTPrint(plist);//SLTPushFront(&plist, 4);//SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);
}int main()
{/*test01();*/test02();return 0;
}

?

鏈表為空,如果還要再刪一次,程序就會assert(斷言)報錯:

?

?

表達式為假,因為*pphead為空了,在69行斷言出現了報錯。

如果初始將prev置為*pphead也要討論:

這樣兩個指針都指向這個節點,讓prev下一個節點置為空——本身它下一個節點就為空,現在把ptail 給 free掉,prev就變成了野指針。

如果只有一個節點,prev->next = NULL;這一行代碼就可以不要了,而且ptailprev都要free。如果不止一個節點的話,那這又是一套邏輯,這種寫法會更復雜一些。

博主給出的這種寫法會簡單一些。?

尾刪的時間復雜度:O(N) 。?

(2)頭刪

與尾刪相似,讓頭指針指向第二個節點,并釋放頭節點——

?

頭刪:

SList.c:

//頭刪
void SLTPopFront(?SLTNode** pphead)
{assert(pphead && *pphead);?SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}

test.c:

void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);//頭刪SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);
}int main()
{/*test01();*/test02();return 0;
}

再刪一次就會斷言報錯——

?

?

頭刪的時間復雜度:O(1) 。

(3)刪除pos節點

讓pos前一個節點指向pos后一個節點——

SList.c:

void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos && *pphead);//pos為頭節點if(pos == *pphead){free(*pphead);*pphead = NULL;pos = NULL;}else{SLTNode* prev = *pphead;while(prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}

test.c:

void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);?SLTNode* pos = SLTFind(plist, 3);SLTErase(&plist, pos);SLTPrint(plist);
}int main()
{/*test01();*/test02();return 0;
}
(4)刪除pos之后的節點

找到pos之后的節點del,連接pos節點和del->next,再釋放del——

SList.c:

void SLTEraseAfter(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos && pos->next && *pphead);SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}

test.c:

void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);?SLTNode* pos = SLTFind(plist, 3);SLTErase(&plist, pos);SLTPrint(plist);
}int main()
{/*test01();*/test02();return 0;
}
?
(1)查找

來個不存在的數據測試一下——?

查找?

SList.c:

//查找
?SLTNode* SLTFind(?SLTNode* phead, SLTDatatype x)
{?SLTNode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}//未找到return NULL;
}

test.c:

void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);?SLTNode* pos = SLTFind(plist, 100);if (pos){printf("找到了!\n");}else{printf("未找到!\n");}
}int main()
{/*test01();*/test02();return 0;
}
改?
(1)修改

修改指定位置的數據:直接修改該節點data的值——

SList.c:

void SLTChangeData(SLTNode* pos, SLTDataType x)
{assert(pos);pos->data = x;
}
?銷毀鏈表

遍歷鏈表,釋放每一個節點,由于需要修改指向頭節點的指針,因此函數形參中要用二級指針——

(1)銷毀鏈表

SList.c:

void SLTDestroy(SLTNode** pphead)
{assert(pphead );SLTNode* pcur = *pphead;while(pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

這里pos=NULL、pcur=NULL、next=NULL加上置為空是養成好習慣,這里是默認置為空。?

test.c:?

void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);?SLTNode* pos = SLTFind(plist, 3);SListDestory(&plist);
}int main()
{/*test01();*/test02();return 0;
}
4、完整代碼?
(1)SList.h:
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//鏈表的結構?
typedef int SLTDatatype;
typedef struct SListNode
{SLTDatatype data;struct SListNode* next;//指向下一個節點的地址
}?SLTNode;//typedef struct SListNode SLTNode;void SLTPrint(?SLTNode* phead);//尾插
void SLTPushBack(?SLTNode** pphead, SLTDatatype x);//頭插
void SLTPushFront(?SLTNode** pphead, SLTDatatype x);//尾刪
void SLTPopBack(?SLTNode** pphead);//頭刪
void SLTPopFront(?SLTNode** pphead);//查找
?SLTNode* SLTFind(?SLTNode* phead, SLTDatatype x);//在指定位置之前插入數據
void SLTInsert(?SLTNode** pphead, ?SLTNode* pos, SLTDatatype x);//在指定位置之后插入數據
void SLTInsertAfter(?SLTNode* pos, SLTDatatype x);//刪除pos節點
void SLTErase(?SLTNode** pphead, ?SLTNode* pos);//刪除pos之后的節點
void SLTEraseAfter(?SLTNode* pos);//修改
void SLTChangeData(?SLTNode* pos, SLTDatatype x);//銷毀鏈表
void SListDestory(?SLTNode** pphead);
(2)SList.c:
#define  _CRT_SECURE_NO_WARNINGS  1#include"SList.h"void SLTPrint(?SLTNode* phead)
{?SLTNode* pcur = phead;while (pcur != NULL){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}//后續我們要申請新節點就直接調用SLTBuyNode方法
?SLTNode* SLTBuyNode(SLTDatatype x)
{?SLTNode* newnode = (?SLTNode*)malloc(sizeof(?SLTNode));//malloc不一定申請成功,我們判斷一下if (newnode == NULL){printf("malloc fail!");exit(1);}//初始化一下newnode->data = x;newnode->next = NULL;return newnode;
}//尾插
void SLTPushBack(?SLTNode** pphead, SLTDatatype x)
{//申請新節點?SLTNode* newnode = SLTBuyNode(x);//鏈表為空——要特殊處理if (*pphead == NULL){*pphead = newnode;}else{?SLTNode* ptail = *pphead;while (ptail->next != NULL){ptail = ptail->next;}//找到了尾節點 ptail newnodeptail->next = newnode;}
}//頭插
void SLTPushFront(?SLTNode** pphead, SLTDatatype x)
{assert(pphead);?SLTNode* newnode = SLTBuyNode(x);//newnode *ppheadnewnode->next = *pphead;*pphead = newnode;
}//尾刪
void SLTPopBack(?SLTNode** pphead)
{//鏈表為空不能刪除assert(pphead && *pphead);//pphead是*pphead的地址//pphead是一個二級指針,我們對pphead解引用一次,*pphead就是指向第一個節點的地址//*pphead為空說明鏈表為空//鏈表只有一個節點的情況if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{?SLTNode* prev = NULL;?SLTNode* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}//prev  ptailprev->next = NULL;free(ptail);ptail = NULL;}
}//頭刪
void SLTPopFront(?SLTNode** pphead)
{assert(pphead && *pphead);?SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}//查找
?SLTNode* SLTFind(?SLTNode* phead, SLTDatatype x)
{?SLTNode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}//未找到return NULL;
}//在指定位置之前插入數據
void SLTInsert(?SLTNode** pphead, ?SLTNode* pos, SLTDatatype x)
{assert(pphead && pos);?SLTNode* newnode = SLTBuyNode(x);//pos指向頭節點if (pos == *pphead){//頭插SLTPushFront(pphead, x);}else{//找pos前一個節點?SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev   newnode  posprev->next = newnode;newnode->next = pos;}
}//在指定位置之后插入數據
void SLTInsertAfter(?SLTNode* pos, SLTDatatype x)
{assert(pos);?SLTNode* newnode = SLTBuyNode(x);//pos newnode pos->nextnewnode->next = pos->next;pos->next = newnode;
}//刪除pos節點
void SLTErase(?SLTNode** pphead, ?SLTNode* pos)
{assert(pphead && pos);//pos剛好是頭節點——頭刪if (pos==*pphead){SLTPopFront(pphead);}else{?SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev pos pos->nextprev->next = pos->next;free(pos);pos = NULL;}
}//刪除pos之后的節點
void SLTEraseAfter(?SLTNode* pos)
{assert(pos);//pos  del  del->next?SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}//修改
void SLTChangeData(?SLTNode* pos, SLTDatatype x)
{assert(pos);pos->data = x;
}//銷毀鏈表
void SListDestory(?SLTNode** pphead)
{//一個一個銷毀assert(pphead);?SLTNode* pcur = *pphead;while (pcur){?SLTNode* next = pcur->next;free(pcur);pcur = next;}//*pphead是野指針,要置為空*pphead = NULL;
}
(3)test.c:
#define  _CRT_SECURE_NO_WARNINGS  1#include"SList.h"int test01()
{//創建一個鏈表——實際上是創建一個一個節點,再把節點連起來?SLTNode*node1=(?SLTNode*)malloc(sizeof(?SLTNode));?SLTNode*node2=(?SLTNode*)malloc(sizeof(?SLTNode));?SLTNode*node3=(?SLTNode*)malloc(sizeof(?SLTNode));?SLTNode*node4=(?SLTNode*)malloc(sizeof(?SLTNode));node1->data = 1;node2->data = 2;node3->data = 3;node4->data = 4;node1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;?SLTNode* plist = node1;//打印鏈表SLTPrint(plist);
}void test02()
{//創建空鏈表?SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);////SLTPushFront(&plist, 1);//SLTPrint(plist);//SLTPushFront(&plist, 2);//SLTPrint(plist);//SLTPushFront(&plist, 3);//SLTPrint(plist);//SLTPushFront(&plist, 4);//SLTPrint(plist);////SLTPopBack(&plist);//SLTPrint(plist);//SLTPopBack(&plist);//SLTPrint(plist);//SLTPopBack(&plist);//SLTPrint(plist);//SLTPopBack(&plist);//SLTPrint(plist);//SLTPopBack(&plist);//SLTPrint(plist);////頭刪
//	SLTPopFront(&plist);
//	SLTPrint(plist);
//	SLTPopFront(&plist);
//	SLTPrint(plist);
//	SLTPopFront(&plist);
//	SLTPrint(plist);
//	SLTPopFront(&plist);
//	SLTPrint(plist);?SLTNode* pos = SLTFind(plist, 3);//if (pos)//{//	printf("找到了!\n");//}//else//{//	printf("未找到!\n");//}//SLTInsert(&plist, pos, 100);/*SLTInsertAfter(pos, 100);*///SLTErase(&plist, pos);//SLTPrint(plist);SListDestory(&plist);
}int main()
{/*test01();*/test02();return 0;
}

結尾

往期回顧:

【數據結構與算法】數據結構初階:詳解順序表和鏈表(三)——單鏈表(上

本期內容需要回顧的C語言知識如下面的截圖中所示(指針博主寫了6篇,列出來有水字數嫌疑了,就只放指針第六篇的網址,博主在指針(六)把指針部分的前五篇的網址都放在【往期回顧】了,點擊【傳送門】就可以看了)。

大家如果對前面部分的知識點印象不深,可以去上一篇文章的結尾部分看看,博主把需要回顧的知識點相關的博客的鏈接都放在上一篇文章了,上一篇文章的鏈接博主放在下面了:

?【數據結構與算法】數據結構初階:詳解順序表和鏈表(三)——單鏈表(上)

結語:本篇文章到這里就結束了,對數據結構的單鏈表知識感興趣的友友們可以在評論區留言,博主創作時可能存在筆誤,或者知識點不嚴謹的地方,大家多擔待,如果大家在閱讀的時候發現了行文有什么錯誤歡迎在評論區斧正,再次感謝友友們的關注和支持!

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

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

相關文章

Java+AI精準廣告革命:實時推送系統實戰指南

? 廣告推送的世紀難題 用戶反感&#xff1a;72%用戶因無關廣告卸載APP 轉化率低&#xff1a;傳統推送轉化率<0.5% 資源浪費&#xff1a;40%廣告預算被無效曝光消耗 &#x1f9e0; 智能廣告系統架構 &#x1f525; 核心模塊實現&#xff08;Java 17&#xff09; 1. 實時…

JVM組成及運行流程 - 面試筆記

JVM整體架構 JVM&#xff08;Java Virtual Machine&#xff09;是Java程序運行的核心環境&#xff0c;主要由以下幾個部分組成&#xff1a;1. 程序計數器&#xff08;Program Counter&#xff09; 特點&#xff1a;線程私有&#xff0c;每個線程都有獨立的程序計數器作用&#…

JavaEE——線程池

目錄前言1. 概念2. 線程池相關參數3. Executors的使用總結前言 線程是為了解決進程太重的問題&#xff0c;操作系統中進程的創建和銷毀需要較多的系統資源&#xff0c;用了輕量級的線程來代替部分線程&#xff0c;但是如果線程創建和銷毀的頻率也開始提升到了一定程度&#xf…

3 c++提高——STL常用容器(一)

目錄 1 string容器 1.1 string基本概念 1.2 string構造函數 1.3 string賦值操作 1.4 string字符串拼接 1.5 string查找和替換 1.6 string字符串比較 1.7 string字符存取 1.8 string插入和刪除 1.9 string子串 2 vector容器 2.1 vector基本概念 2.2 vector構造函數…

手把手教你用【Go】語言調用DeepSeek大模型

1、首先呢&#xff0c;點擊 “DeepSeek”” 這個&#xff0c; 可以充1塊玩玩。 2、然后獲取api-key 3、替換apiKey const (apiURL "https://api.deepseek.com/v1/chat/completions"apiKey "your api key" // 替換為你的實際 API KeymodelName &…

自動化UI測試工具TestComplete的核心功能及應用

對桌面應用穩定性與用戶體驗的挑戰&#xff0c;手動測試效率低、覆蓋有限&#xff0c;而普通自動化工具常難以應對復雜控件識別、腳本靈活性和大規模并行測試的需求。 自動化UI測試工具TestComplete憑借卓越的對象識別能力、靈活的測試創建方式以及高效的跨平臺并行執行功能&a…

【C/C++】邁出編譯第一步——預處理

【C/C】邁出編譯第一步——預處理 在C/C編譯流程中&#xff0c;預處理&#xff08;Preprocessing&#xff09;是第一個也是至關重要的階段。它負責對源代碼進行初步的文本替換與組織&#xff0c;使得編譯器在后續階段能正確地處理規范化的代碼。預處理過程不僅影響編譯效率&…

快捷鍵——VsCode

一鍵折疊所有的代碼塊 先按 ctrl K&#xff0c;再ctrl 0 快速注釋一行 ctrl /

import 和require的區別

概念 import 是es6 規范&#xff0c;主要應用于瀏覽器和主流前端框架當中&#xff0c;export 導出&#xff0c; require 是 commonjs 規范&#xff0c;主要應用于nodejs環境中&#xff0c;module.exports 導出編譯規則 import 靜態導入是編譯時解析&#xff0c;動態導入是執…

8、鴻蒙Harmony Next開發:相對布局 (RelativeContainer)

目錄 概述 基本概念 設置依賴關系 設置參考邊界 設置錨點 設置相對于錨點的對齊位置 子組件位置偏移 多種組件的對齊布局 組件尺寸 多個組件形成鏈 概述 RelativeContainer是一種采用相對布局的容器&#xff0c;支持容器內部的子元素設置相對位置關系&#xff0c;適…

Linux命令的命令歷史

Linux下history命令可以對當前系統中執行過的所有shell命令進行顯示。重復執行命令歷史中的某個命令&#xff0c;使用&#xff1a;!命令編號&#xff1b;環境變量histsize的值保存歷史命令記錄的總行數&#xff1b;可用echo查看一下&#xff1b;需要大寫&#xff1b;環境變量hi…

【C++小白逆襲】內存管理從崩潰到精通的秘籍

目錄【C小白逆襲】內存管理從崩潰到精通的秘籍前言&#xff1a;為什么內存管理讓我掉了N根頭發&#xff1f;內存四區大揭秘&#xff1a;你的變量都住在哪里&#xff1f;&#x1f3e0;內存就像大學宿舍區 &#x1f3d8;?C語言的內存管理&#xff1a;手動搬磚時代 &#x1f9f1;…

【網絡安全】利用 Cookie Sandwich 竊取 HttpOnly Cookie

未經許可,不得轉載。 文章目錄 引言Cookie 三明治原理解析Apache Tomcat 行為Python 框架行為竊取 HttpOnly 的 PHPSESSID Cookie第一步:識別 XSS 漏洞第二步:發現反射型 Cookie 參數第三步:通過 Cookie 降級實現信息泄露第四步:整合攻擊流程修復建議引言 本文將介紹一種…

【工具】什么軟件識別重復數字?

網上的數字統計工具雖多&#xff0c;但處理重復數字時總有點不盡如人意。 要么只能按指定格式輸入&#xff0c;要么重時得手動一點點篩&#xff0c;遇上數據量多的情況&#xff0c;光是找出重復的數字就得另外花不少功夫。? 于是我做了個重復數字統計器&#xff0c;不管是零…

CSS分層渲染與微前端2.0:解鎖前端性能優化的新維度

CSS分層渲染與微前端2.0&#xff1a;解鎖前端性能優化的新維度 當你的頁面加載時間超過3秒&#xff0c;用戶的跳出率可能飆升40%以上。這并非危言聳聽&#xff0c;而是殘酷的現實。在當前前端應用日益復雜、功能日益臃腫的“新常態”下&#xff0c;性能優化早已不是錦上添花的“…

AI Agent開發學習系列 - langchain之Chains的使用(5):Transformation

Transformation&#xff08;轉換鏈&#xff09;是 LangChain 中用于“自定義數據處理”的鏈式工具&#xff0c;允許你在鏈路中插入任意 Python 代碼&#xff0c;對輸入或中間結果進行靈活處理。常用于&#xff1a; 對輸入/輸出做格式化、過濾、摘要、拆分等自定義操作作為 LLMC…

Druid 連接池使用詳解

Druid 連接池使用詳解 一、Druid 核心優勢與架構 1. Druid 核心特性 特性說明價值監控統計內置 SQL 監控/防火墻實時查看 SQL 執行情況防 SQL 注入WallFilter 防御機制提升系統安全性加密支持數據庫密碼加密存儲符合安全審計要求擴展性強Filter 鏈式架構自定義功能擴展高性能…

9.2 埃爾米特矩陣和酉矩陣

一、復向量的長度 本節的主要內容可概括為&#xff1a;當對一個復向量 z\pmb zz 或復矩陣 A\pmb AA 轉置后&#xff0c;還要取復共軛。 不能在 zTz^TzT 或 ATA^TAT 時就停下來&#xff0c;還要對所有的虛部取相反的符號。對于一個分量為 zjajibjz_ja_jib_jzj?aj?ibj? 的列向…

AI驅動的低代碼革命:解構與重塑開發范式

引言&#xff1a;低代碼平臺的范式轉移 當AI技術與低代碼平臺深度融合&#xff0c;軟件開發正經歷從"可視化編程"到"意圖驅動開發"的根本性轉變。這種變革不僅提升了開發效率&#xff0c;更重新定義了人與系統的交互方式。本文將從AI介入的解構層次、交互范…

zookeeper etcd區別

ZooKeeper與etcd的核心區別體現在設計理念、數據模型、一致性協議及適用場景等方面。?ZooKeeper基于ZAB協議實現分布式協調&#xff0c;采用樹形數據結構和臨時節點特性&#xff0c;適合傳統分布式系統&#xff1b;而etcd基于Raft協議&#xff0c;以高性能鍵值對存儲為核心&am…