希尔排序
插入排序的升级版,能使插入排序交换数据的次数减少
增量的选取是认为规定的,普遍采用长度的一半作为初始值,注意最后一遍牌序时增量必须为1,也就是对所有元素进行一次插入排序
void shellSort(int *a, int len)
{
int i, j, k, tmp, gap; // gap 为步长
for (gap = len / 2; gap > 0; gap /= 2) { // 步长初始化为数组长度的一半,每次遍历后步长减半,
for (i = 0; i < gap; ++i) { // 变量 i 为每次分组的第一个元素下标
for (j = i + gap; j < len; j += gap) { //对步长为gap的元素进行直插排序,当gap为1时,就是直插排序
tmp = a[j]; // 备份a[j]的值
k = j - gap; // k初始化为i的前一个元素(与i相差gap长度)
while (k >= 0 && a[k] > tmp) {
a[k + gap] = a[k]; // 将在a[i]前且比tmp的值大的元素向后移动一位
k -= gap;
}
a[k + gap] = tmp;
}
}
}
}
堆排序
根本思想:将待排序的数组构造成一个大顶堆,此时,整个序列的最大值就是他的根节点,将他移走(就是将其与对数组的末尾元素交换),此时末尾元素就是最大值,然后将剩余的n-1个序列重新构造成一个堆,这样就i会得到n个元素中的次小值,如此反复执行…
那么我们需要解决的问题就是1.如何将一个无序序列构造成一个堆 2.如何在输出根元素之后,调整剩余元素成为一个新的堆
void HeapSort(Sqlist *L)
{
int i;
for(i=L->Length/2;i>0;i--)//从有孩子的结点开始,与他们的孩子中的最大节点进行交换
HeapAdjust(L,i,L->length);
for(i=L->Length;i>1;i--)
{
swap(L,1,i);
HeapAdjust(L,1,i-1);
}
}
void HeapAdjust(Sqlist *L,int s,int m)
{
int temp,j;
temp=L->r[s];
for(j=2*s;j<=m;j*=2)//沿节点的左孩子向下进行搜索
{
if(j<m&&l->r[j]<l->r[j+1])//左孩子和右孩子进行比较,选出其中较大的一个
++j;
if(temp>=L->r[j])
break;//两个孩子节点都小于父母节点,无需交换,退出
L->r[s]=L->r[j];
s=j;//交换完后将s指向刚刚交换过的节点孩子节点作为新的父母节点
}
L->r[s]=temp;
}
图
定义
1.图(Graph)——图G是由两个集合V(G)和E(G)组成的,记为G=(V,E)其中:V(G)是顶点的非空有限集,E(G)是边的有限集合,边是顶点的无序对或有序对。
2,有向图——有向图G是由两个集合V(G)和E(G)组成
其中:V(G)是顶点的非空有限集,E(G)是有向边(也称弧)的有限集合,弧是顶点的有序对,记为<v,w>,v,w是顶点,v为弧尾,w为弧头
3.无向图——无向图G是由两个集合V(G)和E(G)组成
其中:V(G)是顶点的非空有限集,E(G)是边的有限集合,边是顶点的无序对,记为(v,w)或(w,v),并且(v,w)=(w,v)。
4.有向完全图——n个顶点的有向图最大边数是n(n-1)
无向完全图——n个顶点的无向图最大边数是n(n-1)/2
5.权——与图的边或弧相关的数
6.网——带权的图
7.顶点的度:
无向图中,顶点的度为与每个顶点相连的边数;
有向图中,顶点的度分成入度与出度;
入度:以该顶点为头的弧的数目;
出度:以该顶点为尾的弧的数目
8.路径长度——沿路径边的数目或沿路径各边权值之和
回路——第一个顶点和最后一个顶点相同的路径
简单路径——序列中顶点不重复出现的路径
简单回路——除了第一个顶点和最后一个顶点外,其余顶点不重复出现的回路
连通——从顶点V到顶点W有一条路径,则说V和W是连通的
连通图——图中任意两个顶点都是连通部分
连通分量——非连通图的每一个连通部分
强连通图——有向图中,如果对每一对 Vi,Vj 从 Vi 到 Vj 和从 Vj 到 Vi 都存在路径。(注意只要存在路径即可,不代表存在一条直线经过两个节点)
邻接矩阵法
无向图的邻接矩阵法
typedef char VertexType;
typedef struct ArcNode
{
int adjvex; //该边的邻接点编号
struct ArcNode* nextarc; //指向下条边的指针
//int weight; //该边的相关信息,如权值
}ArcNode; //边结点的类型
typedef struct VNode
{
VertexType data; //顶点信息
ArcNode* firstarc; //指向第一个边结点
}VNode;
typedef struct
{
VNode adjlist[MaxVertexNum]; //邻接表的头结点数组
int vexnum, arcnum; //图中的顶点数和弧数
}AdjGraph; //完整的图邻接表类型
void CreateVNde(AdjGraph& g)
{
int i;
char ch;
printf("输入顶点信息:");
for (i = 0; i < NodeNum; i++)
{
scanf("%c", &ch);
g.adjlist[i].data = ch;
g.adjlist[i].firstarc = NULL;
g.vexnum = NodeNum;
}
}
void CreateANode(AdjGraph& g, VertexType ch, int num)
{
ArcNode* p, * r = g.adjlist[0].firstarc;
int i, j, k;
while (num--)
{
p = (ArcNode*)malloc(sizeof(ArcNode));
p->nextarc = NULL;
printf("输入顶点的编号:");
scanf("%d", &i);
for (j = 0; j < g.vexnum; j++)
if (g.adjlist[j].data == ch) k = j;
if (i != k)
{
p->adjvex = i;
if (g.adjlist[k].firstarc == NULL)
{
g.adjlist[k].firstarc = p;
r = p;
}
else
{
r->nextarc = p;
r = p;
}
}
}
}
有向图的邻接矩阵法
注意:
1.对于无向图的邻接矩阵法,是对称矩阵,主对角线上的元素是自身到自身的距离,设为0
2.有向图的邻接矩阵法,初始化的时候都设为无穷大,因为有方向,所以不是对称矩阵!在线段很少的时候会大大浪费空间
3.无向图中顶点Vi的度TD(Vi)是邻接矩阵A中第i行元素之和;
4.有向图中, 顶点Vi的出度是A中第i行元素之和; 顶点Vi的入度是A中第i列元素之和;
邻接表法
为了减少空间的浪费,可以使用链表
typedef struct ArcNode {
int adjvex;//存储弧,即另一端顶点在数组中的下标
struct ArcNode* nextarc;//指向下一个结点
}ArcNode;
typedef struct VNode {
VertexType data;//顶点的数据域
ArcNode* firstarc;//指向下一个结点
}VNode, AdjList[MAX_VERTEX_NUM];//存储各链表首元结点的数组
typedef struct {
AdjList vertices; //存储图的邻接表
int vexnum, arcnum;//图中顶点数以及弧数
}ALGraph;
void CreateGraph(ALGraph * graph) {
int i, j;
char VA, VB;
ArcNode* node = NULL;
printf("输入顶点的数目:\n");
scanf("%d", &(graph->vexnum));
printf("输入弧的数目:\n");
scanf("%d", &(graph->arcnum));
scanf("%*[^\n]"); scanf("%*c");
printf("输入各个顶点的值:\n");
for (i = 0; i < graph->vexnum; i++) {
scanf("%c", &(graph->vertices[i].data));
getchar();
graph->vertices[i].firstarc = NULL;
}
//输入弧的信息,并为弧建立结点,链接到对应的链表上
for (i = 0; i < graph->arcnum; i++) {
printf("输入弧(a b 表示弧 a->b):\n");
scanf("%c %c", &VA, &VB);
getchar();
node = (ArcNode*)malloc(sizeof(ArcNode));
node->adjvex = '#';
node->nextarc = NULL;
//存储弧另一端顶点所在顺序表中的下标
for (j = 0; j < graph->vexnum; j++) {
if (VB == graph->vertices[j].data) {
node->adjvex = j;
break;
}
}
//如果未在顺序表中找到另一端顶点,则构建图失败
if (node->adjvex == '#') {
printf("弧信息输入有误\n");
exit(0);
}
//将结点添加到对应的链表中
for (j = 0; j < graph->vexnum; j++) {
if (VA == graph->vertices[j].data) {
//将 node 结点以头插法的方式添加到相应链表中
node->nextarc = graph->vertices[j].firstarc;
graph->vertices[j].firstarc = node;
break;
}
}
if (j == graph->vexnum) {
printf("弧信息输入有误\n");
exit(0);
}
}
}
十字链表
对于有向图来说,邻接表是有缺陷的,只能关注出度或者入度,想要了解另外一个必须遍历整个图才能知道,因此我们可以将邻接表和逆邻接表结合起来,也就是十字链表