图的遍历:
深度优先遍历(DFS)
对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次
广度优先遍历(BFS)
系统地展开并遍历图中的所有节点,而且每个节点只能访问一次,与队列配合进行
图的存储结构:
邻接矩阵:
用一个一维数组存储n个顶点,用一个n*n的二维数组存储边
char V[n] = {A,B,C,D,E,F,G};
A B C D E F G (弧头)
A [0][1][0][1][0][0][0]
B [0][0][0][1][1][0][0]
C [0][0][0][1][0][1][0]
D [0][0][1][0][0][0][1]
E [0][0][0][0][0][0][1]
F [0][0][0][0][0][0][1]
G [0][0][0][0][0][1][0]
(弧尾)
在二维数组E[i][j]值为1,则表示顶点V[i]到V[j]有边
注意:由于不存在自己到自己的边,左对角线上的值一定为0
如果存储的是无向图则二维数组的值沿对角线对称,可以压缩成一维数组(参考矩阵压缩)
优点:可以方便地计算顶点的出度和入度
缺点:当图是稀疏图时,会非常地浪费存储空间
邻接表:(链式+顺序)
边:
顶点下标
指向下一条边的地址
顶点:
顶点数据
指向第一条边的指针
图:
由顶点组成的数组
顶点数量
优点:节约存储空间,计算出度方便
缺点:计算入度麻烦
十字链表:
是一种专门存储有向图的一种结构
边
弧尾下标
弧头下标
指向弧尾相同的下一条边
指向弧头相同的下一条边
顶点:
顶点数据
指向第一条出度的边
指向第一条入度的边
图:
由顶点组成的数组
顶点数量
优点:节约空间、计算出入度很方便
邻接多重表:
是一种专门存储无向图的一种结构
边:
i j 两个相互依附的顶点的下标
inext 指向下一条依附于i顶点的边
jnext 指向下一条依附于j顶点的边
顶点:
顶点数据
指向与顶点有关的一条边的指针
图:
由顶点组成的数组
顶点数量
算法:
数据结构中的算法,指的是数据结构所具备的功能
解决特定问题的方法,它是前辈们的一些优秀的经验总结
一个算法应该具有以下五个重要的特征:
有穷性:算法的有穷性是指算法必须能在执行有限个步骤之后终止;
确切性:算法的每一步骤必须有确切的定义;
输入项:一个算法有0个或多个输入;
输出项:一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;
可行性:算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤,即每个计算步骤都可以在有限时间内完成(也称之为有效性)
如何评价一个算法:
时间复杂度:由于计算机的性能不同,
无法准确地衡量出算法执行所需要的时间
因此我们用算法的执行次数来代表算法的时间复杂度
一般使用 O(公式) 一般忽略常数
常见的时间复杂度:
// O(1)
printf("%d",i);
// O(logn)
for(int i=n; i>=0; i/=2)
{
printf("%d",i);
}
// O(n) O(N)
for(int i=0; i<n; i++)
{
printf("%d",i);
}
// O(nlogn)
for(int i=0; i<n; i++)
{
for(int j=n; j>=0; j/=2)
{
printf("%d",i);
}
}
// O(n^2)
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
printf("%d",i);
}
}
空间复杂度:
执行一个程序所需要的内存空间大小,是对一个算法在运行过程中临时占用存储空间大小的衡量
一般只要算法不涉及动态分配的内存以及递归,通常空间复杂度为O(1)
例如:求第n个斐波那契数列的递归实现算法 空间复杂度O(n)
注意:对于一个算法而言,其时间复杂度与空间复杂度往往是相互影响的,没有唯一的标准,需要结合实际综合考虑
分治:
分而治之,把一个大而复杂的问题,分解成很多小而简单的问题,利用计算机强大的计算能力来解决问题
实现分治的方法:循环、递归
查找算法:
顺序查找
对待查找的数据没有要求,从头到尾逐一比较,在小规模的查找中较为常见,查找效率较低
时间复杂度:O(N)
二分查找(折半查找)
待查找的数据必须有序,从数据中间位置开始比较查找,如果中间值比key小,则从左边继续进行二分查找,反之从右边进行。
时间复杂度:O(logN)
块查找(权重查找)
是一种数据处理的思想,不是一种特定的算法,当数据量非常多时,可以先把数据进行分块处理,然后再根据分块的条件进行查找,例如英文字典
哈希查找