數據結構:二叉樹的鏈式存儲

用鏈表來表示一棵二叉樹,即用指針指向來指示元素的邏輯關系。通常的方法是鏈表中每個結點由三個域組成,數據域和左右指針域,左右指針分別用來給出該結點左孩子和右孩子所在的鏈結點的存儲地址 。

我們之前就已經說過,二叉樹是遞歸定義的,也就是說,一棵二叉樹是由根節點,左子樹,右子樹組成的,而左子樹和右子樹本身又是一棵二叉樹。我們要牢記二叉樹是遞歸定義的這一特點,在后續操作中我們將會體會遞歸的暴力美學。

二叉樹的前中后序遍歷

前序遍歷(先根遍歷):先遍歷根節點,再遍歷左子樹,最后遍歷右子樹。(根左右)

中序遍歷(中根遍歷):先遍歷左子樹,再遍歷根節點,最后遍歷右子樹。(左根右)

后序遍歷(后根遍歷):先遍歷左子樹,再遍歷右子樹,最后再遍歷根節點。(左右根)

根據上述定義,寫出這個二叉樹分別按三種遍歷方式得到的序列:

二叉樹相關方法的代碼實現

二叉樹的結構

//定義樹的結構
typedef char btdatatype;typedef struct btnode
{btdatatype data;  //節點數據struct btnode* left;//左孩子結點struct btnode* right;//右孩子結點
}btnode;

二叉樹節點的創建

//建立新的樹節點
btnode* buynode(btdatatype x)
{btnode* newnode = (btnode*)malloc(sizeof(btnode));newnode->data = x;newnode->left = NULL;newnode->right = NULL;
}

二叉樹的前序遍歷

//前序遍歷樹的節點
void preorder(btnode* root)
{//遞歸的終止條件:遍歷到空節點if (root == NULL){return;}//先訪問根節點printf("%c ", root->data);//再遍歷左子樹preorder(root->left);//再遍歷右子樹preorder(root->right);
}

畫圖說明遞歸過程(紅色的線表示遞推,綠色的線表示回歸,遞推就意味著要創建函數棧楨,回歸表示函數棧楨銷毀,同時要返回值):

二叉樹的中序遍歷

//中序遍歷樹的節點
void inorder(btnode* root)
{if (root == NULL){return;}//先遍歷左子樹inorder(root->left);//再訪問根節點printf("%c ", root->data);//再遍歷右子樹inorder(root->right);
}

在前面兩個遞歸過程的解釋中,我們發現,在遞歸調用時,產生的函數棧楨形成的結構與二叉樹的形態結構長得一模一樣,所以接下來我們模擬遞歸過程的函數棧楨的調用時,就不用再使用具體的函數棧楨圖了,可以直接使用二叉樹的每個節點來進行模擬比較。

二叉樹的后序遍歷

//后序遍歷樹的節點
void postorder(btnode* root)
{if (root == NULL){return;}//先遍歷左子樹postorder(root->left);//再遍歷右子樹postorder(root->right);//最后訪問根節點printf("%c ", root->data);
}

三種遍歷方式下,產生的函數棧楨圖一樣。

二叉樹中結點的個數

//二叉樹節點個數
void binarytreesize_1(btnode* root, int size)
{if (root == NULL){return;}size++;binarytreesize_1(root->left, size);binarytreesize_1(root->right, size);
}

大家看看上面的代碼能正確完成任務嘛,我們可以再寫一個測試代碼來測試一下:

屏幕前的家人們覺得結果會是6嘛?

上面的代碼應該是想實現在遞歸過程中只要遇到的節點不是空節點就對size進行累加,而且我們傳入的size的初始值是0,按理來說應該會完成任務,但是size的大小竟然沒有發生改變。

這是因為,我們采用的是傳值調用,形參的改變不會影響實參,所以size的值不會發生改變,還是0.但我們也得到了解決思路,直接采用傳址調用就好了呀:

