二叉树
1概念
二叉树Binary Tree是n个结点的有限集合。它或者是空集n=0,或者是由一个根结点以及两颗互不相交、分别称为左子树和右子树的二叉树组成。
二叉树与普通有序树不同,二叉树严格区分左子和右子,即使只有一个子结点也要区分左右。
二叉树的树度数最大为2。
2性质*
关于树的一些基本念
(1)度数:一个节点的子树的个数(一个节点的子树的个数称为该节点的度数,3)
(2)树度数:树中节点的最大度数
(3)叶节点或终端节点: 度数为零的节点
(4)分支节点:度数不为零的节点(B一层)
(5)内部节点:除根节点以外的分支节点 (B,C,D)
(6)节点层次: 根节点的层次为1,根节点子树的根为第2层,以此类推
(7)树的深度或高度: 树中所有节点层次的最大值 (4)
1二叉树的第k层上的结点最多个2k-1个
2深度为k的二叉树最多有2k-1个结点
Sn=a1(1-qn)/(1-q)=a1(1-2k)/(1-2)=(1-2k)/-1=2k-1
3在任意一颗二叉树中,树叶的数目比度数为2的结点数目多1。
N:结点的总数
N0:没有子结点的结点个数
N1:只有一个子结点的结点个数
N2:有两个子结点的结点个数
总结点 = 各节点数目之和 N = N0 + N1 + N2
总结点 = 所有子节点 + 根 N = 0 × N0 + 1 × N1 + 2 × N2 + 1
联立以上两式可得: N0 = N2 + 1
(网易)一棵二叉树有8个度为2的节点,5个度为1的节点,那么度为0的节点个数为 ( 9 )
满二叉树和完全二叉树
满二叉树:深度为k(k>=1)时,第k层结点个数为2k-1
完全二叉树:只有最下面两层有度数小于2的节点,且最下面一层的结点集中在最左边的若干位置上。
3实现
二叉树的存储结构有两种,分为顺序存储和链式存储
3.1顺序存储
二叉树的顺序存储,指的是使用顺序表(数组)存储二叉树。需要注意,顺序存储只适用于完全二叉树。换句话说,只有完全二叉树才可以用顺序表存储。因此,如果我们想要顺序存储普通二叉树,就需要将其提前转换成完全二叉树。
普通二叉树转完全二叉树的方法很简单,只需给二叉树额外添加一些结点,将其"拼凑"成一个完全二叉树即可。
如图所示:
普通二叉树的转化
左侧是普通二叉树,右侧是转化后的完全(满)二叉树。
完全(满)二叉树的顺序存储,仅需要从根结点开始,按照层次依次将树中结点存储到数组即可。
完全二叉树示意图
存储图 2 所示的完全二叉树:
完全二叉树存储状态示意图
存储由普通二叉树转化来的完全二叉树也是如此。
图 1 中普通二叉树在顺组中的存储状态如图:
普通二叉树的存储状态
完全二叉树中结点按照层次并从左到右依次编号(123...),若结点i有左子,则其左子的结点编号为2*i,右子编号为2*i+1。
设完全二叉树的结点数为n,某结点的编号为i。
当i>1时(不是根结点时),有父节点,其编号为i/2。
当2*i <= n时,有左子,其编号为2*i,否则没有左子,没左子一定没右子,其本身为叶节点。
当2*i+1 <= n时,有右子,其编号为2*i+1,否则就没有右子。
3.1.1遍历*
先序:根----->左----->右
A B D H I E J C F K G
中序:左----->根----->右
H D I B E J A F K C G
后序:左----->右----->根
H I D J E B K F G C A
已知遍历结果如下,试画出对应的二叉树,写出后续:
先序:A B C E H F I J D G K 根----->左----->右
中序:A H E C I F J B D K G 左----->根----->右
因为先序是根在最前面的,所以在先序中从前往后地取结点拿到中序中作为根,循环重复。
3.2链式存储
普通二叉树示意图
链式存储此二叉树,从根结点开始,将各个结点以及其左右子的地址使用链表进行存储。
二叉树链式存储结构示意图
3.2.1定义操作完全二叉树结构体*
结点结构体由三部分组成:
1指向左子结点的指针(Lchild)
2结点存储的数据(结点编号)
3指向右子结点的指针(Rchild)
(3)二叉树结点结构
// 1. 定义操作二叉树的结构体
typedef char datatype_tree;
typedef struct tree_node_t
{
datatype_tree data;//数据域
struct tree_node_t *lchild;//左子left
struct tree_node_t *rchild;//右子right
}bitree_node_t,*bitree_list_t;
3.2.2创建二叉树*
#include "bitree.h"
// 2. 创建二叉树
// 主函数传参 n 树中结点总数; i 结点编号(从1开始)
bitree_list_t CreateBitree(int n,int i)
{
// 2.1 开辟空间存放结构体
bitree_list_t r = (bitree_list_t)malloc(sizeof(bitree_node_t));
if(NULL == r)
{
printf("CreateBitree malloc failed\n");
return NULL;
}
// 2.2 初始化结构体成员
// 2.3 判断有无左右子
// 2.3.1 有左子
r->data = i;
if(2 * i <= n)
{
r->lchild = CreateBitree(n,2*i);
}
// 2.3.2 无左子
else
{
r->lchild = NULL;
}
// 2.3.3 有右子
if(2*i + 1 <= n)
{
r->rchild = CreateBitree(n,2*i+1);
}
// 2.3.4 无右子
else
{
r->rchild = NULL;
}
return r;
}
#include "bitree.h"
int main(int argc, const char *argv[])
{
bitree_node_t *r = CreateBitree(3,1);
return 0;
}
3.2.3先序遍历*
//前序
// 3. 先序遍历二叉树
// 根——左——右
void PreOrder(bitree_list_t r)
{
if(NULL == r)
return;
printf("%d ",r->data); // 根
// 如果有左子,则将左子作为根将该函数的全部操作走一遍
if(r->lchild != NULL) // 左
PreOrder(r->lchild);
// 如果有右子,则将右子作为根将该函数的全部操作走一遍
if(r->rchild != NULL)
PreOrder(r->rchild); // 右
}
3.2.4中序遍历
// 4. 中序遍历
// 左——根——右
void InOrder(bitree_list_t r)
{
if(NULL == r)
return;
// 如果有左子,则将左子作为根将该函数的全部操作走一遍
if(r->lchild != NULL) // 左
InOrder(r->lchild);
printf("%d ",r->data); // 根
// 如果有右子,则将右子作为根将该函数的全部操作走一遍
if(r->rchild != NULL)
InOrder(r->rchild); // 右
}
3.2.5后序遍历
// 5. 后序遍历
// 左——右——根
void PostOrder(bitree_list_t r)
{
if(NULL == r)
return;
// 如果有左子,则将左子作为根将该函数的全部操作走一遍
if (r->lchild != NULL) // 左
PostOrder(r->lchild);
// 如果有右子,则将右子作为根将该函数的全部操作走一遍
if (r->rchild != NULL) // 右
PostOrder(r->rchild);
printf("%d\t", r->data); // 根
}
总结:
#include "bitree.h"
bitree_list_t CreateBitree(int n,int i)
{
bitree_list_t r = (bitree_list_t)malloc(sizeof(bitree_node_t));
if(NULL == r)
{
printf("CreateBitree malloc failed\n");
return NULL;
}
r->data = i;
if(2 * i <= n)
{
r->lchild = CreateBitree(n,2*i);
}
else
{
r->lchild = NULL;
}
if(2*i + 1 <= n)
{
r->rchild = CreateBitree(n,2*i+1);
}
else
{
r->rchild = NULL;
}
return r;
}
//前序
void PreOrder(bitree_list_t r)
{
if(NULL == r)
return;
printf("%d ",r->data);
if(r->lchild != NULL)
PreOrder(r->lchild);
if(r->rchild != NULL)
PreOrder(r->rchild);
}
//中序
void InOrder(bitree_list_t r)
{
if(NULL == r)
return;
if(r->lchild != NULL)
InOrder(r->lchild);
printf("%d ",r->data);
if(r->rchild != NULL)
InOrder(r->rchild);
}
//后序
void PostOrder(bitree_list_t r)
{
if(NULL == r)
return;
if(r->lchild != NULL)
PostOrder(r->lchild);
if(r->rchild != NULL)
PostOrder(r->rchild);
printf("%d ",r->data);
}
#ifndef _BITREE_H_
#define _BITREE_H_
#include <stdio.h>
#include <stdlib.h>
typedef char datatype_tree;
typedef struct tree_node_t
{
datatype_tree data;//数据域
struct tree_node_t *lchild;//左子left
struct tree_node_t *rchild;//右子right
}bitree_node_t,*bitree_list_t;
bitree_list_t CreateBitree(int n,int i);
//前序
void PreOrder(bitree_list_t r);
//中序
void InOrder(bitree_list_t r);
//后序
void PostOrder(bitree_list_t r);
//层次
void unOrder(bitree_list_t r);
#endif
#include "bitree.h"
int main(int argc, const char *argv[])
{
bitree_node_t *r = CreateBitree(3,1);
PreOrder(r);
printf("\n");
InOrder(r);
printf("\n");
PostOrder(r);
printf("\n");
return 0;
}
3.2.6层序遍历
队列的思想
不需要敲代码,看懂就行
示意图
bitree.h
#ifndef _BITREE_H_
#define _BITREE_H_
typedef char datatype_tree;
typedef struct tree_node_t
{
datatype_tree data;//数据域
struct tree_node_t *lchild;//左子指针
struct tree_node_t *rchild;//右子指针
}bitree_t;
//前序遍历
void preOrder(bitree_t *r);//r二叉树根节点的指针
//中序遍历
void inOrder(bitree_t * r);
//后序遍历
void postOrder(bitree_t *r);
//遍历二叉树
//s 代表的是打印提示, void (*p)(bitree_t *)函数指针 r遍历的树
void showBitree(char *s,void (*p)(bitree_t *),bitree_t *r);
//创建二叉树,用递归函数创建
bitree_t *createBitree();
//层次遍历
void unOrder(bitree_t *r);
#endif
linkqueue.h
#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_
#include "bitree.h"
//将 bitree_t * 改名 为datatype_linkqueue
typedef bitree_t * datatype_linkqueue;//把队列的数据域变成指向树节点的指针
typedef struct node
{
datatype_linkqueue data;//数据域
struct node *next;//指针域
}linkqueue_node_t,*linkqueue_list_t;
//linkqueue_list_t p === linkqueue_node_t *
typedef struct//将队列头指针和尾指针封装到一个结构体里
{
linkqueue_list_t front;//相当于队列的头指针
linkqueue_list_t rear;//相当于队列的尾指针
//有了链表的头指针和尾指针,那么我们就可以操作这个链表
}linkqueue_t;
//1.创建一个空的队列
linkqueue_t *createEmptyLinkQueue();
//2.入列 data代表入列的数据
int inLinkQueue(linkqueue_t *p,datatype_linkqueue data);
//3.出列
datatype_linkqueue outLinkQueue(linkqueue_t *p);
//4.判断队列是否为空
int isEmptyLinkQueue(linkqueue_t *p);
//5.求队列长度的函数
int lengthLinkQueue(linkqueue_t *p);
//6.清空队列
void clearLinkQueue(linkqueue_t *p);
#endif
bitree.c
#include "bitree.h"
#include "linkqueue.h"
#include <stdio.h>
#include <stdlib.h>
//前序遍历
void preOrder(bitree_t *r)//r二叉树根节点的指针
{
if(r == NULL)//递归函数的结束条件
return;
printf("%c ",r->data);//根
preOrder(r->lchild);//左
preOrder(r->rchild);//右
}
//中序遍历
void inOrder(bitree_t * r)
{
if(r == NULL)//递归的结束条件
return;
inOrder(r->lchild);//左
printf("%c ",r->data);//根
inOrder(r->rchild);//右
}
//后序遍历
void postOrder(bitree_t *r)
{
if(r == NULL)//递归函数的结束条件
return;
postOrder(r->lchild);//左
postOrder(r->rchild);//右
printf("%c ",r->data);//根
}
//遍历二叉树
//s 代表的是打印提示, void (*p)(bitree_t *)函数指针 r遍历的树
void showBitree(char *s,void (*p)(bitree_t *),bitree_t *r)
{
printf("%s",s);
p(r);
printf("\n");
}
//创建二叉树,用递归函数创建
bitree_t *createBitree()
{//root
// ABD###CE##F##
bitree_t *r = NULL;//用来保存二叉树的根节点
char ch;
scanf("%c",&ch);
if(ch == '#')//输入是'#',代表没有左子或右子
return NULL;
r = (bitree_t *)malloc(sizeof(bitree_t));
if(NULL == r)
{
perror("r malloc failed");
return NULL;
}
r->data = ch;
r->lchild = createBitree();
r->rchild = createBitree();
return r;
}
//层次遍历
void unOrder(bitree_t *r)
{
//1.创建一个队列,队列的数据域变成指向树节点的指针
linkqueue_t *p = createEmptyLinkQueue();
if(r != NULL)
inLinkQueue(p,r);
//2.循环打印
while(!isEmptyLinkQueue(p))
{
r = outLinkQueue(p);
printf("%c ",r->data);
if(r->lchild != NULL)//只要左子不为空,就入列,之后出列的时候打印
inLinkQueue(p,r->lchild);
if(r->rchild != NULL)//只要右子不为空,就入列,之后出列的时候打印
inLinkQueue(p,r->rchild);
}
}
linkqueue.c
#include "linkqueue.h"
#include <stdio.h>
#include <stdlib.h>
//1.创建一个空的队列
linkqueue_t *createEmptyLinkQueue()
{
linkqueue_t *p = (linkqueue_t *)malloc(sizeof(linkqueue_t));
if(NULL == p)
{
perror("createEmptyLinkQueue p malloc failed");
return NULL;
}//申请空间就是为了装东西
//申请链表的头节点空间,让rear和front都指向头结点
p->front = p->rear = (linkqueue_list_t)malloc(sizeof(linkqueue_node_t));
if(NULL == p->rear)
{
perror("p->rear malloc failed");
return NULL;
}
p->rear->next = NULL;//或者用p->front->next = NULL;因为p->rear 和 p->front 指向同一个位置即头节点
return p;
}
//2.入列 data代表入列的数据
int inLinkQueue(linkqueue_t *p,datatype_linkqueue data)
{
//1.创建一个新的节点,用来保存即将插入的数据
linkqueue_list_t pnew = (linkqueue_list_t)malloc(sizeof(linkqueue_node_t));
if(NULL == pnew)
{
perror("inLinkQueue pnew malloc failed");
return -1;
}
//2.将入列的数据放入到新的节点中
pnew->data = data;
pnew->next = NULL;
//3.将新节点链链接到链表的尾巴
p->rear->next = pnew;//新节点链接到链表的尾
p->rear = pnew;//rear移动,因为rear永远指向当前链表的尾
return 0;
}
//3.出列
datatype_linkqueue outLinkQueue(linkqueue_t *p)
{
linkqueue_list_t pdel = NULL;//指向被删除的节点
//1.容错判断
if(isEmptyLinkQueue(p))
{
printf("isEmptyLinkQueue !!\n");
return NULL;
}
//2.出列数据
//(1)定义pdel指向即将被删除的节点就是front指向的节点,出列每次删除的都是front指向的那个节点
pdel = p->front;
//(2)将front向后移动一个位置
p->front = p->front->next;
//(3)释放被删除节点
free(pdel);
pdel = NULL;
//(4)将数据出列
return p->front->data;
}
//4.判断队列是否为空
int isEmptyLinkQueue(linkqueue_t *p)
{
return p->front == p->rear;
}
//5.求队列长度的函数
int lengthLinkQueue(linkqueue_t *p)
{
int len = 0;
linkqueue_list_t h = p->front;//将链表的头指针保存的地址给h,如果直接用front,求长度之后会找不到链表的头,用h的移动代替front的移动
//求长度,相当于遍历有头的单向链表
while(h->next != NULL)
{
h = h->next;
len++;
}
return len;
}
//6.清空队列
void clearLinkQueue(linkqueue_t *p)
{
while(!isEmptyLinkQueue(p))//只要不为空,就出列
outLinkQueue(p);
}
main.c
#include "bitree.h"
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
bitree_t *r = createBitree();
showBitree("前序:",preOrder,r);
showBitree("中序:",inOrder,r);
showBitree("后序:",postOrder,r);
showBitree("层次:",unOrder,r);
return 0;
}
排序算法
冒泡排序
从终端输入7个数排序好输出
Pseudo_code.c
int main()
{
int a[7];
for (7)
scanf(&a[i]);
for (i=0;i<7-1;i++)
for (7-1-i)
if (a[j] < a[j+1])
exchange(a[j], a[j+1]);
for (0;7;i++)
printf(a[i]);
}
Bubble_Sorting.c
#include <stdio.h>
#define N 7
int main(int argc, char const *argv[])
{
int a[N] = {0};
int i; // 趟数
int j; // 每趟比对次数
printf("Please input array (int a[7]) >");
for (i = 0; i < N; i++)
scanf("%d", &a[i]);
for (i = 0; i < N-1; i++)
for (j = 0; j < N-1-i; j++)
if(a[j] > a[j+1])
{
a[j] ^= a[j+1];
a[j+1] ^= a[j];
a[j] ^= a[j+1];
}
for (i = 0; i < N; i++)
printf("%d\t", a[i]);
puts("");
return 0;
}
选择排序
从终端输入7个数排序好输出
Pseudocode.c
int main()
{
int a[7];
int i; // 趟数
int j; // 每趟比对次数
int k = 0; // 认为最小值的下标
scanf();
for (i=0;i<6;i++)
{
for (j=?; j<?; j++)
if (a[k] > a[j])
k=j;
// 执行到这里时 已拿到最小值的下标——k
// 若下标不是i则交换a[i], a[k]
if (i != k)
exchange();
// k要改变——思考!
k = ?;
}
printf();
}
插值排序
打牌
快速排序
//不稳定
#include <stdio.h>
//piovt 枢轴
intgetPiovt(int*p,intlow, inthigh)
{
intflag=p[low];//flag是镖旗
while(low<high)
{
//最开始从右向左进行扫描
while(flag<=p[high] &&low<high)//只要p[high] >= flag 那么就high--
high--;
if(low<high)
{
p[low] =p[high];//将小于flag的数移动到左边
low++;//准备改变扫描的方向,从左向右进行扫描
}
while(flag>=p[low] &&low<high)//只要p[low] <= flag 那么就low++
low++;
if(low<high)
{
p[high] =p[low];//将大于flag的数,移动到右边
high--;//准备改变扫描的方向,从右往左进行扫描
}
}
//将flag赋值到枢轴位置
p[low] =flag;
returnlow;//最后循环结束low == high 此时的位置就是枢轴位置
}
voidshowArray(int*p, intn)
{
inti;
//从右向左进行扫描
for(i=0; i<n; i++)
{
printf("%d ",p[i]);
}
printf("\n");
}
//写一个递归函数,进行快排
voidquickSort(int*p, intlow,inthigh)
{
intpiovt=getPiovt(p,low,high);
if(low<piovt-1)//递归快排枢轴的左侧
quickSort(p,low,piovt-1);
if(piovt+1<high)//递归快排枢轴的右侧
quickSort(p,piovt+1,high);
}
int main(intargc, constchar*argv[])
{
//快排思想:先找到枢轴的位置,枢轴左侧的数都小于等于枢轴位置数据,右侧大于等于
inta[8] = {32,2,54,6,78,23,17,76};
showArray(a,8);
quickSort(a,0,7);
printf("快速排序之后---------------\n");
showArray(a,8);
return0;
}
查找算法
顺序查找
从头到尾遍历一遍,耗时长、效率低。 1、main函数中定义一个数组int a[10] = {12,34,45,23,54,2,4,65,23}; 2、定义一个函数,查找指定数据 3、如果找到了,返回它的位置,数组下标即可,未找到返回-1 4、main函数中测试
#include <stdio.h>
// p:保存数组首地址,n:元素个数,value:要查找的值
int findByOrder(int *p,int n,int value)
{
for(int i = 0; i < n; i++)
if(p[i] == value)
return i; //返回数组下标
return -1; //如果程序能执行到这肯定没找到
}
int main()
{
int a[10] = {12,34,54,23,12,3453,564,23,121,9};
int num;
int ret;
puts("Please input num:");
scanf("%d", &num);
ret = findByOrder(a, 10, num);
if(ret == -1)
printf("Not find %d\n",num);
else
printf("Find %d\n",a[ret]);
return 0;
}
二分法查找
分半查找、拆半查找
每次都找中间的数。
前提条件:数组中元素必须为(递增/递减)有序序列。
思想:每次进行分半,判断在middle的左边还是右边
#include <stdio.h>
// 二分查找 value代表的是被查找的值
int findByHalf(int *p, int n, int value)
{
int low = 0; //low低
int high = n-1; //high高
int middle; //用来保存中间位置的下标
while(low <= high) //注意此处循环结束的条件,需要加上 =
{
//不断获取中间位置的下标
middle = (low + high) / 2;
if(value < p[middle]) //说明在前半段,移动high
high = middle - 1;
else if(value > p[middle]) //说明在后半段,移动low
low = middle + 1;
else //对应p[middle] == value 情况
return middle;
}
return -1; //代表没有找到
}
int main(int argc, const char *argv[])
{
int a[] = {12,34,56,77,89,342,567,7898};
for(int i = 0; i < sizeof(a)/sizeof(a[0]); i++)//把数组中的每个元素都找一遍,进行测试程序
printf("%d post is %d\n",a[i],findByHalf(a,sizeof(a)/sizeof(a[0]),a[i]));
// 查找10000返回 -1
printf("%d post is %d\n",10000,findByHalf(a,sizeof(a)/sizeof(a[0]),10000));
return 0;
}
图
1概念
图(Graph)是一种非线性数据结构。
2术语
1有向图和无向图
2网: 弧边上有权值,带权值得图成为网
3顶点的度
4路径:路径上边的条数定义为该路径的长度
3特征
任意的两个元素都可能相关,即图中任一元素可以有若干个直接前驱和直接后继,属于网状结构类型。
4存储结构
4.1邻接矩阵(数组)
有向图和无向图
#include <stdio.h>
#include <string.h>
#define N 5
typedef struct
{
int V[N];
int R[N][N];
}adjmatrix_t;
int main(int argc, const char *argv[])
{
adjmatrix_t graph;
int i,j;
// 1.初始顶点集合
for(i = 0; i < N; i++)
graph.V[i] = i;
// 2.将关系集合清空置0,然后输入顶点之间的关系
bzero(graph.R,sizeof(graph.R));
printf("请您输入顶点关系:\n");
while(scanf("(V%d,V%d) ",&i,&j) == 2)//scanf函数,如果输入i j两个值成功,返回值为 2代表输入成功有两个,如果不成功为EOF
{
graph.R[i][j] = 1;//vi --> vj
graph.R[j][i] = 1;//vj --> vi 如何将此行代码注销,可用表示有向图
}
// 3.打印图
for(i = 0; i < N; i++)
{
printf("V%d:",graph.V[i]);
for(j = 0; j < N; j++)
{
if(graph.R[i][j] == 1)// == 1说明vi ---> vj
{
printf("V%d ",graph.V[j]);//将Vi 所有能到达的顶点Vj打印
}
}
printf("\n");
}
return 0;
}
4.2邻接表(链表)
有向图和无向图
#include <stdio.h>
#include <stdlib.h>
#define N 5 //5代表5个顶点
typedef struct node_t
{
int vertex;//保存顶点
struct node_t *next;//指向下一个节点的指针
}adjlist_node_t;
//画边函数,也就是将顶点vj插入到对应的链表vi中 draw 画 edge边
void drawEdge(adjlist_node_t *vi, int vj)//vi代表的是对应链表的头结点的指针
{//在插入的时候对其进行排序同昨天的霍夫曼数插入根据权值排序思想一样
adjlist_node_t *pnew = NULL;
while(vi->next != NULL && vj > vi->next->vertex)
vi = vi->next;
//创建新的节点保存vj
pnew = (adjlist_node_t *)malloc(sizeof(adjlist_node_t));
if(NULL == pnew)
{
perror("pnew malloc failed");
return;
}
//创建pnew后装东西
pnew->vertex = vj;
pnew->next = NULL;
//将节点链接到链表中,先连后面再连前面
pnew->next = vi->next;
vi->next = pnew;
return;
}
int main(int argc, const char *argv[])
{
int i,j;
adjlist_node_t *h = NULL;//用来保存每条链表的头指针(无头)
//1.创建一个结构体数组
adjlist_node_t g[N] = { 0 };
for(i = 0; i < N; i++)
g[i].vertex = i;
printf("请您输入顶点的关系:\n");
// 2.输入顶点之间的关系
while(scanf("(V%d,V%d) ",&i,&j) == 2)
{
//将vj这个顶点插入到vi顶点的链表中
drawEdge(g+i,j);//此行等价于 &g[i]
// drawEdge(g+j,i);//如果输入的想代表无向图,将此行代码解除注释
}
// 3.将各个顶点所有到达的顶点进行打印
for(i = 0; i < N; i++)
{
printf("V%d:",g[i].vertex);//g[i].vertex 代表的是Vi这个顶点
h = g[i].next;
//相当于遍历无头链表,需要对每一条链表进行遍历
while(h != NULL)
{
printf("V%d ",h->vertex);
h = h->next;
}
printf("\n");//遍历完一条链表后打印一个换行
}
printf("\n---------------\n");
return 0;
}
int u;
//这段代码实现将v顶点所有能到达顶点全部找一遍
u = firstAdj(g,v); //u此时为第一个邻接点
while(u != -1)
{
u = nextAdj(g,v,u);//每循环一次,就向下再继续找下一个邻接点
//知道u == -1 说明已经没有v能到达的点了
}
5遍历
5.1深度优先搜索
// 深度搜索 visited 拜访,访问 v 代表的是从哪个顶点开始深度搜索
//visited是一个指向整型数组的指针,用来标记这个顶点是否被访问
void DFS(adjmatrix_t *g, int v, int *visited)
{//visited[v] == 0代表没被访问, 1代表被访问
int u;//用来保存邻接点
if(visited[v] == 0)//说明该顶点没有被访问
{
printf("V%d ",g->V[v]);//相当于已经被访问了
visited[v] = 1;//将标志位置1,变为已经被访问,已经被打印输出
}
//获取v的第一个邻接点
u = firstAdj(g,v);
while(u != -1)//如果u == -1代表,没有邻接点
{
if(visited[u] == 0)//没有被访问
{
DFS(g,u,visited);
}
u = nextAdj(g,v,u);//获取下一个邻接点
}
}
5.2广度优先搜索
// 广度搜索
void BFS(adjmatrix_t *g, int v, int *visited)
{
int u;
//创建一个队列
linkqueue_t *p = createEmptyLinkQueue();
inLinkQueue(p,v);
while(!isEmptyLinkQueue(p))
{
v = outLinkQueue(p);
if(visited[v] == 0)//说明v未被访问
{
printf("V%d ",g->V[v]);
visited[v] = 1;//打印之后标记为已经访问
}
//开始遍历v所有能达到的顶点
u = firstAdj(g,v);
while(u != -1)
{
if(visited[u] == 0)//v到达的顶点没有被访问
{
inLinkQueue(p,u);
}
u = nextAdj(g,v,u);//在上面入列的u基础上,在找到下一个邻接点返回
}
}
}
#include "graph.h"
#include "linkqueue.h"
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
// 1.创建一个图
adjmatrix_t *createGraph()
{
int i,j;
adjmatrix_t *g = (adjmatrix_t *)malloc(sizeof(adjmatrix_t));
if(NULL == g)
{
perror("createGraph malloc failed");
return NULL;
}
//2.初始化顶点集合
for(i = 0; i < N; i++)
g->V[i] = i;
//清空下关系结合
bzero(g->R,sizeof(g->R));
printf("请您输入顶点的关系(V0,V1):\n");
while(scanf("(V%d,V%d) ",&i,&j) == 2)
{
g->R[i][j] = 1;
g->R[j][i] = 1;
}
return g;
}
// 2.获取第一个邻接点 v是被搜索的顶点
int firstAdj(adjmatrix_t *g,int v)//v == 0,找v0第一个邻接点
{
int j;
for(j = 0; j < N; j++)
{
if(g->R[v][j] == 1)
return j;
}
return -1;//代表没有邻接点
}
//int u;//保存v的第一个邻接点
//u = fristAdj(g,0);//得到第一个
//u =====> 1
// u = nextAdj(g,0,u);//下一个
// u = nextAdj(g,0,u);//再下一个
// 3.获取下一个邻接点 v被搜索的顶点 u 前一个邻接点
int nextAdj(adjmatrix_t *g, int v, int u)
{
//获取下一个邻接点函数的使用,需要fristAdj函数的配合
int j;
for(j = u+1; j < N; j++)//j = u+1 是因为第一个邻节点是u,那么继续找下一个从u+1位置开始遍历
{
if(g->R[v][j] == 1)
return j;
}
return -1;
}
// 4.深度搜索 visited 拜访,访问 v代表的是从那个顶点开始深度搜索
//visited是一个指向整型数组的指针,用来标记这个节点是否被访问
void DFS(adjmatrix_t *g, int v, int *visited)
{//visited[v] == 0代表没被访问, 1代表被访问
int u;//用来保存邻接点
if(visited[v] == 0)//说明该顶点没有被访问
{
printf("V%d ",g->V[v]);//相当于已经被访问了
visited[v] = 1;//将标志位置1,变为已经被访问,已经被打印输出
}
//获取v的第一个邻接点
u = firstAdj(g,v);
while(u != -1)//如果u == -1代表,没有邻接点
{
if(visited[u] == 0)//没有被访问
{
DFS(g,u,visited);
}
u = nextAdj(g,v,u);//获取下一个邻接点
}
}
// 5.广度搜索
void BFS(adjmatrix_t *g, int v, int *visited)
{
int u;
//创建一个队列
linkqueue_t *p = createEmptyLinkQueue();
inLinkQueue(p,v);
while(!isEmptyLinkQueue(p))
{
v = outLinkQueue(p);
if(visited[v] == 0)//说明v未被访问
{
printf("V%d ",g->V[v]);
visited[v] = 1;//打印之后标记为已经访问
}
//开始遍历v所有能达到的顶点
u = firstAdj(g,v);
while(u != -1)
{
if(visited[u] == 0)//v到达的顶点没有被访问
{
inLinkQueue(p,u);
}
u = nextAdj(g,v,u);//在上面入列的u基础上,在找到下一个邻接点返回
}
}
}