仅供学习参考,有一些属于模板类算法需要记住,有一些设计应用需要了解大致思路
希望通过这篇文章,读者能了解数据结构大致要学习哪些内容,以便复习参考。
整理作者:黎爬爬(2745907300)
目录
文章目录
前言
一、链表
1.单链表
补充方法
2.双链表
3.循环链表与约瑟夫环
4.双向循环链表
二、栈和队列
1.栈
栈的应用--栈递归汉诺塔
栈的应用--后缀表达式
补充
2.队列
链式队列
顺序队列
循环队列
队列应用--银行排队
三、串和稀疏矩阵
KMP匹配算法
LCS动态规划--最长公共子串
稀疏矩阵的快速转置
四、树
二叉树的创建和两种遍历方法(递归和递归工作栈模拟)
二叉树深度和最长叶路径
二叉树左右子树交换
给定数组创建完全二叉树
根据前中序创建二叉树 找公共祖先
根据中后序创建二叉树
根据广义表创建二叉树 按照层次输出
求二叉树最大宽度的两种算法
最优二叉树(哈夫曼树) 哈夫曼编码
哈夫曼树最短带权路径
中序线索二叉树
五、图
图的邻接表创建
图的邻接矩阵
尾插法建图 BFS广度优先搜索
尾插法建图DFS深度优先搜索
最小生成树kruskal算法
最小生成树--prime算法
有向网的最短路径Dijkstra算法
有向无环图的拓扑排序
图的应用--走迷宫DFS
图的应用--走迷宫BFS
图的应用--DFS解决数独问题
总结
个人笔记总结
#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;
}
}
#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;
}
#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;
}
#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;
}
代码如下(示例):
#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();
#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
#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<
前缀:包含首位字符但不包含末位字符的子串
前缀:包含末位字符但不包含首位字符的子串
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<<"匹配位置:"<
【问题描述】
给定两个字符串,求出它们两个最长的相同子字符串的长度。
最长公共子串(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<
输入样例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 <
输入样例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);
}
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 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;
}
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 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
(第一次大循环)循环后得到的结果:
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;
}
基本策略:按最短路径长度递增的次序求得各条最短路径。
动画演示:
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 << "存在环,无法拓扑排序";
}
/*
从某一点出发,沿着一个方向往下试探
当找到目标位置,还需回溯,以便找到所有路径
再比较最短路径,比较盲目,效率没有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运用了队列
[输入实例]
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;
}
【问题描述】
数独是一种运用纸、笔进行演算的逻辑游戏。
玩家需要根据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;
}
作者(黎爬爬)画了很长时间整理和试验,制作和整理不易,如有错误,还望海涵。
以上代码和思路分享仅供参考。