void binarytreesize_2(btnode* root, int* psize)
{if (root == NULL){return;}(*psize)++;binarytreesize_2(root->left, psize);binarytreesize_2(root->right, psize);
}

我們用相同的測試用例測試一下結果:

看來這個代碼好像確實可以幫我們完成任務。

上面代碼的遞歸過程圖解:

如果我們在多測試幾次代碼:

我們會發現size的值在不斷遞增,這是因為第二次和第三次調用函數時,我們沒有將size重新賦值為0,直接在原來值“6”的基礎上進行累加了,這也正是我們這個代碼的缺點所在:每一次調用函數前都要將size的值重新置為0.

那還有沒有別的算法解決問題呢,有的兄弟們,有的。

我們知道,二叉樹是遞歸定義的,它是由根節點、左子樹、右子樹組成。要求整棵二叉樹節點個數,不就是求根節點的個數加上左子樹中節點的個數再加上右子樹中節點的個數嘛,而根節點的個數本來就是1,這樣我們就把一個大問題拆解成相似的小問題了,這種情況下,使用遞歸簡直就不要太容易了。

int binarytreesize(btnode* root)
{if (root == NULL){return 0;}return 1 + binarytreesize(root->left) + binarytreesize(root->right);
}

我們再來用相同的測試用例測試一下:

可以看到,這個代碼可以通過測試樣例,同時避免了上一個代碼的缺點。

再來模擬一下遞歸調用的過程:

二叉樹中葉子結點的個數

//二叉樹葉子結點個數
int binarytreeleafsize(btnode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return binarytreeleafsize(root->left) + binarytreeleafsize(root->right);
}

求第k層節點的個數

//二叉樹第k層節點個數
int binarytreelevelksize(btnode* root, int k)
{if (root == NULL){return 0;}if (k == 1)//樹的根在第一層{return 1;}return binarytreelevelksize(root->left, k - 1) + binarytreelevelksize(root->right, k - 1);
}

求二叉樹的深度

//二叉樹的深度/高度:深度從1開始哦
int binarytreedepth(btnode* root)
{if (root == NULL){return 0;}int leftlen = binarytreedepth(root->left);int rightlen = binarytreedepth(root->right);return 1 + (leftlen > rightlen ? leftlen : rightlen);
}

在樹中查找節點

//二叉樹查找值為x的節點
btnode* binarytreefind(btnode* root, btdatatype x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}//如果在左子樹中找到了,就不用在右子樹中找了btnode* leftfind = binarytreefind(root->left, x);if (leftfind){return leftfind;}//說明左子樹中沒有找到//再到右子樹中繼續找btnode* rightfind = binarytreefind(root->right, x);if (rightfind){return rightfind;}//說明右子樹中也沒有找到return NULL;
}

二叉樹的銷毀

//二叉樹的銷毀
//二叉樹的銷毀:要銷毀二叉樹,肯定是要先遍歷二叉樹,那么我們應該如何遍歷二叉樹呢?
//可以參考已有的遍歷方式:前序?中序?后序?
//前序和中序在遍歷完子樹前都會先遍歷完根,先銷毀根的話,就沒辦法再找到根的子樹了,所以這兩種遍歷方式都不可取,我們只能后序遍歷來銷毀二叉樹
void  binarytreedestroy(btnode** proot)
{if (*proot == NULL){return;}binarytreedestroy(&((*proot)->left));binarytreedestroy(&((*proot)->right));free(*proot);*proot = NULL;
}

二叉樹的層序遍歷

二叉樹的層序遍歷是指?按照樹的層級順序,從上到下、從左到右依次訪問所有節點。這種遍歷方式也稱為?廣度優先搜索(BFS),通常需要借助?隊列?數據結構來實現。

