希尔排序、堆排序、图(一)

希尔排序

插入排序的升级版,能使插入排序交换数据的次数减少
增量的选取是认为规定的,普遍采用长度的一半作为初始值,注意最后一遍牌序时增量必须为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);
        }
    }
}

十字链表

对于有向图来说,邻接表是有缺陷的,只能关注出度或者入度,想要了解另外一个必须遍历整个图才能知道,因此我们可以将邻接表和逆邻接表结合起来,也就是十字链表
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值