数据结构个人简易总结(DFS BFS WPL 最小生成树 哈夫曼编码 有向图 无向图 二叉树 稀疏矩阵 KMP匹配算法 栈和队列 链表)

仅供学习参考,有一些属于模板类算法需要记住,有一些设计应用需要了解大致思路

希望通过这篇文章,读者能了解数据结构大致要学习哪些内容,以便复习参考。

整理作者:黎爬爬(2745907300)

文章目录

目录

文章目录

前言

一、链表

1.单链表 

补充方法

2.双链表

3.循环链表与约瑟夫环

4.双向循环链表

二、栈和队列

1.栈

栈的应用--栈递归汉诺塔

栈的应用--后缀表达式

补充

2.队列

链式队列

顺序队列

循环队列

队列应用--银行排队

三、串和稀疏矩阵

KMP匹配算法

LCS动态规划--最长公共子串

稀疏矩阵的快速转置

四、树

二叉树的创建和两种遍历方法(递归和递归工作栈模拟)

二叉树深度和最长叶路径

二叉树左右子树交换

给定数组创建完全二叉树

根据前中序创建二叉树 找公共祖先

根据中后序创建二叉树

根据广义表创建二叉树 按照层次输出

求二叉树最大宽度的两种算法

最优二叉树(哈夫曼树) 哈夫曼编码

哈夫曼树最短带权路径

中序线索二叉树

五、图

图的邻接表创建

图的邻接矩阵

尾插法建图 BFS广度优先搜索

尾插法建图DFS深度优先搜索

最小生成树kruskal算法

最小生成树--prime算法

有向网的最短路径Dijkstra算法

有向无环图的拓扑排序

图的应用--走迷宫DFS

图的应用--走迷宫BFS

图的应用--DFS解决数独问题

总结



前言

个人笔记总结

一、链表

1.单链表 

#include
using namespace std;
//结构定义 单链表
typedef struct Lnode
{
int data;
struct Lnode*next;
}Lnode,*LinkList;
//头插法创建
void Create1(LinkList &L)
{
int n;//元素个数
cin>>n;
int temp;
LinkList p;
for(int i=1;i<=n;i++)
{
cin>>temp;
p=new Lnode;
p->data=temp;
p->next=L->next;
L->next=p;
}
}
//尾插法创建
void Create2(LinkList L)
{
int n;//元素个数
cin>>n;
int temp;
LinkList p;
while(n)
{
cin>>temp;
p=new Lnode;
p->data=temp;
L->next=p;
L=p;
}
L->next=NULL;
n--;
}

}
//查找第i个元素并且返回指针
LinkList Get(LinkList L,int i)
{
LinkList p=L;
int j=0;
while(p!=NULL&&jnext;j++;
}
return p;
}
//增加元素
void Insert(LinkList&L,int i,int x)
{//位置i后插入元素x
LinkList p,s;
p=Get(L,i);
if(!p){cout<<"erro"<data=x;
s->next=p->next;
p->next=s;
}
}
//删减元素
void Delete(LinkList L,int i)
{
LinkList p ,s;
p=Get(L,i-1);
if(!p)cout<<"erro"<next==NULL)cout<<"erro"<next;
p->next=s->next;
delete s;
}
}
//改变元素
略
//查找元素
略
//打印链表
void Display(LinkList L)
{
	//单链表的显示
	LinkList p;
    p = L;
	while (p->next)
	{
		cout<next->data<<" ";
        p = p->next;
	}
}
int main()
{
return 0;
}

补充方法

//排序(升序)
void sort(LinkList L)
{
LinkList p,q,temp;
int x;
for(int p=L->next;p->next!=NULL;p=p->next)
{
temp=p;
for(q=temp->next;q!=NULL;q=q->next)
{
if(temp->data>q->data)temp=q;
}
if(t!=p)
{
x=p->data;
p->data=temp->data;
temp->data=x;
}
}
}
//删除重复元素
void deleteRepete(LinkList L)
{
LinkList p =L->next,q;
while(p->next!=NULL)
{
if(p->data==p->next->data)
{
q=p->next;
p->next=q->next;
delete q;
}
else p=p->next;
}
}

2.双链表

#include
using namespace std;
//双链表结构定义
typedef struct Lnode
{
	//链式储存结构定义
	int data; //数据域
	struct Lnode* next;//指针域 指后 
	struct Lnode*last;//指针域 指前 
}LNode,*LinkList;//定义节点和头指针类型名
//创建双链表
void create(LinkList s)
{
LinkList p,head;
head=s;
int n;//元素个数
cin>>n;
while(n)//尾插法
{
p=new LNode;
p->data=n;
s->next=p;
p->last=s;
s=p;
cin>>n;
}
}
int main()
{
LinkList L;
L=new LNode;
L->last=NULL;
L->next=NULL;
create(L);
return 0;
}

3.循环链表与约瑟夫环

#include
using namespace std;
typedef struct Lnode
{
int data;
struct Lnode*next;
}Lnode,*LinkList;
LinkList Head,Tail;//头指针 尾指针 循环链表 头尾相连
//循环链表初始化
void Init()
{
Head=new Lnode;
Head->next=NULL;
Tail=Head;//尾指针指向头部
}
//创建循环链表
void create()
{
LinkList p;
int m;
cin>>m;//约瑟夫环元素个数
for(int i=1;i<=m;i++)
{
p=new Lnode;
p->data=i;
Tail->next=p;
p->next=Head->next;
Tail=p;
}//尾插法建立
Head=Head->next;
}
//打印操作
void display()
{
LinkList q,p;
p=Head;
q=Tail;
while(p!=q)
{
cout<data<<" ";
p=p->next;
}//输出尾巴之前的元素
cout<data<next;
delete p
}//删除尾巴之前的元素
delete Tail;//删除尾巴
}
int main()
{
Init();
create();
display();
desotry();
return 0;
}

4.双向循环链表

#include
using namespace std;
#define flag 0
typedef int ElemType;
typedef struct Lnode
{
	//链式储存结构定义
	int data; //数据域
	struct Lnode* next;//指针域 指后 
	struct Lnode*last;//指针域 指前 
}LNode,*LinkList;//定义节点和头指针类型名

void create(LinkList s)
{
	LinkList p,head;
	head=s;
	int n;
	cin >> n;
	while (n) {//双链表的尾插法 
		p = new LNode;
		p->data = n;
		s->next=p;
		p->last=s;
		s=p;
		cin >> n;
	} 
	head->last=s; 
	s->next=head;
	 
}
void Display(LinkList L)
{
	//单链表的显示
	LinkList p; p = L;
	while (p->next!=L)
	{
		cout<next->data<<" "; 
		p = p->next;
	}
}
void sort(LinkList L){

	LinkList p,q,r;
    p=L->next;
    q=p->next;
    r=q->next;
    while(q!=L)
    {
        while(p!=L && p->data>q->data)
        {
            p=p->last;
        }
        q->last->next=r;
        r->last=q->last;
        q->next=p->next;
        q->last=p;
        p->next->last=q;
        p->next=q;
        q=r;
        p=q->last;
        r=r->next;
    }

}
int main( )
{
	
	LinkList L;
	L=new LNode; 
	L->last=NULL;
	L->next=NULL;
	create(L);
	sort(L);
	Display(L);
	
	return 0;
}

二、栈和队列

1.栈

代码如下(示例):

#include
using namespace std;
typedef struct Snode
{
	int data;
	struct Snode* next;
	
}Snode,*LinkStack;
void Init(LinkStack& top)
{//链栈初始化 
	top=new Snode;
	top->next = NULL;
}
int isEmpty(LinkStack top)
{
	return (top == NULL);
}
void Push(LinkStack& top, int x)
{//入栈操作 
	LinkStack p;//创建新结点 
	p=new Snode;
	p->data = x;//数据域赋值 
	p->next = top;//记录原栈顶位置 
	top = p;//新结点成为新栈顶 
}
int pop(LinkStack& top, int &e)
{//出栈操作 
	LinkStack p;
	if (top == NULL) return 0;//判断是否空栈 
	p = top;//新结点指向栈顶 
	e = p->data;//栈顶元素通过参数返回 
	top = p->next;//原栈顶结点后的结点直接继承为新的栈顶结点 
	delete p;//释放原栈顶结点空间 
	return 1;
}
int Top(LinkStack top, int &e)
{//读取栈顶元素 
	if (top == NULL)return 0;
	e = top->data;
	return 1;
}
void display(LinkStack top)
{//按照“后进先出”顺序读取栈元素 
	LinkStack p = top;
	while (p->next != NULL)
	{
		cout<data<<" "; p = p->next;
	}
	cout<next;//原栈顶结点后的结点直接继承为新的栈顶结点 
	delete p;//释放原栈顶结点空间
	if(top->next==NULL)break;
	}
} 

栈的应用--栈递归汉诺塔

#include
using namespace std;
#define SIZE 100;
//汉诺塔结构定义
typedef struct
{
int n;
char A;
char B;
char C;
}HannioPara;
//栈的结构定义
typedef struct stack
{
int top;
int maxtop;
HannioPara *data;
}stack,*Stack;
//汉诺塔栈初始化
Stack Init(int size)  //初始化
{
    Stack S = new stack;
    S->data = new HanioPara[size];
    S->maxtop = size;
    S->top = -1;
    return S;
}
//判断栈空
int isEmpty(Stack S)  
{
    return S->top<0;
}
//判断栈满 
int isFull(Stack S)  //栈满
{
    return S->top >= S->maxtop;    
}
//入栈操作
int Push(Stack S,ElemList i)  
{
    if(isFull(S))return 0;
    S->data[++S->top]=i;
    return 1;
}
//出栈操作
HanioPara Pop(Stack S) 
{
    if(isEmpty)return 0;
    return S->data[--S->top];
}
//打印
void display(Stack S) 
{
    while (!(isEmpty(S)))
    {
        printf("%d%c%c%C\n", S->data[S->top--].n,S->data[S->top--].A,S->data[S->top--].B,S->data[S->top--].C);
    }    
}
//打印移动过程
void Printmove(char a,char c) 
{
    printf("第%4d次\t\t%c----->%c\n",flag,a,c);
    flag++;
}
//汉诺塔创建函数
int hannio(int n,char a,char b,char c) //模拟递归实现
{
    Stack S = StackInit(200);
    HannioPara tem,tem1,tem2;
    tem.n = n;
    tem.A = a;
    tem.B = b;
    tem.C = c;
    Push(S,tem);//最外层函数最先入栈
    while(S->top > -1) //栈不为空,出栈
    {
        tem1 = StackTop(S);//栈顶元素
        Pop(S); //出栈
        if (tem1.n > 1)
        {
            int n = tem1.n;
            tem2.n = n-1;
            tem2.A = tem1.B;
            tem2.B = tem1.A;
            tem2.C = tem1.C;
            Push(S,tem2);//b,a,c 压栈
            tem2.n = 1;
            tem2.A = tem1.A;
            tem2.C = tem1.C;
            Push(S,tem2);//a,c 压栈
            tem2.n = n-1;
            tem2.A = tem1.A;
            tem2.B = tem1.C;
            tem2.C = tem1.B;
            Push(S,tem2);//a,c,b 压栈
        }
        else printmove(tem1.A,tem1.C);
    }
}

int main()
{
    hannio(3,'A','B','C');  //调用
    return 0;
}

栈的应用--后缀表达式