在這個層序遍歷的實現中,我們需要使用隊列來輔助,所以我們之前寫的隊列的實現方法就派上用場了,還沒看的朋友可以點擊鏈接:數據結構里的 “排隊系統”:隊列的生活映射與代碼實現-CSDN博客進行觀看哦。

如果之前已經實現了隊列相關方法的盆友,可以按照以下步驟直接添加:

找到解決方案資源管理器->右鍵單擊頭文件->添加->現有項->來到你所寫的隊列的實現方法的項目路徑下:

按住ctrl鍵,選中queue.c和queue.h兩個文件,直接ctrl+c復制兩個文件,再來到當前二叉樹實現方法的路徑下,將兩個文件粘貼到當前路徑:

此時兩個文件還是選中的狀態,我們直接點擊添加就好。

添加好以后,將queue.c文件拖拽到源文件的地方就好了。

現在既然我們要是用隊列中的相關方法,還要包含對應的頭文件,那就要在當前二叉樹的項目中的頭文件中添加queue.h的頭文件包含:

同時,之前我們在實現隊列的時候,隊列中存儲的元素類型是int類型的,現在我們要將它改成二叉樹節點類型的,但是隊列的實現方法中并沒有關于二叉樹的結構聲明,難道需要在隊列實現方法的頭文件中加上:"include binarytree.h"?這樣就會造成頭文件的相互包含,就會引發錯誤,正確的方法就是在queue.h文件中加上這樣一句代碼:

typedef struct btnode* Qdatatype;

它的作用是:告訴隊列:你要存的數據類型是一個指向 struct btnode 的指針,但我不關心它長什么樣。換句話說:

  • 隊列只負責“存指針、傳指針”,不關心這個指針指向的結構體有哪些成員。
  • 真正的?struct btnode?定義可以放在別的文件里(比如?tree.c),queue.h?不需要知道細節

那現在我們就來手動實現一下層序遍歷的代碼吧:

//層序遍歷
void levelorder(btnode* root)
{if (root == NULL){return;}//先創建一個隊列Queue q;QueueInit(&q);//先讓隊頭元素入隊列QueuePush(&q, root);//不斷循環直到隊列為空while (!QueueEmpty(&q)){//先取隊頭元素btnode* top = QueueFront(&q);//出隊QueuePop(&q);//訪問隊頭元素printf("%c ", top->data);//如果左孩子不為空,左孩子入隊if (top->left){QueuePush(&q, top->left);}//如果右孩子不為空,右孩子入隊if (top->right){QueuePush(&q, top->right);}}QueueDesTroy(&q);
}

判斷是否為完全二叉樹

//判斷是否為完全二叉樹
bool binarytreecomplete(btnode* root)
{if (root == NULL){return true;}//利用層序遍歷的思想判斷是否為完全二叉樹//先創建一個隊列Queue q;QueueInit(&q);//先讓隊頭元素入隊列QueuePush(&q, root);//不斷循環直到隊列為空while (!QueueEmpty(&q)){//先取隊頭元素btnode* top = QueueFront(&q);//出隊QueuePop(&q);//如果隊頭元素為空,就直接跳出循環if (top == NULL){break;}//走到這里,說明隊列不為空,那么就讓隊頭元素的左右孩子都入隊列QueuePush(&q, top->left);QueuePush(&q, top->right);}//現在就只需要判斷第一次得到空的隊頭元素后,剩下的元素是否是既有空節點又有非空節點(非完全二叉樹)//如果剩下的元素只有空節點(完全二叉樹)while (!QueueEmpty(&q)){//取隊頭元素btnode* top = QueueFront(&q);QueuePop(&q);if (top != NULL){QueueDesTroy(&q);return false;}}QueueDesTroy(&q);return true;
}

今天的內容就是這些,我們可以看到在這一小節中我畫了很多圖,其實畫圖是很利于我們理解算法的,小伙伴們自己也要多練練哦!!

代碼整合

