408数据结构-图的遍历 自学知识点整理

前置知识:图的存储与基本操作


图的遍历是指从图的某一顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问一次,且仅访问一次。因为树是一种特殊的图,所以树的遍历实际上也可以视为一种特殊的图的遍历。图的遍历算法是求解图的连通性问题、拓扑排序和求关键路径等算法的基础。
因为图的任意一个顶点都可能和其余的顶点相邻接,所以再访问过某个顶点后,又可能沿着某条路径搜索回到这个被访问过的结点。为了避免同一个顶点被访问多次,在遍历图的过程中,必须记下每个已访问过的顶点,可以设一个辅助数组 v i s i t e d [ ] visited[\ ] visited[ ]来标记顶点是否被访问过。图的遍历算法主要有两种:广度优先搜索深度优先搜索

广度优先搜索

知识点回顾:二叉树的层次遍历
对二叉树进行按层次的遍历,需要借助一个辅助队列实现,具体思想如下:
①首先将二叉树的根结点入队。
②若队列非空,则队头结点出队,并访问该结点。若该结点有左孩子,则将其左孩子入队;若该结点有右孩子,则将其右孩子入队。
③重复步骤②,直至队列为空。

广度优先搜索 B r e a d t h − F i r s t − S e a r c h , B F S Breadth-First-Search,\ BFS BreadthFirstSearch, BFS)类似于二叉树的层次遍历算法。基本思想是:首先访问起始顶点 v v v,接着从 v v v出发,依次访问 v v v的各个未访问过的邻接顶点 w 1 , w 2 , ⋯ , w i {{w}_{1}},{{w}_{2}},\cdots ,{{w}_{i}} w1,w2,,wi,然后依次访问 w 1 , w 2 , ⋯ , w i {{w}_{1}},{{w}_{2}},\cdots ,{{w}_{i}} w1,w2,,wi的所有未被访问过的邻接顶点;再从这些访问过的顶点出发,访问它们所有未被访问过的邻接顶点,依此类推,直至图中所有顶点都被访问过为止。若此时图中尚有顶点未被访问,则另选图中一个未被访问过的顶点作为起始点,再重复上述过程,直至图中所有顶点都被访问到为止。
D i j k s t r a Dijkstra Dijkstra单源最短路径算法和 P r i m Prim Prim最小生成树算法也应用了类似的思想。
对广度优先搜索遍历图的过程,换而言之,就是以 v v v为起始点,由近至远依次访问和 v v v有路径相通且路径长度为 1 , 2 , ⋯ 1,2,\cdots 1,2,的顶点。广度优先搜索是一种分层的查找过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有往回退的情况,因此它不是一个递归的算法。为了实现逐层的访问,算法必须借助一个辅助队列,以记忆正在访问的顶点的下一层的顶点。

代码实现

首先是预处理,包括结构体定义,基本操作函数等。因为邻接矩阵法写起来相对简单一些,所以我主要用邻接表法来写。

知识点回顾:图的邻接表存储表示法
知识点类比:二叉树的层次遍历

#define MaxVertexNum 810
typedef struct ArcNode {//边结点int adjvex;struct ArcNode* nextarc;
}ArcNode;
typedef struct VNode {//顶点表结点int data;ArcNode* firstarc;
}VNode,AdjList[MaxVertexNum];
typedef struct {AdjList vertices;//邻接表存储图int vexnum, arcnum;
}ALGraph;
bool Visited[MaxVertexNum];//辅助数组标记顶点是否被访问typedef struct LinkNode {int Vertex;//图的顶点编号struct LinkNode* next;
}LinkNode;
typedef struct {LinkNode* front, * rear;//链队列
}LinkQueue;void InitQueue(LinkQueue& Q) {LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode*));p->Vertex = -1;p->next = NULL;Q.front = Q.rear = p;//初始化队列return;
}bool Q_Is_Empty(LinkQueue Q) {//队列判空return Q.front == Q.rear ? true : false;
}void EnQueue(LinkQueue& Q, int i) {//新元素入队LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode*));p->Vertex = i;p->next = NULL;Q.rear->next = p;Q.rear = p;return;
}void DeQueue(LinkQueue& Q, int& x) {//队头元素出队LinkNode* p = Q.front->next;x = p->Vertex;Q.front->next = p->next;if (Q.rear == p)Q.rear = Q.front;free(p);return;
}void Visit(int i) {printf("%d ", i);return;
}

