《数据结构应试手册》
本手册(一座屎山)仅限用于个人应试
author:kkzzjx
date:2020/7/3
重点!!!
机考究极(救急)重点 |
---|
1.链表插入,删除 |
2.二叉树的递归、非递归遍历,层序遍历 |
3.二叉搜索树的插入、删除 |
4.哈夫曼树的构造和哈夫曼编码 |
5.图的建立,遍历 |
6.prim和kruskal算法 |
7.Dijkstra算法 |
8.AOV网拓扑排序 |
9.快速排序 |
10.kmp算法 |
11.二叉搜索树的插入和删除 |
2.二叉树的递归、非递归遍历,层序遍历
定义
#include <stdio.h>
#include <stdlib.h>
#include <queue>
#include <stack>
using namespace std;
typedef struct TNode *Bintree;
struct TNode
{
int data;
Bintree left, right;
};
递归遍历
void preorder(Bintree T)
{
if (T)
{
printf("%d", T->data);
preorder(T->left);
preorder(T->right);
}
}
先序、中序非递归
/* a. 遇到一个节点,访问它,然后把它压栈,并去遍历它的左子树;
b. 当左子树遍历结束后,从栈顶弹出该节点并将其指向右儿子,继续a步骤;
c. 当所有节点访问完即最后访问的树节点为空且栈空时,停止。*/
//非递归算法
void preorder2(Bintree BT)
{
stack<Bintree> s;
Bintree T = BT;
while (T || !s.empty())
{
while (T)
{
printf("%d", T->data);
s.push(T);
T = T->left;
}
T = s.top();
s.pop();
T = T->right;
}
}
void inorder2(Bintree BT) //中序
{
stack<Bintree> s;
Bintree T = BT;
while (T || !s.empty())
{
while (T)
{
s.push(T);
T = T->left; //一直向左并将沿途节点压入堆栈
}
T = s.top();
s.pop(); //节点弹出堆栈
printf("%d", T->data); //(访问) 打印结点
T = T->right; //转向右子树
}
}
后序遍历非递归1
法1(传统方法)
在前面的前序和中序先到最左侧压入栈的时候,两种顺序依次是
- 前序:
中入栈
——>左入栈
——>左出栈——>中出栈——>右入栈
——>右孩子入出——>右出栈 在入栈时候操作即可前序 - 中序: 中入栈——>左入栈——>
左出栈
——>中出栈
——>右入栈 ——>右孩子入出——>右出栈
按照出栈顺序即可完成中序
而在后序遍历中:它有这样的规则:
-
入栈,第一次访问
-
即将出栈。第二次访问,
-
如果有右孩子,先不出栈把右孩子压入栈第一次访问,如果没右孩子。访问从栈中弹出。
-
循环重复,直到栈为空
所以要设置一个标记,记录访问的次数,如果是第二次访问,则要判断该结点有无右子树。
参考一下java代码
实现代码为(用map记录节点出现次数):
public void houxu2(node t) {
Stack<node> q1 = new Stack();
Map<Integer,Integer >map=new HashMap<>();
while(!q1.isEmpty()||t!=null)
{
if (t!=null) {
q1.push(t);
map.put(t.value, 1); //t.value标记这个值节点出现的次数
t=t.left;
}
else {
t=q1.peek();
if(map.get(t.value)==2) {//第二次访问,抛出
q1.pop();
System.out.print(t.value+" ");
t=null;//需要往上走
}
else {
map.put(t.value, 2);
t=t.right;
}
}
}
}
后序遍历非递归2
双栈法
void postorder(Bintree BT)
{
Bintree T=BT;
stack<Bintree> s1;
stack<Bintree> s2;
while (T || !s1.empty())
{
while (T)
{
s1.push(T);//s1用来控制过程
s2.push(T);//s2用来打印结果
T = T->right;
}
T = s1.top();
s1.pop(); //节点弹出堆栈
T = T->left;
}
while(!s2.empty())
{
printf("%d ",s2.top()->data);
s2.pop();
}
}
层序遍历
总结:step1:根结点入队 step2:出队并访问结点,左右儿子入队 重复step2
void levelorder(Bintree BT)
{
queue<Bintree> q;
Bintree T; //存出队的结点
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);
}
}
层序生成二叉树
Bintree create()
{
int data;
queue<Bintree> q;
Bintree BT, T;
//建立根结点
scanf("%d", &data);
if (data == '#')
{
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 != '#')
{
T->left = (Bintree)malloc(sizeof(struct TNode));
T->left->data = data;
T->left->left = NULL;
T->left->right = NULL;
q.push(T->left);
}
else
T->left = NULL;
scanf("%d", &data);
if (data != '#')
{
T->right = (Bintree)malloc(sizeof(struct TNode));
T->right->data = data;
T->right->left = NULL;
T->right->right = NULL;
q.push(T->right);
}
else
T->right = NULL;
}
return BT;
}
先序建立二叉树
Bintree precreate()
{
Bintree BT=NULL;
int data;
scanf("%d",&data);
if(data!='#')
{
BT=(Bintree)malloc(sizeof(struct TNode));
BT->data=data;
BT->left=precreate();
BT->right=precreate();
}
return BT;
}
3.二叉搜索树
最重要的考点:元素的删除
1.要删除叶结点
直接删除,修改父节点的指针为空
2.要删除的结点只有一个孩子结点
删除前:先改变其父节点的指针,指向要删除结点的孩子结点
3.要删除的结点有左、右两棵子树
选择右子树中最小的元素or左子树中最大的元素
无论哪种选择,被选择的结点都必定最多只有一个孩子
链表
链表
#include<stdio.h>
#include<stdlib.h>
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<stdio.h>
#include<stdlib.h>
#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;
理论考试
常常需要补全实现的代码,代码如下:(队列)
顺序队列
//顺序队列 setNull,empty,front取头元素,enqueue,dequeue(delete)出队
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
typedef struct QNode *Queue;
struct QNode{
int *data;
int front,rear;//队列头尾指针
int maxsize;
//int size记录此时队列中元素的数量; int flag; 记录最后一个元素出队or入队
};
Queue creat(int maxsize)//防止假溢出 方法2,少用一个元素空间(这个难一点)
{
Queue Q=(Queue)malloc(sizeof(struct QNode));
Q->data=malloc(maxsize*sizeof(int));
Q->front=0;//0位置空
Q->rear=0;
Q->maxsize=maxsize;
return Q;
}
int isFull(Queue Q)
{
return ((Q->rear+1)%(Q->maxsize)==Q->front);
}
int isempty(Queue Q)
{
return Q->rear==Q->front;
}
void enqueue(Queue Q,int x)
{
if(isFull(Q)==1) printf("queue is full");
else
{
Q->rear=(Q->rear+1)%Q->maxsize;
Q->data[Q->rear]=x;
}
}
int dequeue(Queue Q)
{
if(isempty(Q)==1) printf("queue is empty");
else{
Q->front=(Q->front+1)%Q->maxsize;
return Q->data[Q->front];
}
}
void test(Queue Q)
{
printf("%d ",Q->data[1]);
printf("%d ",Q->data[2]);
printf("%d ",Q->data[3]);
printf("%d ",Q->data[4]);
}
int main()
{
int tmp;
Queue Q=creat(5);
enqueue(Q,32);
enqueue(Q,4);
enqueue(Q,5);
enqueue(Q,6);
test(Q);
tmp=dequeue(Q);
printf("%d ",tmp);
tmp=dequeue(Q);
printf("%d ",tmp);
tmp=dequeue(Q);
printf("%d",tmp);
}
链队列
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode *List;
struct LNode{
int data;
List next;
};
struct QNode{
List front,rear;
int MaxSize;
};
typedef struct QNode *Queue;
Queue CreateQ()
{
Queue Q;
Q = (Queue)malloc(sizeof(struct QNode));
Q->front = NULL;
Q->rear = NULL;
return Q;
}
int isempty(Queue Q)
{
return (Q->front==NULL);
}
void enqueue(Queue Q,int x)
{
List node=(List)malloc(sizeof(struct LNode));
node->data=x;
node->next=NULL;
if(Q->rear==NULL)
{
Q->rear=node;
Q->front=node;
}
else
{
Q->rear->next=node;
Q->rear=node;
}
}
int dequeue(Queue Q)
{
int tmp;
List tmpnode;
if(isempty(Q)) printf("Queue is empty");
else
{
tmpnode=Q->front;
tmp=Q->front->data;
if(Q->front==Q->rear)//只有一个元素
{
Q->front=NULL;
Q->rear=NULL;
}
else
{
Q->front=Q->front->next;
}
free(tmpnode);
return tmp;
}
}
int main()
{
//test
Queue Q=CreateQ();
enqueue(Q,2);
enqueue(Q,3);
enqueue(Q,5);
printf("出队%d\n",dequeue(Q));
printf("出队%d\n",dequeue(Q));
printf("出队%d",dequeue(Q));
}
队列:判断循环队列是否为空
串
KMP算法O(n+m)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JCoWhLO4-1598279792182)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596718799254.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UUDaduP1-1598279792186)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596718822481.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-82Il2p19-1598279792187)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596718859450.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fb5SmVsN-1598279792191)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596718892262.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m3mzqQj6-1598279792239)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1597737150444.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uKo6xLuC-1598279792241)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1597737176845.png)]
S主串 T字串 S成为目标 T称为模式 把从目标S中查找模式为T的字串的过程称为“模式匹配”
#include<iostream>
#include<stdio.h>
#include<string.h>
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;//若pi=pj 则next[j+1]=next[j]+1
}
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的计算
路径的长度
树的路径长度:树根到树中每一节点的路径长度之和(PL)
树的带权路径长度(WPL)
WPL最小:最优二叉树or哈夫曼树
七、哈夫曼树的构造,哈夫曼编码 and性质
八、树、森林和二叉树之间的转换
1.一棵树转换为二叉树
任何一棵树转换为对应的二叉树后,二叉树的右子树为空
2.一棵二叉树转换为树
左孩子的所有右孩子都与根连起来
3.森林转二叉树
森林中每一棵树转为二叉树
https://www.cnblogs.com/wkfvawl/p/10066666.html
习题:
- 一棵度为2的树与一棵二叉树有何区别?
1.度为2的树是不区分左子树和右子树.而二叉树是要分左子树和右子树的.
2.度为2的数不包含空树,而二叉树是可以有空树的. 总之,二叉树的定义要比度为2的树定义更为严格,更为详细.
- 一个深度为L的满k叉树的性质
1,各层结点的数目是多少?
k^(n-1)
2,编号为n的结点的双亲结点(若存在)的编号是多少?
(n-2+k) / k 取下界
3,编号为n的结点的第i个孩子结点
(n-1)k +1 +i4,编号为n的结点有右兄弟的条件是?其右兄弟的编号是多少?对于任意结点p ,他的最右边的孩子结点为 pk+1
所以条件是(n-1)%k 不等于 0
右兄弟编号为:n+1
二叉树建立,遍历
#include <stack>
#include <queue>
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<stdio.h>
#include<stdlib.h>
#include<queue>
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<functional>
即可解决
#include<stdio.h>
#include<queue>
#include<functional>
using namespace std;
priority_queue<int ,vector<int> ,greater<int> > q;//最小堆 第三个参数 less<int> 最大堆
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<stdio.h>
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,沿着路径搜索,如果重复回到了已经搜索过的路径,就说明出现了环
求强连通分量
建图:邻接矩阵&邻接表&DFS&BFS
- 最简单的邻接矩阵建图法及其DFS,BFS
#include<stdio.h>
#include<stdlib.h>
#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<stdio.h>
#include<stdlib.h>
#include<queue>
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;
}
最小生成树
Prim
方法1:
//最小生成树Prim算法
//Prim算法适合稠密图,因此用邻接矩阵存储
#include <stdio.h>
#include <stdlib.h>
#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<v,w> <dist[w])
{
dist[w]=E<v,w>
parent[w]=v;//并查集?可以用来存路径
}
}
}
}
}
*/
#include <stdio.h>
#include <vector>
#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();
}
Kruskal
/*
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 <string>
#include <vector>
#include <queue>
#include <algorithm>
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?xd书错了
}
}
}
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);
}
}
}
最短路径
单源最短路:Djikstra
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include<iostream>
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 dijkstra()
{
memset(dis,127/3,sizeof(dis));//初始化
v[1]=1;
dis[1]=0;
for(int i=1;i<=n;++i)
{
int k=0;
for(int j=1;j<=n;++j)//找出距离最近的点
if(!v[j]&&(k==0||dis[j]<dis[k]))
k=j;
v[k]=1;//加入集合
for(int j=1;j<=n;++j)//松弛
if(!v[j]&&dis[k]+a[k][j]<dis[j])
dis[j]=dis[k]+a[k][j];
}
}
来源:知乎
参考:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define Inf 0x3f3f3f3f
using namespace std;
int map[1005][1005];
int vis[1005],dis[1005];
int n,m;//n个点,m条边
void Init ()
{
memset(map,Inf,sizeof(map));
for(int i=1;i<=n;i++)
{
map[i][i]=0;
}
}
void Getmap()
{
int u,v,w;
for(int t=1;t<=m;t++)
{
scanf("%d%d%d",&u,&v,&w);
if(map[u][v]>w)
{
map[u][v]=w;
map[v][u]=w;
}
}
}
void Dijkstra(int u)
{
memset(vis,0,sizeof(vis));
for(int t=1;t<=n;t++)
{
dis[t]=map[u][t];
}
vis[u]=1;
for(int t=1;t<n;t++)
{
int minn=Inf,temp;
for(int i=1;i<=n;i++)
{
if(!vis[i]&&dis[i]<minn)
{
minn=dis[i];
temp=i;
}
}
vis[temp]=1;
for(int i=1;i<=n;i++)
{
if(map[temp][i]+dis[temp]<dis[i])
{
dis[i]=map[temp][i]+dis[temp];
}
}
}
}
int main()
{
scanf("%d%d",&m,&n);
Init();
Getmap();
Dijkstra(n);
printf("%d\n",dis[1]);
return 0;
}
多源最短路:Floyd
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;
}
}
}
}
}
AOV 网络
拓扑排序 Topsort
#include <stdio.h>
#include <stdlib.h>
#include <queue>
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]);
}
}
}
AOE 网络
拓扑排序Topsort
习题1
判断是否存在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 <stdio.h>
#include <stdlib.h>
#include<vector>
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-HUyVe8LY-1598279792244)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596558028023.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LcYrL7qB-1598279792245)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596558044209.png)]
哈希表
理论考试总结
散列函数的选择有两条标准:简单和均匀。
简单指散列函数的计算简单快速;
均匀指对于关键字集合中的任一关键字,散列函数能以等概率将其映射到表空间的任何一个位置上。也就是说,散列函数能将子集K随机均匀地分布在表的地址集{0,1,…,m-1}上,以使冲突最小化。
考点:1.填空题
稠密索引:一个索引项对应数据表中一个对象的索引结构
稀疏索引:对一组记录建立一个索引项
2.分块查找的基本思想:首先查找索引表,因为索引表是有序表,所以可进行顺序查找或折半查找,以确定待查找的记录在哪一块中;然后在已确定的那一块中进行顺序查找
3.倒排表:先查的是数据(次关键字,也是属性) 一个属性值和该属性的全部记录地址
4.概念:冲突 装填因子
5.处理冲突的两种方法:开放地址法(线性探查法、二次探查法、随机探查法),拉链法
6.开放地址法的删除
7.ASL(平均查找长度)的计算
- 《王道》习题补充知识点:
- 散列表的平均查找长度与α相关,不直接依赖于n或者m
- 散列函数有一个共同的性质,即函数应当以(等概率 )取其值域的每个值
- …
索引结构
包括两部分:索引表and数据表
(一)概念
索引非顺序结构:一个索引项对应数据表中一个对象的索引结构被称为稠密索引
索引顺序结构:数据表中的记录按关键字有序,对一组数据建立一个索引项,稀疏索引
稠密索引,稀疏索引都是线性索引
索引结构的检索分两步完成,step1:查找索引表,step2:根据索引项的指示在数据表中读取数据
(二)分块查找
首先查找索引表
在确定的那一块中进行顺序查找
(三)
多级索引
倒排表:有次索引
次索引表的索引项:次关键字,链表长度,链表本身
倒排表的内容是:一个属性值和该属性的全部记录地址
排序算法
希尔排序
归并排序
归并排序(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-0gt0ZAYD-1598279792247)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596557365420.png)]
- 转载于菜鸟的通俗解释:
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用*分治法(Divide and Conquer)*的一个非常典型的应用。
首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。
快速排序
快速排序算法是基于分治策略的另一个排序算法。其基本思想是:对输入的数组R[l:h]按以下三个步骤进行排序:
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qj5FWNtG-1598279792248)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596557298152.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Npz9VPKj-1598279792250)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596557315808.png)]
- 1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。
- 2.j–由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
- 3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
- 4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。
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趟排序后的状态
排序算法的总结(考点)
- 内部排序
- 冒泡排序 O(N^2)
- 选择排序O(N^2) 不稳定
- 插入排序O(N^2)
- 希尔排序O(N^1.5) 不稳定
- 归并排序O(NlogN)
- 快速排序O(NlogN) 不稳定
- 堆排序O(NlogN) 不稳定
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ci4p0bK7-1598279792252)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596607326539.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PP4pysai-1598279792253)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596607352122.png)]
可以参考https://www.cnblogs.com/onepixel/articles/7674659.html
C++STL库的排序
//从小到大
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[10]={9,6,3,8,5,2,7,4,1,0};
for(int i=0;i<10;i++)
cout<<a[i]<<endl;
sort(a,a+10);//第三个参数不用写(如果是从小到大排序的话)
for(int i=0;i<10;i++)
cout<<a[i]<<endl;
return 0;
}
//从大到小
#include<iostream>
#include<algorithm>
using namespace std;
bool compare(int a,int b)
{
return a>b;
}
int main()
{
int a[10]={9,6,3,8,5,2,7,4,1,0};
for(int i=0;i<10;i++)
cout<<a[i]<<endl;
//从大到小排序
sort(a,a+10,compare);//在这里就不需要对compare函数传入参数了,
//这是规则
for(int i=0;i<10;i++)
cout<<a[i]<<endl;
return 0;
}
习题
1.对任意7个关键字进行基于比较的排序,至少要进行____次关键字之间的两两比较
考虑最坏的情况,公式 [log n!]
n=7代入,得13
动态规划和贪心算法(具体略)
在动态规划算法中,每步所作出的选择往往依赖于相关子问题的解,因此只有在解出相关子问题的解后才能做出选择;
而贪心算法所作的贪心选择仅在当前状态下做出的最好选择,即局部最优选择,然后再求解作出该选择后所产生的相应子问题的解,即贪心算法所作出的贪心选择可以依赖于“过去”所作出的选择,但绝不依赖于将来所作出的选择,也不依赖于子问题的解。
贪心算法和动态规划算法都具有最优子结构性质。
0-1背包问题(略)
ive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
[外链图片转存中…(img-Qj5FWNtG-1598279792248)]
[外链图片转存中…(img-Npz9VPKj-1598279792250)]
- 1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。
- 2.j–由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
- 3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
- 4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。
- [外链图片转存中…(img-ySTCtmdS-1598279792251)]
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趟排序后的状态
排序算法的总结(考点)
- 内部排序
- 冒泡排序 O(N^2)
- 选择排序O(N^2) 不稳定
- 插入排序O(N^2)
- 希尔排序O(N^1.5) 不稳定
- 归并排序O(NlogN)
- 快速排序O(NlogN) 不稳定
- 堆排序O(NlogN) 不稳定
- [外链图片转存中…(img-ci4p0bK7-1598279792252)]
[外链图片转存中…(img-PP4pysai-1598279792253)]
可以参考https://www.cnblogs.com/onepixel/articles/7674659.html
C++STL库的排序
//从小到大
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[10]={9,6,3,8,5,2,7,4,1,0};
for(int i=0;i<10;i++)
cout<<a[i]<<endl;
sort(a,a+10);//第三个参数不用写(如果是从小到大排序的话)
for(int i=0;i<10;i++)
cout<<a[i]<<endl;
return 0;
}
//从大到小
#include<iostream>
#include<algorithm>
using namespace std;
bool compare(int a,int b)
{
return a>b;
}
int main()
{
int a[10]={9,6,3,8,5,2,7,4,1,0};
for(int i=0;i<10;i++)
cout<<a[i]<<endl;
//从大到小排序
sort(a,a+10,compare);//在这里就不需要对compare函数传入参数了,
//这是规则
for(int i=0;i<10;i++)
cout<<a[i]<<endl;
return 0;
}
习题
1.对任意7个关键字进行基于比较的排序,至少要进行____次关键字之间的两两比较
考虑最坏的情况,公式 [log n!]
n=7代入,得13
动态规划和贪心算法(具体略)
在动态规划算法中,每步所作出的选择往往依赖于相关子问题的解,因此只有在解出相关子问题的解后才能做出选择;
而贪心算法所作的贪心选择仅在当前状态下做出的最好选择,即局部最优选择,然后再求解作出该选择后所产生的相应子问题的解,即贪心算法所作出的贪心选择可以依赖于“过去”所作出的选择,但绝不依赖于将来所作出的选择,也不依赖于子问题的解。
贪心算法和动态规划算法都具有最优子结构性质。
0-1背包问题(略)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DK3fCZYM-1598279792254)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\1596557588015.png)]