第九章 樹
第十章 堆和優(yōu)先隊列
第十一章 圖
第九章 樹
樹的基本概念與前幾本書都沒有什么差別,所以基本術(shù)語就不再記錄了
該書有好多易懂的圖,看圖一眼就懂了
最常用表示圖的方法是采用鄰接表表示有向圖。
#ifndef bitree_h
#define bitree_h
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct BiTreeNode_{
void *data;
struct BiTreeNode_ *left;
struct BiTreeNode_ *right;
}BiTreeNode;
typedef struct BiTree_{
//結(jié)點個數(shù)
int size;
//預留的
int (*compare)(const void *key1,const void *key2);
//析構(gòu)函數(shù)
void (*destroy)(void *data);
//指向根結(jié)點的指針
BiTreeNode *root;
}BiTree;
void bitree_init(BiTree *tree,void(*destroy)(void *data));
void bitree_destroy(BiTree *tree);
int bitree_ins_left(BiTree *tree,BiTreeNode *node,const void *data);
int bitree_ins_right(BiTree *tree,BiTreeNode *node,const void *data);
void bitree_rem_left(BiTree *tree,BiTreeNode *node);
void bitree_rem_right(BiTree *tree,BiTreeNode *node);
//將兩棵二叉樹合并為單棵二叉樹
int bitree_merge(BiTree *merge,BiTree *left,BiTree *right,const void *data);
#define bitree_size(tree) ((tree)->size)
#define bitree_root(tree) ((tree)->root)
#define bitree_is_eob(node) ((node)==NULL)
#define bitree_is_leaf(node) ((node)->left==NULL && (node)->right==NULL)
#define bitree_data(node) ((node)->data)
#define bitree_left(node) ((node)->left)
#define bitree_rihgt(node) ((node)->rihgt)
#endif /* bitree_h */
#include "bitree.h"
void bitree_init(BiTree *tree,void(*destroy)(void *data))
{
tree->size=0;
tree->destroy = destroy;
tree->root = NULL;
return;
}
void bitree_destroy(BiTree *tree)
{
bitree_rem_left(tree, NULL);
memset(tree,0,sizeof(BiTree));
return;
}
int bitree_ins_left(BiTree *tree,BiTreeNode *node,const void *data)
{
BiTreeNode *new_node,**position;
//插入根結(jié)點
if (node==NULL)
{
if (bitree_size(tree) > 0) {
return -1;
}
position = &tree->root;
}else{
if (bitree_left(node) != NULL) {
return -1;
}
position = &node->left;
}
if ((new_node = (BiTreeNode *)malloc(sizeof(BiTreeNode))) == NULL){
return -1;
}
new_node->data = (void *)data;
new_node->left = NULL;
new_node->right = NULL;
*position = new_node;
tree->size++;
return 0;
}
//實現(xiàn)與上面基本相同
int bitree_ins_right(BiTree *tree,BiTreeNode *node,const void *data)
{
BiTreeNode *new_node,**position;
//插入根結(jié)點
if (node==NULL)
{
if (bitree_size(tree) > 0) {
return -1;
}
position = &tree->root;
}else{
if (bitree_left(node) != NULL) {
return -1;
}
position = &node->right;
}
if ((new_node = (BiTreeNode *)malloc(sizeof(BiTreeNode))) == NULL){
return -1;
}
new_node->data = (void *)data;
new_node->left = NULL;
new_node->right = NULL;
*position = new_node;
tree->size++;
return 0;
}
void bitree_rem_left(BiTree *tree,BiTreeNode *node)
{
BiTreeNode **position;
if (bitree_size(tree) == 0){
return;
}
if (node==NULL) {
position = &tree->root;
}else{
position = &node->left;
}
if (*position != NULL)
{
bitree_rem_left(tree, *position);
bitree_rem_right(tree, *position);
if (tree->destroy != NULL)
{
tree->destroy((*position)->data);
}
free(*position);
*position = NULL;
tree->size--;
}
return;
}
//實現(xiàn)與上面基本相同,就是把左換成右
void bitree_rem_right(BiTree *tree,BiTreeNode *node)
{
BiTreeNode **position;
if (bitree_size(tree) == 0){
return;
}
if (node==NULL) {
position = &tree->root;
}else{
position = &node->right;
}
if (*position != NULL)
{
bitree_rem_left(tree, *position);
bitree_rem_right(tree, *position);
if (tree->destroy != NULL)
{
tree->destroy((*position)->data);
}
free(*position);
*position = NULL;
tree->size--;
}
return;
}
//將兩棵二叉樹合并為單棵二叉樹
int bitree_merge(BiTree *merge,BiTree *left,BiTree *right,const void *data)
{
bitree_init(merge, left->destroy);
if (bitree_ins_left(merge, NULL, data) != 0)
{
bitree_destroy(merge);
return -1;
}
bitree_root(merge)->left = bitree_root(left);
bitree_root(merge)->right = bitree_root(right);
merge->size = merge->size + bitree_size(left) + bitree_size(right);
left->root = NULL;
left->size = 0;
right->root = NULL;
right->size = 0;
return 0;
}
樹的周游算法
先序遍歷,中序遍歷,后序遍歷,層序遍歷
#include "traverse.h"
#include "list.h"
#include "bitree.h"
//前序
int preorderTraverse(const BiTreeNode *node,List *list)
{
if (!bitree_is_eob(node))
{
if (list_ins_next(list, list_tail(list), bitree_data(node)) != 0)
{
return -1;
}
if (!bitree_is_eob(bitree_left(node)))
{
if (preorderTraverse(bitree_left(node), list) != 0)
{
return -1;
}
if (!bitree_is_eob(bitree_right(node)))
{
if (preorderTraverse(bitree_right(node), list) != 0)
{
return -1;
}
}
}
}
return 0;
}
//中序
int inorderTraverse(const BiTreeNode *node,List *list)
{
if (!bitree_is_eob(node))
{
if (!bitree_is_eob(bitree_left(node)))
{
if (inorderTraverse(bitree_left(node), list) != 0)
{
return -1;
}
}
if (list_ins_next(list, list_tail(list), bitree_data(node)) != 0)
{
return -1;
}
if (!bitree_is_eob(bitree_right(node)))
{
if (inorderTraverse(bitree_right(node), list) != 0)
{
return -1;
}
}
}
return 0;
}
//后序
int postorderTraverse(const BiTreeNode *node,List *list)
{
if (!bitree_is_eob(node))
{
if (!bitree_is_eob(bitree_left(node)))
{
if (postorderTraverse(bitree_left(node), list) != 0)
{
return -1;
}
}
if (!bitree_is_eob(bitree_right(node)))
{
if (postorderTraverse(bitree_right(node), list) != 0)
{
return -1;
}
}
if (list_ins_next(list, list_tail(list), bitree_data(node)) != 0)
{
return -1;
}
}
return 0;
}
樹的平衡
在結(jié)點加入下一層之前必須保證本層結(jié)點滿額,保證樹的高度盡可能短
如果一棵平衡樹最后一層的所有葉子結(jié)點都在最靠左邊的位置上,則稱樹為左平衡的。
這本書也用表達式的處理來?講解二叉樹
二叉搜索樹
由二叉樹組成的專用于查找和搜索目的的一種數(shù)據(jù)結(jié)構(gòu)。
從根結(jié)點開始一層一層往下查找,當遇到一個比目標結(jié)點值大的結(jié)點時,順著該結(jié)點的左子樹繼續(xù)查找;如果遇到的結(jié)點值小于目標結(jié)點,則順著該結(jié)點的右子樹繼續(xù)查找。
但是,只有當二叉搜索樹保持平衡時查找效率才會高,所以出現(xiàn)了AVL樹和紅黑樹。
AVL樹
一種特殊類型的二叉樹,每個結(jié)點都保存一份額外的信息:結(jié)點的平衡因子(右子樹高度減去左子樹高度的結(jié)果)
一棵子樹的根結(jié)點的平衡因子就代表該子樹的平衡性
如果任何平衡因子變?yōu)榱?-2,就必須重新平衡這棵樹,稱為旋轉(zhuǎn)。
AVL樹的旋轉(zhuǎn)
旋轉(zhuǎn)用來重新平衡AVL樹的某個部分,旋轉(zhuǎn)過后,旋轉(zhuǎn)子樹中的所有結(jié)點的平衡因子都為+1,-1,0
有四種:LL、LR、RR、RL(right,left)
這四種分別表示當插入的元素x在A樹的左子樹的左子樹上;在A樹的左子樹的右子樹上;在A樹的右子樹的右子樹上;在A樹的右子樹的左子樹上,平衡這棵樹要進行的操作。
問與答:
1、一些樹除了有從父結(jié)點指向子結(jié)點的正常指針外,還有從子結(jié)點指向父結(jié)點的指針,有的兄弟結(jié)點之間也有相互關(guān)聯(lián)的指針。為什么?
維護附加的指針為遍歷樹時提供了更加靈活的方式。子結(jié)點指向父結(jié)點的指針可以自下而上遍歷,兄弟結(jié)點之間的指針可以不必訪問父結(jié)點。
2、在bitree_rem_left與bitree_rem_right中,為什么要采用后序遍歷的方式來移除子樹?
因為子樹必須在其父結(jié)點之前就被全部移除
3、在二叉搜索樹中我們?nèi)绾握业阶钚〉慕Y(jié)點?在最壞的情況下,在非平衡和平衡二叉搜索樹中該操作的時間復雜度是多少?如何在二叉搜索樹中找到最大的結(jié)點?時間復雜度?
二叉搜索樹中最小結(jié)點是最左邊的那個葉子結(jié)點。從根結(jié)點開始通過左指針逐層下降,直到邊緣。最壞情況為當只有一個單獨的左分支時,O(n)。平均為O(lgn)。
最大結(jié)點是最右邊的那個葉子結(jié)點。時間復雜度一樣。
4、何時應(yīng)該選擇使用分支因子相對較大的樹而不是二叉樹呢?
對于給定的結(jié)點個數(shù),分支因子大能夠保持這棵樹的高度較低,也就能使樹能夠保持相對的平衡性。
在對樹的高度敏感的應(yīng)用中,采用分支因子較大的樹。在內(nèi)存中的性能并沒有顯著提升,但在速度相對較慢的輔助存儲空間(閃存)內(nèi)執(zhí)行查找操作時,在性能上有明顯的區(qū)別。
二叉樹通常用于內(nèi)存中查找操作。
5、在一棵二叉搜索樹中,某結(jié)點的后繼結(jié)點是指x之后的下一個最大結(jié)點。
如24、39、41、55、87、92中,41的后繼是55。如何在二叉搜索樹中找到某個結(jié)點的后繼結(jié)點?時間復雜度?
首先要定位到x。然后移動到x的右子結(jié)點,從這個結(jié)點開始不斷通過左指針訪問下層的左子結(jié)點,直到到達分支的邊緣。這條分支邊緣位置上的結(jié)點即后繼結(jié)點。為O(lgn)。
6、在二叉搜索樹中插入一個結(jié)點,按照一條特定的路徑找到一個合適的位置作為插入點。隨著越來越多的結(jié)點插入樹中,最終,某個值將不再適合插入某區(qū)域中了。所以樹會失去平衡然后必須進行旋轉(zhuǎn)操作。
總之一個原則:左子樹結(jié)點必須小于該結(jié)點,右子樹結(jié)點必須大于該結(jié)點。
第十章 堆和優(yōu)先隊列
堆:一棵二叉樹,分為大頂堆和小頂堆。左平衡的,從左至右增長。
大頂堆根結(jié)點是樹中最大的結(jié)點,每個子結(jié)點存儲的值比父結(jié)點的值小。
小頂堆根結(jié)點是樹中最小的結(jié)點,每個子結(jié)點存儲的值比父結(jié)點的值大。
#ifndef heap_h
#define heap_h
typedef struct Heap_{
int size;
int (*compare)(const void *key1,const void *key2);
void (*destroy)(void *data);
void **tree;
}Heap;
#include <stdio.h>
void heap_init(Heap *heap,int(*compare)(const void *key1,const void *key2),void(*destroy)(void *data));
void heap_destroy(Heap *heap);
//插入新結(jié)點
//注意要重新排序
int heap_insert(Heap *heap,const void *data);
//釋放堆頂部結(jié)點
//刪除頂結(jié)點后,把最后一個結(jié)點放到頂部然后重新排序
int heap_extract(Heap *heap,void **data);
#define heap_size(heap) ((heap)->size)
#endif /* heap_h */
#include "heap.h"
#include <stdlib.h>
#include <string.h>
#define heap_parent(npos) ((int)(((npos)-1)/2))
#define heap_left(npos) (((npos)*2)+1)
#define heap_right(npos) (((npos)*2)+2)
void heap_init(Heap *heap,int(*compare)(const void *key1,const void *key2),void(*destroy)(void *data))
{
heap->size = 0;
heap->compare = compare;
heap->destroy = destroy;
heap->tree = NULL;
return;
}
void heap_destroy(Heap *heap)
{
if (heap->destroy != NULL)
{
for (int i=0; i<heap_size(heap); i++)
{
heap_destroy(heap->tree[i]);
}
}
free(heap->tree);//釋放內(nèi)存
memset(heap, 0, sizeof(Heap));//清空heap
return;
}
int heap_insert(Heap *heap,const void *data)
{
void *temp;
int ipos,ppos;
if ((temp = (void **)realloc(heap->tree, (heap_size(heap)+1)*sizeof(void *))) == NULL)
{
return -1;
}
else
{
heap->tree = temp;
}
heap->tree[heap_size(heap)] = (void *)data;
ipos = heap_size(heap);
ppos = heap_parent(ipos);
while (ipos>0 && heap->compare(heap->tree[ppos],heap->tree[ipos]) < 0)
{
//子結(jié)點與父結(jié)點交換
temp = heap->tree[ppos];
heap->tree[ppos] = heap->tree[ipos];
heap->tree[ipos] = temp;
//向上移動
ipos = ppos;
ppos = heap_parent(ipos);
}
heap->size++;
return 0;
}
int heap_extract(Heap *heap,void **data)
{
void *save,*temp;
int ipos,lpos,rpos,mpos;
if (heap_size(heap) == 0)
{
return -1;
}
*data = heap->tree[0];
save = heap->tree[heap_size(heap)-1];
if (heap_size(heap)-1 > 0)
{
if ((temp = (void **)realloc(heap->tree, (heap_size(heap)-1)*sizeof(void *))) == NULL)
{
return -1;
}
else
{
heap->tree = temp;
}
heap->size--;
}
else
{
free(heap->tree);
heap->tree = NULL;
heap->size = 0;
return 0;
}
//把最后一個元素挪到頂部
heap->tree[0] = save;
//重新堆化樹
ipos = 0;
lpos = heap_left(ipos);
rpos = heap_right(ipos);
while (1)
{
lpos = heap_left(ipos);
rpos = heap_right(ipos);
if (lpos < heap_size(heap) && heap->compare(heap->tree[lpos],heap->tree[ipos]) > 0)
{
mpos = lpos;
}
else
{
mpos = ipos;
}
if (rpos < heap_size(heap) && heap->compare(heap->tree[rpos],heap->tree[mpos]) > 0)
{
mpos = rpos;
}
if (mpos == ipos)
{
break;
}
else
{
//交換當前結(jié)點與選擇的子結(jié)點
temp = heap->tree[mpos];
heap->tree[mpos] = heap->tree[ipos];
heap->tree[ipos] = temp;
ipos = mpos;
}
}
return 0;
}
優(yōu)先隊列:將數(shù)據(jù)按照優(yōu)先級順序排列。
作者是用一個局部有序的堆來實現(xiàn)的。
#ifndef pqueue_h
#define pqueue_h
#include <stdio.h>
#include "heap.h"
typedef Heap PQueue;
#define pqueue_init heap_init
#define pqueue_insert heap_insert
//從優(yōu)先隊列中提取優(yōu)先隊列頂部的元素
#define pqueue_extract heap_extract
//獲取優(yōu)先隊列中優(yōu)先級最高元素
#define pqueue_peek(pqueue) ((pqueue)->tree == NULL ? NULL : (pqueue)->tree[0])
#define pqueue_size heap_size
#endif /* pqueue_h */
優(yōu)先隊列的例子:包裹分揀
快遞公司根據(jù)包裹的緊急程度、服務(wù)類型,將快遞按照優(yōu)先級排序。
問與答:
1、每次向一個堆中插入元素都要重新排序,時間復雜度有點高,是否有其他辦法構(gòu)建一個堆,降低復雜度?
避免重復調(diào)用insert函數(shù),不斷調(diào)整一個結(jié)點數(shù)組,將結(jié)點往樹的根部遞推。首先調(diào)整(n/2)-1的樹,再調(diào)整(n/2)-2的樹,直到調(diào)整根結(jié)點處于0的樹
2、左平衡二叉樹?特別適合存儲于數(shù)組中,為什么不是對所有的二叉樹都成立?
對于n是樹中結(jié)點的個數(shù),因為在位置0和n-1之間肯定會用到結(jié)點,空間利用率高,如果出現(xiàn)個右結(jié)點,數(shù)組中位置會出現(xiàn)空
3、如果用優(yōu)先隊列來存放已經(jīng)設(shè)定好順序的任務(wù),如果系統(tǒng)不斷處理大量高優(yōu)先級的任務(wù),可能會產(chǎn)生什么問題?
低優(yōu)先級的元素可能永遠都沒機會排到隊列的頂部,導致“餓死”。
采取機制提高任務(wù)等級,比如隨著任務(wù)在隊列中待的時間變得越來越長增加。
第十一章 圖
圖由頂點和邊組成,頂點代表對象,邊建立起對象之間的關(guān)系A(chǔ)DG
G=(V,E),V代表頂點的集合,E代表關(guān)系的集合
#ifndef graph_h
#define graph_h
#include <stdio.h>
#include "list.h"
#include "set.h"
//結(jié)點
typedef struct AdjList_{
void *vertex;
Set adjacent;
}AdjList;
typedef struct Graph_{
int vcount;
int ecount;
int (*match)(const void *key1,const void *key2);
void (*destroy)(void *data);
List adjlists;
}Graph;
typedef enum VertexColor_ {
white,gray,black
}VertexColor;
void graph_init(Graph *graph,int (*match)(const void *key1,const void *key2),void (*destroy)(void *data));
void graph_destroy(Graph *graph);
//將一個頂點插入圖中
int graph_ins_vertex(Graph *graph,const void *data);
//將一條邊插入圖中
int graph_ins_edge(Graph *graph,const void *data1,const void *data2);
int graph_rem_vertex(Graph *graph,void **data);
int graph_rem_edge(Graph *graph,void *data1,void **data2);
//取出graph中由data指定的頂點的鄰接表
int graph_adjlist(const Graph *graph,const void *data,AdjList **adjlist);
//判斷由data2所指定的頂點是否與graph中由data1所指定的頂點鄰接
int graph_is_adjacent(const Graph *graph,const void *data1,const void *data2);
#define graph_adjlists(graph) ((graph)->adjlists)
#define graph_vcount(graph) ((graph)->vcount)
#define graph_ecount(graph) ((graph)->ecount)
#endif /* graph_h */
#include "graph.h"
#include <stdlib.h>
#include <string.h>
void graph_init(Graph *graph,int (*match)(const void *key1,const void *key2),void (*destroy)(void *data))
{
graph->vcount = 0;
graph->ecount = 0;
graph->match = match;
graph->destroy = destroy;
list_init(&graph->adjlists, NULL);
return;
}
void graph_destroy(Graph *graph)
{
AdjList *adjlist;
//刪除每個鄰接表結(jié)構(gòu)并摧毀它的鄰接表
while (list_size(&graph->adjlists) > 0)
{
if (list_rem_next(&graph->adjlists, NULL, (void **)&adjlist) == 0)
{
set_destroy(&adjlist->adjacent);
if (graph->destroy != NULL)
{
graph->destroy(adjlist->vertex);
}
free(adjlist);
}
}
list_destroy(&graph->adjlists);
memset(graph, 0, sizeof(Graph));
return;
}
int graph_ins_vertex(Graph *graph,const void *data)
{
ListElmt *element;
AdjList *adjlist;
int retval;
//不允許插入重復的頂點
for (element = list_head(&graph->adjlists); element != NULL; element = list_next(element))
{
if (graph->match(data,((AdjList*)list_data(element))->vertex))
{
return 1;
}
}
//插入
if ((adjlist = (AdjList *)malloc(sizeof(AdjList))) == NULL)
{
return -1;
}
adjlist->vertex = (void *)data;
set_init(&adjlist->adjacent, graph->match, NULL);
if ((retval = list_ins_next(&graph->adjlists, list_tail(&graph->adjlists), adjlist)) != 0)
{
return retval;
}
graph->vcount++;
return 0;
}
int graph_ins_edge(Graph *graph,const void *data1,const void *data2)
{
ListElmt *element;
int retval;
//不允許插入兩個頂點都不在圖上的邊
for (element = list_head(&graph->adjlists); element != NULL; element = list_next(element))
{
if (graph->match(data2,((AdjList *)list_data(element))->vertex))
{
break;
}
}
if (element == NULL)
{
return -1;
}
for (element = list_head(&graph->adjlists); element != NULL; element = list_next(element))
{
if (graph->match(data1,((AdjList *)list_data(element)) -> vertex))
{
break;
}
}
if (element == NULL)
{
return -1;
}
//在鄰接表第一個頂點后插入第二個頂點
if ((retval = set_insert(&((AdjList *)list_data(element))->adjacent, data2)) != 0)
{
return retval;
}
graph -> ecount++;
return 0;
}
int graph_rem_vertex(Graph *graph,void **data)
{
ListElmt *element,*temp = NULL,*prev;
AdjList *adjlist;
int found;
//遍歷每個鄰接表和包含的頂點
prev = NULL;
found = 0;
for (element = list_head(&graph->adjlists); element != NULL; element = list_next(element))
{
//不允許刪除這個頂點,如果在鄰接表中
if (set_is_member(&((AdjList *)list_data(element))->adjacent, *data))
{
return -1;
}
if (graph->match(*data,((AdjList *)list_data(element))->vertex))
{
temp = element;
found = 1;
}
if (!found)
{
prev = element;
}
}
//若果頂點沒有找到就返回
if (!found)
{
return -1;
}
//如果鄰接表不是空的,不允許刪除頂點
if (set_size(&((AdjList *)list_data(temp)) -> adjacent) > 0)
{
return -1;
}
//刪除頂點
if (list_rem_next(&graph->adjlists, prev, (void **)&adjlist)!= 0)
{
return -1;
}
*data = adjlist->vertex;
free(adjlist);
graph->vcount--;
return 0;
}
int graph_rem_edge(Graph *graph,void *data1,void **data2)
{
ListElmt *element;
//找到第一個頂點的鄰接表
for (element = list_head(&graph->adjlists); element != NULL; element = list_next(element))
{
if (graph->match(data1,((AdjList *)list_data(element))->vertex))
{
break;
}
}
if (element == NULL)
{
return -1;
}
//刪除第一個頂點鄰接表的第二個頂點
if (set_remove(&((AdjList *)list_data(element))->adjacent, data2) != 0)
{
return -1;
}
graph->ecount--;
return 0;
}
int graph_adjlist(const Graph *graph,const void *data,AdjList **adjlist)
{
ListElmt *element,*prev;
//定位頂點的鄰接表
prev = NULL;
for (element = list_head(&graph->adjlists); element != NULL; element = list_next(element))
{
if (graph->match(data,((AdjList *)list_data(element))->vertex))
{
break;
}
prev = element;
}
if (element == NULL)
{
return -1;
}
*adjlist = list_data(element);
return 0;
}
int graph_is_adjacent(const Graph *graph,const void *data1,const void *data2)
{
ListElmt *element,*prev;
prev = NULL;
//定位第一個頂點的鄰接表
for (element = list_head(&graph->adjlists); element != NULL; element = list_next(element))
{
if (graph->match(data1,((AdjList *)list_data(element))->vertex))
{
break;
}
prev = element;
}
if (element == NULL)
{
return 0;
}
return set_is_member(&((AdjList *)list_data(element))->adjacent, data2);
}
廣度優(yōu)先搜索,廣度優(yōu)先樹。
深度優(yōu)先搜索,深度優(yōu)先樹。
圖的例子
1、計算網(wǎng)絡(luò)跳數(shù)
確定在互聯(lián)網(wǎng)中從一個結(jié)點到另一個結(jié)點的最佳路徑。采用廣度優(yōu)先搜索來幫助確定結(jié)點間的最小跳數(shù)。
2、拓撲排序
根據(jù)各種事件間的依賴關(guān)系來確定一種可接受的執(zhí)行順序。
比如執(zhí)行完a才能執(zhí)行b,執(zhí)行完b才能執(zhí)行c
通過對事件進行深度優(yōu)先搜索,確定出一種可接受的順序。
問與答:
1、本章的圖中,為什么采用鏈表來實現(xiàn)鄰接表結(jié)構(gòu)鏈表,而鄰接表本身卻采用集合的方式來實現(xiàn)?
用鏈表,能夠動態(tài)的擴展和收縮。用集合,因為鄰接表包含的頂點是無序的,針對鄰接表的主要操作非常適合。
這里鄰接表結(jié)構(gòu)鏈表的主要操作時找出特定頂點的鄰接表,鏈表比集合更適合。
先說數(shù)據(jù)類型的優(yōu)點,然后是否適合當前問題即可。
2、假設(shè)用圖對互聯(lián)網(wǎng)建模,我們確定圖包含一個關(guān)結(jié)點,會有什么影響?
關(guān)結(jié)點代表一個單點故障源,如果位于關(guān)結(jié)點的系統(tǒng)失效,其他結(jié)點上的系統(tǒng)就無法互相通信了。因此在設(shè)計時就要保證沒有關(guān)結(jié)點,通過放置冗余結(jié)點來解決。
3、用圖對航線結(jié)構(gòu)進行建模...?
廣度優(yōu)先搜索
4、模擬十字路口的交通燈系統(tǒng)?
有向圖