本手册(一座屎山)仅限用于个人应试
author:kkzzjx
date:2020/7/3
网盘自取链接:链接:https://pan.baidu.com/s/13F-kjbI_miJwdaXrD2z15A
提取码:flyq
#include
#include
typedef struct node *List;
struct node
{
int data;
List next;
};
List creat1()
{
int k;
List s,head;
List L=(List)malloc(sizeof(struct node));
L->next=NULL;
head=L;
printf("please enter list until -1");
while(1)
{
scanf("%d",&k);
if(k==-1) break;
else
{
s=(List)malloc(sizeof(struct node));
s->data=k;
s->next=L->next;
L->next=s;
}
}
return head;
}
List creat2()
{
int k;
List s,r,head;
List L=(List)malloc(sizeof(struct node));
L->next=NULL;
head=L;
r=L;
printf("please enter list until -1\n");
while(1)
{
scanf("%d",&k);
if(k==-1) break;
else
{
s=(List)malloc(sizeof(struct node));
s->data=k;
r->next=s;
r=s;
}
}
r->next=NULL;
return head;
}
int length(List L)
{
List p=L;
int num=0;
while(p->next!=NULL)
{
num++;
p=p->next;
}
return num;
}
List findKth(List L,int k)//查找位序为i
{
List p=L->next;
int cnt=1;
while(p!=NULL&&cnt!=k)
{
p=p->next;
cnt++;
}
if(p) return p;//此处返回的是指针,,也可以返回值p->data
else return NULL;
}
List find(List L,int x)//查找值为x
{
List p=L->next;
while(p!=NULL&&p->data!=x)
{
p=p->next;
}
if(p->data==x) return p;
else return NULL;
}
List insert(List L,int x,int i) //位置i处插入x
{
List p,s;
if(i<=0||i>length(L)+1)
{
printf("error\n");
return NULL;
}
else
{
s=(List)malloc(sizeof(struct node));
p=findKth(L,i-1); //找到前驱结点
s->data=x;
s->next=p->next;
p->next=s;
printf("success");
return L;
}
}
List delete0(List L,int i) //删除位序i的元素 或者根据元素删...种类好多啊
{
List tmp=L;
if(i<=0||i>length(L))
{
printf("error\n");
return NULL;
}
else //关键容易bug的地方 当i取1时要分开考虑,让2号位接到head后面
{
List s=findKth(L,i);
if(i!=1)
{
List p=findKth(L,i-1);
p->next=s->next;
free(s);
}
else if(i==1)
{
tmp->next=s->next;
free(s);
}
}
return L;
}
void output(List L)
{
List p=L->next;
while(p)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
}
int main()
{
int k,i;
List L=creat2();
output(L);
printf("插入元素k=");
scanf("%d",&k);
printf("插入位置i=");
scanf("%d",&i);
L=insert(L,k,i);
output(L);
printf("删除位置i=");
scanf("%d",&i);
L=delete0(L,i);
output(L);
}
#include
#include
#define MAXSIZE 100
struct SqListNode{
char data[MAXSIZE];//此处的datatype为char
int last;/*last为数组下标,而length为线性表长度 length=last+1
注意在查找的时候用的是位序 ,不是数组下标 */
};
typedef struct SqListNode *List;
List creat()
{
int i=0;
char ch;
List L=(List)malloc(sizeof(struct SqListNode));
L->last=-1;
printf("请输入顺序表L中元素,以#结束\n" );
while((ch=getchar())!='#')
{
L->data[i++]=ch;
L->last++;
}
return L;
}
void insert(List L,char x,int i)
{
//注意插入时的溢出情况和位置不合法情况
int j;
if((L->last)>=MAXSIZE-1)
{
printf("overflow\n");
}
else if(i<1||i>(L->last)+2)
{
printf("error\n");
}
else
{
for(j=L->last;j>=i-1;j--) //数组元素向后退
{
L->data[j+1]=L->data[j];
}
L->last++;
L->data[i-1]=x;
printf("success\n");
}
}
void output(List L)
{
int i;
for(i=0;i<=L->last;i++)
printf("%c",L->data[i]);
printf("\n");
}
int main()
{
char ch;
int i=0;
List L=creat();
output(L);
printf("INPUT ch:\n");
ch=getche();
printf("\nINPUT position:\n");
scanf("%d",&i);
insert(L,ch,i);
output(L);
}
这个直接利用C++的stl库即可
#include
#include
using namespace std;
stack s;
queue q;
#include
#include
#include
using namespace std;
int kmp(char S[],char T[],int next[])
{
int i=1,j=1;
while(i<=strlen(S)&&j<=strlen(T))
{
if(j==0||S[i]==T[j])
{
++i;++j;
}
else
j=next[j];
}
int tmp;
if(j>strlen(T))
{
tmp=i-strlen(T);
return tmp;
}
else return 0;
}
void getnext(char T[],int next[])
{
int i=1,j=0;
next[1]=0;
while(i<strlen(T))
{
if(j==0||T[i]==T[j])
{
i++;j++;
next[i]=j;
}
else
{
j=next[j];
}
}
}
int main()
{
//char s[100]="abcabcad";
char s[100];
int i=0;
gets(s);
char t[100]="cad";
int next[1000]={0};
getnext(t,next);
int k=kmp(s,t,next);
printf("%d",k);
}
一、树的概念
结点的度
树的度
树的深度
兄弟
堂兄弟
森林
满二叉树 深度k 有2^k-1个结点
完全二叉树
二、二叉树性质
1. 第 i 层 至 多 有 2 i / 2 个 结 点 2. 深 度 为 k 的 二 叉 树 至 多 有 2 k − 1 个 结 点 3. 对 任 何 一 棵 二 叉 树 , 如 果 终 端 结 点 数 n 0 , 度 为 2 的 结 点 数 n 2 则 n 0 = n 2 + 1 证 明 4. n 个 结 点 的 完 全 二 叉 树 深 度 l o g 2 n + 1 或 l o g ( n + 1 ) 1.第i层至多有2^i/2个结点\\ 2.深度为k的二叉树至多有2^k-1个结点\\ 3.对任何一棵二叉树,如果终端结点数n_0,度为2的结点数n_2 则n_0=n_2+1\\ 证明\\ 4.n个结点的完全二叉树深度log_2n+1 或 log(n+1) 1.第i层至多有2i/2个结点2.深度为k的二叉树至多有2k−1个结点3.对任何一棵二叉树,如果终端结点数n0,度为2的结点数n2则n0=n2+1证明4.n个结点的完全二叉树深度log2n+1或log(n+1)
三、二叉树遍历
递归&&非递归(栈
层序遍历(队列
四、应用之表达式
中缀表达式:普通~~
前缀表达式;后~
五、利用两种遍历序列确定二叉树:必须要有中序遍历
六、哈夫曼树WPL的计算
七、哈夫曼树的构造,哈夫曼编码
八、树、森林和二叉树之间的转换
1.一棵树转换为二叉树
2.一棵二叉树转换为树
左孩子的所有右孩子都与根连起来
3.森林转二叉树
森林中每一棵树转为二叉树
习题:
一棵度为2的树与一棵二叉树有何区别?
1.度为2的树是不区分左子树和右子树.而二叉树是要分左子树和右子树的.
2.度为2的数不包含空树,而二叉树是可以有空树的. 总之,二叉树的定义要比度为2的树定义更为严格,更为详细.
#include
#include
using namespace std;
typedef struct TNode *bintree;
struct TNode
{
int data;
bintree left, right;
};
void preorder(bintree t)
{
if (t)
{
visit(t);
preorder(t->left);
preorder(t->right);
}
}
void inorder(bintree t)
{
if (t)
{
inorder(t->left);
visit(t);
inorder(t->right);
}
}
void postorder(bintree t)
{
if (t)
{
postorder(t->left);
postorder(t->right);
visit(t); //printf("%d",t->data);
}
}
/*------------以上为递归遍历*/
//非递归
void inorder2(bintree t)
{
stack<int> s;
bintree p = t;
while (p || !s.empty())
{
if (p)
{
s.push(p);
p = p->left;
}
else
{
printf("%d", s.top());
s.pop();
p = p->right;
}
}
}
//LevelOrder
void levelorder(bintree BT)
{
bintree T;
queue<bintree> q;
if (!BT)
return;
q.push(BT);
while (!q.empty())
{
T = q.front();
q.pop();
printf("%d", T->data);
if (T->left)
q.push(T->left);
if (T->right)
q.push(T->right);
}
}
void levelorderprint(bintree BT)
{
bintree T;
queue<bintree> q;
q.push(BT);
while (!q.empty())
{
T = q.front();
q.pop();
printf("%d ", T->data);
if (T->left)
q.push(T->left);
if (T->right)
q.push(T->right);
}
}
#define NoInfo -1;
bintree creat() //enter data until -1
{
int data;
bintree BT, T;
queue<bintree> q;
scanf("%d", &data);
if (data != NoInfo)
{
BT = (bintree)malloc(sizeof(struct TNode));
BT->data = data;
BT->left = NULL;
BT->right = NULL;
q.push(BT);
}
else
return NULL;
while (!q.empty())
{
T = q.front();
q.pop();
scanf("%d", &data);
if (data == NoInfo)
T->left = NULL;
else
{
T->left = (bintree)malloc(sizeof(struct TNode));
T->left->data = data;
T->left->left = NULL;
T->left->right = NULL;
q.push(T->left);
}
scanf("%d", &data);
if (data == NoInfo)
T->right = NULL;
else
{
T->right = (bintree)malloc(sizeof(struct TNode));
T->right->data = data;
T->right->left = NULL;
T->right->right = NULL;
q.push(T->right);
}
}
return BT;
}
#include
#include
#include
using namespace std;
/*二叉搜索树*/
typedef struct TNode *Bintree;
struct TNode{
int data;
Bintree left,right;
};
Bintree insert(Bintree BST,int x)
{
if(!BST)
{
BST=(Bintree)malloc(sizeof(struct TNode));
BST->data=x;
BST->left=BST->right=NULL;
}
else
{
if(x<BST->data) insert(BST->left,x);
else if(x>BST->data) insert(BST->right,x);
}
return BST;
}
Bintree Findmin(Bintree BST)
{
if(BST)
{
while(BST->left)
{
BST=BST->left;
}
}
return BST;
}
Bintree deletex(Bintree BST,int x)
{
Bintree tmp;
if(!BST)
{
printf("要删除的元素未找到");
}
else
{
if(x<BST->data) deletex(BST->left,x);
else if(x>BST->data) deletex(BST->right,x);
else
{
if(BST->left!=NULL||BST->right!=NULL)
{
tmp=Findmin(BST);
BST->data=tmp->data;
BST->right=deletex(BST->right,BST->data);
}
else
{
tmp=BST;
if(!BST->left) BST=BST->right;
else BST=BST->left;
free(tmp);
}
}
}
return tmp;
}
void LevelOrderTraversal(Bintree BT){
queue<Bintree> q;
Bintree T;
if(!BT)
return;
q.push(BT); // BT 入队
while(!q.empty()){
T = q.front(); // 访问队首元素
q.pop(); // 出队
printf("%d",T->data);
if(T->left)
q.push(T->left);
if(T->right)
q.push(T->right);
}
}
int main()
{
Bintree BST;
BST=insert(BST,21);
BST=insert(BST,18);
BST=insert(BST,37);
BST=insert(BST,42);
BST=insert(BST,65);
BST=insert(BST,24);
BST=insert(BST,19);
BST=insert(BST,26);
BST=insert(BST,45);
BST=insert(BST,25);
BST=deletex(BST,37);
LevelOrderTraversal(BST);
}
(xdu版本)
typedef struct{
char bits[N];
int start;
datatype data;
}codetype;
codetype code[N];
typedef struct{
float weight;
datatype data;
int left,right,parent;
} huffman;
void enc(codetypde code[],huffman tree[])
{
int i,c,p;
codetype cd;
for(i=0;i<N;i++)
{
cd.start=N;
c=i;//c存走过的孩子
p=tree[c].parent;
while(p!=-1)
{
cd.start--;
if(tree[p].left==c)
cd.bits[cd.start]='0';
else
cd.bits[cd.start]='1';
c=p;//往上移动
p=tree[c].parent;//往上移动
}
code[i]=cd;
}
}
void dec(codetype code[],huffman tree[])
{
int i,c,m,b;
int endflag=-1;
i=m-1; //此时i是根节点
scanf("%d",&b);//读入一个代码
while(b!=endflag)
{
if(b==0)
i=tree[i].left;
else
i=tree[i].right;
if(tree[i].left==-1)
{
putchar(code[i].data);
i=m-1;
}
scanf("%d",&b);
}
if(tree[i].left!=0&&i!=m-1) printf("ERROR");
}
https://blog.csdn.net/qq_39679772/article/details/106853322
时间限制: 1 秒
内存限制: 256KB
问题描述
假设用于通信的电文由n个字符组成,字符在电文中出现的频度(权值)为w1,w2,…,wn,试根据该权值序列构造哈夫曼树,并计算该树的带权路径长度。
输入说明
第1行为n的值,第2行为n个整数,表示字符的出现频度。
输出说明
输出所构造哈夫曼树的带权路径长度。
输入样例
8
7 19 2 6 32 3 21 10
输出样例
261
思路:
选2个最小的跳出容器,然后和进容器,把和加载wpl值上。
然后啊这,C++最小堆竟然过不了= =
error C2065: “greater”: 未声明的标识符
解决方案:在头文件中加入#include
即可解决
#include
#include
#include
using namespace std;
priority_queue<int ,vector<int> ,greater<int> > q;//最小堆 第三个参数 less 最大堆
int main()
{
int n,wpl=0,i,t[1000];
int a,b;
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d",&t[i]);
q.push(t[i]);
}
while(q.size()>=2)
{
a=q.top();q.pop();
b=q.top();q.pop();
q.push(a+b);
wpl=wpl+a+b;
}
//author:kkzzjx
printf("%d",wpl);
}
时间限制: 1 秒
内存限制: 256KB
问题描述
建立二叉链表,统计二叉树中的叶子结点数并输出。
输入说明
按照完全二叉树的形式输入二叉树的各结点数据(字符),其中虚结点用’@‘表示。输入以’#'结束。
输出说明
输出叶子结点的个数及具体值。第一行为叶子结点的数据值,各数据用空格分隔,第二行为叶子结点的个数。
输入样例
abc@@de#
输出样例
b d e
3
#include
int main()
{
char tree[1000];
char ch;
scanf("%c",&ch);
int i=1,j,s,cnt=0;
for(j=1;j<1000;j++) //全赋值为@
{
tree[j]='@';
}
while(ch!='#')
{//author:kkzzjx
tree[i]=ch;
scanf("%c",&ch);
i++;
}
s=i;
for(i=1;i<s;i++)
{
if(tree[i]!='@'&&tree[i*2]=='@'&&tree[i*2+1]=='@')
{
printf("%c ",tree[i]);
cnt++;
}
}
//author:kkzzjx
printf("\n%d",cnt);
}
int GetHeight(Bintree BT)
{
int HL,HR,MaxH;
if(BT)
{
HL=GetHeight(BT->left);
HR=GetHeight(BT->right);
MaxH=HL>HR?HL:HR;
return(MaxH+1)
}
else return 0;//空树高度0
}
一、写出n与e关系
1.完全无向图
2.完全有向图
3.稀疏图
4.稠密图
二、概念解释
1.度=出度+入度 e=∑D(Vi)/2
2.网络
3.路径 简单路径
4.简单回路/简单环
5.有根图
6.连通图
7.连通分量
8.强连通,强连通图,强连通分量------都是指有向图
三、图的存储方法
1.邻接矩阵,邻接表
考点:画出xx的邻接矩阵,画出xx邻接表
有向图:边表
无向图:邻接表/出边表,逆邻接表/入边表
2.特点
判定是否为边,求边的数目
3.遍历
考点:写出某个图DFS/BFS的结果
BFS要用到队列:对于连通图,队列为空即遍历结束;对于非连通图,队列为空且图的所有节点都被遍历才结束
四、图的应用
(一)概念解释
1.生成树
2.最小生成树Minimum Spanning Tree MST
3.MST性质
*4.Prim算法
O(n^2) 与边数无关,是和边稠密网络
通俗地说:找一条边,离已经生成的树最近的那条边
*5.Kruskal算法
O(eloge) 与边数有关,适合稀疏网络
通俗地说:初始把每个点都看作不同的连通分量,随着边的加入,有些成为了同一连通分量。
每次找顶点在不同连通分量上的最小边即可。 构造出的树不唯一。
6.考查方式:写出prim,kruskal的过程
(三)概念解释2
1.源点source,终点destination
2.最短路径
一是从某个源点到其余各顶点的最短路径
二是每一对顶点之间的最短路径
Dijkstra算法–单源最短路O(n^2)
考法:
①dist[]数组的变化过程,最短距离数组;path[i]存放的是顶点i的前趋结点;sign[n]记录最短路径生成情况,0表示最短路径还未产生
d,p,s数组变化过程
②求某点到其他各点的最短路径
③如果最后的路径长度还有无穷,说明非连通图
Floyd算法–多源最短路O(n^3)
dist[i][j]
(Vi,Vj)
(Vi,Vk,Vj)
path[i][j]
保存的是Vi到Vj时,Vj的前趋结点
(四)概念解释3
1.AOV网 activity on vertex network
顶点表示活动,有向边表示活动间的先后关系的有向图
AOV网不可能存在有向环
2.拓扑序列
3.拓扑排序:入度为0的顶点输出 O(n+e)
4.可以判断图中有无环:网中顶点未被全部输出,剩余的顶点入度均不为0,说明存在有向环
5.AOE网络 结点表示状态,边表示活动的带权有向网络;
特点:只有一个入度为0的顶点(源点),一个出度为0的顶点(汇点)
应用:解决最短工期问题等
6.关键路径 O(n+e)
7.最早发生时间e(i)
8.最迟发生时间
9.最迟开始时间l(i)
10.关键活动 l(i)=e(i),关键路径
习题:
判定一个有向图是否存在回路:拓扑排序或DFS
1.最终一定会有度不为0的点
2.用dfs,沿着路径搜索,如果重复回到了已经搜索过的路径,就说明出现了环
求强连通分量
#include
#include
#define MAXN 100
int G[MAXN][MAXN],Nv,Ne;
void BuildGraph(){
int i,j,v1,v2,w;
scanf("%d",&Nv);
// 初始化图
for(i=0;i<Nv;i++)
for(j=0;j<Nv;j++)
G[i][j] = 0;
scanf("%d",&Ne);
// 插入边
for(i=0;i<Ne;i++){
scanf("%d %d %d",&v1,&v2,&w);
G[v1][v2] = w;
G[v2][v1] = w;
}
}
// 遍历图
void print(){
int i,j;
for(i=0;i<Nv;i++){
for(j=0;j<Nv;j++)
printf("%d ",G[i][j]);
printf("\n");
}
}
int main(){
BuildGraph();
print();
return 0;
}
#include
#include
#include
using namespace std;
#define MaxVertexNum 100
typedef struct AdjVNode *AdjList;
struct AdjVNode{
int weight; // 权值
int adjv; // 下标
AdjList next; // 其后一个
};
AdjList Graph[MaxVertexNum];//全局变量了
int Ne,Nv;
// 建图
void BuildGraph(){
int i;
int v1,v2,w;
AdjList NewNode;
scanf("%d",&Nv);
for(i=0;i<Nv;i++){//初始化
Graph[i] = (AdjList)malloc(sizeof(struct AdjVNode));
Graph[i]->adjv = i;
Graph[i]->next = NULL;
}
scanf("%d",&Ne);
for(i=0;i<Ne;i++){//加入边
scanf("%d %d %d",&v1,&v2,&w);
NewNode = (AdjList)malloc(sizeof(struct AdjVNode));
NewNode->adjv = v1;
NewNode->weight = w;
NewNode->next = Graph[v2]->next;//链表的头插
Graph[v2]->next = NewNode;
//这里是个无向图,有向图的话把上面5行删除即可
NewNode = (AdjList)malloc(sizeof(struct AdjVNode));
NewNode->adjv = v2;
NewNode->weight = w;
NewNode->next = Graph[v1]->next;
Graph[v1]->next = NewNode;
}
}
void print(){
AdjList tmp;
int i;
for(i=0;i<Nv;i++){
tmp = Graph[i];
while(tmp){
printf("%d ",tmp->adjv);
tmp = tmp->next;
}
printf("\n");
}
}
void DFS(int i)
{
bool visited[MaxVertexNum];
AdjList p;
visited[i]=true;
p=Graph[i];
printf("%d ",Graph[i]->adjv);
while(p)
{
if(!visited[p->adjv])
DFS(p->adjv);
p=p->next;
}
}
void BFS(int i)
{
queue<int> q;
int temp;
bool visited[MaxVertexNum];
AdjList p;
printf("%d",i);
q.push(i);
while(!q.empty())
{
temp=q.front();
q.pop();
for(p=Graph[temp];p!=NULL;p=p->next)
{
if(!visited[p->adjv])
{
visited[p->adjv]=true;
printf("%d ",p->adjv);
q.push(p->adjv);
}
}
}
}
int main(){
BuildGraph();
print();
DFS(0);
BFS(0);
return 0;
}
方法1:
//最小生成树Prim算法
//Prim算法适合稠密图,因此用邻接矩阵存储
#include
#include
#define N 4
#define E 4
using namespace std;
struct graph
{
char vex[N];
int arc[N][N];
};
typedef struct graph *Graph;
Graph creatGraph() //也可以提前输入N,E
{
Graph G = (Graph)malloc(sizeof(struct graph));
int i, j, k;
float weight;
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
G->arc[i][j] = 100000;
}
}
printf("请输入结点的内容:\n");
for (i = 0; i < N; i++)
{
scanf("%c", &G->vex[i]);
}
printf("请依次输入起点,终点和权值:\n");
for (k = 0; k < E; k++)
{
scanf("%d %d %d", &i, &j, &weight);
G->arc[i][j] = weight;
G->arc[j][i] = weight;
}
return G;
}
typedef struct
{
int start, end;
float weight;
} edge;
edge T[N - 1]; //T存放的是边信息
void Prim(int i, Graph G) //i为选取的第一个顶点的下标,最终结果存在T[N-1]中
{
int j, k, m, v, min, max = 100000;
int path[N]; //存放路径信息
path[i] = -1;
edge e;
v = i;
for (j = 0; j < N - 1; j++) /*循环就是建立好第1个结点和其他各点的边
(注意:此图中,两边无联系需要设置为无穷*/
{
T[j].start = v;
if (j >= v)
{
T[j].end = j + 1;
T[j].weight = G->arc[v][j + 1];
}
else
{
T[j].end = j;
T[j].weight = G->arc[v][j];
}
}
for (k = 0; k < N - 1; k++) //第k条边,前面k-1都处理好了,so T[k-1]以前的已经存放最小了。
{
min = max;
for (j = k; j < N - 1; j++)
{
if (T[j].weight < min&& T[j].weight!=0)
{
min = T[j].weight;
m = j; //最小边的下标存在m里面
}
e = T[m]; //e是交换的中间变量,交换两条边
T[m] = T[k];
T[k] = e; //将最短的边存放到T[k]单元
v = T[k].end; //v中存放找到的最短边在V-U中的顶点
float d;
for (j = k + 1; j < N - 1; j++)
{
d = G->arc[v][T[j].end]; //其实这个写法真的很妙啊,T[x].end里面都是没有访问过的点
if (d < T[j].weight)
{
T[j].weight = d;
T[j].start = v;
}
}
}
}
}
int main()
{
int i;
Graph G = creatGraph();
Prim(0, G);
for (i = 0; i < N; i++)
{
printf("%d %d %d %.1f\n", i, T[i].start, T[i].end, T[i].weight);
}
}
方法2:
/*
void prim()
{
MSF{};
while(1)
{
v=未收入顶点中dist最小者//dist是指顶点Vj到树的最小距离
if(v不存在)
break;
for(v的每个邻接点w)
{
if(w未被收录)
{
if(E
parent[w]=v;//并查集?可以用来存路径
}
}
}
}
}
*/
#include
#include
#define INF 100000
#define MaxVertex 105
int G[MaxVertex][MaxVertex];
int parent[MaxVertex]; // 并查集 ,可以得到path,如果设立了这个,那么parent[0]=-1 0位置要空出来
int dist[MaxVertex]; // 距离 一点离树的距离的最小值
int Nv; // 结点数
int Ne; // 边数
int sum; // 权重和
using namespace std;
vector<int> MST; // 最小生成树 存放里面已经有的结点
void build()
{
printf("请输入结点数、边数\n");
scanf("%d %d", &Nv, &Ne);
int i, j, k;
int weight;
for (i = 0; i < Nv; i++)
{
for (j = 0; j < Nv; j++)
{
G[i][j] = INF;
}
dist[i] = INF; //距离初始化
parent[i] = -1; //并查集初始化
}
printf("请输入边 起点,终点,权值\n");
for (k = 0; k < Ne; k++)
{
scanf("%d %d %d", &i, &j, &weight);
G[i][j] = weight;
G[j][i] = weight;
}
}
void IniPrim(int s) //prim算法前的初始化,树从s点开始生长
{
dist[s] = 0; //初始点s收入MST
int i;
MST.push_back(s);
for (i = 0; i <= Nv; i++)
{
if (G[s][i] < INF)
{
dist[i] = G[s][i]; //s点的邻接点
parent[i] = s;
}
}
}
int FindMin() //查找未收录的点中dist最小的点
{
int min = INF;
int v = -1;
for (int i = 0; i < Nv; i++)
{
if (dist[i] != 0 && dist[i] < min)
{
min = dist[i];
v = i;
}
}
return v;
}
void Prim(int s)
{
IniPrim(s); //从s点开始生长,初始化
while (1)
{
int v = FindMin(); //未收录顶点中dist最小者(dist[i]!=0)
if (v == -1)
break;
sum += dist[v];
dist[v] = 0; //表示已经收录
MST.push_back(v);
for (int w = 0; w < Nv; w++) //v的每个邻接点w
{
if (G[v][w] < INF && dist[w]) //是v的邻接点并且没被收录
{
if (G[v][w] < dist[w])
{
dist[w] = G[v][w];
parent[w] = v;
}
}
}
}
}
void output()
{
printf("收录顺序:\n");
for (int i = 0; i < Nv; i++)
printf("%d ", MST[i]);
printf("\n生成树:(s的parent结点)\n");
for (int i = 0; i < Nv; i++)
printf("%d ", parent[i]);
}
int main()
{
build();
Prim(1);
output();
}
/*
int Kruskal()
{
MST=包含所有顶点但没有边的图;//初始状态
while(MST中收集的边不到Nv-1条&&原图的边集E非空)
{
从E中选择最小代价边(V,W) //用最小堆
从E中删除此边(V,W)
if((V,W)的选取不在MST中构成回路)//判断用并查集 Find ,不在MST中构成回路,计V,W不在同一颗树上
将(V,W)加入MST;
else
彻底丢弃(V,W)
}
if(MST中收集的边不到Nv-1条)
return error
else
return 最小权重和sum
}
*/
//Kruskal
#include
#include
#include
#include
using namespace std;
#define INF 100000
#define MaxVertex 100
int parent[MaxVertex]; // 并查集最小生成树
int Nv; // 结点
int Ne; // 边
int sum; // 权重和
struct edge
{
int v1, v2;
int weight;
int sign; //该边是否已经被选择过
};
struct edge E[MaxVertex];
vector<int> MST; // 最小生成树
int G[MaxVertex]; //数组G 用于判定所选择的边是否在同一个分量上
void IniGraph()
{
int v1, v2, w;
int i;
printf("请输入结点数、边数\n");
scanf("%d %d", &Nv, &Ne);
printf("请输入起点、终点、权值\n");
for (i = 0; i < Ne; i++)
{
scanf("%d %d %d", &v1, &v2, &w);
E[i].v1 = v1;
E[i].v2 = v2;
E[i].weight = w;
E[i].sign = 0; //说明还没有进入MST
}
for (i = 0; i < Nv; i++)
{
G[i] = i; //说明在哪个分量上
}
}
void Kruskal()
{
int i, j = 0, MinEdge, start, end;
int Min;
while (j < Nv - 1)
{
Min = INF;
for (i = 0; i < Ne; i++) //寻找最短边
{
if (E[i].sign == 0)
{
if (E[i].weight < Min)
{
Min = E[i].weight;
MinEdge = i;
start = E[i].v1;
end = E[i].v2;
// E[i].sign=1;//why?
}
}
}
E[MinEdge].sign = 1;
if (G[start] == G[end])
E[MinEdge].sign = 2; //在同一个分量上,舍去 最终结果:sign=1的在树里面,sign=2的已经被丢弃,sign=0的是还没被选到
else
{
j++;
for (i = 0; i < Nv; i++)
{
if (G[i] == end) //并入同一分量
G[i] = G[start];
}
}
}
}
int main()
{
IniGraph();
Kruskal();
for (int i = 0; i < Ne; i++)
{
if (E[i].sign == 1)
{
printf("%d %d %d\n", E[i].v1, E[i].v2, E[i].weight);
}
}
}
#include
#include
#include
#include
using namespace std;
#define MAXN 100
#define INF 10000
#define ERROR -1
int G[MAXN][MAXN], Nv, Ne;
int path[MAXN];
int dist[MAXN];
bool collected[MAXN];
void BuildGraph()
{
int i, j, v1, v2, w;
scanf("%d", &Nv);
// 初始化图
for (i = 0; i < Nv; i++)
for (j = 0; j < Nv; j++)
G[i][j] = 0;
scanf("%d", &Ne);
// 插入边
for (i = 0; i < Ne; i++)
{
scanf("%d %d %d", &v1, &v2, &w);
G[v1][v2] = w;
G[v2][v1] = w;
}
}
// 遍历图
void print()
{
int i, j;
for (i = 0; i < Nv; i++)
{
for (j = 0; j < Nv; j++)
{
printf("%d ", G[i][j]);
}
printf("\n");
}
}
int FindMinDist()
{
int Minv,v;
int MinDist=INF;
for(v=0;v<Nv;v++)
{
if(collected[v]==false&&dist[v]<MinDist)
{
MinDist=dist[v];
Minv=v;//更新结点
}
}
if(MinDist<INF)
return Minv;
}
bool Dijkstra(int s) //dist是不断更新的长度值,path记录的是parent,s是图的顶点
//dist[s]=0
{
//collect[v] 表示顶点已经求得最短路径
int v, w;
//初始化
for(v=0;v<Nv;v++)
{
dist[v]=G[s][v];
if(dist[v]<INF)
path[v]=s;
else
path[v]=-1;
collected[v]=false;
}
dist[s]=0;
collected[s]=true;
while(1)
{
v=FindMinDist();//v为未被收录顶点中dist最小者
if(v==ERROR)
break;
collected[v]=true;
for(w=0;w<Nv;w++)
{
if(collected[w]==false&&G[v][w]<INF)//w是v的邻接点且未被收录
{
if(G[v][w]<0) //如果负边,Dijkstra不行
return false;
if(dist[v]+G[v][w]<dist[w])//收录v使得dist[w]更小
{
dist[w]=dist[v]+G[v][w];//那就更新d[w]吧!
path[w]=v;
}
}
}
return true;
}
}
void output(){
for(int i=0;i<Nv;i++)
cout<<dist[i]<<" ";
cout<<endl;
for(int i=0;i<Nv;i++)
cout<<path[i]<<" ";
cout<<endl;
}
int main(){
BuildGraph();
Dijkstra(1);
output();
return 0;
}
void Floyd()
{
for(Vertex i=1;i<=Nv;i++)
{
for(Vertex j=1;j<=Nv;j++)
{
dist[i][j] = G[i][j];
path[i][j] = -1;
}
}
for(Vertex k=1;k<=Nv;k++)
{
for(Vertex i=1;i<=Nv;i++)
{
for(Vertex j=1;j<=Nv;j++)
{
if(dist[i][k] + dist[k][j] < dist[i][j])
{
dist[i][j] = dist[i][k] + dist[k][j];
path[i][j] = k;
}
}
}
}
}
#include
#include
#include
using namespace std;
#define MaxVertexNum 100
typedef struct AdjVNode *AdjList;
struct AdjVNode
{
int weight; // 权值
int adjv; // 下标
AdjList next; // 其后一个
};
AdjList Graph[MaxVertexNum]; //全局变量了
int Ne, Nv;
// 建图
void BuildGraph()
{
int i;
int v1, v2, w;
AdjList NewNode;
scanf("%d", &Nv);
for (i = 0; i < Nv; i++)
{ //初始化
Graph[i] = (AdjList)malloc(sizeof(struct AdjVNode));
Graph[i]->adjv = i;
Graph[i]->next = NULL;
}
scanf("%d", &Ne);
for (i = 0; i < Ne; i++)
{ //加入边
scanf("%d %d %d", &v1, &v2, &w);
NewNode = (AdjList)malloc(sizeof(struct AdjVNode));
NewNode->adjv = v2;
NewNode->weight = w;
NewNode->next = Graph[v1]->next;
Graph[v1]->next = NewNode;
}
}
int Indegree[MaxVertexNum];
int TopOrder[MaxVertexNum];
bool Topsort()
{
//初始化Indegree
int i, v;
AdjList w;
queue<int> q;
for (i = 0; i < Nv; i++)
Indegree[i] = 0;
for (i = 0; i < Nv; i++)
{
for (w = Graph[i]->next; w; w = w->next)
{
Indegree[w->adjv]++;
}
}
//将所有入度为0的顶点入列
for (i = 0; i < Nv; i++)
{
if (Indegree[i] == 0)
q.push(i);
}
for (i = 0; i < Nv; i++)
printf("%d", Indegree[i]);
//拓扑排序
int cnt = 0;
while (!q.empty())
{
v = q.front();
q.pop();
TopOrder[cnt++] = v;
for (w = Graph[v]; w; w = w->next)
{
if (--Indegree[w->adjv] == 0)
q.push(w->adjv);
}
}
if (cnt != Nv)
{
printf("图中有回路,失败!");
return false;
}
else
return true;
}
void print()
{
AdjList tmp;
int i;
for (i = 0; i < Nv; i++)
{
tmp = Graph[i];
while (tmp)
{
printf("%d ", tmp->adjv);
tmp = tmp->next;
}
printf("\n");
}
}
int main()
{
BuildGraph();
bool f = Topsort();
if (f)
{
for (int i = 0; i < Nv; i++)
{
printf("%d ", TopOrder[i]);
}
}
}
判断是否存在vi到vj的路径
增加一个判断函数:
其中遍历过的一个连通分量存在vector<> path中,DFS/BFS时把元素加入这个容器中即可,无论是邻接表还是矩阵都是同理
bool Ispath(int i,int j)
{
bool f1=false,f2=false;
for(int k=0;k<path.size();k++)
{
if(path[k]==i) f1=true;
if(path[k]==j) f2=true;
}
if(f1&&f2) return true;
else return false;
}
完整程序:
#include
#include
#include
using namespace std;
#define MAXN 100
int G[MAXN][MAXN], Nv, Ne;
vector<int> path;
void BuildGraph()
{
int i, j, v1, v2, w;
scanf("%d", &Nv);
// 初始化图
for (i = 0; i < Nv; i++)
for (j = 0; j < Nv; j++)
G[i][j] = 0;
scanf("%d", &Ne);
// 插入边
for (i = 0; i < Ne; i++)
{
scanf("%d %d %d", &v1, &v2, &w);
G[v1][v2] = w;
G[v2][v1] = w;
}
}
// 遍历图
void print()
{
int i, j;
for (i = 0; i < Nv; i++)
{
for (j = 0; j < Nv; j++)
{
printf("%d ", G[i][j]);
path.push_back(G[i][j]);
}
printf("\n");
}
}
bool Ispath(int i,int j)
{
bool f1=false,f2=false;
for(int k=0;k<path.size();k++)
{
if(path[k]==i) f1=true;
if(path[k]==j) f2=true;
}
if(f1&&f2) return true;
else return false;
}
int main()
{
BuildGraph();
print();
return 0;
}
给的答案:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qjh4DC64-1596643429823)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596558028023.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SQIbRkMo-1596643429825)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596558044209.png)]
考点:1.填空题
稠密索引:一个索引项对应数据表中一个对象的索引结构
稀疏索引:对一组记录建立一个索引项
2.分块查找的基本思想:首先查找索引表,因为索引表是有序表,所以可进行顺序查找或折半查找,以确定待查找的记录在哪一块中;然后在已确定的那一块中进行顺序查找
3.倒排表:先查的是数据(次关键字,也是属性) 一个属性值和该属性的全部记录地址
4.概念:冲突 装填因子
5.处理冲突的两种方法:开放地址法(线性探查法、二次探查法、随机探查法),拉链法
6.开放地址法的删除
7.ASL(平均查找长度)的计算
归并排序(Merge Sort)是将两个或两个以上的有序表合成一个新的有序表。
归并排序基本思想是:
1)当n=1时,终止排序;
2)否则将待排序的元素分成大致相同的两个子集合,分别对两个子集合进行归并排序;
3)将排好序的子集合合并成所要求的排序结果
void MergeSort(DataType R[], int left, int right)
{
if (left < right ) {//至少有两个元素
i = ( left + right )/2;//取中点
MergeSort ( R, left, i );
MergeSort ( R, i, right );
Merge ( a, b, left, i, right );归并到b数组
Copy ( a, b, left, right);//复制回a数组
}
}
上述归并算法的递归过程只是将待排序的顺序表一分为二,直至待排序顺序表中只剩下一个元素为止;然后不断合并两个排好序的数组段。
可以从分治策略入手,消除算法中的递归。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uS8AnlCP-1596643429827)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596557365420.png)]
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用*分治法(Divide and Conquer)*的一个非常典型的应用。
首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。
快速排序算法是基于分治策略的另一个排序算法。其基本思想是:对输入的数组R[l:h]按以下三个步骤进行排序:
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iSGG4qGS-1596643429874)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596557298152.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gcn1cDHq-1596643429875)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596557315808.png)]
int AdjustArray(int s[],int l,int r) //挖坑填数的代码,相当于是一轮
{
int i=l,j=r;
int x=s[l];
while(i<j)
{
while(i<j&&s[j]>x) //从右向左找小于x的数来填s[i]
j--;
if(i<j)
{
s[i]=s[j];
i++;
}
while(i<j&&s[i]<x)
i++;
if(i<j)
{
s[j]=s[i];
j--;
}
}
//退出时,i==j 将x填入此坑中
s[i]=x;
return i;
}
void quick_sort1(int s[],int l,int r)
{
if(l<r)
{
int i=AdjustArray(s,l,r);
quick_sort1(s,l,i-1);
quick_sort1(s,i+1,r);
}
}
/*---------------------------分割线------------------------------*/
//组合整理(整合为一个函数)
void quick_sort(int s[],int l,int r)
{
if(l<r)
{
int i=l,j=r,x=s[l];
while(i<j)
{
while(i<j&&s[j]>=x)
j--;
if(i<j)
s[i++]=s[j];
while(i<j&&s[i]<=x)
i++;
if(i<j)
s[j--]=s[i];
}
s[i]=x;
quick_sort(s,l,i-1);
quick_sort(s,l+1,r);
}
}
考点:求第N趟排序后的状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zzcfrepj-1596643429877)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596607352122.png)]
可以参考https://www.cnblogs.com/onepixel/articles/7674659.html
1.对任意7个关键字进行基于比较的排序,至少要进行____次关键字之间的两两比较
考虑最坏的情况,公式 [log n!]
n=7代入,得13
在动态规划算法中,每步所作出的选择往往依赖于相关子问题的解,因此只有在解出相关子问题的解后才能做出选择;
而贪心算法所作的贪心选择仅在当前状态下做出的最好选择,即局部最优选择,然后再求解作出该选择后所产生的相应子问题的解,即贪心算法所作出的贪心选择可以依赖于“过去”所作出的选择,但绝不依赖于将来所作出的选择,也不依赖于子问题的解。
贪心算法和动态规划算法都具有最优子结构性质。
操作;
[外链图片转存中…(img-iSGG4qGS-1596643429874)]
[外链图片转存中…(img-Gcn1cDHq-1596643429875)]
int AdjustArray(int s[],int l,int r) //挖坑填数的代码,相当于是一轮
{
int i=l,j=r;
int x=s[l];
while(i<j)
{
while(i<j&&s[j]>x) //从右向左找小于x的数来填s[i]
j--;
if(i<j)
{
s[i]=s[j];
i++;
}
while(i<j&&s[i]<x)
i++;
if(i<j)
{
s[j]=s[i];
j--;
}
}
//退出时,i==j 将x填入此坑中
s[i]=x;
return i;
}
void quick_sort1(int s[],int l,int r)
{
if(l<r)
{
int i=AdjustArray(s,l,r);
quick_sort1(s,l,i-1);
quick_sort1(s,i+1,r);
}
}
/*---------------------------分割线------------------------------*/
//组合整理(整合为一个函数)
void quick_sort(int s[],int l,int r)
{
if(l<r)
{
int i=l,j=r,x=s[l];
while(i<j)
{
while(i<j&&s[j]>=x)
j--;
if(i<j)
s[i++]=s[j];
while(i<j&&s[i]<=x)
i++;
if(i<j)
s[j--]=s[i];
}
s[i]=x;
quick_sort(s,l,i-1);
quick_sort(s,l+1,r);
}
}
考点:求第N趟排序后的状态
[外链图片转存中…(img-zzcfrepj-1596643429877)]
可以参考https://www.cnblogs.com/onepixel/articles/7674659.html
1.对任意7个关键字进行基于比较的排序,至少要进行____次关键字之间的两两比较
考虑最坏的情况,公式 [log n!]
n=7代入,得13
在动态规划算法中,每步所作出的选择往往依赖于相关子问题的解,因此只有在解出相关子问题的解后才能做出选择;
而贪心算法所作的贪心选择仅在当前状态下做出的最好选择,即局部最优选择,然后再求解作出该选择后所产生的相应子问题的解,即贪心算法所作出的贪心选择可以依赖于“过去”所作出的选择,但绝不依赖于将来所作出的选择,也不依赖于子问题的解。
贪心算法和动态规划算法都具有最优子结构性质。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GxI4vwBi-1596643429878)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596557588015.png)]