算法设计期末考试总结

一.简单题:

1.基本概念
  • 算法:对问题求解步骤的准确而完整的描述,是一系列有穷规则指令集合。
  • 贪心法:是一种在每一步选择中都采取在当前状态下最有利的选择,从而希望导致结果最有 利的算法。
  • 时间复杂性(时间复杂度):在算法运行期间所花费的时间。
  • 分治法:(设计思想–分而治之)将一个规模为n的问题分解为k个规模较小的子问题,这些 子问题互相独立且与原问题相同,然后递归求解子问题,最后子问题的解合并得到原问题的 解。
  • 搜索法:利用计算机的高性能来有目的的穷举一个问题的部分或所有的可能结果,从而求出 问题的解的算法。
  • 递归:直接或间接调用自身的算法。
  • 算法分析:指对计算机算法的时间和空间复杂度进行定量的分析。
2.要会画归调用时调用栈里的主要内容(局部变量、形式参数)。
  • 每一次递归调用都会在栈中开辟新的一层:每一层包含一个地址(调用完后需返回到上一层 执行的址),这一层传来的形参和局部变量。
  • 函数中的形式参数先进栈,然后局部变量进栈,当本次递归调用结束后,局部变量先出栈, 形式参数再出栈。
3.哈夫曼树:

​ 1.权值分别为W1、W2…Wn的n个结点,构成只有根结点的n个二叉树,组成森林即n个数 的权值保存在数组W[ ]中

​ 2.用selectMin( )方法:在森林中选出两个根结点权值最小的树,合并作为一棵新树的左右 子树且新树的根结点权值为其左右结点权值之和

​ 3.从森林中删除刚才选取的两棵树,并将新树加入森林

​ 4.重复2、3步骤,直到森林只剩一棵树为止,该树为哈夫曼树。

  • 哈夫曼编码:规定哈夫曼树的左分支代表0,右分支代表1,则从根结点到叶子结点所经过的 路径分支组成的0和1的序列为改结点对应字符的编码,即哈夫曼编码。
  • 解码:哈夫曼编码中若得到如a :01 b :101对应关系,通过这个关系直接将像“asdsdfdfg”直接 转换为“01110101”这样二进制编码。译码的时候,读取二进制编码,先读取一位,然后在关 系表中查找该二进制数对应的字符,如果没有找到,继续读取二位,然后继续在关系表中查 找该二位二进制对应的字符。如此循环,知道找到字符位置,然后将二进制数替换为相应的 字符,知道所有的数都替换完为止。
4.常见的最小生成树的构造方法有哪两种?要能够说明他们的算法思想。
  1. ​ Kruskal算法思想:就是先把所有边取出来,然后按从小到大排序,每次取出一条边, 判断是否形成回路,如果是就丢弃,否则这条边就是我们想要的权值最小的边。
  2. ​ Prim算法思想:从一定点出发,找出此点邻接边中最短的边加入对应顶点,再次寻找这 两条边的最短邻接边,依次类推,加入所有没有被访问过的边。
5.深度优先搜索

如算法名称那样,深度优先搜索所遵循的搜索策略是尽可能“深”地搜索树。它的基本思想是:为了求得问题的解,先选择某一种可能情况向前(子结点)探索,在探索过程中,一旦发现原来的选择不符合要求,就回溯至父亲结点重新选择另一结点,继续向前探索,如此反复进行,直至求得最优解。深度优先搜索的实现方式可以采用递归或者栈来实现。由此可见,把通常问题转化为树的问题是至关重要的一步,完成了树的转换基本完成了问题求解。
(1)减少节点数,思想:尽可能减少生成的节点数
(2)定制回溯边界,思想:定制回溯边界条件,剪掉不可能得到最优解的子树
在很多情况下,我们已经找到了一组比较好的解。但是计算机仍然会义无返顾地去搜索比它更“劣”的其他解,搜索到后也只能回溯。为了避免出现这种情况,我们需要灵活地去定制回溯搜索的边界。
在深度优先搜索的过程当中,往往有很多走不通的“死路”。假如我们把这些“死路”排除在外,不是可以节省很多的时间吗?打一个比方,前面有一个路径,别人已经提示:“这是死路,肯定不通”,而你的程序仍然很“执着”地要继续朝这个方向走,走到头来才发现,别人的提示是正确的。这样,浪费了很多的时间。针对这种情况,我们可以把“死路”给标记一下不走,就可以得到更高的搜索效率。