//binarytree.h
#pragma once#include"queue.h"
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//定義樹的結構
typedef char btdatatype;typedef struct btnode
{btdatatype data;struct btnode* left;struct btnode* right;
}btnode;//建立新的樹節點
btnode* buynode(btdatatype x);//前序遍歷樹的節點
void preorder(btnode* root);//中序遍歷樹的節點
void inorder(btnode* root);//后序遍歷樹的節點
void postorder(btnode* root);//二叉樹節點個數
int binarytreesize(btnode* root);
void binarytreesize_1(btnode* root, int size);
void binarytreesize_2(btnode* root, int* psize);//二叉樹葉子結點個數
int binarytreeleafsize(btnode* root);//二叉樹第k層節點個數
int binarytreelevelksize(btnode* root, int k);//二叉樹的深度/高度
int binarytreedepth(btnode* root);//二叉樹查找值為x的節點
btnode* binarytreefind(btnode* root, btdatatype x);//二叉樹的銷毀
void  binarytreedestroy(btnode* root);//層序遍歷
void levelorder(btnode* root);//判斷是否為完全二叉樹
bool binarytreecomplete(btnode* root);
//binarytree.c
#define  _CRT_SECURE_NO_WARNINGS 1#include"binarytree.h"//建立新的樹節點
btnode* buynode(btdatatype x)
{btnode* newnode = (btnode*)malloc(sizeof(btnode));newnode->data = x;newnode->left = NULL;newnode->right = NULL;
}//前序遍歷樹的節點
void preorder(btnode* root)
{//遞歸的終止條件:遍歷到空節點if (root == NULL){return;}//先訪問根節點printf("%c ", root->data);//再遍歷左子樹preorder(root->left);//再遍歷右子樹preorder(root->right);
}//中序遍歷樹的節點
void inorder(btnode* root)
{if (root == NULL){return;}//先遍歷左子樹inorder(root->left);//再訪問根節點printf("%c ", root->data);//再遍歷右子樹inorder(root->right);
}//后序遍歷樹的節點
void postorder(btnode* root)
{if (root == NULL){return;}//先遍歷左子樹postorder(root->left);//再遍歷右子樹postorder(root->right);//最后訪問根節點printf("%c ", root->data);
}//二叉樹節點個數
void binarytreesize_1(btnode* root, int size)
{if (root == NULL){return;}size++;binarytreesize_1(root->left, size);binarytreesize_1(root->right, size);
}void binarytreesize_2(btnode* root, int* psize)
{if (root == NULL){return;}(*psize)++;binarytreesize_2(root->left, psize);binarytreesize_2(root->right, psize);
}int binarytreesize(btnode* root)
{if (root == NULL){return 0;}return 1 + binarytreesize(root->left) + binarytreesize(root->right);
}//二叉樹葉子結點個數
int binarytreeleafsize(btnode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return binarytreeleafsize(root->left) + binarytreeleafsize(root->right);
}//二叉樹第k層節點個數
int binarytreelevelksize(btnode* root, int k)
{if (root == NULL){return 0;}if (k == 1)//樹的根在第一層{return 1;}return binarytreelevelksize(root->left, k - 1) + binarytreelevelksize(root->right, k - 1);
}//二叉樹的深度/高度
int binarytreedepth(btnode* root)
{if (root == NULL){return 0;}int leftlen = binarytreedepth(root->left);int rightlen = binarytreedepth(root->right);return 1 + (leftlen > rightlen ? leftlen : rightlen);
}//二叉樹查找值為x的節點
btnode* binarytreefind(btnode* root, btdatatype x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}//如果在左子樹中找到了,就不用在右子樹中找了btnode* leftfind = binarytreefind(root->left, x);if (leftfind){return leftfind;}//說明左子樹中沒有找到//再到右子樹中繼續找btnode* rightfind = binarytreefind(root->right, x);if (rightfind){return rightfind;}//說明右子樹中也沒有找到return NULL;
}//二叉樹的銷毀
//二叉樹的銷毀:要銷毀二叉樹,肯定是要先遍歷二叉樹,那么我們應該如何遍歷二叉樹呢?
//可以參考已有的遍歷方式:前序?中序?后序?
//前序和中序在遍歷完子樹前都會先遍歷完根,先銷毀根的話,就沒辦法再找到根的子樹了,所以這兩種遍歷方式都不可取,我們只能后序遍歷來銷毀二叉樹
void  binarytreedestroy(btnode** proot)
{if (*proot == NULL){return;}binarytreedestroy(&((*proot)->left));binarytreedestroy(&((*proot)->right));free(*proot);*proot = NULL;
}
//
//層序遍歷
void levelorder(btnode* root)
{if (root == NULL){return;}//先創建一個隊列Queue q;QueueInit(&q);//先讓隊頭元素入隊列QueuePush(&q, root);//不斷循環直到隊列為空while (!QueueEmpty(&q)){//先取隊頭元素btnode* top = QueueFront(&q);//出隊QueuePop(&q);//訪問隊頭元素printf("%c ", top->data);//如果左孩子不為空,左孩子入隊if (top->left){QueuePush(&q, top->left);}//如果右孩子不為空,右孩子入隊if (top->right){QueuePush(&q, top->right);}}QueueDesTroy(&q);
}//判斷是否為完全二叉樹
bool binarytreecomplete(btnode* root)
{if (root == NULL){return true;}//利用層序遍歷的思想判斷是否為完全二叉樹//先創建一個隊列Queue q;QueueInit(&q);//先讓隊頭元素入隊列QueuePush(&q, root);//不斷循環直到隊列為空while (!QueueEmpty(&q)){//先取隊頭元素btnode* top = QueueFront(&q);//出隊QueuePop(&q);//如果隊頭元素為空,就直接跳出循環if (top == NULL){break;}//走到這里,說明隊列不為空,那么就讓隊頭元素的左右孩子都入隊列QueuePush(&q, top->left);QueuePush(&q, top->right);}//現在就只需要判斷第一次得到空的隊頭元素后,剩下的元素是否是既有空節點又有非空節點(非完全二叉樹)//如果剩下的元素只有空節點(完全二叉樹)while (!QueueEmpty(&q)){//取隊頭元素btnode* top = QueueFront(&q);QueuePop(&q);if (top != NULL){QueueDesTroy(&q);return false;}}QueueDesTroy(&q);return true;
}
//test.c
#define  _CRT_SECURE_NO_WARNINGS 1#include"binarytree.h"btnode* creattree()
{btnode* A = buynode('A');btnode* B = buynode('B');btnode* C = buynode('C');btnode* D = buynode('D');btnode* E = buynode('E');btnode* F = buynode('F');A->left = B;A->right = C;B->left = D;C->left = E;C->right = F;return A;
}void test1()
{btnode* root=creattree();preorder(root);
}void test2()
{btnode* root = creattree();inorder(root);
}void test3()
{btnode* root = creattree();postorder(root);
}void test4()
{btnode* root = creattree();int size = 0;binarytreesize_1(root, size);printf("樹中的節點個數:%d\n", size);
}void test5()
{btnode* root = creattree();int size = 0;binarytreesize_2(root, &size);printf("樹中的節點個數:%d\n", size);binarytreesize_2(root, &size);printf("樹中的節點個數:%d\n", size);binarytreesize_2(root, &size);printf("樹中的節點個數:%d\n", size);
}void test6()
{btnode* root = creattree();int size = binarytreesize(root);printf("樹中的節點個數:%d\n", size);size = binarytreesize(root);printf("樹中的節點個數:%d\n", size);size = binarytreesize(root);printf("樹中的節點個數:%d\n", size);
}void test7()
{btnode* root = creattree();int leafsize = binarytreeleafsize(root);printf("葉子結點的個數:%d\n", leafsize);
}
void test8()
{btnode* root = creattree();int levelksize = binarytreelevelksize(root,3);printf("第三層共有%d個節點\n", levelksize);
}void test9()
{btnode* root = creattree();int depth= binarytreedepth(root);printf("樹的深度:%d\n", depth);
}void test10()
{btnode* root = creattree();btnode* find = binarytreefind(root, 'C');if (find){printf("yes:%c\n", find->data);}else{printf("no\n");}
}void test11()
{btnode* root = creattree();levelorder(root);
}void test12()
{btnode* root = creattree();if (binarytreecomplete(root)){printf("是完全二叉樹\n");}else{printf("不是完全二叉樹\n");}
}
int main()
{//test1();//test2();//test3();//test4();//	test5();//test6();//test7();//test8();//test9();//test10();//test11();test12();return 0;
}
//queue.c
#define  _CRT_SECURE_NO_WARNINGS 1#include"queue.h"//初始化
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}//銷毀
void QueueDesTroy(Queue* pq)
{QueueNode* pcur = pq->phead;while (pcur){QueueNode* pnext = pcur->next;free(pcur);pcur = pnext;}pq->phead = pq->ptail = NULL;pq->size = 0;
}//入隊列
//入隊列是在隊尾入的,所以入隊列相當于鏈表的尾插
void QueuePush(Queue* pq, Qdatatype x)
{assert(pq);//申請新的節點空間QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));newnode->next = NULL;newnode->data = x;//尾插//如果此時隊列中一個元素都沒有if (pq->phead == NULL){pq->phead = pq->ptail = newnode;}else//隊列本來就有元素{pq->ptail->next = newnode;pq->ptail = newnode;}(pq->size)++;
}//判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}//出隊列
//出隊列是在隊頭出的,相當于鏈表的頭刪
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));//如果鏈表中只有一個元素if (pq->phead->next == NULL){free(pq->phead);pq->phead = pq->ptail = NULL;}else//直接頭刪{QueueNode* newhead = pq->phead->next;free(pq->phead);pq->phead = newhead;}(pq->size)--;
}//取隊頭數據
Qdatatype QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}//取隊尾數據
Qdatatype QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;
}//隊列有效元素個數
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}
//queue.h
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>//隊列的結構
//先定義隊列中節點的結構——隊列的底層是鏈表
typedef struct btnode* Qdatatype;typedef struct QueueNode
{Qdatatype data;struct QueueNode* next;
}QueueNode;//隊列的結構定義:
typedef struct Queue
{QueueNode* phead;//隊頭QueueNode* ptail;//隊尾int size;//隊列中有效數據個數
}Queue;//初始化
void QueueInit(Queue* pq);
//銷毀
void QueueDesTroy(Queue* pq);//入隊列
void QueuePush(Queue* pq, Qdatatype x);
//出隊列
void QueuePop(Queue* pq);
//取隊頭數據
Qdatatype QueueFront(Queue* pq);
//取隊尾數據
Qdatatype QueueBack(Queue* pq);
//判空
bool QueueEmpty(Queue* pq);
//隊列有效元素個數
int QueueSize(Queue* pq);

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

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