邻接表实现广度优先搜索的代码如下:

void BFS(ALGraph& G, LinkQueue& Q, int i) {//广度优先搜索(对连通分量)Visit(i);//先访问iVisited[i] = true;EnQueue(Q, i);int v, w;//v记录队首元素,w用于临时记录访问的顶点while (!Q_Is_Empty(Q)) {//当队列不为空时DeQueue(Q, v);//队首元素出队for (ArcNode* p = G.vertices[v].firstarc; p != NULL; p = p->nextarc) {//访问v所有的邻接顶点w = p->adjvex;if (!Visited[w]) {//若顶点w未被访问过Visit(w);//访问顶点wVisited[w] = true;//对顶点w作已访问标记EnQueue(Q, w);//顶点w入队}}}return;
}void BFSTraverse(ALGraph& G, LinkQueue& Q) {//对图G进行广度优先遍历for (int i = 1; i <= G.vexnum; ++i) {Visited[i] = false;//访问标记数组初始化G.vertices[i].data = i;//其实这一步没用}InitQueue(Q);//初始化队列for (int i = 1; i <= G.vexnum; ++i)//从1号顶点开始遍历if (!Visited[i])//对每个连通分量调用一次BFS()BFS(G, Q, i);//若顶点v_i未被访问过,则从顶点v_i处开始调用BFS()return;
}

邻接矩阵实现广度优先搜索算法如下:(并不完整,感兴趣的读者可以尝试自己写一下)

#define MaxVertexNum 114
typedef struct {int Vex[MaxVertexNum];int Edge[MaxVertexNum][MaxVertexNum];int vexnum, arcnum;
}MGraph;void BFS_(MGraph& G, LinkQueue& Q, int i) {Visit(i);Visited[i] = true;EnQueue(Q, i);int v, w;while (!Q_Is_Empty(Q)) {DeQueue(Q, v);for (w = 1; w <= G.vexnum; ++w) {if (!Visited[w] && G.Edge[v][w]) {Visit(w);Visited[w] = true;EnQueue(Q, w);}}}return;
}

辅助数组 V i s i t e d [ ] Visited[\ ] Visited[ ]标志顶点是否被访问过,其初始值为 f a l s e false false。在图的遍历过程中,一旦某个顶点 v i v_i vi被访问,就立即置 V i s i t e d [ i ] Visited[i] Visited[i]值为 t r u e true true,防止其被多次访问。

B F S BFS BFS算法性能分析

无论是邻接表还是邻接矩阵的存储方式, B F S BFS BFS算法都需要借助一个辅助队列 Q Q Q,所有顶点均需入队一次,在最坏的情况下,空间复杂度为 O ( ∣ V ∣ ) O(|V|) O(V)
遍历图的过程实质上是对每个顶点查找其邻接点的过,耗费的时间取决于所采用的存储结构。
采用邻接表存储时,每个顶点均需入队一次,时间复杂度为 O ( ∣ V ∣ ) O(|V|) O(V),在搜索每个顶点的邻接点时,每条边至少访问一次,时间复杂度为 O ( ∣ E ∣ ) O(|E|) O(E)。因此,总的时间复杂度为 O ( ∣ V ∣ + ∣ E ∣ ) O(|V|+|E|) O(V+E)
采用邻接矩阵存储时,查找每个顶点的邻接点所需时间为 O ( ∣ V ∣ ) O(|V|) O(V),因此,总的时间复杂度为 O ( ∣ V ∣ 2 ) O(|V|^{2}) O(V2)

B F S BFS BFS算法求解单源最短路径