6.广度优先搜索

类似树的按层遍历,其过程为:首先访问初始点Vi,并将其标记为已访问过,接着访问Vi的所有未被访问过可到达的邻接点Vi1、Vi2……Vit,并均标记为已访问过,然后再按照Vi1、Vi2……Vit的次序,访问每一个顶点的所有未被访问过的邻接点,并均标记为已访问过,依此类推,直到图中所有和初始点Vi有路径相通的顶点都被访问过为止。
递归

7.递归

1.编写递归函数的步骤,可以分解为三个。
递归第一个步骤:明确函数要做什么
对于递归,一个最重要的事情就是要明确这个函数的功能。这个函数要完成一样什么样的事情,是完全由程序员来定义的,当写一个递归函数的时候,先不要管函数里面的代码是什么,而要先明确这个函数是实现什么功能的。
2.递归第二个步骤:明确递归的结束(退出递归)条件
所谓递归,就是会在函数的内部逻辑代码中,调用这个函数本身。因此必须在函数内部明确递归的结束(退出)递归条件,否则函数会一直调用自己形成死循环。意思就是说,需要有一个条件(标识符参数)去引导递归结束,直接将结果返回。要注意的是,这个标识符参数需要是可以预见的,对于函数的执行返回结果也是可以预见的。
比如在上面的计算n的阶乘的函数中,当n=1的时候,肯定能知道f(n)对应的结果是1,因为1的阶乘就是1,那么我们就可以接着完善函数内部的逻辑代码,即将第二元素(递归结束条件)加进代码里面。
3.递归的第三个步骤:找到函数的等价关系式
递归的第三个步骤就是要不断地缩小参数的范围,缩小之后就可以通过一些辅助的变量或操作使原函数的结果不变。比如在上面的计算n的阶乘的函数中,要缩小f(n)的范围,就可以让f(n)=n* f(n-1),这样范围就从n变成了n-1,范围变小了,直到范围抵达n<=2退出递归。并且为了维持原函数不变,我们需要让f(n-1)乘上n。说白了,就是要找到一个原函数的等价关系式。在这里,f(n)的等价关系式为nf(n-1),即f(n)=nf(n-1)。

8.最小生成树

Prim算法(稠密图)
(1) 此算法可以称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。
(2) 图的所有顶点集合为V;初始令集合u={s},v=V−u ;
(3) 在两个集合u,vu,v能够组成的边中,选择一条代价最小的边(u0,v0),加入到最小生成树中,并把v0并入到集合u中。
(4) 重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。
(5) 由于不断向集合u中加点,所以最小代价边必须同步更新;需要建立一个辅助数组closedge,用来维护集合v中每个顶点与集合u中最小代价边信息:
Kruskal算法(稀疏图)
(7) 此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。
(8) 把图中的所有边按代价从小到大排序;
(9) 把图中的n个顶点看成独立的n棵树组成的森林;
(10) 按权值从小到大选择边,所选的边连接的两个顶点ui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。
(11) 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。

二.算法大题