相關文章

【Spring Boot把日志記錄到文件里面】

<?xml version"1.0" encoding"UTF-8"?> <configuration><!-- 日志輸出格式 --><property name"LOG_PATTERN" value"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" /><!-- 日志…

大數據服務完全分布式部署- 其他組件(阿里云版)

ZooKeeper 安裝 官網 解壓 cd /export/server/ tar -zxvf /export/server/apache-zookeeper-3.9.3-bin.tar.gz -C /export/server/軟鏈接 ln -s /export/server/apache-zookeeper-3.9.3-bin /export/server/zookeeper配置 cd /export/server/zookeeper/ mkdir zkDatamyid…

Windows 平板/電腦 上使用 DHCPSRV 搭建 DHCP 服務器

一、DHCPSRV 核心優勢 輕量便攜:單文件綠色軟件,無需安裝 全圖形界面:比命令行工具更友好 支持IPv4/IPv6:滿足現代網絡需求 低資源占用:適合平板電腦運行(內存<10MB) 租約管理:可查看實時IP分配情況 二、超詳細配置流程 1. 下載與初始化 官網下載:http://www…

ArcGIS動態表格批量出圖

前言&#xff1a;產品介紹&#xff1a;ArcGIS動態表格擴展模塊Mapping and Charting Solutions&#xff0c;可用于插入動態表格&#xff0c;與數據驅動結合&#xff0c;出圖效率無敵。注&#xff1a;優先選擇arcgis10.2.2。 一、首先是根據自身攜帶的arcgis數據進行下載對應的…