若图 G = ( V , E ) G=(V,E) G=(V,E)为无权图,定义从顶点 u u u到顶点 v v v的最短路径 d ( u , v ) d(u,v) d(u,v) u u u v v v的任何路径中最少的边数;若从 u u u v v v没有通路,则 d ( u , v ) = ∞ d(u,v)=\infin d(u,v)=
使用 B F S BFS BFS,我们可以求解一个满足上述定义的“无权图的单源最短路径问题”,这是由广度优先搜索总是按照距离由近到远来遍历图中每个顶点的性质决定的。
B F S BFS BFS算法求解单源最短路径问题的算法如下,我根据王道书上的伪代码自己稍作了一些改动:

#define INF 0x7fffffff
void BFS_MIN_Distance(ALGraph G, LinkQueue& Q, int u) {int d[MaxVertexNum], v;//d[i]表示从u到i结点的最短路径memset(d, INF, sizeof(d));//初始化路径长度为无穷大Visited[u] = true, d[u] = 0;EnQueue(Q, u);while (!Q_Is_Empty(Q)) {//BFS主过程DeQueue(Q, u);//队头出队for (ArcNode* w = G.vertices[u].firstarc; w != NULL; w = w->nextarc) {//遍历u的所有邻接点v = w->adjvex;if (!Visited[v]) {//若未访问Visited[v] = true;//标记访问d[v] = d[u] + 1;//路径长度加1EnQueue(Q, v);//顶点w入队}}}return;
}
广搜的完整代码可以看我的Github:传送门

广度优先生成树

在广度优先遍历的过程中,我们可以得到一棵遍历树,称为广度优先生成树。需要注意的是,同一个图的邻接矩阵存储表示是唯一的,所以其广度优先生成树也是唯一的,但是因为邻接表存储表示不唯一,所以其广度优先生成树也不是唯一的。广度优先遍历序列同理,因此不再展开讲)

(下图来自王道考研408数据结构课程视频的截图 - 图的广度优先遍历)
图片来自王道考研408数据结构课程视频
对非连通图的广度优先遍历,可以得到广度优先生成森林。其中,对每一个连通分量进行广度优先遍历得到的广度优先生成树的集合,就是这个非连通图的广度优先生成森林。
这期视频最后的思考题,我个人认为答案如下:

  1. 1 1 1出发,需要调用 4 4 4 B F S BFS BFS函数,过程分别是: 1 → 5 1→5 15 2 2 2 3 → 6 3→6 36 4 → 7 → 8 4→7→8 478
  2. 7 7 7出发,需要调用 1 1 1 B F S BFS BFS函数,过程是: 7 → 3 → 6 → 8 → 2 → 4 → 1 → 5 7→3→6→8→2→4→1→5 73682415

深度优先搜索

知识点回顾:二叉树的先序遍历
若二叉树为空,则什么也不做;否则:

  • 访问根结点;
  • 先序遍历左子树;
  • 先序遍历右子树。

对应的递归算法如下:

void PreOrder(BiTree T) {//先序遍历(根左右)if (T != NULL) {visit(T);//访问根结点PreOrder(T->lchild);//递归访问左子树PreOrder(T->rchild);//递归访问右子树}return;
}

与广度优先搜索不同,深度优先搜索 D e p t h − F i r s t − S e a r c h Depth-First-Search DepthFirstSearch D F S DFS DFS)类似于树的先序遍历。如果其名称中所暗含的意思一样,这种搜索算法所遵循的策略是尽可能“深”地搜索一个图。
它的基本思想是:首先访问图中某一起始顶点 v v v,然后从 v v v出发,访问与 v v v邻接且未被访问的任意一个顶点 w 1 w_1 w1,再访问与 w 1 w_1 w1邻接且未被访问的任意一个顶点 w 2 w_2 w2……重复上述过程。当不能再继续往下访问时,依次退回到最近被访问的顶点,若它还有邻接顶点未被访问过,则再从该点开始继续上述搜索过程,直至图中所有顶点均被访问过为止。

代码实现

首先预处理,使用邻接表的方式存储图。

#define MaxVertexNum 114
typedef struct ArcNode {int adjvex;struct ArcNode* nextarc;
}ArcNode;
typedef struct VNode {int data;ArcNode* firstarc;
}VNode, AdjList[MaxVertexNum];
typedef struct {AdjList vertices;int vexnum, arcnum;
}ALGraph;bool Visited[MaxVertexNum];//访问标记数组inline void Visit(int i) {Visited[i] = true;//对i作已访问标记printf("%d ", i);return;
}

