数据结构中树、图、排序、查找源代码复现(参考)
数据结构中树、图、排序、查找代码参考
写在前面
头文件定义
#include <stdio.h>
#include <malloc.h>
#define OK 1
#define ERROR 0
typedef int Status;
typedef char TElemType;
一、树
1.1 结构体定义
typedef struct BiTNode
{
TElemType data;
//结点结构
//数据域
struct BiTNode *lchild,*rchild; //左孩子域和右孩子域
}BiTNode, *BiTree;
1.2 一些操作的实现
(1)由先根遍历序列(‘#’号表空)生成二叉树 CreateBiTree
Status CreateBiTree(BiTree &T)
{
// 由标明空子树的完整先根遍历序列建立一棵二叉树
char ch;
scanf("%c",&ch);
if(ch=='#')
T=NULL; //"#"字符表示空树
else
{
T=(BiTree)malloc(sizeof(BiTNode));
T->data=ch; //生成根结点
CreateBiTree(T->lchild); //构造左子树CreateBiTree(T->rchild);
CreateBiTree(T->rchild); //构造右子树
}
return OK;
}
(2)遍历递归算法 Traverse
void PreRootTraverse(BiTree T) //先根遍历二叉树 T 的递归算法
{
if(T)
{
printf("%c",T->data); //访问根结点
PreRootTraverse(T->lchild); //先根遍历左子树
PreRootTraverse(T->rchild); //先根遍历右子树
}
}
void InRootTraverse(BiTree T)
//中根遍历二叉树 T 的递归算法
{
if(T)
{
InRootTraverse(T->lchild); //中根遍历左子树
printf("%c",T->data); //访问根结点
InRootTraverse(T->rchild); //中根遍历右子树
}
}
void PostRootTraverse(BiTree T)
//后根遍历二叉树 T 的递归算法
{
if(T)
{
PostRootTraverse(T->lchild); //后根遍历左子树
PostRootTraverse(T->rchild); //后根遍历右子树
printf("%c",T->data); //访问根结点
}
}
(3)由先序序列与中序序列生成一颗树PICreateBiTree
BiTree PICreateBiTree(char PreOrder[],char InOrder[],int PreIndex,int InIndex,int count)
//已知一棵二叉树的先根遍历和中根遍历序列,建立这棵二叉树
{
BiTree T;
if(count>0) //先根和中根非空,count 表示二叉树中结点数
{
char r=PreOrder[PreIndex]; //取先根遍历序列中的第一个元素
int i=0;
for(;i<count;i++) //寻找根结点在中根遍历序列中的位置
{
if(r==InOrder[i+InIndex])
break;
}
T=(BiTree)malloc(sizeof(BiTNode));
T->data=r; //根结点r赋值
T->lchild=PICreateBiTree(PreOrder,InOrder,PreIndex+1,InIndex,i);
//i是中根遍历查找到的位置,T的左子树的元素序列是根节点左子树中根遍历序列,对应先序下标+1,中序不变,左子树节点数是i
T->rchild=PICreateBiTree(PreOrder,InOrder,PreIndex+i+1,InIndex+i+1,count-i-1);
//T的右子树序列是根节点右子树中跟遍历序列,先序下标 +i+1,中序+i+1,右子树节点树是N-(i+1)
}
else
T=NULL;
return T;
}
这里说一下关键点:根节点是通过先序序列访问,左子树,右子树通过中序序列中的根节点前后位置来确定的。
读者可以自行想一下中序和后续如何确定一颗二叉树
请注意,只有先序和中序遍历是无法确定一颗二叉树的。
(4)通过遍历可以实现的操作(以先根遍历为例)
1.复制一棵树T2 CopyTree
int CopyTree(BiTree T1,BiTree &T2)
//复制操作
//已知一棵二叉树 T1,T2 是由 T1 复制而得
{
if(T1)
{
//为根结点 T2 分配空间
T2 = (BiTree )malloc(sizeof(BiTNode));
if (!T2)
//空间分配失败
return 0;
T2->data=T1->data;
//根结点 T2 的数据域值与根结点 T1 相等
CopyTree(T1->lchild,T2->lchild);
//复制左子树
CopyTree(T1->rchild,T2->rchild);
//复制右子树
}
else
T2=NULL;
return 1;
}
2.判断两颗树是否相等 PreCompare
int PreCompare(BiTree T1, BiTree T2)
//比较操作
//用先根遍历方法判断两棵二叉树是否相等
{
if (!T1&&!T2)
return 1;
if (T1&&T2)
if (T1->data==T2->data)
if (PreCompare(T1->lchild,T2->lchild))
if(PreCompare(T1->rchild,T2->rchild))
return 1;
return 0;
}
3.二叉树深度Depth
int depth(BiTree T)
//采用后根遍历对二叉树遍历,遍历过程中求得二叉树的深度
{
int depthLeft,depthRight,depthval;
if(T!=NULL)
{
depthLeft=Depth(T->lchild);
depthRight=Depth(T->rchild);
depthval=1+(depthLeft>depthRight?depthLeft:depthRight);
}
else
{
depthval=0;
}
return depthval;
}
4.统计(叶子)结点个数
void CountLeaf(BiTree T,int &num)
{
if(T!=NULL)
{
if(!T->lchild&&!T->rchild){ //满足叶子节点度为0的条件
num++;
}
CountLeaf(T->lchild,num); //查找左子树的满足条件的节点
CountLeaf(T->rchild,num); //查找右子树满足条件节点
}
}
二、图
2.1 结构体构造
2.1.1 邻接矩阵(注意图和网的区别)
const int maxn=105;
int adj[maxn][maxn]={0}; //定义邻接矩阵
2.1.2 邻接表
//边
typedef struct arcnode
{
int adjvex; //邻接顶点的位置序号值
struct arcnode *nextarc; //指向下一条边的指针
} arcnode; //边的结点类型
//顶点
typedef struct vexnode
{
arcnode *firstarc; //指向与该顶点相关联的第一条边的指针
int tag; //顶点的访问标志
} vexnode; //顶点类型
typedef struct
{
vexnode adjlist[MAX_VERTEX_NUM]; //存储顶点信息的数组
int vexnum, arcnum; //顶点数和边数
} Agraph; //用邻接表表示的图类型
2.1.3 前向星
struct Edge
{
int to, w, next; //终点,边权,同起点的上一条边的编号
} edge[maxn]; //边集
int head[maxn]; // head[i],表示以i为起点的第一条边在边集数组的位置(编号)
2.2 一些操作(参考)
关键在于应用,这里仅给出思路模板。
(1)DFS搜索
void dfs()//参数用来表示状态
{
if(到达终点状态)
{
...//根据需求添加
return;
}
if(越界或者是不合法状态)
return;
if(特殊状态)//剪枝,去除一些不需要访问的场景,不一定i俺家
return ;
for(扩展方式)
{
if(扩展方式所达到状态合法)
{
修改操作;//根据题意来添加
标记;
dfs();
(还原标记);
//是否还原标记根据题意
//如果加上(还原标记)就是 回溯法
}
}
}
(2)BFS搜索
/**
* 返回合适的检索数据
*/
int BFS(Node root, Node target)
{
Queue<Node> queue; //创建队列
int step = 0; // 当前队列的步骤点
// initialize
add root to queue;
// BFS
while (queue is not empty)
{
step = step + 1;
//步数逐渐增加
int size = queue.size();
for (int i = 0; i < size; ++i)
{
Node cur = the first node in queue;
if cur is target
return step - 1;
for (Node next : the neighbors of cur)
{//这里常用一个二维方向数组实现
add next to queue;
}
remove the first node from queue;
}
}
return -1; //出错返回值
}
(3)Prim算法
#include <stdio.h>
#include <stdlib.h>
#define n 20
#define MaxNum 10000 /*定义一个最大整数*/
/*定义邻接矩阵类型*/
typedef int adjmatrix[n + 1][n + 1];
typedef struct {
int fromvex, tovex; //生成树的起点和终点
int weight; //边的权重
} Edge;
typedef Edge *EdgeNode; //定义生成树的别名
int arcnum; /*边的个数*/
/*建立图的邻接矩阵*/
void CreatMatrix(adjmatrix GA) {
int i, j, k, e;
printf("=============================\n");
printf("图中有%d个顶点\n", n);
for(i=1; i<=n; i++) {
for(j=1; j<=n; j++) {
if(i==j) {
GA[i][j]=0; /*对角线的值置为0*/
} else {
GA[i][j]=MaxNum; /*其他位置的值置初始化为一个最大整数*/
}
}
}
printf("请输入边的个数:\n");
scanf("%d", &arcnum);
printf("请输入边的信息,依照起点,终点,权值的形式输入:\n");
for(k=1; k<=arcnum; k++) {
scanf("%d,%d,%d",&i,&j,&e); /*读入边的信息*/
GA[i][j]=e;
GA[j][i]=e;
}
}
/*初始化图的边集数组*/
void InitEdge(EdgeNode GE,int m) {
int i;
for(i=1; i<=m; i++) {
GE[i].weight=0;
}
}
/*依据图的邻接矩阵生成图的边集数组*/
void GetEdgeSet(adjmatrix GA,EdgeNode GE) {
int i, j, k = 1;
for(i=1; i<=n; i++) {
for(j=i+1; j<=n; j++) {
if(GA[i][j] !=0 && GA[i][j] != MaxNum) {
GE[k].fromvex = i;
GE[k].tovex = j;
GE[k].weight = GA[i][j];
k++;
}
}
}
}
/*按升序排列图的边集数组*/
void SortEdge(EdgeNode GE,int m) {
int i,j,k;
Edge temp;
for(i=1; i<m; i++) {
k=i;
for(j=i+1; j<=m; j++) {
if(GE[k].weight > GE[j].weight) {
k=j;
}
}
if(k!=i) {
temp = GE[i];
GE[i]=GE[k];
GE[k]=temp;
}
}
}
/*利用普里姆算法从初始点v出发求邻接矩阵表示的图的最小生成树*/
void Prim(adjmatrix GA,EdgeNode T) {
int i,j,k,min,u,m,w;
Edge temp;
/*给T赋初值。相应为v1依次到其余各顶点的边*/
k=1;
for(i=1; i<=n; i++) {
if(i!=1) {
T[k].fromvex=1;
T[k].tovex=i;
T[k].weight=GA[1][i];
k++;
}
}
/*进行n-1次循环,每次求出最小生成树中的第k条边*/
for(k=1; k<n; k++) {
min=MaxNum;
m=k;
for(j=k; j<n; j++) {
if(T[j].weight<min) {
min=T[j].weight;
m=j;
}
}
/*把最短边对调到k-1下标位置*/ 可用swap替换
temp=T[k];
T[k]=T[m];
T[m]=temp;
/*把新增加最小生成树T中的顶点序号赋给j*/
j=T[k].tovex;
/*改动有关边,使T中到T外的每个顶点保持一条到眼下为止最短的边*/
for(i=k+1; i<n; i++) {
u=T[i].tovex;
w=GA[j][u];
if(w<T[i].weight) {
T[i].weight=w;
T[i].fromvex=j;
}
}
}
}
/*输出边集数组的每条边*/
void OutEdge(EdgeNode GE,int e) {
int i;
printf("依照起点,终点。权值的形式输出的最小生成树为:\n");
for(i=1; i<=e; i++) {
printf("%d,%d,%d\n",GE[i].fromvex,GE[i].tovex,GE[i].weight);
}
printf("=============================\n");
}
int main() {
adjmatrix GA;
Edge GE[n*(n-1)/2], T[n];
CreatMatrix(GA);
InitEdge(GE,arcnum);
GetEdgeSet(GA,GE);
SortEdge(GE,arcnum);
Prim(GA,T);
printf("\n");
OutEdge(T,n-1);
return 0;
}
(4)Kruskal算法
#include<stdio.h>
#define MAXEDGE 100
#define MAXVERTEX 100
typedef struct Edge {
int begin;//边的起点
int end; //边的终点
int wight;//边的权值
} Edge;
typedef struct Graph {
char vertex[MAXVERTEX];//顶点
Edge edges[MAXEDGE];//边
int numvertex,numedges;//顶点和边的个数
} MGraph;
void CreateGraph(MGraph* G) {
printf("请输入顶点和边的个数:\n");
scanf("%d%d", &G->numvertex, &G->numedges);
printf("请输入顶点:\n");
getchar();//利用该函数除去上一系我们在输入结束时按得回车符
for (int i = 0; i < G->numvertex; i++) {
scanf("%c", &G->vertex[i]);
}
printf("按权值从小到大输入边(vi,vj)对应的起点和终点的下标,begin,end以及权值wight:\n");
for (int k = 0; k < G->numedges; k++) {
Edge e;
scanf("%d%d%d", &e.begin, &e.end, &e.wight);
G->edges[k] = e;
}
}
int Find(int *parent, int f) {
while (parent[f]>0) {
f = parent[f];
}
return f;
}
//最小生成树,克鲁斯卡尔算法
void Kruskal(MGraph *G) {
int parent[MAXVERTEX];//存放最小生成树的顶点
for (int i = 0; i < G->numvertex; i++) {
parent[i] = 0;
}
int m, n;
for (int i = 0; i < G->numedges; i++) {
n = Find(parent, G->edges[i].begin);
m = Find(parent, G->edges[i].end);
if (n != m) { //m=n说明有环
parent[n] = m;
printf("(%d,%d) %d\t", G->edges[i].begin, G->edges[i].end, G->edges[i].wight);//打印边和权值
}
}
}
int main() {
MGraph G;
CreateGraph(&G);
Kruskal(&G);
return 0;
}
三、排序
3.1插入排序
void Sort_Insert(SqList &L)
//带监视哨的直接插入排序算法
{
int i, j;
for (i = 2; i <= L.length; ++i)
if (L.r[i].key < L.r[i - 1].key) //将L.r[i]插入它前面的有序子表
{
L.r[0] = L.r[i]; //将待插入的第i条记录暂存在r[0]中,同时r[0]为监视哨
L.r[i] = L.r[i - 1]; // 将前面的较大者L.r[i-1]后移
for (j = i - 2; L.r[0].key < L.r[j].key; --j)
L.r[j + 1] = L.r[j]; //后移
L.r[j + 1] = L.r[0]; // 将L.r[i]插入第j+1个位置
} // if
}
3.2冒泡选择排序(交换)
void Sort_Bubble(SqList &L)
//对顺序表L进行冒泡排序
{
int i, j, change;
RedType temp;
change = TRUE; //设置交换标志变量,初值为真
for (i = L.length; i > 1 && change; --i) //控制做n-1趟排序
{
change = FALSE; //每趟排序开始时设置交换标志变量值为假
for (j = 1; j < i; ++j)
if (L.r[j].key > L.r[j + 1].key)
{
temp = L.r[j];
L.r[j] = L.r[j + 1];
L.r[j + 1] = temp;
change = TRUE;
} // if
} // for i
}
void Sort_Select(SqList &L)
//对表L进行直接选择排序
{
for (int i = 1; i < L.length; ++i) //控制n-1趟排
{
int min = i; //假设无序子表中的第一条记录的关键字最小
for (int j = i + 1; j <= L.length; ++j)
if (L.r[j].key < L.r[min].key)
min = j;
if (min != i) //如果最小关键字记录不在无序子表的第一个位置,则交换
{
RedType temp = L.r[i];
L.r[i] = L.r[min];
L.r[min] = temp;
} // if
} // for i
}
3.3希尔排序 Sort_Shell
void Sort_Shell(SqList &L, int dk)
//对表L做一趟希尔排序,增量为dk
{
int i, j;
for (i = 1 + dk; i <= L.length; i++)
if (L.r[i].key < L.r[i - dk].key) //将 L.r[i]插入有序增量子表
{
L.r[0] = L.r[i]; //将待插入的第i个记录暂存在r[0]中,同时r[0]为监视哨
L.r[i] = L.r[i - dk];
for (j = i - 2 * dk; j > 0 && L.r[0].key < L.r[j].key; j -= dk)
L.r[j + dk] = L.r[j]; // 将前面的较大者L.r[j+dk]后移
L.r[j + dk] = L.r[0]; // 将L.r[i]插入第j+dk个位置
}
}
void Sort_Shell(SqList &L, int dlta[], int t)
// 按照增量dlta[0..t-1]对L做希尔排序
{
for (int k = 0; k < t; ++k)
Sort_Shell(L, dlta[k]); //完成一趟增量为dlta[k]的希尔排序
}
3.4快速排序Partition
int Partition(SqList &L, int low, int high)
//对L中的子表L.r[low..high]做一趟快速排序
{
L.r[0] = L.r[low]; //将无序区低下标的记录设置为枢轴,并暂存在r[0]中
KeyType privotkey = L.r[low].key; //将枢轴记录的关键字暂存在变量pivotkey中
while (low < high) // 当low==high时,结束本趟排序
{
while (low < high && L.r[high].key >= privotkey) //向前搜索
--high;
if (low < high)
L.r[low++] = L.r[high]; //将比枢轴小的记录移至低端low的位置 ,然后low后移一位
while (low < high && L.r[low].key <= privotkey) //向后搜索
++low;
if (low < high)
L.r[high--] = L.r[low]; //将比枢轴小的记录移至低端low的位置 ,然后high前移一位
}
L.r[low] = L.r[0]; //枢轴记录移至最后位置
return low; //返回枢轴所在的位置
} //算法7-5结束
void Q_Sort(SqList & L, int low, int high)
//对表r[low..high]采用递归形式的快速排序算法
{
if (low < high) //如果无序表长大于1
{
int pivotloc = Partition(L, low, high); //完成一次划分,确定枢轴位置
Q_Sort(L, low, pivotloc - 1); //递归调用,完成左子表的排序
Q_Sort(L, pivotloc + 1, high); //递归调用,完成右子表的排序
}
} //算法7-6结束
void Sort_Quick(SqList & L)
{
Q_Sort(L, 1, L.length);
} //算法7-7结束
(5) 归并排序Merge
void Merge(RedType SR[], RedType TR[], int i, int m, int n)
//将两个相邻有序表SR[i .. m] 与SR[m+1.. n]归并为有序表TR[i .. n]
{
int j = m + 1, k = i;
while (i <= m && j <= n) // 将SR中两个相邻有就有序子表由小到大并入TR中
{
if (SR[i].key <= SR[j].key)
TR[k++] = SR[i++];
else
TR[k++] = SR[j++];
}
while (i <= m) //将前一有序子表的剩余部分复制到TR
TR[k++] = SR[i++];
while (j <= n) //将后一有序子表的剩余部分复制到TR
TR[k++] = SR[j++];
} //算法7-10
void M_Sort(RedType SR[], RedType TR1[], int s, int t)
// 将SR[s..t]归并排序为TR[s..t]
{
RedType TR2[MAXSIZE + 1];
if (s == t)
TR1[s] = SR[s];
else
{ //待排序的记录序列只含一条记录
int m = (s + t) / 2; // 以m为分界点,将无序表分成前、后两部分
M_Sort(SR, TR2, s, m); //对前部分递归归并为有序子表TR2[s..m]
M_Sort(SR, TR2, m + 1, t); //对后部分递归归并为有序子表TR2[m+1..t]
Merge(TR2, TR1, s, m, t); //将TR2[s..m]与TR2[s..m]归并成有序表TR1[s..t]
}
} //算法7-11
四、查找
4.1 结构体构造
typedef struct
{
KeyType key; //关键字域
InfoType otherinfo; // 其它数据项
} ElemType; //记录类型
typedef struct
{
ElemType *elem; //记录存储空间基地址,构造表时按实际长度分配,0号单元留空
int length; //表长度
} SSTable; //静态查找表类型
4.2 一些操作
(1)顺序查找Search_Seq
int Search_Seq(SSTable ST, KeyType key) //顺序查找函数
// 采用带监视哨的顺序查找方法在查找表的n个记录中查找出关键字值为key的记录
//若查找成功,则返回其下标位置;否则,返回0
{
int i;
ST.elem[0].key = key; //"哨兵"
for (i = ST.length; ST.elem[i].key != key; i--)
; //从后往前进行查找
return i; //若找到,则返回下标位置;若没有找到,则返回0
} // Search_Seq
(2)二分查找 Search_Bin
int Search_Bin(SSTable ST, KeyType key) //二分查找函数
// 采用二分查找方法在有序表的n个记录中查找出关键字值为key的记录
//若查找成功,则返回其下标位置;否则,返回0
{
int low = 1; //查找范围的下界
int high = ST.length; //查找范围的上界
while (low <= high)
{
int mid = (low + high) / 2; //中间位置,当前比较的记录位置
if (key == ST.elem[mid].key)
return mid; //查找成功,返回下标位置
else if (key < ST.elem[mid].key)
high = mid - 1; //查找范围缩小到前半段
else
low = mid + 1; //查找范围缩小到后半段
}
return 0;
} // Search_Bin
(3)分块查找(索引)
源代码
#include<stdio.h>
#include<malloc.h>
#define MAXSIZE 100
#define OK 1
#define OVERFLOW -1
typedef int Status;
typedef int KeyType; //整型关键字类型
typedef struct BNode
{
KeyType key; //存储块中记录的关键字值
struct BNode *next; //指向块链中下一记录结点的指针
} BNode; //块链中的结点类型
typedef struct SNode
{
KeyType Maxkey; //存储各块记录中的最大关键字值
BNode *head; //块链的头指针
} SNode; //索引表中元素的类型
typedef struct
{
SNode r[MAXSIZE]; //用于存放索引表中快元素的数组
int length; //用于存放索引表中快的个数
} STable; //索引查找表的结构类型
Status Creat_STable(STable &ST) //构造一个空的索引表
{
for (int i = 1; i <= MAXSIZE; i++)
ST.r[i].head = NULL;
ST.length = 0;
return OK;
} // Creat_SSTable
int FindBlock(STable ST, KeyType key)
//用二分查找法在索引查找表ST的索引表中确定关键字值为key的待查找记录所在的块号,
//函数并返回值其块号值
{
int low, high, mid;
low = 1;
high = ST.length;
while (low <= high)
{
mid = (low + high) / 2;
if (key == ST.r[mid].Maxkey)
return mid;
else if (key < ST.r[mid].Maxkey)
high = mid - 1;
else
low = mid + 1;
}
return high + 1;
} // FindBlock
void FindRec(STable ST, KeyType key, int &Bno, int &pos)
//用顺序查找法在索引查找表的块链中确定关键字值为key的待查找记录的存储位置
//如果查找成功,记录所在的快号和在快中的位序号分别为Bno和pos,否则都为0
{
BNode *p;
int i = 1;
Bno = FindBlock(ST, key); //调用函数,确定待查记录所在块号
p = ST.r[Bno].head; //取到待查记录所在块链的头指针
while (p && p->key != key) //顺着链指针依次查找
{
p = p->next;
i++;
}
if (p)
pos = i; //查找成功,在快中的位置为i
else
pos = 0; //查找不成功,在快中的位置为0
}
int main()
{
char ch;
int Bno=0,pos=0,n;
STable ST;
KeyType key;
Creat_STable(ST);
printf("兄弟,索引表需要多少长?\n");
scanf("%d",&ST.length);
getchar();
for(int i=1;i<=ST.length;i++){
printf("现在输入第%d列的数据!(注意大于前一列的最大值)\n",i);
ST.r[i].Maxkey=-999999;
ST.r[i].head=(BNode*)malloc(sizeof(BNode));
BNode *p=ST.r[i].head;
p->key=ST.r[i].Maxkey;
do{
p->next=(BNode*)malloc(sizeof(BNode));
scanf("%d",&p->next->key);
p=p->next;
ST.r[i].Maxkey=p->key>ST.r[i].Maxkey?p->key:ST.r[i].Maxkey;
}while((ch=getchar())!='\n');
}
//debug
printf("可以,你输完了所有数据。现在的情况是:\n");
for(int i=0;i<ST.length;i++){
printf("第%d个索引表的最大长度:%d\n",i+1,ST.r[i+1].Maxkey);
}
while(true){
Bno=0;pos=0;
printf("请输入你想查找的关键字(输入-1以结束)\n");
scanf("%d",&key);
if(key==-1){
printf("bye!\n");
break;
}
FindRec(ST,key,Bno,pos);
if(Bno==0||pos==0){
printf("查找失败!!\n");
}
else{
printf("他在索引表的第%d块的第%d个位置,下标为%d\n",Bno,pos,pos-1);
}
}
return 0;
}