Linux小白加油站,第三周周考

1.如何查看當前系統中所有磁盤設備及其分區結構(如磁盤名稱、大小、掛載點等)? lsblk # 顯示磁盤名稱、大小、掛載點&#xff08;P21&#xff09;2.若需對空閑磁盤(如/dev/sdb)進行交互式劃分&#xff0c;如何進入操作界面并創建一個5GB的主分區(類型為Linux默認文件系統)? …

SEO的紅利沒了,下一個風口叫GEO

一、 搜索在退場&#xff0c;答案在上臺過去二十多年&#xff0c;我們習慣了這樣的路徑&#xff1a;輸入關鍵詞 → 點開一堆藍色鏈接 → 慢慢篩出想要的信息。SEO&#xff08;搜索引擎優化&#xff09;就是圍繞這套游戲規則展開的&#xff0c;誰玩得溜&#xff0c;誰就有流量、…

Kubernetes 的 YAML 配置文件-apiVersion

Kubernetes的YAML配置文件–apiVersion 關于 Kubernetes 的 apiVersion 說明 以及 生產環境中推薦使用的版本 的完整指南,幫助你正確、安全地編寫 Kubernetes 配置文件。 一、什么是 apiVersion? 在 Kubernetes 的 YAML 配置文件中,apiVersion 字段用于指定你所使用的 Kub…

