树和二叉树
二叉树
2.二叉树的基本操作(6学时):
(1)采用二叉链表结构建立二叉树;
(2)编程实现二叉树的先序、中序、后序和层序遍历;
(3 ) 编程实现非递归中序遍历
(4)编程实现:求二叉树的高度和叶子结点个数;
#include <iostream>
using namespace std;
typedef char elemType;
//树 结点
struct BitNode
{
elemType data;//结点数据域
BitNode* lChild;
BitNode* rChild;//左右孩子指针
};
typedef BitNode* BitTree;
// 队结点
struct QueueNode
{
BitNode* data;
QueueNode* next;
};
//队列
struct LinkQueue
{
QueueNode* rear;
QueueNode* front;
};
//初始化一个队
void initial(LinkQueue&);
//入队
void enLinkQueue(BitNode&, LinkQueue& );
//出队
BitTree deLinkQueue(LinkQueue& Q);
//栈
struct StackNode
{
BitNode* data;
StackNode* next;
};
typedef StackNode* Stack;
//入栈
void push(Stack&);
//出栈
BitTree pop(Stack&);
//得到栈顶元素
BitTree gettop(Stack&);
//利用 扩展先序遍历序列 创建二叉树
void creat(BitTree &bit);
//先序遍历
void preOderTraverse(BitTree& bit);
//中序遍历
void inOderTraverse(BitTree& bit);
//后序遍历
void postOderTraverse(BitTree& bit);
//层次遍历
void layerTraverse(BitTree& bit);
//中序非递归遍历
void nonrecursiveInOderTraverse(BitTree& bit);
//求树的深度
int treeHeight(BitTree& bit);
//求树的叶子结点个数
int treeLeafSize(BitTree& bit);
int main()
{
BitTree bit;
bit = new BitNode;
cout << "请输入 扩展先序遍历序列:" << endl;
creat(bit);
cout << "先序遍历" << endl;
preOderTraverse(bit);
cout << endl;
cout << "中序遍历" << endl;
inOderTraverse(bit);
cout << endl;
cout << "后序遍历" << endl;
postOderTraverse(bit);
cout << endl;
cout << "层次遍历" << endl;
layerTraverse(bit);
cout << endl;
cout << "非递归中序遍历" << endl;
nonrecursiveInOderTraverse(bit);
cout << endl;
cout << "树的深度" << endl;
cout<<treeHeight(bit);
cout << endl;
cout << "树的叶子结点数" << endl;
cout << treeLeafSize(bit);
cout << endl;
}
void creat(BitTree& bit) {
char ch;
ch = getchar();
if (ch == '#') bit = NULL;
else {
bit = new BitNode;
bit->data = ch;
creat(bit->lChild);
creat(bit->rChild);
}
}
void preOderTraverse(BitTree& bit) {
if (bit) {
//如果二叉树非空
cout << bit->data;
preOderTraverse(bit->lChild);
preOderTraverse(bit->rChild);
}
}
void inOderTraverse(BitTree& bit) {
if (bit) {
//如果二叉树非空
inOderTraverse(bit->lChild);
cout << bit->data;
inOderTraverse(bit->rChild);
}
}
void postOderTraverse(BitTree& bit) {
if (bit) {
//如果二叉树非空
postOderTraverse(bit->lChild);
postOderTraverse(bit->rChild);
cout << bit->data;
}
}
void initial(LinkQueue& Q) {
//构造一个空队列Q
//生成新的结点作为头节点,队列的头指针和尾指针都指向该结点
Q.front = Q.rear = new QueueNode;
//头节点的指针域置为空
Q.front->next = NULL;
}
void enLinkQueue(BitTree& bit, LinkQueue& Q) {
//为入队元素分配结点空间,用指针p指向
QueueNode* p = new QueueNode;
p->data = bit; //将新结点数据置为树结点
p->next = NULL;
Q.rear->next = p;//将新结点插入到队尾
Q.rear = p;//修改队尾指针
}
BitTree deLinkQueue( LinkQueue& Q) {
//返回队头的树结点
BitNode* e;
//是否队空
if (Q.rear == Q.front) return NULL;
//临时保存队头元素空间以备释放
//p 指向队头元素
QueueNode* p;
p = Q.front->next;
//e 保存队头元素的值
e = p->data;
//修改头指针的指针域,指向下一个结点
Q.front->next = p->next;
//如果最后一个元素被删,队尾指针指向头节点
if (Q.rear == p) Q.rear = Q.front;
delete p;//释放原队头元素的空间
return e;
}
void layerTraverse(BitTree& bit) {
if (bit) {
//如果二叉树非空
//新建并初始化队列
LinkQueue Q;
initial(Q);
//访问根节点
cout << bit->data;
//如果存在左孩子就把左孩子入队
if (bit->lChild) enLinkQueue(bit->lChild, Q);
//如果存在右孩子就把右孩子入队
if (bit->rChild) enLinkQueue(bit->rChild, Q);
//出队一个结点 //如果当前结点不为空
BitTree b;
while (b=deLinkQueue(Q))
{
//访问当前结点
cout << b->data;
//如果存在左孩子就把左孩子入队
if (b->lChild) enLinkQueue(b->lChild, Q);
//如果存在右孩子就把右孩子入队
if (b->rChild) enLinkQueue(b->rChild, Q);
}
}
}
void push(Stack& S,BitTree& bit) {
Stack p = new StackNode;//生成一个新结点
p->data = bit;//将新结点数据域置为e
p->next = S;//将新节点插入栈顶
S = p;//修改栈顶指针为p
}
BitTree pop(Stack& S) {
BitTree b;
b = S->data; //将栈顶元素赋给b
Stack p = S; //临时保存栈顶元素的空间以备释放
S = p->next; //修改栈顶指针,指向新的栈顶元素
delete p; //释放原栈顶元素的空间
return b;
}
BitTree gettop(Stack& S) {
BitTree b;
b = S->data; //将栈顶元素赋给b
return b;
}
void nonrecursiveInOderTraverse(BitTree& bit) {
//新建栈,根指针入栈
Stack S = NULL;
push(S, bit);
BitTree b;
//当栈非空时
while (S)
{
//当栈顶非空时,把栈顶的左孩子入栈 (结束的时候栈顶为空)
while (b=gettop(S))
{
push(S, b->lChild);
}
//把栈顶的空指针出栈
b=pop(S);
if (S) {//如果栈非空
//栈顶出栈并访问
b = pop(S);
cout << b->data;
//栈顶的右孩子入栈
push(S, b->rChild);
}
}
}
int treeHeight(BitTree& bit) {
if (bit == NULL)
{
//空树
return 0;
}
if (bit->lChild == NULL && bit->rChild == NULL)
{
return 1;
}
int lHeight = treeHeight(bit->lChild);
int rHeight = treeHeight(bit->rChild);
//左右子树谁的高度高就返回谁的值 +1是当前结点的一层
return 1 + (lHeight > rHeight ? lHeight : rHeight);
}
int treeLeafSize(BitTree& bit) {
if (bit) {
//如果二叉树非空
if ((bit->lChild == NULL) && (bit->rChild == NULL)) {
return 1;
}
return treeLeafSize(bit->lChild) + treeLeafSize(bit->rChild);
}
else {
return 0;
}
}
哈夫曼树编码和译码
- 创建 先把所有叶子节点初始化,存进一个哈夫曼树数组,然后选择两个权值最小的子树合并成新树加入数组(总结点数2n-1)
- 编码 (从叶子结点起,向上找父母,如果是父母的左孩子就编码0,反之编码1,编码串的设置很有借鉴意义,利用start和length
- 译码 (由编码从根节点开始0向左1向右遍历,如果是叶子结点就输出,然后重新从根节点遍历
#include <iostream>
using namespace std;
#define MAXVALUE 2000 // 最大权值
//#define MAXBIT 20 // 哈夫曼编码最大程度
//哈夫曼树
struct Htreetype
{
char ch;
int weight;
int parent;
int Lchild, Rchild;
};
//哈夫曼编码串
struct Hcodetype
{
char bit[100];// 位串
int start; // 编码在位串中的起始位置
char ch;
};
// 选择权值最小的结点
void select(Htreetype t[], int tlength, int* p1, int* p2)
{
*p1 = *p2 = 0;
int small1, small2;
small1 = small2 = MAXVALUE;
for (int i = 0 ; i < tlength; i++)
{
if (t[i].parent == -1)
{
if (t[i].weight < small1)
{
small2 = small1;
small1 = t[i].weight;
*p2 = *p1;
*p1 = i;
}
else if (t[i].weight < small2)
{
small2 = t[i].weight;
*p2 = i;
}
}
}
}
//创建哈夫曼树
void HuffmanTree(Htreetype t[], int tlength,int length, char chs[], int weights[]) // 构造哈夫曼树数组
{
int p1, p2;
p1 = p2 = 0;
//初始化 哈夫曼树
for (int i = 0; i < length; i++)
{
t[i].Lchild = -1;
t[i].parent = -1;
t[i].Rchild = -1;
t[i].ch = chs[i];
t[i].weight = weights[i];
}
// 构造哈夫曼树
for (int i = length; i < tlength; i++)
{
select(t, i, &p1, &p2);
t[p1].parent = i;
t[p2].parent = i;
t[i].Lchild = p1;
t[i].Rchild = p2;
t[i].weight = t[p1].weight + t[p2].weight;
t[i].parent = -1;
}
}
//创建哈夫曼编码
void HuffmanCode(Hcodetype code[], Htreetype t[], int tlength, int length, char chs[], int weights[])
{
int i, c, p;
Hcodetype cd; // 缓冲变量,暂时存储
HuffmanTree(t,tlength,length,chs,weights);
for (i = 0; i < length; i++)
{
cd.start = length;
cd.ch = t[i].ch;
c = i; // 从叶子结点向上
p = t[i].parent; // t[p]是t[i]的双亲
while (p != -1)
{
cd.start--;
if (c == t[p].Lchild)
cd.bit[cd.start] = '0'; //左子树编为0;
else
cd.bit[cd.start] = '1'; //右子树编为1;
c = p; // 移动
p = t[c].parent;
}
code[i] = cd; // 第i + 1个字符的编码存入code
}
}
//输出编制的哈夫曼编码
void show(Htreetype t[], Hcodetype code[],int length,string codestr[][2])
{
for (int i = 0; i < length; i++)
{
codestr[i][0] = code[i].ch;
cout << codestr[i][0] << ": ";
string s = "";
for (int j = code[i].start; j < length; j++)
{
s+= code[i].bit[j];
}
codestr[i][1] = s;
cout << codestr[i][1] << endl;
}
}
//输入句子输出哈夫曼编码
void encode( Hcodetype code[], int length,string codestr[][2]) {
cout << "模拟发送端" << endl;
cout << " 输入:";
char a[50];
cin.getline(a, 50); //接受含空格的字符串
cout << " 输出:";
for (int i = 0; i < 50; i++) {
for (int j = 0; j < length; j++) {
string m;
m += a[i];
if (m == codestr[j][0]) {
cout << codestr[j][1];
break;
}
}
}
cout << endl;
}
//输入哈夫曼编码输出句子
void decode(Htreetype t[], Hcodetype code[], int tlength) {
cout << "模拟接收端" << endl;
cout << " 输入:";
char a[500];
cin.getline(a, 500); //接受含空格的字符串
cout << " 输出:";
//从根节点开始,0向左遍历,1向右,遇到叶子结点输出
int current = tlength - 1;//t[current]是根结点
for (int i = 0; i < 500; i++) {
char next = a[i];
if(next==NULL) break;
if (next == '0') current = t[current].Lchild;
else current = t[current].Rchild;
if (t[current].Lchild == -1 && t[current].Rchild == -1) {
cout << t[current].ch;
current = tlength - 1;//int current =。。。。在这里有int时输出第二个ch就有问题了
}
}
}
int main()
{
//被编码字符
char chs[27];
for (int i = 0; i <26; i++) {
chs[i] = 'a' + i;
}
chs[26] = ' ';
//被编码字符对应权重
int weights[27] = { 64, 13, 22, 32, 103, 21, 15,
47, 57, 1, 5, 32, 20, 57, 63, 15, 1, 48, 51,
80, 23, 8, 18, 1, 16, 1, 168 };
const int length=27, tlength = 2 * length - 1;
Htreetype t[tlength];//声明哈夫曼树的数组
Hcodetype code[length];//声明字符编码的数组
string codestr[length][2];//保存编码的数组
HuffmanCode(code, t,tlength,length,chs,weights);//创建哈夫曼树及其编码
cout << "输出编制的哈夫曼编码" << endl;
show(t, code,length,codestr);
encode(code, length, codestr);
decode(t, code, tlength);
return 0;
}
图
知识理解
- 图的存储结构
邻接矩阵存储:一个数组存储顶点,一个矩阵存储顶点之间的边及边的权值。行列数均为顶点数的矩阵,对应位置的元素表示行列两个点之间边的权值,如果两点之间没有边,该位置元素设置为无限大。如果是无向图就是对称矩阵。
邻接表:一个顶点结构 的数组,也就是头结点表。一般头节点连接的边结点都是出边,存储时,生成边结点插到头节点上。 - 深度优先遍历 类似于树的先序遍历,根左右,在图里“左右”的顺序是取决于存储时的顺序。先访问该顶点,输出顶点之后利用visted数组对该顶点做访问标记,然后访问它的邻接点,这样递归下去。
- 广度优先遍历类似于树的层序遍历,利用队列。把第一个顶点入队,然后出队并访问,输出顶点之后利用visted数组对该顶点做访问标记,然后把它所有的邻接点都入队。接着,出队一个顶点如果对应visted数组未被标记就访问,输出顶点之后利用visted数组对该顶点做访问标记,然后把它所有的邻接点都入队。重复直到队列为空。
- 拓扑排序 按照先后顺序(有向图)给遍历排序,不唯一,能判断是否有回路(遍历完的顶点数小于图的总数)。核心思想是重复选择没有前驱的顶点,删除该顶点和以该顶点为起点的弧。AOV网 顶点表示活动,弧表示优先关系。
- AOE网边表示活动,(边的权值表示活动持续时间)顶点表示事件
关键路径 源点(没有入度的顶点)到汇点(没有出度的顶点)最长的路径。关键路径所用时间也是这个工程最少的时间,因为只要花费时间最长的都完成了,这个工程才会完成。
**ve(i),vl(i)**为事件发生的最早时间(源点到顶点i的最长路径的长度)和最晚时间(在关键路径上,汇点到顶点i的逆拓扑序列,关键路径长度减去汇点到该点的最长路径长度)
e(i),l(i) 活动发生的最早时间和最晚时间。最早时间:源点到活动起点的最长路径长度。最晚时间:关键路径长度 减去 汇点到起点的最长路径长度。活动i的松弛时间 l(i)-e(i). - Dijstra算法 填表格:
选择一个点作为起点,这个选择的是a,A,可以分别到达bcd三个点,在表格中填写相应的权值和路径,选择其中最小的C加入终点集。第二步,从c点开始c可以到e和f点,从c开始不能到达的点,它的路径还按照上一列写,经过c点可以到达的点的路径的权值和上一列相比哪个权值小填写哪个。再在这一列选择最小的f加入中点集。以f为起点同上。。。。
邻接矩阵的存储和遍历
# include<iostream>;
using namespace std;
#define MaxVertexNum 50 //最大顶点数
#define MaxInt 32767 //表示极大值
typedef char VertexType;//假设顶点的数据类型为字符型
typedef int ArcType; //假设边的权值类型为整型
int visted[MaxVertexNum];//全局变量初始化会自动全部设置为0
//visted数组置0
void newVisted(int visted[MaxVertexNum]);
// 队结点
struct QueueNode
{
int data;
QueueNode* next;
};
//队列
struct LinkQueue
{
QueueNode* rear;
QueueNode* front;
};
//初始化一个队
void initial(LinkQueue&);
//入队
void enLinkQueue(int, LinkQueue&);
//出队
int deLinkQueue(LinkQueue& Q);
//邻接矩阵的存储表示
struct AMGraph {
VertexType vertices[MaxVertexNum];//顶点表
ArcType arcs[MaxVertexNum][MaxVertexNum];//邻接矩阵
int vertexNum, arcNum;//图的顶点和边数
int kind;//kind=0为无向图,=1为有向图
};
//创建图
void creatAMGraph(AMGraph&);
//返回输入顶点的位置
int locateVex(AMGraph&, VertexType);
//深度优先遍历
void DFS_AM(AMGraph, int);
//广度优先遍历
void BFS_AM(AMGraph, int);
int main() {
cout << "test" << endl;
AMGraph G;
creatAMGraph(G);
cout << "深度优先遍历:";
DFS_AM(G,0);
cout << endl;
newVisted(visted);
cout << "广度优先遍历:";
BFS_AM(G,0);
cout << endl;
}
void newVisted(int visted[MaxVertexNum]) {
for (int i = 0; i < MaxVertexNum; i++) {
visted[i] = 0;
}
}
void creatAMGraph(AMGraph& G) {//采用邻接矩阵表示法,创建无向网G
cout << "输入总顶点数:";
cin >> G.vertexNum;
cout << "输入总边数";
cin >> G.arcNum;
cout << endl;
cout << "依次输入顶点的信息:";
for (int i = 0; i < G.vertexNum; i++) {
cin >> G.vertices[i];
}
cout << "如果是无向图请输入0,有向图输入1:";
cin >> G.kind;
//初始化邻接矩阵,边的权值均为极大值
for (int i = 0; i < G.vertexNum; i++) {
for (int j = 0; j < G.vertexNum; j++) {
G.arcs[i][j] = MaxInt;
}
}
//构造邻接矩阵
for (int k = 0; k < G.arcNum; k++) {
cout << "输入一条边依附的顶点和权值:";
VertexType v1, v2;
ArcType w;
cin >> v1 >> v2 >> w;
int i, j;
i = locateVex(G, v1);
j = locateVex(G, v2); //确定v1和v2在G中的位置
if ((i == -1) || (j == -1)) {
cout << "请输入正确信息";
k--;
}
else {
G.arcs[i][j] = w; //边<v1, v2>的权值置为w
if (G.kind == 0) {
G.arcs[j][i] = G.arcs[i][j]; //置<v1, v2>的对称边<v2, v1>的权值为w
}
}
cout << endl;
}
}
int locateVex(AMGraph& G, VertexType v) {
for (int i = 0; i < G.vertexNum; i++) {
if (v == G.vertices[i]) {
return i;
}
}
return -1;
}
void DFS_AM(AMGraph G, int v) {//图G为邻接矩阵类型
//访问第v个顶点
cout << G.vertices[v] << " ";
visted[v] = 1;
int c = v;
//依次检查邻接矩阵v所在的行
for (int w = 0; w < G.vertexNum; w++) {
if ((G.arcs[v][w] != MaxInt) && (visted[w] == 0)) {
DFS_AM(G, w); //w是v的邻接点,如果w未访问,则递归调用DFS
}
}
}
void initial(LinkQueue& Q) {
//构造一个空队列Q
//生成新的结点作为头节点,队列的头指针和尾指针都指向该结点
Q.front = Q.rear = new QueueNode;
//头节点的指针域置为空
Q.front->next = NULL;
}
void enLinkQueue(int v, LinkQueue& Q) {
//为入队元素分配结点空间,用指针p指向
QueueNode* p = new QueueNode;
p->data = v; //将新结点数据置为顶点
p->next = NULL;
Q.rear->next = p;//将新结点插入到队尾
Q.rear = p;//修改队尾指针
}
int deLinkQueue(LinkQueue& Q) {
//是否队空
if (Q.rear == Q.front) return NULL;
//临时保存队头元素空间以备释放
//p 指向队头元素
QueueNode* p;
p = Q.front->next;
//e 保存队头元素的值
int e = p->data;
//修改头指针的指针域,指向下一个结点
Q.front->next = p->next;
//如果最后一个元素被删,队尾指针指向头节点
if (Q.rear == p) Q.rear = Q.front;
delete p;//释放原队头元素的空间
return e;
}
void BFS_AM(AMGraph G, int v) {
//新建并初始化队列
LinkQueue Q;
initial(Q);
enLinkQueue(v, Q);//第v 个顶点入队
//访问第v个顶点
cout << G.vertices[v] << " ";
visted[v] = 1;
//把第v个顶点的邻接点入队
for (int w = 0; w < G.vertexNum; w++) {
if ((G.arcs[v][w] != MaxInt) && (visted[w] == 0)) {
enLinkQueue(w, Q);
}
}
int c = deLinkQueue(Q);//把第v个顶点出队
while (Q.length!=0)//队非空时,出队一个顶点
{
c = deLinkQueue(Q);
if (visted[c] == 0) {//如果该顶点未被访问过
//访问当前出队的顶点
cout << G.vertices[c] << " ";
visted[c] = 1;
//把第c个顶点的邻接点入队
for (int w = 0; w < G.vertexNum; w++) {
if ((G.arcs[c][w] != MaxInt) && (visted[w] == 0)) {
enLinkQueue(w, Q);
}
}
}
}
}
- 问题与解决:
调式的时候务必断点调试每次输入测试数据都要输好久。。。while()里面,对于c++来说0就是非,注意判断条件最好不要是某个变量。
邻接表存储结构和遍历
# include<iostream>;
using namespace std;
#define MaxVertexNum 50 //最大顶点数
#define MaxInt 32767 //表示极大值
typedef char VertexType;//假设顶点的数据类型为字符型
typedef int ArcType; //假设边的权值类型为整型
int visted[MaxVertexNum];
//visted数组置0
void newVisted(int visted[MaxVertexNum]);
// 队结点
struct QueueNode
{
int data;
QueueNode* next;
};
//队列
struct LinkQueue
{
QueueNode* rear;
QueueNode* front;
int length;
};
//初始化一个队
void initial(LinkQueue&);
//入队
void enLinkQueue(int, LinkQueue&);
//出队
int deLinkQueue(LinkQueue& Q);
//邻接表的存储表示
//边结构
struct ArcNode {
int adjvex;//该边所指向的顶点位置
struct ArcNode* next;//指向下一条边的指针
ArcType info;//和边相关的信息,权值
};
//顶点结构
struct VNode {
VertexType data;//顶点信息
ArcNode* firstArc;//指向依附该顶点的第一条弧的指针
};
//图结构
struct ALGraph {
VNode vertices[MaxVertexNum];//邻接表
int vexNum, arcNum;//顶点数和弧数
int kind;//图的种类
};
//创建图
int locateVex(ALGraph& G, VertexType v);
void CreatALGraph(ALGraph&);
//深度优先遍历
void DFS_AL(ALGraph, int);
//广度优先遍历
void BFS_AL(ALGraph, int);
int main() {
cout << "1812030065-李诗雨" << endl;
ALGraph G;
CreatALGraph(G);
cout << "深度优先遍历:";
DFS_AL(G, 0);
cout << endl;
newVisted(visted);
cout << "广度优先遍历:";
BFS_AL(G, 0);
cout << endl;
}
void newVisted(int visted[MaxVertexNum]) {
for (int i = 0; i < MaxVertexNum; i++) {
visted[i] = 0;
}
}
void CreatALGraph(ALGraph& G) {
cout << "输入总顶点数:";
cin >> G.vexNum;
cout << "输入总边数";
cin >> G.arcNum;
cout << endl;
cout << "依次输入顶点的信息:";
for (int i = 0; i < G.vexNum; ++i) { //输入各点,构造头结点表
cin >> G.vertices[i].data; //输入顶点值
G.vertices[i].firstArc = NULL; //初始化表头结点的指针域为NULL
}
cout << "如果是无向图请输入0,有向图输入1:";
cin >> G.kind;
for (int k = 0; k < G.arcNum; k++) { //输入各边,构造邻接表
cout << "输入一条边依附的顶点和权值:";
VertexType v1, v2;
ArcType w;
cin >> v1 >> v2 >> w;
int i, j;
i = locateVex(G, v1);
j = locateVex(G, v2); //确定v1和v2在G中的位置
if ((i == -1) || (j == -1)) {
cout << "请输入正确信息";
k--;
}
else {
ArcNode* p1=new ArcNode; //生成一个新的边结点*p1
p1->adjvex = j; //邻接点序号为j
p1->info = w;
p1->next = G.vertices[i].firstArc;
G.vertices[i].firstArc= p1;//将新结点*p1插入顶点vi的边表头部
if (G.kind == 0) {
ArcNode* p2=new ArcNode; //生成一个新的边结点*p2
p2->adjvex = i; //邻接点序号为i
p2->info = w;
p2->next = G.vertices[i].firstArc;
G.vertices[j].firstArc = p2;//将新结点*p2插入顶点vj的边表头部
}
}
cout << endl;
}
}
int locateVex(ALGraph& G, VertexType v) {
for (int i = 0; i < G.vexNum; i++) {
if (v == G.vertices[i].data) {
return i;
}
}
return -1;
}
void DFS_AL(ALGraph G, int v) {
//访问第v个顶点
cout << G.vertices[v].data << " ";
visted[v] = 1;
ArcNode* p = G.vertices[v].firstArc;; //p 指向 v 的边链表的第一个边结点
if (p != NULL) {//边结点非空
if (visted[p->adjvex] == 0) {
DFS_AL(G, p->adjvex);//如果v的邻接点 未访问,则递归调用DFS
}
p = p->next;; //p 指向下一个边结点
if (visted[p->adjvex] == 0) {
DFS_AL(G, p->adjvex);//如果v的邻接点 未访问,则递归调用DFS
}
}
}
void initial(LinkQueue& Q) {
//构造一个空队列Q
//生成新的结点作为头节点,队列的头指针和尾指针都指向该结点
Q.front = Q.rear = new QueueNode;
//头节点的指针域置为空
Q.front->next = NULL;
Q.length = 0;
}
void enLinkQueue(int v, LinkQueue& Q) {
//为入队元素分配结点空间,用指针p指向
QueueNode* p = new QueueNode;
p->data = v; //将新结点数据置为顶点
p->next = NULL;
Q.rear->next = p;//将新结点插入到队尾
Q.rear = p;//修改队尾指针
Q.length++;
}
int deLinkQueue(LinkQueue& Q) {
//是否队空
if (Q.rear == Q.front) return NULL;
//临时保存队头元素空间以备释放
//p 指向队头元素
QueueNode* p;
p = Q.front->next;
//e 保存队头元素的值
int e = p->data;
//修改头指针的指针域,指向下一个结点
Q.front->next = p->next;
//如果最后一个元素被删,队尾指针指向头节点
if (Q.rear == p) Q.rear = Q.front;
delete p;//释放原队头元素的空间
Q.length--;
return e;
}
void BFS_AL(ALGraph G, int v) {
//新建并初始化队列
LinkQueue Q;
initial(Q);
//第v 个顶点入队
enLinkQueue(v, Q);
//把第v个顶点的邻接点也入队
ArcNode* arc = G.vertices[v].firstArc;
while (arc != NULL) {
enLinkQueue(arc->adjvex, Q);
arc = arc->next;
}
v = deLinkQueue(Q);//把第v个顶点出队
//访问第v个顶点
cout << G.vertices[v].data << " ";
visted[v] = 1;
//如果队非空,出队一个顶点结点,如果该顶点未被访问过,访问该顶点,然后把该顶点的邻接顶点入队
while (Q.length!=0)
{
v = deLinkQueue(Q);
if (visted[v] == 0) {
//访问当前出队的顶点
cout << G.vertices[v].data << " ";
visted[v] = 1;
//把第v个顶点的邻接点入队
ArcNode* arc = G.vertices[v].firstArc;
while (arc != NULL) {
enLinkQueue(arc->adjvex, Q);
arc = arc->next;
}
}
}
}
- 问题与解决: 存储的时候注意边结点的链表是怎么增加的,深度优先遍历要理解递归。
dijkstra算法求最短路径
- 理解:求最短路径跟上面的表一样的原理。直接看代码吧
- 问题:输出方面:我第一个想法是正向找,v0到vi,当第i行输出到vi就停止输出。但是只要手动实现一遍而且找了一个包含情况多的例子就会发现问题。当我意识到要倒着从vi回到v0的时候很容易就想到了再加一个数组。重要的是:先手动实现算法,测试的时候用一个更普遍的例子
基于邻接矩阵的dijkstra算法求最短路径。
void ShortestPath_DIJ(AMGraph G, int v0) {
//用Dijkstra算法求有向网G的v0顶点到其余顶点的最短路径
int n = G.vertexNum; //n为G中顶点的个数
bool S[MaxVertexNum]; //终点集,作为目前最短路径上已经访问过的顶点标记
int D[MaxVertexNum]; //保存v0到各个终点的最短路径长度,例如D(2)的值为v0到v2的最短路径
int Path[MaxVertexNum]; //保存每个顶点的前驱顶点的编号
//n 个顶点依次初始化
for (int v = 0; v < n; ++v) {
S[v] = false; //S 初始为空集
D[v] = G.arcs[v0][v]; //将v0到各个终点的最短路径长度初始化
if (D[v] < MaxInt) Path[v] = v0; //v0和v之间有弧,将v的前驱置为v0
else Path[v] = -1; //如果v0和v之间无弧,则将v的前驱置为-1
}
S[v0] = true; //将v0加入S
D[v0] = 0; //源点到源点的距离为0
int v;
/*―开始主循环,每次求得v0到某个顶点v的最短路径,将v加到S集―*/
for (int i = 1; i < n; ++i) {//对其余n−1个顶点,依次进行计算
int min = MaxInt;
//找到v0经过一条弧到的最短路径到达的终点v
for (int w = 0; w < n; ++w) {
if (!S[w] && D[w] < min)//如果未加入S且v0和vi间有弧
{
v = w; min = D[w];
} //选择一条当前的最短路径,终点为v
}
S[v] = true; //将v加入S
for (int w = 0; w < n; ++w) {
//更新从v0出发到集合V−S上所有顶点的最短路径长度
if (!S[w] && (D[v] + G.arcs[v][w] < D[w])) {
D[w] = D[v] + G.arcs[v][w]; //更新D[w]
Path[w] = v; //更改w的前驱为v
}
}
}
//输出最短路径
for (int i = 1; i < G.vertexNum; i++) {
int a[MaxVertexNum];//存放v0到vi的最短路径的数组
int k = i; //用来在path路径中从vi往v0回溯查找
a[0] = i; //vi 终点
int j = 1; //存放v0到vi的最短路径数组的下标
for (;;) {
a[j] = Path[k];
k = a[j];
if (k == -1) {//说明a[j-1]==0
break;
}
j++;
}
cout << endl;
for (int k = j - 1; k >= 0; k--) {//倒序从v0开始
int m = a[k];
cout << G.vertices[m] << "(" << D[m] << ")";
if (k !=0) {
cout << "->";
}
}
cout << endl;
}
}
- 结果参考
- 结果:
查找和排序
折半查找法
套用以前的模板,不简洁,但能用就行。
#include<iostream>
using namespace std;
//常量
#define MAXSIZE 1000
//数据元素类型约定为ElemType
typedef int ElemType;
int Searchcount = 0; //查找次数
//顺序表的建立,定义了一个名为SqList的结构体
struct SqList {
//当前长度
int length;
//存储空间的基地址
ElemType* elem;
};
//初始化顺序表
int SqList_Init(SqList&);
//顺序表读入值
void SqList_read(int, SqList&);
//显示菜单
void Menu();
void check_n(int n, SqList& L);
//折半查找法
int Search1(SqList&, ElemType k);
void Searchzheban(SqList&);
int main() {
int n;
//初始化一个顺序表
SqList L;
if (SqList_Init(L) == -2) {
cout << "初始化失败";
return 0;
}
//给顺序表赋值
int m;//把m个元素放入顺序表
for (;;) {
cout << "请输入顺序表的元素个数m:" << endl;
cin >> m;
if (m < 0) {
cout << "请输入合法的顺序表的元素个数m:" << endl;
}
else break;
}
SqList_read(m, L);
for (;;) {
//显示菜单
Menu();
//输入操作码
for (;;) {
cout << "请输入操作代码" << endl;
cout << " 输入负数退出" << endl;
cin >> n;
if (n ==1) {
//根据操作码选择操作
check_n(n, L);
}
else if (n < 0) {
return 0;
}
else {
cout << "请输入正确操作码" << endl;
}
}
}
return 0;
}
void Menu() {
cout << endl;
cout << "1812030065 李诗雨" << endl;
cout << "1-------折半查找法" << endl;
}
void Status_check(int n) {
if (n == 0) return;
if (n == -2) return;
}
void check_n(int n, SqList& L) {
//不要忘了每个case里都要break
switch (n)
{
case 1:
Searchzheban(L);
break;
default:
break;
}
}
int SqList_Init(SqList& L) {
//构造一个新的顺序表L 并为之分配大小为MAXSIZE的空间
L.elem = new ElemType[MAXSIZE];//关于elemtype...
if (L.elem == NULL) {
cout << "存储空间分配失败!" << endl;
return -2;
}
L.length = 0;
cout << "顺序表初始化成功" << endl;
return 1;
}
void SqList_read(int m, SqList& L) {
for (int i = 0; i < m; i++) {
ElemType e;
cin >> e;
L.elem[i] = e;
L.length++;
}
}
int Search1(SqList& L, ElemType k) {
int low, high, mid;
low = 0; high = L.length - 1;
Searchcount = 0;
while (low <= high)
{
Searchcount++;
mid = (low + high) / 2;
if (k > L.elem[mid])
{
low = mid + 1;
}
else if (k == L.elem[mid])
{
return mid;
}
else
high = mid - 1;
}
return -1;
}
void Searchzheban(SqList& L) {
cout << "请输入要查找的元素:";
ElemType k;
cin >> k;
int position = Search1(L, k);
if (position == -1) {
cout << "折半查找法: 查找失败 ,查找次数为:" << Searchcount << endl;
}
else {
cout << "折半查找法: " << k << " 的位置为:" << position << "查找次数为:" << Searchcount << endl;
}
}
二叉排序树查找法
参考链接 二叉排序树的构造和应用
#include<iostream>
using namespace std;
#define MAX 100
typedef struct TNODE
{
int data;
int index;
TNODE* lchild, * rchild;
};
void create();
void insert(int m,int i); //插入二叉排序树的结点
void inOrder(TNODE* ptr); //中序遍历
int searchCount = 0;
int SearchBST2(TNODE* t, int k);
void Search(TNODE* t);
TNODE* root = NULL;
int main()
{
cout << "1812030065 李诗雨" << endl;
create();
cout << endl;
cout << " 按中序遍历所得到的中序序列是一个递增有序序列。" << endl;
inOrder(root);
cout << endl;
for (;;) {
int n;
cout << "输入0开始查找元素,输入-1 结束程序" << endl;
cin >> n;
cout << endl;
if (n == -1) {
return 0;
}
Search(root);
}
}
void inOrder(TNODE* ptr)
{
if (ptr != NULL)
{
inOrder(ptr->lchild);
cout << ptr->data<<" ";
inOrder(ptr->rchild);
}
}
void create()
{
int n, i;
int k[MAX];
cout << "请输入元素结点个数:";
cin >> n;
cout << "输入元素:";
for (i = 0; i < n; i++)
{
cin >> k[i];
}
for (i = 0; i < n; i++) {
insert(k[i],i);
}
}
void insert(int m,int i)
{
TNODE* p1, * p2;
//如果二叉树为空树,创建根节点
if (root == NULL)
{
root = new TNODE;
root->data = m;
root->index = i;
root->lchild = root->rchild = NULL;
}
else
{
p1 = root;
while (m != p1->data)
{
if ((m < p1->data) && (p1->lchild != NULL))
p1 = p1->lchild;
else if ((m > p1->data) && (p1->rchild != NULL))
p1 = p1->rchild;
else if ((m < p1->data) && (p1->lchild == NULL))
{
p2 = new TNODE;
p2->data = m;
p2->index = i;
p2->lchild = p2->rchild = NULL;
p1->lchild = p2;
return;
}
else if ((m > p1->data) && (p1->rchild == NULL))
{
p2 = new TNODE;
p2->data = m;
p2->index = i;
p2->lchild = p2->rchild = NULL;
p1->rchild = p2;
return;
}
}
}
}
int SearchBST2(TNODE *t, int k)
{
searchCount = 0;
TNODE* p = t;
while (p != NULL && p->data != k)
{
searchCount++;
if (k < p->data)
p = p->lchild;
else
p = p->rchild;
}
searchCount++;//p == NULL 或 p->data == k 也算查找了一次
if (p == NULL)
return -1;
else
return p->index;
}
void Search(TNODE* t) {
cout << "二叉排序树查找法: 请输入要查找的元素:";
int k;
cin >> k;
int position = SearchBST2(t, k);
if (position == -1)
cout << "查找失败,查找次数为:" << searchCount << endl;
else
cout<<"查找成功,元素 "<<k<<" 的位置是 "<<position<<" 查找次数为:" << searchCount << endl;
}
折半查找插入法
#include<iostream>
using namespace std;
//常量
#define MAXSIZE 1000
//数据元素类型约定为ElemType
typedef int ElemType;
int Searchcount = 0; //查找次数
//顺序表的建立,定义了一个名为SqList的结构体
struct SqList {
//当前长度
int length;
//存储空间的基地址
ElemType* elem;
};
//初始化顺序表
int SqList_Init(SqList&);
//顺序表读入值
void SqList_read(int, SqList&);
//显示菜单
void Menu();
void check_n(int n, SqList& L);
//折半插入排序
void BInsertSort(SqList& L);
int main() {
int n;
//初始化一个顺序表
SqList L;
if (SqList_Init(L) == -2) {
cout << "初始化失败";
return 0;
}
//给顺序表赋值
int m;//把m个元素放入顺序表
for (;;) {
cout << "请输入顺序表的元素个数m:" << endl;
cin >> m;
if (m < 0) {
cout << "请输入合法的顺序表的元素个数m:" << endl;
}
else break;
}
SqList_read(m, L);
for (;;) {
//显示菜单
Menu();
//输入操作码
for (;;) {
cout << "请输入操作代码" << endl;
cout << " 输入负数退出" << endl;
cin >> n;
if (n == 1) {
//根据操作码选择操作
check_n(n, L);
}
else if (n < 0) {
return 0;
}
else {
cout << "请输入正确操作码" << endl;
}
}
}
return 0;
}
void Menu() {
cout << endl;
cout << "1812030065 李诗雨" << endl;
cout << "1-------折半插入排序" << endl;
}
void check_n(int n, SqList& L) {
//不要忘了每个case里都要break
switch (n)
{
case 1:
BInsertSort(L);
break;
default:
break;
}
}
int SqList_Init(SqList& L) {
//构造一个新的顺序表L 并为之分配大小为MAXSIZE的空间
L.elem = new ElemType[MAXSIZE];//关于elemtype...
if (L.elem == NULL) {
cout << "存储空间分配失败!" << endl;
return -2;
}
L.length = 0;
cout << "顺序表初始化成功" << endl;
return 1;
}
void SqList_read(int m, SqList& L) {
for (int i = 1; i <= m; i++) {
ElemType e;
cin >> e;
L.elem[i] = e;
L.length++;
}
}
void BInsertSort(SqList& L)
{
int low, high, m;
for (int i = 2; i <= L.length; ++i)
{
L.elem[0] = L.elem[i];
low = 1; high = i - 1;
while (low <= high)
{
m = (low + high) / 2;
if (L.elem[0]< L.elem[m]) high = m - 1;
else low = m + 1;
}
for (int j = i - 1; j >= high + 1; --j) L.elem[j + 1] = L.elem[j];
L.elem[high + 1] = L.elem[0];
}
cout << "折半插入排序: ";
for (int i = 1; i <= L.length; i++) {
cout << L.elem[i] << " ";
}
cout << endl;
}
冒泡排序
#include<iostream>
using namespace std;
//常量
#define MAXSIZE 1000
//数据元素类型约定为ElemType
typedef int ElemType;
int Searchcount = 0; //查找次数
//顺序表的建立,定义了一个名为SqList的结构体
struct SqList {
//当前长度
int length;
//存储空间的基地址
ElemType* elem;
};
//初始化顺序表
int SqList_Init(SqList&);
//顺序表读入值
void SqList_read(int, SqList&);
//显示菜单
void Menu();
void check_n(int n, SqList& L);
//冒泡排序
void BubbleSort(SqList& L);
int main() {
int n;
//初始化一个顺序表
SqList L;
if (SqList_Init(L) == -2) {
cout << "初始化失败";
return 0;
}
//给顺序表赋值
int m;//把m个元素放入顺序表
for (;;) {
cout << "请输入顺序表的元素个数m:" << endl;
cin >> m;
if (m < 0) {
cout << "请输入合法的顺序表的元素个数m:" << endl;
}
else break;
}
SqList_read(m, L);
for (;;) {
//显示菜单
Menu();
//输入操作码
for (;;) {
cout << "请输入操作代码" << endl;
cout << " 输入负数退出" << endl;
cin >> n;
if (n == 1) {
//根据操作码选择操作
check_n(n, L);
}
else if (n < 0) {
return 0;
}
else {
cout << "请输入正确操作码" << endl;
}
}
}
return 0;
}
void Menu() {
cout << endl;
cout << "1812030065 李诗雨" << endl;
cout << "1------- 冒泡排序" << endl;
}
void check_n(int n, SqList& L) {
//不要忘了每个case里都要break
switch (n)
{
case 1:
BubbleSort(L);
break;
default:
break;
}
}
int SqList_Init(SqList& L) {
//构造一个新的顺序表L 并为之分配大小为MAXSIZE的空间
L.elem = new ElemType[MAXSIZE];//关于elemtype...
if (L.elem == NULL) {
cout << "存储空间分配失败!" << endl;
return -2;
}
L.length = 0;
cout << "顺序表初始化成功" << endl;
return 1;
}
void SqList_read(int m, SqList& L) {
for (int i = 0; i < m; i++) {
ElemType e;
cin >> e;
L.elem[i] = e;
L.length++;
}
}
void BubbleSort(SqList& L) {
int j = L.length;
while (j>1)
{
int lastExchangeIndex = 0;
for (int i = 1; i < j; i++) {
if (L.elem[i] < L.elem[i - 1]) {
int t = L.elem[i];
L.elem[i] = L.elem[i - 1];
L.elem[i - 1] = t;
lastExchangeIndex = i;
}
}
j = lastExchangeIndex;
}
cout << "冒泡排序: ";
for (int i = 0; i < L.length; i++) {
cout << L.elem[i] << " ";
}
cout << endl;
}