1.冒泡排序
#include "iostream"
using namespace std;
int main(){
	int nums[]={2,34,56,7,8,9};
	int length=sizeof(nums)/sizeof(int);
	for(int i=0;i<length-1;i++){
		for(int j=0;j<length-i-1;j++){
			if(nums[j]>nums[j+1])
			{
				int temp=nums[j];
				nums[j]=nums[j+1];
				nums[j+1]=temp;
			}
		}
	}
	for(int i=0;i<length;i++){
	printf("%d-",nums[i]);
	}
} 
2.折半查找
#include "iostream"
int main(){
	int nums[]={1,22,33,44,55,66,77,88};
	int length=sizeof(nums)/sizeof(int);
	int left=0;
	int right=length-1;
	int mid;
	int findval=1;
	while(left<=right){
		mid=(left+right)/2;
		if(findval>nums[mid]){
			left=mid+1;
		}
		else{
			right=mid-1;
		}
	}
	printf("%d",right);
} 
3.快速排序
#include "iostream"
using namespace std;
void quickSort(int arr[],int l ,int r){
	if(l<r){
		int i,j,x;
		i=l;
		j=r;
		x=arr[i];
		while(i<j){
			while(i<j){
				while(i<j&&arr[j]>x){
					j--;
				}
				if(i<j){
					arr[i++]=arr[j];
				}
				while(i<j&&arr[i]<arr[j]){
					i++;
				}
				if(i<j){
					arr[j--]=arr[i];
				}
			}
			arr[i]=x;
			quickSort(arr,l,i-1);
			quickSort(arr,i+1,r);
		}
	}
}
	int main() {
		int nums[]={11,22,5,23,55,33};
		int length=sizeof(nums)/sizeof(int);
		quickSort(nums,0,length-1);
		for(int i=0;i<length;i++){
			printf("%d,",nums[i]);
		}
	
}
4.会议安排
#include"iostream"
using namespace std;
#define N 5
struct TypeElem{
	float Begin;
	float End;
};
TypeElem A[N]={
{8,13},{9,9.5},{10,11.5},{12,14},{10,13}
};
void mySort(TypeElem data[],int n){
	TypeElem temp;
	for(int i=0;i<n;i++){
		for(int j=1;j<n-i-1;j++){
			if(data[i].End>data[j].End){
				temp =data[i];
				data[i]=data[j];
				data[j]=temp;
			}
		}
	} 
}
int main(){
	mySort(A,N) ;
	int T=8;
	for(int i=0;i<N;i++){
		if(A[i].Begin>=T){
			T=A[i].End;
			cout<<"["<<A[i].Begin<<","<<A[i].End<<"]"<<endl ;
		}
	}
	return 0;
}
5.01背包问题
#include <iostream>
#include <math.h>
using namespace std;
#define n 5
struct TypeE {
    int w, v;
}; 
TypeE a[n] = {{2, 6},{2, 3},{6, 5},{5, 4},{4, 6}};
// 返回方案s的总重量,引用v里是总价值
int WV(unsigned long s, int &v) {
    int w = 0;
    v = 0;
    unsigned long k = 0x01; // k时逻辑尺 
    for(int i=0; i<n; i++) {
        if(k&s) {
            w += a[i].w;
            v += a[i].v;
        }
        k = k << 1;
    }
    return w;
} 
int main(){
    int tC = 10; // 总容量 
    int v = 0;   // 价值 
    int S = pow(2, n)-1; // n个1的十进制 
    int optimalV = 0, optimalC = 0; // 最优容量和最优价值 
    cout << "方案如下: " << endl;
    for(int s = 0; s < S; s++) {
        int w = WV(s, v);
        if(w <= tC) {
            if (optimalV <= v) {
                optimalV = v;
                optimalC = w;
            }
            cout << "容量:" << w << " 价值:" << v << endl;
        }       
    }
    cout << "最终方案:" << endl;
    cout << "容量:" << optimalC << " 价值:" << optimalV << endl;
    return 0;
}

6.图的广度优先搜索
#include <iostream>
using namespace std;

#define MaxNodeNum 10
#define QueueLen 100

struct TreeNode {
    char data;
    TreeNode* nodes[MaxNodeNum];
    TreeNode(char data): data(data) {
        for (int i=0; i<MaxNodeNum; i++)    nodes[i] = NULL;
    }
};
struct Queue {
    TreeNode* buffer[QueueLen];
    int front, rear;
    int inc(int d) { return (d+1)%QueueLen; }
    bool empty() { return front == rear; }
    bool full() { return inc(rear) == front; }
    bool push(TreeNode *p) {
        if (full()) return false;
        rear = inc(rear);
        buffer[rear] = p;
        return true;
    }
    TreeNode* removeHead() {
        if(empty()) return NULL;
        front = inc(front);
        return buffer[front];
    }
    Queue() {
        front = rear = 0;
    }
};