uniapp 5+App項目,在android studio模擬器上運行調試

1.安裝android studio&#xff0c;默認安裝即可 點擊下載android studio 2.安裝完成后&#xff0c;添加設備 選擇機型并下載 啟動模擬機&#xff08;啟動比較慢&#xff0c;稍等一會即可&#xff09; 3.等待模擬器啟動后&#xff0c;在uniapp上運行項目到模擬器 如果下…

Qt猜數字游戲項目開發教程 - 從零開始構建趣味小游戲

Qt猜數字游戲項目開發教程 - 從零開始構建趣味小游戲 項目概述 本項目是一個基于Qt框架開發的猜數字游戲&#xff0c;具有現代化的UI設計和完整的游戲邏輯。項目采用C語言開發&#xff0c;使用Qt的信號槽機制實現界面交互&#xff0c;通過隨機數生成和狀態管理實現完整的游戲…

初識CNN05——經典網絡認識2

系列文章目錄 初識CNN01——認識CNN 初識CNN02——認識CNN2 初識CNN03——預訓練與遷移學習 初識CNN04——經典網絡認識 文章目錄系列文章目錄一、GoogleNet——Inception1.1 1x1卷積1.2 維度升降1.3 網絡結構1.4 Inception Module1.5 輔助分類器二、ResNet——越深越好2.1 梯…

學習筆記分享——基于STM32的平衡車項目

學習筆記分享——基于STM32的平衡車項目前言筆記正文結語前言 本文是我在學習鐵頭山羊的平衡車教程的過程中&#xff0c;記錄的筆記&#xff0c;里面不但有Up主的講解&#xff0c;也有我個人的學習心得&#xff0c;還有查閱的資料&#xff0c;由于內容太多&#xff0c;不方便逐…