邻接表实现深度优先搜索算法如下:

inline void DFS(ALGraph G, int i) {//注:可以不加inline,仅个人习惯Visit(i);//访问初始顶点ifor (ArcNode* p = G.vertices[i].firstarc; p != NULL; p = p->nextarc) {//检测i的所有邻接点int j = p->adjvex;if (!Visited[j])DFS(G, j);//若j未被访问,则递归访问j}return;
}inline void DFSTraverse(ALGraph G) {for (int i = 1; i <= G.vexnum; ++i) {Visited[i] = false;//初始化访问标记数组G.vertices[i].data = i;}for (int i = 1; i <= G.vexnum; ++i) {//从1开始遍历if (!Visited[i])//若当前顶点未访问则调用DFS()DFS(G, i);}return;
}

这样写 D F S T r a v e r s e ( ) DFSTraverse() DFSTraverse()函数也能同时解决非连通图的遍历问题。
同样的,我也写了个不完整的邻接矩阵实现深度优先搜索算法,感兴趣的读者可以自己动手试试补全。

#define MaxVertexNum 114
typedef struct {int Vex[MaxVertexNum];int Edge[MaxVertexNum][MaxVertexNum];int vexnum, arcnum;
}MGraph;inline void DFS_(MGraph G, int i) {Visit(i);//访问初始顶点ifor (int j = 1; j <= G.vexnum; ++j) {//检查i的所有邻接点if (!Visited[j] && G.Edge[i][j])DFS_(G, j);//j为i尚未访问的邻接点,递归访问j}return;
}
深搜的完整代码可以看我的Github:传送门

D F S DFS DFS算法的性能分析

D F S DFS DFS是一个递归算法,主要空间复杂度来自递归函数工作栈,最坏情况下空间复杂度为 O ( ∣ V ∣ ) O(|V|) O(V)
遍历图的过程实质上是通过边查找邻接点的过程,因此两种遍历方式的时间复杂度都相同,不同之处仅在于对顶点的访问顺序不同。采用邻接矩阵存储时,总的时间复杂度为 O ( ∣ V ∣ 2 ) O(|V|^{2}) O(V2);采用邻接表存储时,总的时间复杂度为 O ( ∣ V ∣ + ∣ E ∣ ) O(|V|+|E|) O(V+E)

深度优先生成树和生成森林

与广度优先搜索一样,深度优先搜索也会产生一棵深度优先生成树。当然,这是有条件的,即对连通图调用 D F S DFS DFS才能产生深度优先生成树,否则产生的将是深度优先生成森林
(下图来自王道考研408数据结构课程视频的截图 - 图的深度优先遍历)
图来自王道考研408数据结构课程视频
2 2 2出发,易知深度优先遍历序列为 2 → 1 → 5 → 6 → 3 → 4 → 7 → 8 2→1→5→6→3→4→7→8 21563478
图来自王道考研408数据结构课程视频
与广度优先搜索类似,邻接矩阵生成的深度优先序列和深度优先搜索树都是唯一的;而邻接表生成的深度优先序列和深度优先搜索树都是不唯一的,具体由邻接表边表的存储顺序决定。

图的遍历与图的连通性

图的遍历算法可以用来判断图的连通性。对于无向图来说,若无向图是连通的,则从任意一个顶点出发,仅需一次遍历就能够访问图中的所有顶点;若无向图是非连通的,则从某一个顶点出发,一次遍历只能访问到该顶点所在连通分量的所有顶点,而对于图中其他连通分量的顶点,则无法通过这次遍历访问到。对于有向图来说,若从初始顶点出发到图中的每个顶点都有路径,则能够访问到图中的所有顶点,否则不能访问到所有顶点。
因此,在 B F S T r a v e r s e ( ) BFSTraverse() BFSTraverse() D F S T r a v e r s e ( ) DFSTraverse() DFSTraverse()函数中,添加了第二个 f o r for for循环,用于多次选取初始点,继续进行遍历,防止出现一次无法遍历图中所有顶点的情况。对于无向图,上述两个函数调用 B F S ( G , i ) BFS(G,i) BFS(G,i) D F S ( G , i ) DFS(G,i) DFS(G,i)的次数,等于该图的连通分量数;而对于有向图则不是这样,因为一个连通的有向图分为强连通的和非强连通的,它的连通子图也分为强连通分量和非强连通分量,对非强连通分量一次调用 B F S ( G , i ) BFS(G,i) BFS(G,i) D F S ( G , i ) DFS(G,i) DFS(G,i)存在无法访问到该连通分量所有顶点的情况。

知识点回顾:图的基本概念


对图的遍历,408考研初试中经常在选择题里考察遍历序列和遍历生成树的问题,对代码要求不那么高。因此要结合课后习题,在充分理解概念和算法思想的基础上,重点掌握手推遍历序列的能力。
以上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/853317.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用鱼骨图进行项目问题复盘与改进

一、引言 在项目管理中&#xff0c;问题复盘是一个至关重要的环节。它不仅能帮助我们识别项目执行过程中出现的问题&#xff0c;还能促使我们深入探究问题的根本原因&#xff0c;从而采取有效的改进措施。在这个过程中&#xff0c;鱼骨图作为一种强大的工具&#xff0c;为我们…

MEMS:Lecture 16 Gyros

陀螺仪原理 A classic spinning gyroscope measures the rotation rate by utilizing the conservation of angular momentum. 经典旋转陀螺仪通过利用角动量守恒来测量旋转速率。 Coriolis Effect and Coriolis Force 科里奥利效应是一种出现在旋转参考系中的现象。它描述了…

Internet Download Manager ( 极速下载器 ) 序列号注册码 IDM下载器注册机中文激活破解版

IDM下载器(Internet Download Manager)是一款专业的下载管理软件&#xff0c;它通过多线程技术和智能文件分段技术&#xff0c;有效提升下载速度&#xff0c;并支持断点续传&#xff0c;还具有计划下载功能&#xff0c;用户可以设置特定的下载时间&#xff0c;非常适合需要在特…

代码随想录算法训练营刷题复习1 :动态规划背包问题 01背包+完全背包

动态规划刷题复习 一、01背包 416. 分割等和子集1049. 最后一块石头的重量 II494. 目标和474. 一和零 416. 分割等和子集 class Solution { public:bool canPartition(vector<int>& nums) {int sum0;for(int i0;i<nums.size();i) {sumnums[i];}if(sum%2!0)retu…

IP地址、子网掩码、网段、网关

前面相同就是在同一个网段 如果子网掩码和网络号相与的结果是一样的&#xff0c;那么他们就在同一个子网 IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段/数字-如192.168.0.1/24是什么意思?_掩码248可以用几个ip-CSDN博客

谷歌上架,APP被移除了,没封号,换个包名还能重新提审上架?

对于在Google Play上架应用的开发者来说&#xff0c;尤其是那些矩阵式上架马甲包的开发者&#xff0c;可能已经遭遇过无数次应用被暂停或移除的情况了。通常这种情况下&#xff0c;账号也随之会被封&#xff0c;且大多数开发者认为&#xff0c;没有立马收到封号邮件的话&#x…

基于scikit-learn的机器学习分类任务实践——集成学习

一、传统机器学习分类流程与经典思想算法简述 传统机器学习是指&#xff0c;利用线性代数、数理统计与优化算法等数学方式从设计获取的数据集中构建预测学习器&#xff0c;进而对未知数据分类或回归。其主要流程大致可分为七个部分&#xff0c;依次为设计获取数据特征集&#x…

【C++】stack、queue和deque的使用

&#x1f497;个人主页&#x1f497; ⭐个人专栏——C学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读 一、stack 1. stack介绍 2. stack使用 二、queue 1. queue介绍 2. queue使用 三、deque 1. deque介绍 2. deque的…

【Pandas驯化-01】一文介绍 Pandas中的Dataframe和Series 区别

【Pandas】驯化-01一文介绍 Pandas中的Dataframe和Series 区别 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 相关内容文档获取 微信公众…

第九届星华杯网络邀请赛

T1喵星人的身高 T2犇犇碑 T3嘤嘤词典 T4三角区间和

简单Mesh多线程合并,使用什么库性能更高

1&#xff09;简单Mesh多线程合并&#xff0c;使用什么库性能更高 2&#xff09;Unity Semaphore.WaitForSignal耗时高 3&#xff09;VS编辑的C#代码注释的中文部分乱码 4&#xff09;变量IntPtr m_cachePtr切换线程后变空 这是第389篇UWA技术知识分享的推送&#xff0c;精选了…

HBase中Master初始化错误~

ERROR&#xff1a;org.apache.hadoop.hbase.PleaseHoldException:Master is initializing 1、停止HBase运行 2、启动zookeeper中的zkCli.sh服务 ./zookeeper/bin/zkCli.sh 3、执行完毕显示以下结果,删除habse文件夹 4、重新启动HBase即可。

python-02

面向对象 Python中把具有相同属性和方法的对象归为一个类。 class ClassName: 语句 class Myclass: # 定义类Myclassdef pp(self): # 定义方法pp()print("这是产品说明书")myclass Myclass() # 实例化类Myclass myclass.pp() # 调用Myclass中的方法pp()打印…

《幻影大师:透视缠中说禅的虚像与真相》

而且他从不犯错&#xff0c;至少在他的叙述中是这样&#xff0c;所有的文章和言论都被粉饰得完美无瑕&#xff0c;即便有误&#xff0c;他也绝不公开承认&#xff0c;更别提什么真诚的道歉和改正了。那些对他推崇备至的人&#xff0c;多是盲目追随&#xff0c;将他神化为无所不…

《Nest系列 - 1. 运行一个Nest项目以及整体目录学习》

初识Nest心路历程 作为一名前端开发&#xff0c;说实话&#xff0c;学习Nest后端技术, 会有一定的成本。我试着阅读文档&#xff0c;安装项目&#xff0c;把项目跑起来&#xff0c; 当我看到久违的Hellow world 后&#xff0c;还来不及欣喜&#xff0c;就困惑了, 作为一个后端…

Java的集合框架总结

Map接口和Collection接口是所有集合框架的父接口&#xff1a; Collection接口的子接口包括&#xff1a;Set接口和List接口 Map接口的实现类主要有&#xff1a;HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等 Set接口的实现类主要有&#xff1a;HashSet、Tr…

大数据实训项目(小麦种子)-03、大数据环境Hadoop、Mapreduce、Hive、Hbase、HDFS搭建服务及调试

文章目录 前言一、Linux系统Centos7安装配置JDK8二、Linxu系统Centos7中搭建Hadoop3.1.0服务下载地址服务1&#xff1a;详细步骤&#xff08;初始化与启动dfs服务&#xff09;详细步骤配置环境变量 服务2&#xff1a;Hadoop(YARN)环境搭建 三、Linux系统搭建Hive3.1.2服务前提条…

记录AE快捷键(持续补充中。。。)

记录AE快捷键 快捷键常用快捷键图层快捷键工具栏图层与属性常用指令视图菜单时间轴常规快捷键项目首选项功能摄像机操作 常用操作导入AI/PS工程文件加选一个关键参数快速回到上下一帧隐藏/显示图层关键帧拉长缩短关键帧按着鼠标左键不松手&#xff0c;在秒表那一列往下移动会都…

深度学习(PyTorch)批注理解,建议边学可以边看这个笔记

前言 动手学习深度学习&#xff0c;内容丰富&#xff0c;但是对于初学者有很多晦涩难懂的地方&#xff0c;我将日常更新这篇文章以截图的形式&#xff0c;每天高强度学习四五个小时&#xff0c;精力缺乏&#xff0c;我认为&#xff0c;如果想学习这个深度学习&#xff0c;你需…

ubuntu vnc

如何在Ubuntu 18.04安装VNC | myfreax sudo apt install xfce4 xfce4-goodies xorg dbus-x11 x11-xserver-utils sudo apt install tigervnc-standalone-server tigervnc-common vncserver sudo apt install xfce4 xfce4-goodies xorg dbus-x11 x11-xserver-utils sudo apt ins…