数据结构应试手册3.0

《数据结构应试手册》

本手册(一座屎山)仅限用于个人应试

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(传统方法)

在前面的前序和中序先到最左侧压入栈的时候,两种顺序依次是

  • 前序: 中入栈——>左入栈——>左出栈——>中出栈——>右入栈——>右孩子入出——>右出栈 在入栈时候操作即可前序
  • 中序: 中入栈——>左入栈——>左出栈——>中出栈——>右入栈 ——>右孩子入出——>右出栈按照出栈顺序即可完成中序

而在后序遍历中:它有这样的规则:

  • 入栈,第一次访问

  • 即将出栈。第二次访问,

  • 如果有右孩子,先不出栈把右孩子压入栈第一次访问,如果没右孩子。访问从栈中弹出。

  • 循环重复,直到栈为空

    所以要设置一个标记,记录访问的次数,如果是第二次访问,则要判断该结点有无右子树。

img

参考一下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.i2i/22.k2k13.n0,2n2n0=n2+14.nlog2n+1logn+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(平均查找长度)的计算

  • 《王道》习题补充知识点:
    1. 散列表的平均查找长度与α相关,不直接依赖于n或者m
    2. 散列函数有一个共同的性质,即函数应当以(等概率 )取其值域的每个值

索引结构

包括两部分:索引表and数据表

(一)概念

索引非顺序结构:一个索引项对应数据表中一个对象的索引结构被称为稠密索引

索引顺序结构:数据表中的记录按关键字有序,对一组数据建立一个索引项,稀疏索引

稠密索引,稀疏索引都是线性索引

索引结构的检索分两步完成,step1:查找索引表,step2:根据索引项的指示在数据表中读取数据

(二)分块查找

首先查找索引表

在确定的那一块中进行顺序查找

(三)

多级索引

倒排表:有次索引

次索引表的索引项:次关键字,链表长度,链表本身

倒排表的内容是:一个属性值和该属性的全部记录地址

排序算法

希尔排序

img

归并排序

归并排序(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]中。
  • img
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)]

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值