學習strandsagents的http_request tool

今天我們通過來拆strandsagents官網的一個例子來學習strandsagents的http_request tool https://strandsagents.com/latest/documentation/docs/examples/python/agents_workflows/ 看上去能做實事核查,實際上沒那么高大上。 Show me the code https://github.com/strands-…

大模型對齊算法(四): DAPO,VAPO,GMPO,GSPO, CISPO,GFPO

DAPO DAPO 在 GRPO 的基礎上做了 4 處關鍵升級&#xff0c;既保持 GRPO 的“無價值函數 組內歸一化”思想&#xff0c;又通過 剪枝、采樣、Token 級梯度、長度懲罰 解決長 Chain-of-Thought RL 的四大痛點。 1 剪枝范圍解耦&#xff1a;Clip-Higher GRPO&#xff1a;單一對稱…

OpenHarmony之「星鏈Data」—— 分布式數據管理子系統核心架構與實戰解密

目錄 系統概述 架構設計 核心模塊詳解 數據庫實現與設計原理 關鍵函數調用流程鏈 實際案例分析 常見需求與Bug分析 性能監控與調優

基于SpringBoot+Vue的寫真館預約管理系統(郵箱通知、WebSocket及時通訊、協同過濾算法)

&#x1f388;系統亮點&#xff1a;郵箱通知、WebSocket及時通訊、協同過濾算法&#xff1b;一.系統開發工具與環境搭建1.系統設計開發工具前后端分離項目架構&#xff1a;B/S架構 運行環境&#xff1a;win10/win11、jdk17前端&#xff1a; 技術&#xff1a;框架Vue.js&#xf…

linux下timerfd和posix timer為什么存在較大的抖動?

在linux中開發引用&#xff0c;timerfd和posix timer是最常用的定時器。timerfd是linux特有的定時器&#xff0c;通過fd來實現定時器&#xff0c;體現了linux"一切皆文件"的思想&#xff1b;posix timer&#xff0c;只要符合posix標準的操作系統&#xff0c;均應支持…

網絡聚合鏈路與軟件網橋配置指南

網絡聚合鏈路與軟件網橋配置指南一、聚合鏈路&#xff08;Team&#xff09; 網絡組隊&#xff08;聚合鏈路&#xff09;是一種將多個網絡接口控制器&#xff08;NIC&#xff0c;Network Interface Controller&#xff09;以邏輯方式組合在一起的技術&#xff0c;通過這種方式可…

IDE/去讀懂STM32CubeMX 時鐘配置圖(有源/無源晶振、旁路/晶振模式、倍頻/分頻)

文章目錄概述配置圖元素說明RCCHSI/LSI/HSE/LSEAHB 和 APBSYSCLK 和 HCLKMux 多路復用器Prescaler 預分頻器PLL 鎖相環PLL 配置寄存器時鐘物理源內部時鐘和驅動無源晶振和驅動有源晶振和驅動MCO 時鐘信號音頻時鐘配置晶體振蕩器&#xff1f;外部時鐘源類型RCC 如何選擇旁路模式…

8 文本分析

全文檢索與常規關系型數據庫SQL查詢的顯著區別&#xff0c;就是全文檢索具備對大段文本進行分析的能力&#xff0c;它可以通過文本分析把大段的文本切分為細粒度的分詞。 elasticsearch在兩種情況下會用到文本分析&#xff1a; 原始數據寫入索引時&#xff0c;如果索引的某個字…

告別 Count Distinct 慢查詢:StarRocks 高效去重全攻略

在大數據分析中&#xff0c;去重計算&#xff08;如 Count Distinct&#xff09;是一個常見但計算開銷極高的操作&#xff0c;尤其在高基數和高并發場景下&#xff0c;常常成為查詢性能的瓶頸。以用戶訪問行為為例&#xff0c;同一用戶一天內多次訪問頁面時&#xff0c;PV 會累…