/*洛谷题目 后缀表达式
所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级)。

如:3*(5–2)+7对应的后缀表达式为:3.5.2.-*7.+@。’@’为表达式的结束符号。‘.’为操作数的结束符号。
【输入样例】
3.5.2.-*7.+@
【输出样例】
16
*/
#include
#include
using namespace std;
//数学基础运算函数  运算符号c  运算数字1:a 运算数字2:b
int Math(int a,char c,int b)
{
	if (c == '+')return a + b;
	else if (c == '-')return a - b;
	else if (c == '*')return a * b;
	else return a / b;
}
int main()
{
stackds;//创建工作栈
char ch;
int a,b,temp;
cin>>ch;
while(cj!='@')//@为结束符号
{
if(ch>='0'&&ch<='9')
{//输入的是数字字符
cin.pushback(ch)//将输入的数字字符退回
cin>>temp;//将退回的数字字符重新输入 赋值给temp
ds.push(temp);//遇到数字无条件入栈
cin>>ch;//重新输入字符
}
else if(ch=='.')cin>>ch;
else
{//遇到非数字字符也就是运算符
a=ds.top();ds.pop();//弹出两个栈顶数字
b=ds.top();ds.pop();
ds.push(Math(b,ch,a));//将运算的结果压入数字栈
cin>>ch;//重新输入
}
}
cout<

补充

//栈在c++中可以直接调用
#include

//1.创建一个栈
stackS1;
stacks2;

//2.入栈操作
S1.push(1);
S1.push(2);
S2.push(a);
S2.push(b);

//3.出栈操作
s1.pop()//弹出栈顶元素

//4.获取栈顶元素
char a;
a=s2.top();

//是否空 
printf(s1.empty());
//6.清空
s1.clear();
s2.clear();

2.队列

链式队列

#include
using namespace std;
//队列结构定义
struct Node
{
Node *next;
int data;
};
struct Queue
{
Node*front;//队首
Node*rear;//队尾
}
//初始化
void Init(Queue&queue)
{
Node*p;
p=new Node;
p->next=NULL;//队首
queue.front=p;
queue.rear=p;
}
//入队操作  遵循原则先进先出 从队尾进 从队首出
void Enqueue(Queue&queue,int x)
{
Node*p;
p=new Node;
p->next=NULL;
p->data=x;
queue.rear->next=p;
queue.rear=p;
}
//出队操作  先进先出  尾进首出
void Dequeue(Queue&queue,int &x)
{
Node*p;
p=queue.front->next;
x=p->data;
queue.front->next=p->next;
if(p==queue.rear)queue.rear=queue.front;//如果仅剩下一个元素 队首等于队尾
delete p;
}
//判断是否为空
int isempty(Queue&queue)
{
if(queue.front==queue.rear)return 1;//队首等于对尾 为空返回真
else return 0;
}
//清空队列
void Distory(Queue &queue)
{
while(queue.front)
{
queue.rear=queue.front;
delete queue.front;
queue.front=queue.rear;
}
}

顺序队列

 #include
#define MAXSIZE 10
using namespace std;
//顺序存储结构定义
typedef struct
{
int queue[MAXSIZE];
int rear;
int front;
}Queue;
//队列初始化
void Init(Queue *Q)
{
Q->front=Q->rear=0;
}
//判断是否为空队列
bool isempty(Queue*Q)
{
return Q->front==Q->rear;
}
//判断队列是否已满
bool isfull(Queue*Q)
{
return (Q->rear+1)%MAXSIZE==Q->front;
}
//获取队列长度
int Getlength(Queue*Q)
{
int length=(Q->rear-Q->front+MAXSIZE)%MAXSIZE;
return length;
}
//入队操作   先进先出  尾进首出
void EnQueue(Queue*Q,int x)
{
if(isfull(Q))cout<<"队列已满"<queue[Q->rear]=x;//入队值赋给队尾指针指向的地方
Q->rear=(Q->rear+1)%MAXSIZE//队尾指针后移
}
}
void DeQueue(Queue*Q,int &x)
{
if(isempty(Q))cout<<"已空"<queue[Q->front];
Q->front=(Q->front+1)%MAXSIZE;
}
}
//清空队列
void clear(Queue*Q)
{
Q->front=Q->rear=0;
}
//打印队列
void display(Queue*Q)
{
if(isempty(Q))COUT<<"空队列"<front;i%MAXSZIErear;i++)
{
if (i == Q->rear - 1)
		{
			//遍历到尾元素
			cout << Q->queue[i] << endl;
		}
		else
		{
			cout << Q->queue[i] << " ";
		}
}
}
}

循环队列


循环队列入队, 队尾循环后移: SQ->rear =(SQ->rear+1)%Maxsize;
循环队列出队, 队首循环后移: SQ->front =(SQ->front+1)%Maxsize;
队空: SQ.front=SQ.rear; // SQ.rear 和 SQ.front 指向同一个位置
队满: (SQ.rear+1) %Maxsize=SQ.front; // SQ.rear 向后移一位正好是 SQ.front
计算元素个数:
可以分两种情况判断:

如果 SQ.rear>= SQ.front:元素个数为 SQ.rear-SQ.front;
如果 SQ.rear 采用取模的方法把两种情况统一为:(SQ.rear-SQ.front+Maxsize)% Maxsize

 #include