7.图的深度优先搜索
#include <iostream>
using namespace std;
#define n 7
int c[n+1][n+1] = {
    {0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 1, 1, 1, 0, 0, 0},
    {0, 1, 0, 0, 1, 1, 0, 0},
    {0, 1, 0, 0, 1, 0, 0, 0},
    {0, 0, 1, 1, 0, 1, 0, 0},
    {0, 0, 1, 0, 1, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 1}    
};
int vertex[n+1] = {0, 1, 2, 3, 4, 5, 6, 7};
bool visited[n+1];
// 从顶点k出发进行深度优先搜索
void Dfsk(int k) {
    visited[k] = 1;
    cout << vertex[k] << ", ";
    for(int j=1; j<=n; j++)
        if(c[k][j] == 1 && visited[j]==0)
            Dfsk(j);    
}
int main() {
    for(int i=1; i<n; i++)
        visited[i] = 0;
    // 深度优先搜索整个图 
    for(int i=1; i<=n; i++)
        if(visited[i]==0)
            Dfsk(i);
    return 0;
}

8.二叉树的遍历
#include "iostream"
#include "stdlib.h"
using namespace std;
typedef char ElemType;
/**
* 二叉树存储结构
*/
typedef struct BiTNode {
ElemType data;
struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
/**
* 生成二叉树
*/
void CreateBiTree(BiTree *T) {
ElemType ch;
cin >> ch;
if (ch == '#')
*T = NULL; //保证是叶结点
else {
*T = (BiTree)malloc(sizeof(BiTNode));
//if (!*T)
//exit(OVERFLOW); //内存分配失败则退出。
(*T)->data = ch;//生成结点
CreateBiTree(&(*T)->lchild);//构造左子树
CreateBiTree(&(*T)->rchild);//构造右子树
}
}
//表示对遍历到的结点数据进行的处理操作,此处操作是将树结点前序遍历输出
void operation1(ElemType ch) {
cout << ch << " ";
}
//此处在输出的基础上,并输出层数
void operation2(ElemType ch, int level) {
cout << ch << "在第" << level << "层" << endl;
}
//递归方式前序遍历二叉树
void PreOrderTraverse(BiTree T, int level) {
if (T == NULL)
return;
/*此处表示对遍历的树结点进行的操作,根据你自己的要求进行操作,这里只是输出了结点
的数据*/
operation1(T->data);
// operation2(T->data, level); //输出了层数
PreOrderTraverse(T->lchild, level + 1);
PreOrderTraverse(T->rchild, level + 1);
}
//递归方式中序遍历二叉树
void InOrderTraverse(BiTree T,int level) {
if(T==NULL)
return;
InOrderTraverse(T->lchild,level+1);
operation1(T->data);
// operation2(T->data, level); //输出了层数
InOrderTraverse(T->rchild,level+1);
}
//递归方式后序遍历二叉树
void PostOrderTraverse(BiTree T,int level) {
if(T==NULL)
return;
PostOrderTraverse(T->lchild,level+1);
PostOrderTraverse(T->rchild,level+1);
operation1(T->data);
// operation2(T->data, level); //输出了层数
}
int main() {
int level = 1;
BiTree T = NULL;
cout << "请以前序遍历的方式输入扩展二叉树:"; //AB#D##C##
CreateBiTree(&T);
cout << "递归前序遍历输出为:" << endl;
PreOrderTraverse(T, level);
cout << endl;
cout << "递归中序遍历输出为:" << endl;
InOrderTraverse(T, level);
cout << endl;
cout << "递归后序遍历输出为:" << endl;
PostOrderTraverse(T, level);
cout << endl;
return 0;
}

9.布线问题
#include <iostream>
#include <queue>
using namespace std;
typedef struct {
    int row;
    int col;
}Position;
#define m 8
#define n 8
int grid[n+2][m+2];
void showPath() {
    for(int i=1; i<=m; i++) {
        for(int j=1; j<=n; j++)
            cout << grid[i][j] << " ";
        cout<<endl;
    }
    cout<<"------------------"<<endl;
}
bool findpath(Position start, Position finish, int &pathLen, Position *&path) {
    if ((start.row == finish.row) && (start.col == finish.col)) {
        pathLen = 0;
        return true;
    }//start=finish
    //初始化图,-1为未访问
    for(int i=1; i<9; i++) {
        for(int j=1; j<9; j++)
            grid[i][j]=-1;
    }
    //添加阻挡点
    grid[2][3]=-2;
    // 方格列阵上下的围墙 
    for(int i=0; i<=m+1; i++)
        grid[0][i] = grid[n+1][i] = -2;
    // 方格列阵左右的围墙 
    for(int i=0; i<=n+1; i++)
        grid[i][0] = grid[i][n+1] = -2;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值