#define MAXSIZE 10
using namespace std;
//循环队列结构定义
typedef struct
{
int queue[MAXSIZE];
int rear;
int front;
int count;//计数器
}Queue;
//初始化
void Init(Queue* Q) 
{
	Q->front = Q->rear = Q->count = 0;
}
//销毁顺序队列
void clear(Queue* Q)
{
		Q->front = Q->rear = Q->count = 0;
}
//判断顺序队列是否为空
int IsEmpty(Queue* Q) {
	return (Q->count == 0);
}
//判断顺序队列是否已满
int iSFull(Queue* Q) {
	return (Q->count == MAXSIZE);
}
//求得顺序队列的长度
int GetLength(Queue* Q) {
	return Q->count;
}
//获得队首元素
void GetHead(Queue*Q)
{
if(IsEmpty(Q))cout<<"空队列"<queue[Q->front]<queue[(Q->rear - 1 + MAXSIZE) % MAXSIZE]<queue[Q->rear]=x;
Q->rear=(Q->rear+1)%MAXSIZE;
Q->count++;
}
}
//出队操作
void Dequeue(Queue *Q)
{
if(IsEmpty(Q))cout<<"空队列"<front=(Q->front+1)%MAXSZIE;
Q->count--;
}
}
//打印顺序队列
void display(Queue* Q) {
	int i = 0;
	int j = Q->front;
	if (IsEmpty(Q)) {
	cout<<"空队列"<count) {
			cout<queue[j]<<" ";
			j = (j + 1) % MAXSIZE;
			i++;
		}
		cout<

队列应用--银行排队

 

三、串和稀疏矩阵

KMP匹配算法


前缀:包含首位字符但不包含末位字符的子串
前缀:包含末位字符但不包含首位字符的子串
next数组的定义:当主串与模式串的某一位字符不匹配时,模式串要回退的位置
next[j]:其值=第j位前面j-1位字符组成的子串的前后缀重合字符数+1
[输入样例1]
ludongli
dongli    
[输出样例1]2
[输入样例2]
ababcabcacbab
abc
[输出样例2]
3
[输入样例3]
abcdefghigk
higke
[输出样例2]
no
 


#include
using namespace std;
#define MAXSIZE 100
//串的结构定义
typedef struct 
{
char data[MAXSIZE];
int length;
}String;
int Strlen(String s)
{
return s.length;
}
//求子串的next数组函数
void GetNext(char ch[],int length,int *next)
{//length是子串ch的长度 next为数组这里用到址传递
next[1]=0;//0位置不存储数据
int i=1,j=0;
while(i<=length)
{
if(j==0||ch[i]==ch[j])next[++i]=++j;
else j=next[j];
}
}
//KMP快速匹配算法 母串和子串 返回匹配的字符串在母串中第一个字符出现的索引位置
int KMP(String Mother,String Son)
{
int next[MAXSIZE];
int i=1,j=1;
int len=Strlen(Son);
GetNext(Son.data,len,next);
/*	cout<<"子串的next数组为:";
	for(int i=1;i<=len;i++)
	{
		cout<Son.length) return i-Son.length;//返回匹配的子串在母串中第一个字符出现的索引位置
else return -1;//匹配失败 没有找到
}
//串的创建
void Strcre(String *s)
{
char temp;
int i=1;
s->length=0;
while(1)
{
t=getchar();
if(t!='\n')
{
s->data[i]=t;
i++;
s->length++;
}
else break;
}
}
int main()
{
String Mother,Son;
Strcre(&Mother);
Strcre(&Son);
int flag=KMP(Mother,Son);
if(flag!=-1)
{
cout<<"匹配位置:"<

LCS动态规划--最长公共子串

【问题描述】
给定两个字符串,求出它们两个最长的相同子字符串的长度。
最长公共子串(Longest Common Substring)是一个非常经典的面试题目,在实际的程序中也有很高的实用价值。同学们不应该单单只是写出该问题的基本解决代码而已,关键还是享受把算法一步步的优化,让时间和空间复杂度一步步的减少的惊喜。
【输入形式】
两个字符串
最长的公共子串,若没有则输出“no”
【输出形式】
【样例输入】
acbcbcef
abcbced
【样例输出】
abcbce
【解释说明】
1.设计状态
f[i][j]表示序列s1 ,s2的最长公共子序列的长度
2.状态转移方程
f[I][j]=0,i=0或者j=0
f[i][j]=1+f[i-1][j-1], xi=yi
f[i][j]=max{f[i-1][j],f[i][j-1]}  xi!=yi
时间复杂度O(mn)+O(m+n)


#include
#include
#define MAX 1010
using namespace std;
char s1[MAX],s2[MAX];//两个字符串
int f[MAX][MAX],n,m;//动态规划矩阵 n m记录两个串长度

//LCS函数部分
void LCS(int i,int j)
{
//设置退出条件|终止条件
if(i==0;j==0)return;
if(s1[i-1]==s2[j-1])
{
LCS(i-1,j-1);
cout<f[i-1][j]) LCS(i,j-1);
else LCS(i-1,j);
}
int main()
{
	
	cin>>s1>>s2;
	n = strlen(s1);
	m = strlen(s2);
	for (int i = 1; i <= n; i++)
	{//填充状态表格
		for (int j = 1; j <= m; j++)
		{
			if (s1[i - 1] == s2[j - 1])
			{//xi=yi  
				f[i][j] = 1 + f[i - 1][j - 1];
			}
			else
			{//xi!=yi
				f[i][j] = max(f[i - 1][j], f[i][j - 1]);
			}
		}
	}
	LCS(n, m);
	return 0;
}

稀疏矩阵的快速转置

稀疏矩阵基本操作
1.什么是稀疏矩阵?非零元素组成的矩阵,只存储非零元素
2.用三元组法表示稀疏矩阵   i,j,data  行 列 数据
3.快速转置
输入样例:
3 3
0 0 0
1 2 3
0 0 0
输出样例:
行 列 元素值
2 1 1
2 2 2
2 3 3
快速转置后:
1 2 1
2 2 2
3 2 3

#include
#define MAX 100
using namespace std;
typedef int DataType;
//三元组结构定义
typedef struct
{
int row ,col;//行列坐标
DataType val;//数据
}SPNode;
//稀疏矩阵结构定义
typedef struct
{
int rows,cols;//行列数
int n;//非零元素个数
SPNode data[MAX];
}SPMatrix;
//创建稀疏矩阵  输入二维矩阵  变成 稀疏矩阵存储方式 节省内存空间
void create(SPMatrix& s, DataType a[MAX][MAX])
{
	int p = 1,i,j;
	for (i = 1; i <= s.rows; i++)
	{

		for (j = 1; j <= s.cols; j++)
		{
			if (a[i][j] != 0)
			{
				s.data[p].row = i, s.data[p].col = j;
				s.data[p].val = a[i][j]; p++;
			}
		}
	}
	s.n = p-1 ;
}
//输出稀疏矩阵的三元组表示形式
void display(SPMatrix s)
{
	cout << "行 列 元素值" << endl;
	for (int i = 1; i <= s.n; i++)
	{
		cout << s.data[i].row << " " << s.data[i].col << " " << s.data[i].val << endl;
	}
}
//快速转置算法
void FastTran(SPMatrix t,SPMatrix&t2)
{
int q, number[MAX], position[MAX];
	//number[]统计某个列元素出现的个数
	// position[]统计某个列元素第一次出现的位置
	
//两稀疏矩阵行列交换 
    t2.cols = t.rows;
	t2.rows = t.cols;
	t2.n = t.n;
//扫描列 所有列元素可能的值的个数重置为0 
	for (int j = 1; j <= t.cols; j++)
	{
		number[j] = 0;
	}
//列元素出现个数统计 
	for (int j = 1; j <= t.n; j++)
	{
		number[t.data[j].col]++;
	}
position[1] = 1; int j;
    for (j = 2; j <= t.cols; j++)
	{
		position[j] = position[j - 1] + number[j - 1];
	}
for (int p = 1; p <= t.n; p++)
	{
		j = t.data[p].col;
		q = position[j];//取首地址
		t2.data[q].col = t.data[q].row;
		t2.data[q].row = t.data[q].col;
		t2.data[q].val = t.data[q].val;
		position[j]++;
	}
}
int main()
{
	SPMatrix s,s2;
	DataType a[MAX][MAX];
	int i, j;
	cout << "输入行 列" << endl;
	cin >> s.rows >> s.cols;
	for (i = 1; i <= s.rows; i++)
	{
		for (j = 1; j <= s.cols; j++)
		{
			cin >> a[i][j];
		}
	}
	create(s, a);
	display(s);
	FastTran(s, s2);
	cout << "快速转置后:" << endl;
	display (s2);
}

四、树

二叉树的创建和两种遍历方法(递归和递归工作栈模拟)

【问题描述】
 给出一个按照先序遍历得出的字符串,'#' 代表空的子节点,大写字母代表节点内容。请通过这个字符串建立二叉树,
 并分别采用“递归”和“非递归”的先序、中序、后序遍历的算法分别输出每一个非空节点。
【输入形式】
 输入只有一行,包含一个字符串S,用来建立二叉树。保证S为合法的二叉树先序遍历字符串,
 节点内容只有大写字母,且S的长度不超过100。
【输出形式】
 共有6行,每一行包含一串字符,表示分别按递归和非递归的先序、中序、后序遍历得出的节点内容,
 每个字母后输出一个空格。请注意行尾输出换行。
【样例输入】
ABC##DE#G##F###
【样例输出】
A B C D E G F
C B E G D F A
C G E F D B A
A B C D E G F
C B E G D F A
C G E F D B A

#include
#include
#include
using namespace std;
//二叉树结构定义
typedef struct Node
{
    char date;
    struct Node* lchild, * rchild;
} Node, * Tree;
int d = 0, ma = 0, num = 0;
//创建二叉树
void build(Tree&T)
{
char ch;
cin>>ch;
if(ch==='#')T=NULL;
else if(ch=="\n")return ;
else
{
T=new Node;
T->data=ch;
build(T->lchild);
build(T->rchild);
}
}
//递归遍历
void DLR(Tree T)
{
    if (T)
    {
        cout << T->date << " ";
        DLR(T->lchild);

        DLR(T->rchild);
    }
}
void LDR(Tree T)
{
    if (T)
    {
        LDR(T->lchild);
        cout << T->date << " ";
        LDR(T->rchild);
    }
}

void LRD(Tree T)
{
    if (T)
    {
        LRD(T->lchild);
        LRD(T->rchild);
        cout << T->date << " ";
        if (ma < d)
            ma = d;//防止同一层多次相加
    }
}
//非递归算法
//先序遍历
/*1. 建立一指针root,令其指向二叉树的根节点
2. 如果栈为空则返回,不为空则循根节点->左子树的顺序一路向左访问节点并入栈
3. 当到达最左边节点时,栈中最上方元素出栈
4. 如果出栈元素有右子树,则令root指向它,并重复2、3步
5. 如果出栈元素无右子树,则继续出栈元素并重复4、 5步
*/
void preorder(Tree root)
{
if(!root)return ;
stacks;//递归工作栈模拟
while(root)
{
cout<data<<" ";
if(root->lchile)
{//左子树存在 当前根入栈 待输出
s.push(root);
//更新根结点
root=root->lchild;
}
else if(root->rchild)
{//右子树存在 更新根结点
root=root->rchild;
}
else
{
while (!root->rchild && !s.empty())//右子树非空 或 栈非空
            {//先序遍历: 根 左 右
                root = s.top();//回溯到之前的根结点
                s.pop();//取出栈元素
            }
            root = root->rchild;//更新根结点
}
}
}
/*
1.设立指针root,并指向二叉树的根节点
2.找到root最左边的节点,并将沿路节点入栈
3.元素出栈并令root指向它,访问之
4.如果root有右子树,令root指向它,算法回到2
5.如果root没有右子树,重复3、4、5*/
void inorder(Tree root)
{//中序遍历 左 根 右
    if (!root)return;
    stacks;
    while (root)
    {
        while (root->lchild)//找到最左边的结点
        {
            s.push(root);
            root = root->lchild;
        }
        cout << root->date << " ";//访问
        while (!root->rchild && !s.empty())
        {
            root = s.top();
            s.pop();
            cout << root->date << " ";
        }
        root = root->rchild;
    }
}
/*
1.设立指针root,指向二叉树根节点
2.一路向左走,走到最左边节点,并将沿途节点压人栈中,且标记为true
3.访问栈顶节点
4.如果栈顶节点标记为true,将其标记为false,令root指向其右子树
5.如果栈顶节点标记为false,将其出栈并访问之
注意后序遍历需要有一个标记的东东看看是否被访问过*/
struct TreeNodeWithMark
{
    Tree node;
    bool isFirst;
    TreeNodeWithMark(Tree p):node(p),isFirst(true){}//初始化设置可访问
};
void postorder(Tree root)
{//后序遍历 左 右 根
        if (!root)
        {
            return;
        }
        stack s;
        s.push(new TreeNodeWithMark(root));
        TreeNodeWithMark* curr;
        while (!s.empty())
        {
            curr = s.top();
            root = curr->node;
            s.pop();
            if (curr->isFirst)
            {
                curr->isFirst = false;
                s.push(curr);
                if (root->rchild)
                {
                    s.push(new TreeNodeWithMark(root->rchild));
                }

                if (root->lchild)
                {
                    s.push(new TreeNodeWithMark(root->lchild));
                }
            }
            else
            {
                cout << root->date << " ";
            }
        }
    }


int main()
{
    Tree T;
    build(T);
    DLR(T); cout << endl;
    LDR(T); cout << endl;
    LRD(T); cout << endl;

    preorder(T); cout << endl;
    inorder(T); cout << endl;
    postorder(T); cout << endl;
    return 0;
}

二叉树深度和最长叶路径


【问题描述】考研真题:求二叉树的深度及二叉树中最远两个结点的距离。
【输入形式】拓展的前序遍历序列
【输出形式】深度和距离
【样例输入】
AB#C##DE#G#H##F##
【样例输出】
5 6
[解析]
         A
     B        D
    # C    E    F
     # #  # G  # #
           # H
            # #
求最远两个结点的距离,实际上就是两个叶子的距离 
 首先明确距离最大的两个结点出现位置:1,同时在根结点的左子树中;2,同时在根结点的右子树中;3,左右子树中各有一个结点。
  将左子树两个结点最长距离,右结点两个结点最长距离,左、右子树高度之和比较,取得三者中的最大值,即为这棵二叉树中两个结点距离的最大值。
1 全局变量flag保存结果
2 对T求最大深度
求节点最大深度的函数:
1 求左子树最大深度leftHeight
2 求右子树最大深度rightHeight
3 计算当前节点的直径 temp = left+right,判断temp是否大于flag,维护全局变量flag
4 返回当前节点深度 1 + max(leftHeight, rightHeight)
 

#include
using namespace std;
//二叉树结构定义
typedef struct Node
{
    char date;
    struct Node* lchild, * rchild;
} Node, * Tree;
int d = 0, ma = 0, num = 0;
//拓展序列创建二叉树
void build(Tree& T)
{
    char ch;
    cin >> ch;
    if (ch == '#')
        T = NULL;
    else if (ch == '\n')
    {
        return;
    }
    else
    {
        T = new Node;// 让指针实体化 new返回的是jied的指针
        T->date = ch;
        build(T->lchild);
        build(T->rchild);
    }
}
//求取深度
int Depth(Tree T)
{
if(T==NULL)return 0;//递归退出条件
int leftDepth=Depth(T->lchild);
int rightDepth=Depth(T->rchild);
//三目运算符 如果左深度大于右深度 左+1 否则 右+1
return leftDepth>rightDepth?leftDepth+1:rightDepth+1;
}
int flag;
//求取二叉树最大宽度
int findMaxLen(Tree T,int &flag)
{
if(T==NULL)return 0;
int lMaxlen=findMaxLen(T->lchild,flag);
int rMaxlen=findMaxLen(T->rchild,flag);
flag=max(flag,lMaxlen+rMaxlen);
return max(lMaxlen,rMaxlen)+1;
}
int main()
{
    Tree T;
    build(T);
    int deep = Depth(T);
    cout <

二叉树左右子树交换

【问题描述】二叉树按照二叉链表的方式存储。
编写程序,计算二叉树中叶子结点的数目并输出;
编写程序,将二叉树的每个结点的左、右子树进行交换,
请注意不是只交换结点的data值,而是左、右孩子指针指向的交换,最后输出交换后的二叉树的后序遍历序列。
【输入形式】二叉树的前序遍历序列,空指针的位置输入字符#
【输出形式】叶子结点的数目;左右子树交换后,后序遍历的序列,空子树的位置输出字符#
【样例输入】
ABE##F##CG###
【样例输出】
3
###GC##F##EBA
[解析]叶子特点就是 左右孩子都是空
所谓左右子树交换倒不如说是按照 根右左的方式遍历得到 AC#G##BF##E## 然后将这个序列按照先序序列创建一个“交换后”的树 ,然后按照后序顺序输出 遇到空输出@
 

#include
using namespace std;
//二叉树结构定义
typedef struct Node
{
    char date;
    struct Node* lchild, * rchild;
} Node, * Tree;
//二叉树的创建
void build(Tree& T) {
    char ch;
    cin >> ch;
    if (ch == '#')T = NULL;
    else if (ch == '\n')return;
    else
    {
        T = new Node;
        T->date = ch;
        build(T->lchild);
        build(T->rchild);
    }
}
int n;
n=0;
//输出叶子总数
void findLeaf(Tree root)
{
    if (root == NULL)return;
    if (root->lchild != NULL)findLeaf(root->lchild);
    if (root->rchild != NULL)findLeaf(root->rchild);
    if (root->lchild == NULL && root->rchild == NULL)n++;
    return;
}

void LRD(Tree T)
{
    if (T)
    {
        LRD(T->lchild);
        LRD(T->rchild);
        cout << T->date;
    }
    else
    {
        cout << "#";
    }
}
char a[100];
char* p = a;
//将二叉树按照 根右左 的序列输出赋值为数组a[]
void DRL(Tree root)
{
    if (root)
    {
        *p = root->date;
        p++;
        DRL(root->rchild);
        DRL(root->lchild);
    }
    else
    {//遇到空赋值#
        *p = '#';
        p++;
    }
}
string b(a);
int index = 0;
//根据数组a交换后的先序拓展序列创建二叉树
void Build(Tree& T) {
    char ch;
    ch = b[index]; index++;
    if (ch == '#')T = NULL;
    else if (ch == 0)return;
    else
    {
        T = new Node;
        T->date = ch;
        Build(T->lchild);
        Build(T->rchild);
    }
}
int main ()
{
    Tree T;
    build(T); n = 0;
    findLeaf(T);
    cout << n << endl;

    DRL(T);//得到a[]=AC#G##BF##E## 赋值给string b
    b = a;
    //cout << b << endl;
    Tree t;
    Build(t);
    LRD(t);
    return 0;
}

给定数组创建完全二叉树

【问题描述】课后作业习题25:n个结点的完全二叉树顺序存储在一维数组a中,设计一个算法,实现对此二叉树的先序遍历。
【输入形式】一维数组a,以#结束输入,请接收后存储在一维数组a中
【输出形式】先序遍历序列
【样例输入】ABCDEF#
【样例输出】ABDECF
【解析】
ABCDEF 在数组中的位置对应于:(实际上位置1对应a[1-1] 位置6对应a[6-1]这个要注意一下)
123456 1根 2i是左子树 3i是右子树   一共6 (假设有n)
step1:build(T,1) 初始化 左右子树都设置空 
step2:当前为编号1的结点 赋值为A   T->data=a[1-1]=a[0]='A' 
step3:然后开始判断要不要建立左子树 和 右子树
判断条件:2*i左孩子编号<=6(总结点数) 就递归创建左子树 
step:4:   2*i+1右孩子编号<=6 表示 右孩子存在 就递归创建右子树

#include
using namespace std;
char a[100];
int n;
//二叉树结构定义
typedef struct Node
{
    char date;
    struct Node* lchild, * rchild;
} Node, * Tree;
//根据数组创建二叉树
Tree build(Tree& T,int i) {
    if (i > n)return NULL;
    else
    {
        T = new Node;
        T->lchild = NULL;//初始化
        T->rchild = NULL;
        T->date = a[i-1];//给当前根赋值 注意对应到数组时要减1 因为数组索引是从0开始
        if (2 * i <= n)
        {
            build(T->lchild,2*i);
        }
        if (2 * i + 1 <= n)
        {
            build(T->rchild,2*i+1);
        }
    }
    return T;
}
void DLR(Tree T)
{
    if (T!=NULL)
    {
        cout << T->date;
        DLR(T->lchild);
        DLR(T->rchild);
    }
}
int main ()
{
    Tree T;
    cin>> a;
    for (int i = 0; a[i]; i++)
    {
        n++;
    }
    n = n - 1;//将结束符不计算在里面 也就是ABCDEF一共6个结点
    Tree t=build(T,1);
    DLR(t);
    return 0;
}

根据前中序创建二叉树 找公共祖先

【问题描述】假设二叉树采用二叉链表方式存储,root指向根结点,p所指结点和q所指结点为二叉树中的两个不同结点,
且互不成为根到该结点的路径上的点,编程求解距离它们最近的共同祖先。
【输入形式】二叉树的前序和中序遍历序列,用以创建该二叉树的链式存储结构;以及二叉树的两个结点数据 x 和 y
【输出形式】结点数据值为 x 和结点数据值为 y 的最近的共同祖先,若没有共同祖先则输出NULL,请注意一个结点本身不能成为另一个结点的共同祖先。
【样例输入】
GABDCEF
BDAEFCG                  
DF
abcdefgh
bcafegdh 
【样例输出】
A
【基本思路】首先要解决的是根据前中序列创建二叉树
根据先序遍历的特点,可知preorder[0]一定是整棵二叉树的根节点,那么,如果已确定根节点值在中序遍历序列inorder中的下标是i,就一定有
inorder[0 : i - 1]这些值属于根节点的左子树
inorder[i + 1, n - 1]这些值属于根节点的右子树
因为中序遍历是从最左边开始遍历,所以当遍历到根节点inorder[i]时,之前遍历到的节点一定都属于左子树
那么现在的目的就是如何确定左右子树的根节点,由先序遍历可知
preorder[0 + 1]一定是左子树的根节点
preorder[0 + 1 + (i - 0)]一定是右子树的根节点
因为先序遍历遍历到的节点顺序是从根节点到最左边,再到右边,那么既然已经直到左子树上有多少节点了(i - 0个),那么就可以确定右子树的根节点
直接递归即可

#include
#include
using namespace std;
//二叉树的结构定义
typedef struct Node
{
    char date;
    struct Node* lchild, * rchild;
} Node, * Tree;
//根据先序中序创建二叉树
Tree build(int prestart, int instart, int inend, vectorpreorder, vectorinorder)
{
    if (prestart >=preorder.size() || instart > inend)return NULL;
    Tree root = new Node;
    root->date = preorder[prestart];
    int inIndex = 0;
    for (int i = instart; i <= inend; i++)
    {//找到根节点
        if (inorder[i] == preorder[prestart])
        {
            inIndex = i; break;
        }
    }//根节点索引为inIndex
    //递归调用 创建这个根节点的左子树和柚子树
    root->lchild = build(prestart + 1, instart, inIndex - 1, preorder, inorder);
    root->rchild = build(prestart + 1 + inIndex - instart, inIndex + 1, inend, preorder, inorder);
return root;
}
void DLR(Tree T)
{
    if (T)
    {
        cout << T->date ;
        DLR(T->lchild);
        DLR(T->rchild);
    }
}
void LRD(Tree T)
{
    if (T)
    {
        LRD(T->lchild);
        LRD(T->rchild);
        cout << T->date;
    }
}
//寻找公共祖先
Tree search(Tree T, Tree node1, Tree node2)
{
    if (T == NULL)return NULL;
    if (T->date == node1->date || T->date == node2->date)return T;
    Tree left = search(T->lchild, node1, node2);
    Tree right = search(T->rchild, node1, node2);
    if (left && right)return T;
    if (left)return left;
    if (right)return right;
    return NULL;
}
Tree Getparent(Tree T, Tree node1, Tree node2)
{
    Tree p = search(T, node1, node2);
    return p;
}
int main ()
{
    char a[100], b[100];
    cin >> a >> b;

    vectorinorder;
    vectorpreorder;
    for (int i = 0; a[i]; i++)
    {
        preorder.push_back(a[i]);
        inorder.push_back(b[i]);
    }
    //cout << inorder.size() << preorder.size();
    Tree T;
    T = build(0, 0, inorder.size()-1,preorder,inorder);
    LRD(T); cout << endl;
    return 0;
}

根据中后序创建二叉树

【问题描述】
 根据一棵二叉树的中序遍历序列和后序遍历序列,
求这棵树的前序遍历序列。
【输入形式】 
一棵树的中序遍历序列和该树后序遍历序列。
输入序列中仅含有小写字母,且没有重复的字母
【输出形式】 一棵树的前序遍历序列
【样例输入】
dbeafcg
debfgca
【样例输出】
abdecfg
 

#include
#include
using namespace std;
typedef struct Node
{
    char date;
    struct Node* lchild, * rchild;
} Node, * Tree;
Tree build(vectorinordeer, vectorpostorder, int instart, int inend,int postart, int poend)
{//inorder中序序列数组 postorder后序序列数组 instart在中序数组中需要检索的开始位置 postart后序数组序号检索的开始位置,poend终止位置 对应根的位置
    if (instart > inend)return NULL;
    else
    {
        Tree root = new Node;
        root->date = postorder[poend];//找到根位置并且赋值
        int pos;//记录根的索引
        //在中序中找到根所在的位置
        for (pos = instart; pos <= inend; pos++)
        {
            if (inordeer[pos] == root->date)break;
        }
    //递归查找左子树 和右子树 
           root->lchild = build(inordeer, postorder, instart, pos - 1, postart, postart + pos - instart - 1);
           root->rchild = build(inordeer, postorder, pos + 1, inend, pos - instart + postart, poend - 1);
           return root;
    }   
}
void DLR(Tree T)
{
    
    if (T)
    {
        cout << T->date ;
        DLR(T->lchild);
        DLR(T->rchild);
    }
}
int main ()
{
    char a[100], b[100];
    cin >> a >> b;
    vectorinorder;
    vectorpostorder;
    for (int i = 0; a[i]; i++)
    {
        inorder.push_back(a[i]);
        postorder.push_back(b[i]);
    }
    /*cout << inorder.size() << postorder.size();*/
    Tree T;
    T = build(inorder, postorder, 0, inorder.size()-1, 0, postorder.size()-1);
    DLR(T); cout << endl;

    return 0;
}

根据广义表创建二叉树 按照层次输出

问题描述】 考研真题:给定一颗二叉树,要求从下至上按层遍历二叉树,每层的访问顺序是从左到右,每一层单独输出一行。
【输入形式】 广义表表示的二叉树,结点元素类型为整型,且都大于0,例如:1( 2( 3 ( 4, 5 ) ), 6( 7, 8( 9, 10 ) ) )
【输出形式】 从下至上,打印每一层的结点元素值,元素间以空格隔开。每层的访问顺序是从左到右,每一层单独输出一行。
【样例输入】 
1(2(3(4,5)),6(7,8(9,10)))
,字符串内没有空格
【样例输出】
 4 5 9 10
 3 7 8
 2 6
 1
【解析】
由于是倒着从最后一层开始从左至右按层遍历
所以面临以下问题:
1.如何根据广义表构造树(注意节点不是字母,而是数字(可能是多位数)!!!!!)
2.如何从最后一层依次向上面的层遍历
3.如何输出一层之后换行再输出另一行
针对问题2,只要在按层遍历的时候从右至左遍历,最后输出到数组里,再将数组逆序输出就好
针对问题3,就是设置一个东西来记录节点的层数。这里用到了二维数组,int a[][2],
    其中a[][0]用来记录节点的数字,a[][1]用来记录层数
针对问题1,解决办法是先用gets将广义表读入字符串s[],然后依次从开头往后读,
    发现‘0’~‘9’之间的字符,就放到字符数组num[]中,直到不再是‘0’~‘9’之间的字符,
    然后将num[]转换成数字(可以用sscanf)

#include
#include						// strlen(), memset()
#include 
#define MAXSIZE 100
using namespace std;
typedef struct node
{
	int data;
	int depth;						//记录深度,同一深度输出到同一行
	struct node* lchild, * rchild;
}Node, * Tree;
int a[MAXSIZE][2];
Tree create()
{
Tree stack[MAXSIZE],p,T=NULL;
	char ch, s[MAXSIZE] = { 0 }, num[10] = { 0 };
	int flag, top = -1, len, i = 0, j = 0, sum = 0;
	fgets(s, MAXSIZE - 1, stdin);			//先读到数组中,这样做,数据就可以重复读了!功 能: 从流中读取一字符串
	len = strlen(s);
	while (1)
	{
		memset(num, '\0', 10);			//每次别忘了对num清零!
		ch = s[i];
		if (s[i] >= '0' && s[i] <= '9')	        //发现一个字符是'0'~'9',可能是多位数
		{
			j = 0;
			while (s[i] >= '0' && s[i] <= '9')
			{
				num[j++] = s[i++];
			}
			sscanf(num, "%d", &sum); //sscanf字符串转数字
		}
		else
			i++;					//如果不是数字,要i++,下次就可以读下一个字符

		if (ch == '\n' || i >= len)		        //发现'\n'或者是s[]到头了就返回
			return T;
		else
		{
			switch (ch)
			{
			case '(': stack[++top] = p;
				flag = 1;
				break;
			case ')': top--;
				break;
			case ',': flag = 2;
				break;
			default: p=new Node;
				p->data = sum;
				p->depth = 0xfffffff;//初始化
				p->lchild = NULL;
				p->rchild = NULL;
				if (T == NULL)
				{
					T = p;
					p->depth = 1;			//记录深度
				}
				else if (flag == 1)
				{
					stack[top]->lchild = p;
					p->depth = top + 2;	//记录深度
				}
				else
				{
					stack[top]->rchild = p;
					p->depth = top + 2;	//记录深度
				}
			}
		}
	}
}
int layer(Tree T)
{//用队列来实现层数遍历
	Tree queue[MAXSIZE], p;
	int front, rear, i = 0;
	if (T != NULL)
	{
		queue[0] = T;
		front = -1;
		rear = 0;
		while (front < rear)
		{
			p = queue[++front];
			a[i][0] = p->data;
			a[i++][1] = p->depth;				//将遍历的节点记录到a[][]
			if (p->rchild != NULL)				//先右,便是从右至左遍历
				queue[++rear] = p->rchild;
			if (p->lchild != NULL)
				queue[++rear] = p->lchild;
		}
	}
	return --i;							//返回节点个数!别忘了是--i
}
int main()
{
	Tree T;
	int i = 0, dep;
	T = create();					//根据广义表构建树
	i = layer(T);						//按层遍历,从右至左
	dep = a[i][1];
	for (; i >= 0; i--)					//遍历所有的节点以输出
	{
		if (dep != a[i][1])				//如果发现深度跟之前的不同,就换行
		{
			cout << endl;
			dep = a[i][1];
		}
		cout<

求二叉树最大宽度的两种算法

【问题描述】求解二叉树的宽度,请用递归和非递归两种方法求解。
【输入形式】前序和中序序列建树
【输出形式】递归和非递归求解的宽度值
【样例输入】
abcdefg
cbdaegf
【样例输出】
3 3
【解析】本案例的树为:
       a             宽度为1
    b     e          宽度为2
  c   d  # f         宽度为3
 # # # #  # g        宽度为1
           # #

#include
#include
#include
using namespace std;
typedef struct Node
{
    char date;
    struct Node* lchild, * rchild;
} Node, * Tree;

Tree build(int prestart, int instart, int inend, vectorpreorder, vectorinorder)
{
    if (prestart >= preorder.size() || instart > inend)return NULL;
    Tree root = new Node;
    root->date = preorder[prestart];
    int inIndex = 0;
    for (int i = instart; i <= inend; i++)
    {//找到根节点
        if (inorder[i] == preorder[prestart])
        {
            inIndex = i; break;
        }
    }//根节点索引为inIndex
    //递归调用 创建这个根节点的左子树和柚子树
    root->lchild = build(prestart + 1, instart, inIndex - 1, preorder, inorder);
    root->rchild = build(prestart + 1 + inIndex - instart, inIndex + 1, inend, preorder, inorder);
    return root;
}
int Depth(Tree T)
{
    if (T == NULL)return 0;//退出条件
    int leftDepth = Depth(T->lchild);
    int rightDepth = Depth(T->rchild);
    return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
    //三目运算符 如果左深度大于右深度 左+1 否则 右+1
}
int Width(Tree T)
{//非递归算法求宽度
    queueq;
    int count, max=0;//记录每一层宽度和最大宽度
    Tree temp;
    if (T == NULL)return max;
    q.push(T);//当前根入队
    while (!q.empty())//队列不为空
    {
        count = q.size();//当前层宽度等于队列长度
        if (count > max)max = count;//更新最大宽度
        for(int i=0;ilchild != NULL)//左子树非空 左子树入队
                q.push(temp->lchild);
            if (temp->rchild != NULL)
                q.push(temp->rchild);
        }
    }
    return max;
}
int Count[100];
int Max = -1;
void width(Tree T, int k)//k为当前结点所在层数
{//递归算法求最大宽度
    if (T == NULL)return;
    Count[k]++;//当前结点非空 记录当前层数的Count++
    if (Max < Count[k])Max = Count[k];//更新最大宽度
    width(T->lchild,k+1);
    width(T->rchild, k + 1);
}
int main()
{
    char a[100], b[100];
    cin >> a >> b;
    //    char p, q;
    //    cin >> p >> q;
    vectorinorder;
    vectorpreorder;
    for (int i = 0; a[i]; i++)
    {
        preorder.push_back(a[i]);
        inorder.push_back(b[i]);
    }
    //cout << inorder.size() << preorder.size();
    Tree T;
    T = build(0, 0, inorder.size() - 1, preorder, inorder);
    width(T,0);//递归求最大宽度;
    cout << Width(T)<<" "<

最优二叉树(哈夫曼树) 哈夫曼编码

【问题描述】
读入n个字符所对应的权值,构造一棵哈夫曼树,自底向上生成每一个字符对应的哈夫曼编码,并依次输出。
【输入形式】
输入的第一行包含一个正整数n,表示共有n个字符需要编码。其中n不超过100。
第二行中有n个用空格隔开的正整数,分别表示n个字符的权值。
【输出形式】
共n行,每行一个字符串,表示对应字符的哈夫曼编码。
【注意】保证每次左子树比右子树的权值小;如出现相同权值的,则先出现的在左子树。
【样例输入】
8
5 29 7 8 14 23 3 11
【样例输出】
0001
10
1110
1111
110
01
0000
001

[解析]
输入
30 5 10 20 
哈夫曼编码表:前四个是叶子 后面2*n-1到n+1编号为分支节点 
HT        1    2    3     4    5       6     7
weight    30    5    10   20      15   35    65
parent  7   5   5    6    6    7     0
lchild  0   0   0    0    2    5     1
rchild  0   0   0    0    3    4     6
哈夫曼树:
       65
 (1)30     (0)35
         (1)15  (0)20
       (1)5  (0)10
哈夫曼编码:
30 :'0'
5  :'100'
10 :'101'
20 :'11'

#include
#include
#include
#define MAX 20000
using namespace std;
int n;//叶子结点个数
int m;//总结点个数
typedef struct
{
	int weight;//权重
	int parent;//父节点
	int lchild;//左孩子
	int rchild;//右孩子
}HTNode;
typedef HTNode HuffmanTree[MAX];//哈夫曼树
typedef char* HuffmanCode[MAX];//哈夫曼编码

void select(HuffmanTree HT, int k, int& s1, int& s2)
{//寻找叶子结点1和k之前权最小的两个点 记录编号s1s2并且返回
	int tmp = MAX, tmpi = 0;
	for (int i = 1; i <= k; i++)
	{//第一次查找叶子1和k之间最小的点
		if (!HT[i].parent)
		{//无parent

			if (tmp > HT[i].weight)
			{
				tmp = HT[i].weight;
				tmpi = i;
			}
		}
	}
	s1 = tmpi;//找到了第一个最小的点
	tmp = MAX; tmpi = 0;
	for (int i = 1; i <= k; i++)
	{
		if ((!HT[i].parent) && i != s1)
		{//无parent且跟s1相异
			if (tmp > HT[i].weight)
			{
				tmp = HT[i].weight;
				tmpi = i;
			}
		}
	}
	s2 = tmpi;
}
void creatrHuffmanTree(HuffmanTree& HT, int* w)
{
	for (int i = 1; i <= n; i++)
	{//叶子初始化
		HT[i].weight = w[i];
		HT[i].lchild = 0;
		HT[i].rchild = 0;
		HT[i].parent = 0;
	}
	for (int i = n + 1; i <= m; i++)
	{//初始化分支节点 从n+1到2*n-1=m
		HT[i].weight = 0;
		HT[i].lchild = 0;
		HT[i].parent = 0;
		HT[i].rchild = 0;
	}
	for (int i = n + 1; i <= m; i++)
	{
		int s1, s2;
		select(HT, i - 1, s1, s2);//查找目前未连接的结点权最小的两个结点的编号
		HT[s1].parent = i;//构建parent结点
		HT[s2].parent = i;
		if (HT[s1].weight > HT[s2].weight)
		{
			HT[i].lchild = s1;
			HT[i].rchild = s2;
		}
		else
		{
			HT[i].lchild = s2;
			HT[i].rchild = s1;
		}
		HT[i].weight = HT[s1].weight + HT[s2].weight;
	}
}
void encodingHuffmanCode(HuffmanTree HT, HuffmanCode& HC)
{//根据哈夫曼树生成哈夫曼编码HC
	char tmp[MAX];
	tmp[n - 1] = '\0';
	int start, child, father;
	for (int i = 1; i <= n; i++)
	{//根据叶子结点 向上追溯 到根节点退出
		start = n - 1;
		child = i;
		father = HT[i].parent;
		while (father)
		{//追溯到根结点就退出
			if (HT[father].lchild == child)tmp[--start] = '1';//如果左孩子存在编码1
			else tmp[--start] = '0';//右孩子存在 编码为0
			child = father;//向上遍历
			father = HT[father].parent;
		}
		HC[i] = new char[n - start];
		strcpy(HC[i], &tmp[start]);
	}
}
void displayHuffmanCoding(HuffmanCode HC) {
	for (int i = 1; i <= n; i++) {
		printf("%s\n", HC[i]);
	}
	cout<> n;
	m=2*n-1;
	int w[MAX];
	for (int i = 1; i <= n; i++)
	{
		cin >> w[i];
	}
	HuffmanTree HT;
	HuffmanCode HC;
	creatrHuffmanTree(HT, w);
	encodingHuffmanCode(HT, HC);
	displayHuffmanCoding(HC);
	return 0;
}

哈夫曼树最短带权路径

【问题描述】
 已知输入一串正整数,正整数之间用空格键分开,请建立一个哈夫曼树,以输入的数字为叶节点,求这棵哈夫曼树的带权路径长度。
【输入形式】
 首先输入正整数的个数,然后接下来为接下来的正整数,正整数个数不超过10个
【输出形式】
 输出相应的权值
【样例输入】
 5 
 4 5 6 7 8
【样例输出】
 69
【解析】
输入
4
30 5 10 20
哈夫曼编码表:前四个是叶子 后面2*n-1到n+1编号为分支节点
HT        1    2    3     4    5       6     7
weight    30    5    10   20      15   35    65
parent  7   5   5    6    6    7     0
lchild  0   0   0    0    2    5     1
rchild  0   0   0    0    3    4     6
depth   1   3   3    2    0    0     0
哈夫曼树:
       65
    30     35
         15  20
       5  10
带权路径长度=叶子权*叶子深度 求和
W(T)=30*1+5*3+10*3+20*2=115

#include
#define MAX 20000
using namespace std;
int N;//叶子结点个数
int M;//总结点个数
typedef struct
{
	int weight;//权重
	int parent;//父节点
	int lchild;//左孩子
	int rchild;//右孩子
}HTNode;
typedef HTNode HuffmanTree[MAX];//哈夫曼树
void select(HuffmanTree HT, int k, int& s1, int& s2)
{//寻找结点1到结点k之间最小权的两个结点 编号记为s1 s2
	int tmp = MAX, tmpi = 0;
	for (int i = 1; i <= k; i++)
	{//第一次查找叶子1和k之间最小权的结点
		if (!HT[i].parent)
		{//无parent 可以构建
			if (tmp > HT[i].weight)
			{
				tmp = HT[i].weight;
				tmpi = i;
			}
		}
	}
	s1 = tmpi;//找到了第一个权最小的结点编号为s1
	tmp = MAX; tmpi = 0;
	for (int i = 1; i <= k; i++)
	{
		if ((!HT[i].parent) && i != s1)
		{//无parent且与s1相异的点
			if (tmp > HT[i].weight)
			{
				tmp = HT[i].weight;
				tmpi = i;
			}
		}
	}
	s2 = tmpi;
}
void createHuffmanTree(HuffmanTree& HT, int* w)
{//w为权数组
	for (int i = 1; i <= N; i++)
	{//叶子哈夫曼表初始化
		HT[i].weight = w[i];
		HT[i].lchild = 0;
		HT[i].rchild = 0;
		HT[i].parent = 0;
		
	}
	for (int i = N + 1; i <= M; i++)
	{//初始化分支节点 从n+
		HT[i].weight = 0;
		HT[i].lchild = 0;
		HT[i].parent = 0;
		HT[i].rchild = 0;
	
	}
	for (int i = N + 1; i <= M; i++)
	{
		int s1, s2;
		select(HT, i - 1, s1, s2);
		HT[s1].parent = i;
		HT[s2].parent = i;
		HT[i].lchild = s1;
		HT[i].rchild = s2;
		HT[i].weight = HT[s1].weight + HT[s2].weight;
	}
}
/*计算机步骤:
1.第一个for和第二个for循环后得到
HT		1	2	3	 4    5	   6	 7
weight	30	5	10   20	  0    0     0
parent  0   0   0    0    0    0     0
lchild  0   0   0    0    0    0     0
rchild  0   0   0    0    0    0     0
第三个for循环详细步骤:
第一次: i=5 s1=2 s2=3 
更新哈夫曼表:
HT		1	2	3	 4    5	   6	 7
weight	30	5	10   20	  15   0     0
parent  0   5   5    0    0    0     0
lchild  0   0   0    0    2    0     0
rchild  0   0   0    0    3    0     0
第二次:i=6 s1=4 s2=5
更新哈夫曼表:
HT		1	2	3	 4    5	   6	 7
weight	30	5	10   20	  15   35    0
parent  0   5   5    6    6    0     0
lchild  0   0   0    0    2    4     0
rchild  0   0   0    0    3    5     0
第三次:i=7 s1=1 s2=6
更新哈夫曼表:
HT		1	2	3	 4    5	   6	 7
weight	30	5	10   20	  15   35    65
parent  7   5   5    6    6    7     0
lchild  0   0   0    0    2    4     1
rchild  0   0   0    0    3    5     6
*/
int WPL(HuffmanTree HT, int i, int depth)
{//递归计算最短带权路径长度
	if (HT[i].lchild == 0 && HT[i].rchild == 0)
	{//i为叶子结点
		return HT[i].weight * depth;
	}
	else
	{
		return WPL(HT, HT[i].lchild, depth + 1) + WPL(HT, HT[i].rchild, depth + 1);
	}
}
/*
* 【样例输入】
4
30 5 10 20
 哈夫曼树:
	   65
	30     35
		 15  20
	   5  10
HT		1	2	3	 4    5	   6	 7
weight	30	5	10   20	  15   35    65
parent  7   5   5    6    6    7     0
lchild  0   0   0    0    2    5     1
rchild  0   0   0    0    3    4     6
depth   1   3   3    2    0    0     0
计算机步骤:
step1:i=2*N-1=M=7 WPL(HT,7的左子树编号:1,深度:1)+WPL(HT,7的右子树编号:6,深度:1)
step2:i=1	编号为1的结点的左右为空 是叶子结点 返回权*深度:30*1 返回到step1的左WPL
step3:i=6   WPL(HT,6的左子树编号:5 深度:2)+WPL(HT,6的右子树编号:4 深度:2)
step4:i=5   WPL(HT,5的左子树编号:2,深度:3)+WPL(HT,5的右子树编号:3 深度:3)
step5:i=2  编号为2的结点的左右为空 是叶子结点 返回权*深度:5*3 返回到setp4的左WPL
step6:i=3  编号为3的结点是叶子结点 返回权*深度:10*3 返回到setp4的右WPL 回到setp4:5*3+10*3整体作为一个结果返回到step3的左WPL
step7:i=4  编号为4的结点是叶子结点 返回权*深度:20*2 返回到stpe3的右WPL 回到step3:5*3+10*3+20*2整体作为一个结果返回到step1的右WPL
step8: 返回step1的左右WPL到主函数:30*1+5*3+10*3+20*2=115
*/
int main()
{
	HuffmanTree T;
	cin >> N;//叶子结点个数
	M = 2 * N - 1;//总结点个数
	int w[MAX];
	for (int i = 1; i <= N; i++)
	{
		cin >> w[i];
	}
	createHuffmanTree(T, w);
	cout << WPL(T, M, 0);
	return 0;

}

中序线索二叉树

【问题描述】创建一棵二叉树,接着中序线索化该二叉树,然后编写相应函数遍历该中序线索二叉树
【编码要求】线索二叉树遍历过程中不能使用递归、不能使用栈。
【输入形式】二叉树拓展的前序遍历序列
【输出形式】中序遍历序列
【样例输入】AB#D##CE###
【样例输出】BDAEC

【解析】
【样例输入2】
ABC##DE#G##F###
【输出样例2】
CBEGDFA
中序二叉树:
          A
    B           #
 C     D
# #  E    F
    # G  # #
     # #
中序遍历:CBEGDFA
标记前驱后继结点:
如果有左孩子Lchild就指向左孩子,Ltag=0
没有的话Lchild指向遍历前驱,Ltag=1
如果有右孩子Rchild就指向右孩子,Rtag=0
没有的话Rchild指向遍历后继,Rtag=1

#include
using namespace std;
typedef struct Node
{
    char date;
    struct Node* lchild, * rchild;
    int Ltag, Rtag;//标记前驱后继结点  如果有左孩子Lchild就指向左孩子,Ltag=0||没有的话Lchild指向遍历前驱,Ltag=1
    //如果有右孩子Rchild就指向右孩子,Rtag=0 ||没有的话Rchild指向遍历后继,Rtag=1
} Node, * Tree;
void build(Tree& T)
{//根据先序扩展构建二叉树
    char ch;
    cin >> ch;
    if (ch == '#')T = NULL;
    else if (ch == '\n')return;
    else
    {
        T = new Node;
        T->date = ch;
        T->Ltag = 0;
        T->Rtag = 0;
        build(T->lchild);
        build(T->rchild);
    }
}
Node* pre;//前驱结点
void Inthread(Tree root)
{//二叉树中序线索化 更新结点的Ltag和Rtag
    if (root!=NULL)
    {
        Inthread(root->lchild);//线索化左子树
        if (root->lchild == NULL)
        {//没有左孩子,Ltag=1 左孩子指向前驱
            root->lchild = pre;
            root->Ltag = 1;
        }
        if (pre!=NULL && pre->rchild==NULL)
        {//前驱非空 且 前驱没有右孩子 前驱Rtag=1 前驱右孩子指向当前的结点
            pre->rchild = root;
            pre->Rtag = 1;
        }
        pre = root;
        Inthread(root->rchild);//线索化右子树
    }
}
void TInOrder(Tree BT)
{//根据中序线索化二叉树中序遍历输出
    Tree p = BT;
    while (p != NULL)
    {//当前结点非空
        while (p->Ltag == 0)
            //在线索树中找到中序遍历第一个结点
            p = p->lchild;
        cout << p->date;//输出中序遍历第一个结点
        while (p->Rtag == 1 && p->rchild != NULL)
        {//如果有后继结点且右子树非空
            p = p->rchild;//移步到右子树
            cout << p->date;
        }
        p = p->rchild;//移步到右子树 中序遍历:左中右
    }
}
int main()
{
    Tree T;
    build(T);//先序扩展建立二叉树
    Inthread(T);//中序线索化
    pre->Rtag = 1;//前驱结点有后继
    TInOrder(T);//根据中序线索化二叉树中序输出
    return 0;
}

五、图

图的邻接表创建

数据结构图的创建
图的分类:
无向图 有向图
存储结构:
邻接矩阵 邻接表
输入解析
顶点数 边数
顶点信息
边的信息(起点 终点)
输入样例1:
4 5
a b c d
a b
b c
c d
d a
b a
5 6
a b c d e 
a b
b c
c d
d e
e a
a c
f a
输入样例2:
5 6
1 2 3 4 5
1 2
1 4
2 3
2 5
3 4
3 5
5 6
1 2 3 4 5
1 2
1 4
2 3
2 5
3 4
3 5

#include
using namespace std;
#define ElemType char//顶点的数据类型
#define MAX 20 //最大顶点个数
typedef struct ArcNode
{
	int addressvex;//存储的是该顶点数组中的位置
	struct ArcNode* next;
}ArcNode;//边表

typedef struct VNode
{
	ElemType vertex;//是哪一个顶点
	struct ArcNode* firstarc;
}VNode;//单个顶点

typedef struct
{
	VNode AdjList[MAX];//顶点构成的结构体数组
	int vexnum, arcnum;//顶点数和边数
	int kind;//图的类型: 无向图 有向图
}ALGraph;//图的结构定义

int LocateVex(ALGraph* G, ElemType c)
{//在顶点表中查找顶点的位置
	
	for (int i = 1; i <= G->vexnum; i++)
	{
		if (c == G->AdjList[i].vertex)return i;
	}
	cout << "没有找到点" << c << endl; return -1;
}

void CreateDG(ALGraph* G)
{//创建有向图
	//1.输入顶点数和边数
	cin >> G->vexnum >> G->arcnum;
	//2.顶点表数据填值,初始化顶点表指针域
	for (int i = 1; i <= G->vexnum; i++)
	{
		cin >> G->AdjList[i].vertex;
		G->AdjList[i].firstarc = NULL;
	}
	//3.输入边的信息构造邻接表 
	ElemType v1, v2;
	int n, m;//记住点在顶点表中的位置
	ArcNode* p;
	for (int i = 1; i <= G->arcnum; i++)
	{
		cin >> v1 >> v2;
		n = LocateVex(G, v1);
		m = LocateVex(G, v2);
		if (n == -1 || m == -1)
		{
			cout << "没有这个顶点!" << endl; return;
		}
		p = new ArcNode;
		p->addressvex = m;
		p->next = G->AdjList[n].firstarc;//头插法
		G->AdjList[n].firstarc = p;
	}
	G->kind = 1;
}
void CreateUDG(ALGraph* G)
{//创建无向图
	//#1.输入顶点数和边数
	cin >> G->vexnum >> G->arcnum;
	//2.输入顶点元素
	for (int i = 1; i <= G->vexnum; i++)
	{
		cin >> G->AdjList[i].vertex;
		G->AdjList[i].firstarc = NULL;
	}
	//3.输入边信息构造邻接表

	ElemType v1, v2;
	int n, m;//记住点在顶点表中的位置
	ArcNode* p1,  *p2;
	for (int i = 1; i <= G->arcnum; i++)
	{
		cin >> v1 >> v2;
		n = LocateVex(G, v1);
		m = LocateVex(G, v2);
		if (n == -1 || m == -1)
		{
			cout << "没有找到该点!" << endl;
			return;
		}
		p1 = new ArcNode;
		p1->addressvex = m;//第二个点在第一个点的数组中的位置
		p1->next = G->AdjList[n].firstarc;//头插法
		G->AdjList[n].firstarc = p1;

		p2 = new ArcNode;
		p2->addressvex = n;
		p2->next = G->AdjList[m].firstarc;
		G->AdjList[m].firstarc = p2;
	}
	G->kind = 2;
}
void display(ALGraph G)
{//打印邻接表
	ArcNode* p;
	for (int i = 1; i <= G.vexnum; i++)
	{
		cout << endl << G.AdjList[i].vertex;
		p = G.AdjList[i].firstarc;
		while (p != NULL)
		{//遍历i为起点的其他边
			cout << "-->" << p->addressvex;
			p = p->next;
		}
	}
	cout << endl;
}
int main()
{
	ALGraph G,D;
	cout << "创建有向图:" << endl;
	CreateDG(&G);
	display(G);
	cout << "创建无向图:" << endl;
	CreateUDG(&D);
	display(D);
}

图的邻接矩阵

数据结构图的创建
图的分类:
无向图 有向图
存储结构:
邻接矩阵 邻接表
输入解析
顶点数 边数
顶点信息
边的信息(起点 终点)
输入样例1(带权有向图+不带权的无向图):
5 8
1 2 3 4 5
1 2 4
2 1 5
1 3 6
3 1 7
3 4 8
3 5 6
5 3 9
4 3 10
4 5
a b c d
1 2 1 
2 3 1
3 4 1
4 1 1
2 1 1
输入样例2(带权的有向图+带权的无向图)
5 8
1 2 3 4 5
1 2 4
2 1 5
1 3 6
3 1 7
3 4 8
3 5 6
5 3 9
4 3 10
5 6
a b c d e
1 2 10
2 3 11 
3 4 5
4 5 4
5 1 6
1 3 8 
1 4 520
 

#include
using namespace std;
#define MAX 20

typedef int EdgeType;//边类型
typedef char VertexType;//顶点类型
typedef struct
{
	VertexType vex[MAX];//顶点数组
	EdgeType Edge[MAX][MAX];//领结数组
	int vexnum, edgenum;//顶点数和边数
	int kind;//类型 1是有向图 2是无向图
}AdjMatrix;
void CreateGraph(AdjMatrix* G,int kind)
{
	G->kind = 1;
	//#1.输入顶点数边数  初始化领接矩阵
	cin >> G->vexnum >> G->edgenum;
	for (int i = 1; i <= G->vexnum; i++)
	{
		for (int j = 1; j <= G->vexnum; j++)
		{
			if (i == j) G->Edge[i][j] = 0;
			else		G->Edge[i][j] = 32767;
		}
	}
	//#2.顶点信息 边信息

	for (int i = 1; i <= G->vexnum;i++)
	{
		cin >> G->vex[i];
	}
	int v1, v2;
	int w;
	for (int i = 1; i <= G->edgenum; i++)
	{
		cin >> v1 >> v2 >>w ;
		//若为不带权值的图 则w输入1
		//若为带权值的图 则w输入对应权值
		if (G->kind == 1) { G->Edge[v1][v2] = w; continue; }//①
		else
		{//无向图
			G->Edge[v1][v2] = w;//①
			G->Edge[v2][v1] = w;//②
		}
		//无向图具有对称性 通过①②实现
		//有向图没有对称性 只需要①
	}
	
}
void Display(AdjMatrix G)
{//输出领借矩阵
	
	for (int i = 1; i <= G.vexnum; i++)
	{
		cout<<"    "<< G.vex[i] << " ";
	}

	for (int i = 1; i <= G.vexnum; i++)
	{
		cout << endl<

尾插法建图 BFS广度优先搜索

输入样例1(带权有向图+不带权的无向图):
5 8
1 2 3 4 5
1 2
2 1
1 3
3 1
3 4
3 5
5 3
4 3
输入2:
4 5
a b c d
1 2
2 3
3 4
4 1
2 1

概念
广度优先搜索遍历类似于树的按层次遍历的过程。
广度优先搜索算法(又称宽度优先搜索)是最简便的图的搜索算法之一,
这一算法也是很多重要的图的算法的原型。
其别名又叫BFS,属于一种盲目搜寻法,
目的是系统地展开并检查图中的所有节点,以找寻结果。
换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
对于连通图(图如上深度搜索的连通图)
1.从图中某个顶点出发,访问v;
2.依次访问v的各个未曾访问过的邻接点;
3.分别从这些邻接点出发依次访问它们的邻接点,
并使“先被访问的顶点的邻接点”先于“后被访问的邻接点”被访问。
重复步骤3,直至图中所有已被访问的顶点的邻接点都被访问到。

广度优先搜索的特点是:
尽可能先对横向进行搜索。
设x和y是两个相继被访问过的顶点,若当前以x为出发点进行搜索,
则在访问x的所有未被访问过的邻接点之后,紧接着是以y为出发点进行横向搜索,
y的邻接点中尚未被访问的顶点进行访问。
也就是说,先访问的顶点其邻接点亦先被访问。
为此,算法实现时需要引进队列保存已被访问过的顶点。

#include
#include
using namespace std;
#define MAX 20
typedef int EdgeType;//边类型
typedef char VertexType;//顶点类型
typedef struct
{
	VertexType vex[MAX];//顶点数组
	EdgeType Edge[MAX][MAX];//领结数组
	int vexnum, edgenum;//顶点数和边数
	int kind;//类型 1是有向图 2是无向图
}AdjMatrix;
void CreateGraph(AdjMatrix* G, int kind)
{
	G->kind = 1;
	//#1.输入顶点数边数  初始化领接矩阵
	cin >> G->vexnum >> G->edgenum;
	for (int i = 1; i <= G->vexnum; i++)
	{
		for (int j = 1; j <= G->vexnum; j++)
		{
			G->Edge[i][j] = 0;
		}
	}
	//#2.顶点信息 边信息

	for (int i = 1; i <= G->vexnum; i++)
	{
		cin >> G->vex[i];
	}
	int v1, v2;
	for (int i = 1; i <= G->edgenum; i++)
	{
		cin >> v1 >> v2;
			G->Edge[v1][v2] = 1;
			G->Edge[v2][v1] = 1;
	}

}
int visited[MAX];//如果被访问 ==1
void BFS(AdjMatrix* G, int i)
{//广度优先搜索
	queue q;
	cout << G->vex[i]<< " ";//输出起点
	visited[i] = 1;//设置为已经访问
	q.push(i);//将第一个结点入队
	while (!q.empty())//队列非空
	{
		i = q.front(); q.pop();
		for (int j = 1; j <= G->vexnum; j++)
		{
			if (G->Edge[i][j] == 1 && visited[j] != 1)
			{
				cout << G->vex[j] << " ";
				visited[j] = 1;//已经访问
				q.push(j);
			}
		}
	}
}
int main()
{
	AdjMatrix G, D;
	//cout << "创建有向图:" << endl;
	//CreateGraph(&G,1);
	//cout << "BFS:" << endl;
	//BFS(&G,1);
	cout <

尾插法建图DFS深度优先搜索

输入样例1:
4 5
a b c d
a b
b c
c d
d a
b a
5 6
a b c d e
a b
b c
c d
d e
e a
a c
f a
输入样例2:
5 6
1 2 3 4 5
1 2
1 4
2 3
2 5
3 4
3 5
5 6
1 2 3 4 5
1 2
1 4
2 3
2 5
3 4
3 5
图的结构:多对多的关系
图中有点和边,点:数据,边:描述点之间的关系
主要描述方式:邻接矩阵,邻接表
邻接矩阵:基于数组->用一个一维数组存储顶点,用一个二位数组存储边
邻接表:基于链表->用一个一维数组或者一个链表来存储所有顶点
其中每个顶点都是一个链表的头结点,后面跟着所有能通过边到达的顶点
 

#include
using namespace std;
#define ElemType char//顶点的数据类型
#define MAX 20 //最大顶点个数
typedef struct ArcNode
{
	int addressvex;//存储的是该顶点数组中的位置
	struct ArcNode* next;
}ArcNode;//边表

typedef struct VNode
{
	ElemType vertex;//是哪一个顶点
	struct ArcNode* firstarc;
}VNode;//单个顶点

typedef struct
{
	VNode AdjList[MAX];//顶点构成的结构体数组
	int vexnum, arcnum;//顶点数和边数
	int kind;//图的类型: 无向图 有向图
}ALGraph;//图的结构定义

int LocateVex(ALGraph* G, ElemType c)
{//在顶点表中查找顶点的位置

	for (int i = 1; i <= G->vexnum; i++)
	{
		if (c == G->AdjList[i].vertex)return i;
	}
	cout << "没有找到点" << c << endl; return -1;
}
void CreateDG(ALGraph* G)
{//创建有向图
	//1.输入顶点数和边数
	cin >> G->vexnum >> G->arcnum;
	//2.顶点表数据填值,初始化顶点表指针域
	for (int i = 1; i <= G->vexnum; i++)
	{
		cin >> G->AdjList[i].vertex;
		G->AdjList[i].firstarc = NULL;
	}
	//3.输入边的信息构造邻接表 
	ElemType v1, v2;
	int n, m;//记住点在顶点表中的位置
	ArcNode* p;
	for (int i = 1; i <= G->arcnum; i++)
	{
		cin >> v1 >> v2;
		n = LocateVex(G, v1);
		m = LocateVex(G, v2);
		if (n == -1 || m == -1)
		{
			cout << "没有这个顶点!" << endl; return;
		}
		p = new ArcNode;
		p->addressvex = m;
		p->next = G->AdjList[n].firstarc;//头插法
		G->AdjList[n].firstarc = p;
	}
	G->kind = 1;
}
void CreateUDG(ALGraph* G)
{//创建无向图
	//#1.输入顶点数和边数
	cin >> G->vexnum >> G->arcnum;
	//2.输入顶点元素
	for (int i = 1; i <= G->vexnum; i++)
	{
		cin >> G->AdjList[i].vertex;
		G->AdjList[i].firstarc = NULL;
	}
	//3.输入边信息构造邻接表

	ElemType v1, v2;
	int n, m;//记住点在顶点表中的位置
	ArcNode* p1, * p2,*head,*head2;
	for (int i = 1; i <= G->arcnum; i++)
	{
		cin >> v1 >> v2;
		n = LocateVex(G, v1);
		m = LocateVex(G, v2);
		if (n == -1 || m == -1)
		{
			cout << "没有找到该点!" << endl;
			return;
		}
		p1 = new ArcNode;
		p1->addressvex = m;//第二个点在第一个点的数组中的位置
		p1->next = NULL;//尾插法
		head = G->AdjList[n].firstarc;
		if (head == NULL)
		{
			G->AdjList[n].firstarc = p1;
		}
		else
		{
			while (head->next != NULL)
			{
				head = head->next;
			}
			head->next = p1;
		}

		p2 = new ArcNode;
		p2->addressvex = n;
		p2->next = NULL;//尾插法
		head2 = G->AdjList[m].firstarc;
		if (head2 == NULL)
		{
			G->AdjList[m].firstarc = p2;
		}
		else
		{
			while (head2->next != NULL)
			{
				head2 = head2->next;
			}
			head2->next = p2;
		}
	}
	G->kind = 2;
}

int visited[MAX];//如果被访问 ==1
/*对于一个连通图,深度优先搜索遍历过程:
1.从图中某个顶点v出发,访问v;
2.找出刚刚访问过的顶点的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,重复该步骤,
直至刚访问过的顶点没有未被访问的邻接点为止;(递归思想)
3.返回前一个访问过的且仍有未被访问的邻接点的顶点,找出该顶点的下一个未被访问的邻接点,访问该顶点;
4.重复第2、3步骤,直至图中所有顶点都被访问过,搜索结束。
*/
void DFS(ALGraph* G, int i)
{//深度优先搜索
	ArcNode* p;
	cout << G->AdjList[i].vertex<<"  ";
	visited[i] = 1;//当前结点被访问
	p = G->AdjList[i].firstarc;
	while (p)
	{
		if (!visited[p->addressvex])//如果该点没有被访问就深度搜索该点
			DFS(G, p->addressvex);
		p = p->next;
	}
}
void DFSTraverse(ALGraph* G)
{
	for (int i = 1; i <= G->vexnum; i++)
	{//初始化
		visited[i] = 0;
	}
	for (int i = 1; i <= G->vexnum; i++)
	{
		if (!visited[i])DFS(G, i);//深度优先搜索
		cout << endl;
	}
}

int main()
{
	ALGraph G, D;
	cout << "创建有向图:" << endl;
	CreateDG(&G);
	cout << "DFS:" << endl;
	DFSTraverse(&G);
	cout << "创建无向图:" << endl;
	CreateUDG(&D);
	cout << "DFS:" << endl;
	DFSTraverse(&D);
}

最小生成树kruskal算法

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。
输入格式:
第一行包含两个整数 N,M,表示该图共有 N个结点和 M 条无向边。
接下来 M 行每行包含三个整数 Xi,Yi,Zi? 表示有一条长度为 Zi ?的无向边连接结点 Xi,Yi ?
输出格式:
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz。
输入输出样例
输入 #1复制
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
输出
7
处理过程:
根据权的大小给边数组排序:
1 2 2
1 3 2
1 4 3
3 4 3
2 3 4
再通过并查集判断某条边的两个端点是不是同一个组
如果不是那就连起来  所连边数+1 距离更新一下
如果是就跳到下一组边的两个端点判断
step1:遍历边1 检查端点1和端点2分别属于组1和组2 不同组 所以连起来  更新端点1属于组2 
        当前所连条数:1 长度:2
step2:遍历边2检查端点1和端点3分别属于组1和组3不同组 所以连起来 point[端点1的组:2]点2的组=组3
        当前所连条数:2 长度:2+2
step3:遍历边3检查端点1和端点4分别属于组1和组4不同组 所以连起来 point[端点1的组:2]点2的组=组4
        当前所连条数:3 长度:2+2+3
四个点构成树最多三条边条件满足可以退出
点 1 2 3 4
组 2 4 3 4
step4:遍历边4检查端点2和端点3 都属于组1 不更新
step5:遍历边5检查端点3和端点4都属于组1 不更新
并查集:https://blog.csdn.net/bjweimengshu/article/details/108332389

#include
#include //用到排序函数sort
using namespace std;
struct Line
{
	int a, b, lenth;//端点a  b 长度(权)lenth
};
bool cmp(Line l1, Line l2)
{//比较边l1和l2的长度(权)如果小于就返回1
	return l1.lenth < l2.lenth;
}
Line lines[1024];//存储边的数组
int point[1024];//点的数组 用法:标记每个点都是哪个组的
int getFather(int x)
{//并查集:
	return point[x] == x ? x : point[x] = getFather(point[x]);
	//如果相等 返回x
}
int main()
{
	int N, M;
	cin >> N >> M;
	for (int i = 1; i <= M; i++)
	{//输入M条边的端点和权
		cin >> lines[i].a >> lines[i].b >> lines[i].lenth;
	}
	sort(lines + 1, lines + 1 + M, cmp);//从lines[1]到lines[M] 按照cmp函数规则排序
	for (int i = 1; i <= N; i++)point[i] = i;//并查集初始化  默认各个点分组为其本身:点1 就是1组 点5就是5组
	int total = 0, distance = 0;//表示现在最小树所连接的边数 距离(权)
	for (int i = 1; total < N - 1; i++)
	{//N个点最多也就N-1个边遍历到连上N-1个边为止
		int groupnumber_a = getFather(lines[i].a);//利用并查集,找到两个端点所在组的组号
		int groupnumber_b = getFather(lines[i].b);
		if (groupnumber_a != groupnumber_b)
		{
			point[groupnumber_a] = point[groupnumber_b];
			total++;//当前所连的边数+1
			distance += lines[i].lenth;
		}
	}
	
	cout << distance;
}

最小生成树--prime算法

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。
输入格式:
第一行包含两个整数 N,M,表示该图共有 N个结点和 M 条无向边。
接下来 M 行每行包含三个整数 Xi,Yi,Zi 表示有一条长度为 Zi 的无向边连接结点 Xi,Yi 
输出格式:
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz。
输入输出样例
输入 
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
输出
7
处理过程:
last=1
首先从点1开始 探索到1-2 1-3 1-4 三条边 选择最小权的边进行连接:连接1-2 生成树条数:1 长度:2 last=2
探点2 探索到 1-3 1-4 2-3 三条边 选择权最小且未连接的边进行连接:连接1-3 生成树条数:2 长度2+2 last=3
探索点 3 探索到1-4 2-3 3-4 三条边 选择权最小的边进行连接:连接1-4 生成树条数:3 长度2+2+3
4个点已经连接了3条边退出 输出长度为7

以点1为起点的边 way[1]中:  (同时更新以点i为起点以点1为终点的边way[i])
输入:1 2 2 1 3 2 1 4 3
way[1][0].to:2  权:2     (way[2][0].to:1 权2)
way[1][1].to:3  权:2     (way[3][0].to:1 权2)
way[1][2].to:4  权:3     (way[4][0].to:1 权3)
输入:2 3 4 3 4 3
way[2][1].to:3  权:4     (way[3][1].to:2 权4)
way[3][1].to:4  权:3     (way[4][1].to:3 权3)
然后设置点i处到其他点最短距离:
distence[i] = 0x3f3f3f3f   i:1-n 设置为无穷
然后初始化g[](g[1]=1表示点1已连接 g[2]=0表示点2未连接)全部设置为0 未连接 再设置g[1]=1 已连接
然后设置last表示当前需要判断连接哪条边  的起点 初始化时候last=1 表示从点1开始判断要连哪条边
开始大循环:循环退出条件(已经连好n-1条边 cnt 小循环1:遍历点last的way数组 设置终点的distance
(第一次大循环)循环后得到的结果:
distance[2]=way[1][0]=2
distance[3]=2
distance[4]=3
小循环2:遍历点1到点n
(第一次大循环)循环后得到的结果:
min_dis等于distance[1]到distance[4]中最小的一个值:
min_dis=distance[2]=2
min_to=i=2
相当于找到了点1为起点 所有可能终点中 权最短的边1-2 将其连接
设置last=min_to=2 更新last起点
cnt=1 更新总连接的边数
g[last]=1 更新该次循环中起点last状态设置为已连接 : 点1已连接
last=2进入第二次大循环:
(第二次大循环)小循环1后得到的结果:
distance[1]=2 因为点1已经连接过这时这个数据已经没有用了
distance[3]=2     (2<4所以取小的)
distance[4]仍然是3没有更新
(第二次大循环)小循环2后得到的结果:
min_dis=distance数组中最小且没有连接过的也就是diatance[3]=2
min_to=3

这个算法的动态演示:
https://www.bilibili.com/video/BV1gM4y1L7aN
这个算法的代码演示:
https://www.bilibili.com/video/BV1W44y1177D
这个算法用到了容器vector CSDN用法补充:
https://blog.csdn.net/msdnwolaile/article/details/52708144

#include
#include
using namespace std;
struct Line {
	int to, len;//to表示这条边指向哪 len表示权(长度)
};
int distence[5005];//从没连接的组到已连接的组中每个点的最短距离
int g[5005];//组号存储 =1表示已经连好的组 =0表示还没有连的组
vectorways[5005];//动态数组ways[5][3]表示从点5出发的第(3+1)条边 ways[5][3].to表示这条从点5出发的边指向哪个点
int main()
{
	int n, m;
	cin >> n >> m;//n个结点 m个边
	for (int i = 1; i <= m; i++)
	{//输入处理
		int a, b, v;//两个端点和长度
		cin >> a >> b >> v;
		Line t1, t2;//由于是无相图,两个方向都可以到达,建立两个边 终点分别设置为对方
		t1.to = b; t2.to = a;
		t1.len = t2.len = v;
		ways[a].push_back(t1);//记录点a的情况:终点是b 长度是len
		ways[b].push_back(t2);
	}
	for (int i = 1; i <= n; i++)
	{//因为要选取最短距离 所以初始状态设置为无穷大
		distence[i] = 0x3f3f3f3f;
	}
	int ans = 0, cnt = 1, last = 1;//lsat:上一次最后一个连接的点 cnt:已连接点的数目 ans:长度和
	g[1] = 1;//从点1出发标记点1为已连接
	while (cnt < n)
	{//没连接到n-1个点就继续连
		for (int i = 0; i < ways[last].size(); i++)
		{//探索点last为起点的所有边
			int id = ways[last][i].to;//当前这个边指向的点
			if (g[id] == 0)
			{//如果点id未连接
				distence[id] = min(distence[id], ways[last][i].len);//更新last为起点 id为终点的权最小的边
			}	
		}
		int min_to = 0, min_dis = 0x3f3f3f3f;
		for (int i = 1; i <= n; i++)
		{
			if (distence[i] < min_dis && g[i] == 0)
			{
				min_dis = distence[i];
				min_to = i;
			}
		}
		last = min_to;
		g[last] = 1;
		ans += min_dis;
		cnt++;
	}
	cout << ans;
}

有向网的最短路径Dijkstra算法

基本策略:按最短路径长度递增的次序求得各条最短路径。
动画演示:
https://www.bilibili.com/video/BV1zz4y1m7Nq?spm_id_from=333.337.search-card.all.click 
基本思路:
1.每次从未标记的节点中选择距离出发点最近的节点,标记收录到最优路径的集合中
2.计算刚加入标记集合的 节点A的邻近节点B(不包含已经标记的)的距离
若 节点A到出发点的距离+节点A B的距离<节点B的边长就更新结点B距离出发点的距离和B的前驱结点
输入样例:
9 16
1 2 3 4 5 6 7 8 9
1 2 1
1 3 5
2 3 3
2 4 7
2 5 5
3 5 1
3 6 7
4 5 2
5 6 3
4 7 3
5 7 6
5 8 9
6 8 5
7 8 2
7 9 7
8 9 4
样例输入2:
9 14
1 2 3 4 5 6 7 8 9
1 2 4
1 8 8
2 8 11
2 3 8
3 4 7
3 6 4
3 9 2
4 5 9
4 6 14
5 6 10
6 7 2
7 9 6
7 8 1
8 9 7
输出:
0 4 12 19 28 16 18 8 14
NULL 1 2 3 4 3 6 1 3
输出解释:
1到i最短距离:0 4 12 19 28 16 18 8 14  i从1到9 例如 14表示从1到9的最短距离
1前驱 2前驱  3前驱 4前驱 5前驱 6前驱 7前驱 8前驱 9前驱
NULL  1      2     3     4     3     6       1     3 
例如我要找到结点1到结点5的最短路径:5 <- 4 <- 3 <- 2 <-1
结点1到结点9的最短路径:9 <- 3 <- 2 <- 1

#include
using namespace std;
#define MAX 20
#define inf 99999
#include
using namespace std;
#define ElemType char//顶点的数据类型
#define MAX 20 //最大顶点个数
typedef struct ArcNode
{
	int addressvex;//存储的是该顶点数组中的位置
	int weight;//权值
	struct ArcNode* next;
}ArcNode;//边表

typedef struct VNode
{
	ElemType vertex;//是哪一个顶点
	struct ArcNode* firstarc;
}VNode;//单个顶点

typedef struct
{
	VNode AdjList[MAX];//顶点构成的结构体数组
	int vexnum, arcnum;//顶点数和边数
}ALGraph;//图的结构定义

int LocateVex(ALGraph* G, ElemType c)
{//在顶点表中查找顶点的位置

	for (int i = 1; i <= G->vexnum; i++)
	{
		if (c == G->AdjList[i].vertex)return i;
	}
	cout << "没有找到点" << c << endl; return -1;
}

void CreateDG(ALGraph* G)
{//创建有向图
	//1.输入顶点数和边数
	cin >> G->vexnum >> G->arcnum;
	//2.顶点表数据填值,初始化顶点表指针域
	for (int i = 1; i <= G->vexnum; i++)
	{
		cin >> G->AdjList[i].vertex;
		G->AdjList[i].firstarc = NULL;
	}
	//3.输入边的信息构造邻接表 
	ElemType v1, v2;
	int n, m;//记住点在顶点表中的位置
	ArcNode* p;
	int w;
	for (int i = 1; i <= G->arcnum; i++)
	{
		cin >> v1 >> v2>>w;
		n = LocateVex(G, v1);
		m = LocateVex(G, v2);
		if (n == -1 || m == -1)
		{
			cout << "没有这个顶点!" << endl; return;
		}
		p = new ArcNode;
		p->addressvex = m;
		p->weight = w;
		p->next = G->AdjList[n].firstarc;//头插法
		G->AdjList[n].firstarc = p;
	}
}
int visited[MAX] , dst[MAX] , last[MAX];//visited[i]记录i是否被标记 dst[i]记录点i到出发点的路径长度 last[i]记录点i的前一个点
void Dijkstra(ALGraph G)
{//计算起点v1到终点v2的最短路径
	for (int i = 1; i <= G.vexnum; i++)
	{
		visited[i] = 0;
		dst[i] = inf;
	}
	dst[1] = 0;//起点到起点距离为0

	for (int i = 1; i <= G.vexnum; i++)
	{
		int min = inf;
		int minnum;
		for (int j = 1; j <= G.vexnum; j++)
		{//找到路径最短的顶点
			if (visited[j] == 0 && dst[j] < min) {
				min = dst[j];
				minnum = j;
			}
		}
		int v = minnum;//记录当前未标记的点集中 最短dst点编号
		visited[v] = 1;//设置已访问
		ArcNode* p = G.AdjList[v].firstarc;
		while (p != NULL)
		{//遍历该点的领接点,如果未访问且权值+距离<原距离 就更新当前距离 和前驱结点
			if (visited[p->addressvex] == 0 && p->weight + dst[v] < dst[p->addressvex])
			{
				dst[p->addressvex] = p->weight + dst[v];
				last[p->addressvex] = v;
			}
			p = p->next;
		}
	}
}
int main()
{
	ALGraph G;
	CreateDG(&G);
	Dijkstra(G);
	for (int i = 1; i <= G.vexnum; i++)
		if (dst[i] != inf)
			printf("%d ", dst[i]);
	cout << endl<<"NULL ";
	for (int i = 2; i <= G.vexnum; i++)
			cout<

有向无环图的拓扑排序

什么是拓扑排序?
动画讲解:
https://www.bilibili.com/video/BV1Hy4y1179t?spm_id_from=333.337.search-card.all.click
代码和思路讲解:
https://www.bilibili.com/video/BV11P4y1x76D?spm_id_from=333.337.search-card.all.click
拓扑排序(directed acyclic graph)可行的充要条件:给定的图是有向无环图
方法:用递归的深度优先搜索DFS
1.同时要检测环:如果DFS找到了正在进行的DFS的点,
则证明有环!因为如果x出边找到的点y的DFS还在进行,证明x就是
顺着y往下找到的,这就是环!
2.用一个数组记录每个点的状态:①还没遇到②DFS进行中③DFS已完成
3.每个DFS完成的点要排在最靠后的空位上,也就是所有移交排过的点的前面
4.最后要从每个点开始尝试DFS 保证给每个点都排过

输入样例:
5 6
1 2
2 3
3 4
4 5
2 4
2 5
输出样例:
1 2 3 4 5
输入样例2:
6 6
1 2 
1 6
2 3
6 3
3 5
5 4
输出样例2;
1 2 6 3 5 4
样例输入3:
6 7
1 2
1 6
2 3
6 3
3 5
5 4
4 3

#include
#include
#define MAX 100
using namespace std;
struct EDGE
{
	int v1, v2;
	EDGE* next;
}epool[MAX];
struct NODE
{
	int mark;//0:没有遇到 1:已经完成DFS -1:正在DFS中
	EDGE* first;
}nodes[MAX];
int N, M, etop;//点数 边数 内存值位置
bool valid = true;//DFS过程中是否已经遇到环 遇到环变成FALSE
stackansstack; //拓扑排序存起来
void addedge(int v1, int v2)
{//添加边的情况
	epool[etop].v1 = v1;
	epool[etop].v2 = v2;
	epool[etop].next = nodes[v1].first;
	nodes[v1].first = &epool[etop];
	etop++;
}
void dfs(int id)
{
	if (nodes[id].mark == -1)
	{//正在DFS 表示存在环
		valid = false;
		return;
	}
	if (nodes[id].mark == 1)
	{//已经完成DFS 直接返回
		return;
	}
	//之前没有遇到
	nodes[id].mark = -1;//标记为正在DFS
	EDGE* e = nodes[id].first;
	while (e != NULL)
	{
		dfs(e->v2);
		e = e->next;
	}
	ansstack.push(id);
	nodes[id].mark = 1;//标记为已经完成DFS
}
int main()
{
	int x, y;
	cin >> N >> M;
	for (int i = 1; i <= M; i++)
	{//建有向图
		cin >> x >> y;
		addedge(x, y);
	}
	for (int i = 1; i <= N; i++)
	{//对每个点都进行dfs避免漏点
		dfs(i);
	}
	if (valid)
	{//valid==true 可以得到拓扑排序
		while (!ansstack.empty())
		{//输出拓扑排序
			cout << ansstack.top() << " ";
			ansstack.pop();
		}
		cout << endl;
	}
	else cout << "存在环,无法拓扑排序";
}

图的应用--走迷宫DFS

/*
从某一点出发,沿着一个方向往下试探
当找到目标位置,还需回溯,以便找到所有路径
再比较最短路径,比较盲目,效率没有BFS高。
DFS运用到了栈 
【问题描述】给定一个迷宫,求从起点到终点最短步长 
5 4
1 1 2 1
1 1 1 1
1 1 2 1
1 2 1 1
1 1 1 2
1 1 4 3
*/

#include
using namespace std;
int m, n, p, q, Min=99999;
int a[100][100];//1表示空地 2表示障碍物
int v[100][100];//0表示未访问 1表示访问
int dx[4] = { 0,1,0,-1 }, dy[4] = {1,0,-1,0};//方向数组
void dfs(int x,int y,int step)
{

	if (x == p && y == q)
	{
		if (step < Min)
		{
			Min = step;
			return;//回溯
		}
	}
		//顺时针试探
		for (int k = 0; k <4; k++)
		{
			int tx, ty;
			tx = x + dx[k];
			ty = y + dy[k];
			if (a[tx][ty] == 1 && v[tx][ty] == 0)
			{
				v[tx][ty] = 1;
				dfs(tx, ty, step + 1);
				v[tx][ty] = 0;
			}
		}
//		//右
//		if (a[x][y + 1] == 1 && v[x][y + 1] == 0)//右边为空地且未访问
//		{
//			v[x][y + 1] = 1;//设置当前位置的右边为已访问
//			dfs(x, y + 1, step + 1);//再从右边这个点进行深度优先搜索
//			v[x][y + 1] = 0;//回溯后该右边的点重新设置为未访问
//		}
//		//下
//		if (a[x+1][y] == 1 && v[x+1][y] == 0)
//		{
//			v[x+1][y] = 1;
//			dfs(x+1, y , step + 1);
//			v[x+1][y] = 0;
//		}
//		//左
//			if (a[x][y-1 ] == 1 && v[x][y-1] == 0)
//		{
//			v[x][y-1] = 1;
//			dfs(x , y-1, step + 1);
//			v[x ][y-1] = 0;
//		}
//		//上
//			if (a[x - 1][y] == 1 && v[x - 1][y] == 0)
//			{
//				v[x - 1][y] = 1;
//				dfs(x - 1, y, step + 1);
//				v[x - 1][y] = 0;
//			}
	return;
}
int main()
{
	int startx,starty;
	cin >> m >> n;
	for (int i = 1; i <= m; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			cin >> a[i][j];//1空地 2障碍物
		}
	}
	cin >> startx >> starty;
	cin>> p >> q;//输入起点终点坐标
	v[startx][starty] = 1;
	dfs(startx, starty, 0);
	cout << Min;
	return 0;
}

图的应用--走迷宫BFS


广度优先解决迷宫问题
求解思路:将队首结点可拓展的点入队,
如果没有可扩展的点,就将队首结点出队。
重复以上步骤,直到到达目标位置或者队列为空,
BFS搜索的结果一定是最短的。BFS运用了队列
[输入实例]
5 4 
1 1 2 1
1 1 1 1
1 1 2 1
1 2 1 1
1 1 1 1
1 1 4 3
[输出实例]7
*/

#include
#include
using namespace std;
int a[100][100], v[100][100];
struct point{
	int x, y;
	int step;
};
queuer;//申请队列
int dx[4] = { 0,1,0,-1 }, dy[4] = {1,0,-1,0};//方向数组右下左上

int main()
{
	//输入
	int n, m,startx,starty,p,q;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			cin >> a[i][j];
		}
	}
	cin >> startx >> starty >> p >> q;
	//BFS广义搜索
	point start;
	start.x = startx;
	start.y = starty;
	start.step = 0;
	r.push(start);//将起点入队
	v[startx][starty] = 1;
	int flag;
	while (!r.empty())
	{
		int x = r.front().x;
		int y = r.front().y;
		if (x == p && y == q)
		{
			flag = 1;
			cout << r.front().step; break;
		}
		for (int k = 0; k < 4; k++)
		{
			int tx, ty;
			tx = x + dx[k];
			ty = y + dy[k];
			if (a[tx][ty] == 1 && v[tx][ty] == 0)
			{
				//入队
				point temp;
				temp.x = tx;
				temp.y=ty;
				temp.step = r.front().step + 1;
				r.push(temp);
				v[tx][ty] = 1;
			}
		}
		r.pop();//拓展完毕需要将队首元素出队
	}
	if(flag==0)
	{
		cout << "no answer!";
	}
	return 0;

}

图的应用--DFS 解决n皇后问题


这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3个解。最后一行是解的总个数。
输入格式
一行一个正整数 nn,表示棋盘是 n  n×n 大小的。
输出格式
只有一个数字,表示解的总数。
输入输出样例
输入 
8
输出 
92


#include
using namespace std;
const int N = 10;
int a[N];//a[i]表示第i行上的皇后放于a[i]列上
int cnt;
int n;
bool check(int x,int y)
{
	for (int i = 1; i <= x; i++)
	{
		if (a[i] == y)return false;
		if (i + a[i] == x + y)return false;
		if (i - a[i] == x - y)return false;
	}
	return true;
}
void dfs(int row)
{//表示第row皇后放在何处
	if (row == n + 1)
	{//产生了一组解
		for (int i = 1; i <= n; i++)
		{
			cout << a[i] << " ";
		}
		cout << endl;
		cnt++; return;
	}
	for (int i = 1; i <= n; i++)
	{
		if (check(row, i))
		{
			a[row] = i;
			dfs(row + 1);
			a[row] = 0;
		}
	}
}
int main()
{
	cin >> n;
	dfs(1);
	cout << cnt;//输出n皇后有多少组解
	return 0;
}

图的应用--DFS解决数独问题

【问题描述】
 数独是一种运用纸、笔进行演算的逻辑游戏。
 玩家需要根据9X9盘面上的已知数字,推理出所有剩余空格的数字,
数独问题的约束条件为:
l)数值范围仅限于l一9。
2)行中不允许重复数字。
3)列中不允许重复数字。
4)小九宫内不允许重复数字。
 【输入形式】
 输入为9X9的二维数组,每个数字均为0-9之间的数字,其中0表示该位置的数字为未知。
【输出形式】
 输出为9X9的二维数组,每个数字均为1-9之间的数字,满足
【样例输入】
0 0 3 5 0 0 0 0 2
0 0 8 6 0 0 0 0 0
0 7 0 0 0 0 1 0 0
0 1 0 0 0 0 6 0 0
0 5 0 0 1 0 0 7 0
0 0 6 9 0 0 0 3 0
0 0 9 0 0 0 0 5 0
0 0 0 0 0 9 7 0 0
6 0 0 0 0 8 9 0 0
【样例输出】
1 6 3 5 4 7 8 9 2
5 9 8 6 2 1 3 4 7
2 7 4 8 9 3 1 6 5
3 1 7 4 8 5 6 2 9
9 5 2 3 1 6 4 7 8
8 4 6 9 7 2 5 3 1
7 8 9 1 6 4 2 5 3
4 3 1 2 5 9 7 8 6
6 2 5 7 3 8 9 1 4

#include
using namespace std;
int a[9][9];
 //检查是否符合约束条件
bool check(int x, int y, int id)
{
	for (int i = 0; i < 9; i++)
	{
		if (a[i][y] == id)
		{//检查该列
			return false;
		}
		if (a[x][i] == id)
		{//检查该行
			return false;
		}
	}
	//检查该元素所在的九宫格是否有重复元素
	for (int i = x / 3 * 3; i < x / 3 * 3 + 3; i++)
	{
		for (int j = y / 3 * 3; j < y / 3 * 3 + 3; j++)
		{
			if (a[i][j] == id)
			{
				return false;
			}
		}
	}
}
void dfs(int x, int y)
{
	//最终递归退出条件
	if (x == 8 && y == 9)
	{//检测到第八行最后一个元素的时候基本上已经可以确定了,因为只剩下第九行了
		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{
				cout << a[i][j]<<" ";
			}
			cout << endl;
		}
		return;//搜索完毕,输出完毕退出DFS深度搜索 
	}
	if (y == 9)
	{//检索到每一行的最后一个元素 下一个遍历的是下一行第一个元素
		y = 0;
		x++;
	}
	if (a[x][y] != 0)
	{//检测到非零元素(已经确定)直接跳过检索下一个
		dfs(x, y + 1); return;
	}
	for (int i = 1; i <= 9; i++)
	{//对当前位置检查看看符不符合约束条件
		if (check(x, y, i))
		{
			a[x][y] = i;//暂时确定该位置为i 
			dfs(x, y + 1);//检索下一个位置 
			a[x][y] = 0;//如果退出了就回溯,重新检查当前位置的i,直到得到正确结果 
		}
	}
}
int main()
{
	for (int i = 0; i < 9; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			cin >> a[i][j];
		}
	}
	dfs(0, 0);
	return 0;
}


总结

作者(黎爬爬)画了很长时间整理和试验,制作和整理不易,如有错误,还望海涵。

以上代码和思路分享仅供参考。

你可能感兴趣的:(链表,数据结构,算法,c++)