国庆编程笔记(上)c++算法教材大全


一.array容器

1,特点:

大小固定,无法增加或删除元素。无法借由增加或移除元素而改变其大小,它只允许访问或者替换存储的元素,内置函数较多。

2,头文件 :#include

3,初始化

std::array<类型,数量>名称;

如:std::array<int,10>values{1,2,3,4,};
定义一个数量为10整型array,前四个元素为1,2,3,4.

4,函数

成员函数 功能
begin() 返回指向容器中第一个元素的随机访问迭代器。
end() 返回指向容器最后一个元素之后一个位置的随机访问迭代器,通常和 begin() 结合使用。
rbegin() 返回指向最后一个元素的随机访问迭代器。
rend() 返回指向第一个元素之前一个位置的随机访问迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上增加了 const 属性,不能用于修改元素。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
size() 返回容器中当前元素的数量,其值始终等于初始化 array 类的第二个模板参数 N。
max_size() 返回容器可容纳元素的最大数量,其值始终等于初始化 array 类的第二个模板参数 N。
empty() 判断容器是否为空,和通过 size()==0 的判断条件功能相同,但其效率可能更快。
at(n) 返回容器中 n 位置处元素的引用,该函数自动检查 n 是否在有效的范围内,如果不是则抛出 out_of_range 异常。
front() 返回容器中第一个元素的直接引用,该函数不适用于空的 array 容器。
back() 返回容器中最后一个元素的直接应用,该函数同样不适用于空的 array 容器。
data() 返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能。
fill(val) 将 val 这个值赋值给容器中的每个元素。
array1.swap(array2) 交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型。
At() 给某元素赋值

get<>() 重载函数获取指定函数位置

二.vector动态数组

1.定义

Vector<类型>名称.

2.特性:

vector的插入和删除比数组更方便,不定长。

3.读入

读入不能直接cin;而要先:
cin>>a;
h.push_back(a);

4.函数

push_back();尾部插入元素
pop_back()删除最后一个元素
insert(const_iterator a,b);在迭代器指向a的位置处插入一个元素b
insert(const_iterator a,int count,b);在迭代器指向的位置a处插 入 count个元素b
erase(const_iterator pos); 删除迭代器指向pop的元素
erase(const_iterator a,const_iterator b);删除迭代器从a到b之间的元素
clear();删除容器中所有元素

三、迭代器

用来指向、遍历、修改容器元素的变量。
定义迭代器
如:正vector::iterater it;
反Vector::reverse_iterater it;
注意:it是迭代器的名称。
it是指针,*it是元素。
使用迭代器遍历vector:
for(it=v.begin();it!=v.end();it++){
cout<<*it<<” “;
for(it=v.rbegin();it!=v.rend;it++){
Cout<<*it<<” “;
迭代器分为 输入、输出、正向、双向、随机5种
正向:p++,*p;
双向:正向、p–
随机:双向、p-=i,p+=i,p+i,p-i,p[i];可以进行大小比较
容器 迭代器
vector 随机
depue 随机
list 双向
set/multiset 双向
map/multimap 双向
stack 不支持
queue 不支持
priority_queue 不支持

四.set集合

set是一种关联容器,是排序好了的集合,元素不能重复;
初始化
sets; 因为不能有重复的元素,所以不能定义为
sets(5);
可以利用数组初始化,
int a[]={1,2,3,4,5};
sets(a,a+5);
3.set只支持双向迭代器。

五、广度优先搜索

广搜与深搜不同,没有回退过程;广搜从起点开始,以右下左上的优先级遍历他的子节点,然后再依次以他的子节点作为起点进行遍历。
广搜:效率高的同时深度小,内存耗费大。
深搜:适合于遍历,深度小时效率高。
广搜需要给已遍历过的点标注,可以使用二维数组,加入头指针和尾指针两个变量进行标注,头指针代表当根结点,为指针代表子节点,当头指针大于为指针时,便利结束。广搜也可以用树性结构模拟。
如:3*3矩阵
1 2 4 树性图模拟最短路径 11-12-13-23-33
3 5 7 二维数组:
6 8 9 1 1 2 1 2 3 2 3 2
1 2 1 3 2 1 3 2 3

六、STL标准模板库——栈(stack)

容器适配器stack,queue,priority queue不适合迭代器
定义 stacks.
函数 s.push();元素入栈
s.pop();栈顶元素出栈
s.top();返回栈顶元素

七、桶排序

1.桶排序利用一个数组来存储,当未排序的数列中出现v,则在下标为v的元素上加1.利用数组本身的有序性,最后输出数组:如数组下标为3的元素值为2则输出两个3.数组a[i]代表i出现的次数。 桶排序不适合排序数字值波动较大的数列。
桶排序伪代码:

cin>>n;
for(i=0;i<n;i++){
	cin>>x;
	a[x]++;//记录x出现了多少次 
} 
for(i=0;i<n;i++){
	for(j=1;j<=a[i];j++){//若a[i]=0,则不输出
		//a[i]用来计数,输出a[i]次i; 
		cout<<i<<" "; 
	}
}

八.Pair

pair数组是一种结构体,一般每个子结构分为两个元素,first和second,类似宽为二的二维数组排序时两个子元素一起改变下标
九、快排

九、快速排序

快速的特点是速度快,利用分治思想建立“哨兵”,然后比较与其大小,分立“哨兵”两旁,以此类推;
程序笔记:

void qsort(int l,int r)
{
    int mid=a[(l+r)/2];
    int i=l,j=r;
    do{
        while(a[i]<mid) i++;
        while(a[j]>mid) j--;
        if(i<=j)
        {
            swap(a[i],a[j]);
            i++;
            j--;
        }
    }while(i<=j);
    if(l<j) qsort(l,j);
    if(i<r) qsort(i,r);
}

十、DP动态规划:

动态规划是一种抽象的思想,常用于找到最短路径或最小最大和。一般分为三个步骤
划分阶段(子问题)
确定阶段之间的转移关系,建立状态转移方程
使用递归、递推或记忆化搜索(遍历过的标记)解决问题
DP动态规划 题解笔记(内含多种方法):
数塔问题
一个数塔,从顶部出发,在每个节点可以选择向左走或是向右走,一直走到底层,要求找出一条路径,使路径上的值最大。
来源:八中OJ #25203 http://222.180.160.110:1024/problem/25203
题解:

#include<bits/stdc++.h>
using namespace std;
int n,a[101][101],i,j;
int d[101][101]; 
int ma_x;
//深度优先搜索
void dfs(int x,int y,int sum){
	if(x<=n){
		ma_x=max(ma_x,sum); 
		dfs(x+1,y,sum+a[x+1][y]);
		dfs(x+1,y+1,sum+a[x+1][y+1]);
	}
}
//动态规划——递推 
void fun1(){
	int i;
	for(i=1;i<=n;i++)
		d[n][i]=a[n][i];
	for(i=n-1;i>=1;i--){
		for(j=1;j<=i;j++)
			d[i][j]=a[i][j]+max(d[i+1][j+1],d[i+1][j]);
	}
}
//动态规划——递归
int fun2(int x,int y){//倒推 
	if(x<n) 
		return a[x][y]+max(fun2(x+1,y),fun2(x+1,y+1));
	else return a[x][y];//让递归到大底层时停止 
} 
//重复计算 

//动态规划——记忆化搜索
//若你n>0,即值为零的数未计算过,若你n>=0,把用来存储的数组初始值为-1; 
int fun3(int x,int y){ 
	if(x==n) 
		return a[x][y];
	else {
		if(d[x][y]==-1) return a[x][y];
		else return a[x][y]+max(fun3(x+1,y),fun3(x+1,y+1));
	}
} 

int main()
{
	cin>>n;
	for(i=1;i<=n;i++){
		for(j=1;j<=i;j++)//j<=i,因为是倒三角 
			cin>>a[i][j];
	}
	//深度优先搜索 :
	//x=y=1;表示从第一行、第一列开始
	dfs(1,1,a[1][1]);
	cout<<ma_x<<endl;
	
	//动态规划——递推:
	fun1(); 
	cout<<d[1][1]<<endl;
	
	//动态规划——递归:
	cout<<fun2(1,1)<<endl;//有返回值问题直接 cout;

	//记忆化搜索:
	cout<<fun3(1,1)<<endl;
}

十一.链表

list是一个线性双向链表结构,每一个节点都包括一个信息块、前驱指针和后驱指针list可以迅速的插入和删除,但不能随机访问,list由指针连接起来,没有下标,只能用双象迭代器遍历。
在x的位置插入y:
list::iterator it=l.begin();

list<int>::iterator it=l.begin();
cin>>x>>y;
for(i=1;i<x;i++)
It++;//让it等于x的位置
l.insert(it,y);

remove() 删除此元素
.sort p排序

十二.计数排序

计数排序是假设输入的数据都属于一个小区间内的整数,而桶排序则假设输入是由一个随机过程产生的。当桶排序的桶达到最大值时,就是计数排序。
计数排序不用定义最大值+1个数组长度,只定义最大值-最小值+1即可,但输出时要等范围增加数值,这样大大节省了空间。
类似桶排序,见昨日(10.3)笔记。

十三.基数排序

将未排序的数列从地位未开始排序,把此位相同的数放在同一个桶子里,从低位到高位,依次进行,知道排序完为止。
如:14 ,56 ,7,89 ,97, 60
按各位排:
0 60
1
2
3
4 14
5
6 56
7 7,97
8
9 89
按十位排:
0 7
1 14
2
3
4
5 56
6 60
7
8 89
9 97
排序后:7,14,56,60,89,97

基数排序是稳定的分配式排序,一定程度上融入了桶排序思想。原来我有疑问:高位排序会打乱低位排序,为什么还要从低位起,后来发现如果从高位排起,那么还要对每个桶内进行排序,时间复杂度大大提升,而且还要考虑位数,从低位排起省略了这几点。

十四.图及图论

一、图论基础知识:

1.图分为有向图,无向图,完全有向图(任意两个点上都存在边),完全无向图,带权图(有距离的图),连通图(无向图中任意两点存在路径可达),强连通图(有向图中任意两点存在路径可达)。
2.n个顶点的完全无向图有n*(n-1)/2条边,n个顶点的完全有向图有n*(n-1)条边。
3.顶点的度:
无向图:与此点相连的边的个数
有向图分为出度和入度。
图的度:图中所有点的度数之和
4.路径与回路:
路径:点a到点b所经过的所有的边
简单路径:除起点和中点外,点各不相同。
回路:起点和终点相同的路径。
简单回路:由简单路径组成的回路。
5.连通分量:在一个不连通的图中取出连通的一部分,叫做连通分量。

二、图的存储:

1.邻接矩阵:
邻接矩阵用下标表示点,内容表示是否有线,若为一,则有线,若为零,则无线。
无向图的链接矩阵是对称的,有向图邻接矩阵,出度=第i行元素之和,入度=第i列元素之和,图的度=1的个数*2.

三、Dijkstra (迪杰斯特拉)算法

是一种单源最短路径算法,迪杰斯特拉不适用于有负边权的图。
此算法基于贪心思想,迪杰斯特拉算法以找中间节点的方式将最短路径不断更新,
最后求出最短路径。最多循环n-2次。
代码实现:
1.需要定义一个visit数组,用来标记已访问过的中间节点。
2.定义一个dist数组,记录目前两点之间的最短距离。
3.min_dist,存储从起点到其他未被访问的点的最短距离。
4.middle存储最短路径结点编号。

#include <iostream>
#include <cstring>
#include <stack>
using namespace std;
#define MAX 100
#define INF 0x3f3f3f3f
int dist[MAX], path[MAX];
struct MGraph
{
	int edges[MAX][MAX];//邻接矩阵,记录的是两点之间的距离,也就是权值 
	int n,e;//顶点数和边数
}G;
void init() {
    memset(G.edges, INF, sizeof(G.edges));//默认为INF
}
void insert(int u, int v, int w) {
    G.edges[u][v] = w;//
}
void printfPath(int path[], int a){
	stack<int> s;
	//这个循环以由叶子结点到根结点的顺序将其入栈
	while(path[a] != -1){
		s.push(a);
		a = path[a];
	} 
	s.push(a);
	while(!s.empty()){
		cout << s.top() << " ";//打印栈顶元素,实现了顶点的逆序打印
		s.pop(); 
	}
	cout << endl;
} 
void Dijkstra(MGraph g, int v, int dist[], int path[]){ //顶点默认从0到n 
	int set[MAX], min, i, j, u;
	//对各个数组进行初始化
	for(i = 0; i < g.n; i++){
		dist[i] = g.edges[v][i];
		set[i] = 0;
		if(g.edges[v][i] < INF){
			path[i] = v;
		}else{
			path[i] = -1;
		}
	} 
	set[v] = 1; 
	path[v] = -1;
	//初始化结束,关键操作开始
	for(i = 0; i < g.n - 1; i++)
	{
		min = INF;//找到的点   目前最小 
		//这个循环每次从剩余顶点中选出一个顶点,通往这个顶点的路径在通往所有剩余顶点的路径中是长度最短的
		for(j = 0; j < g.n; j++){
			if(set[j] == 0 && dist[j] < min){
				u = j;
				min = dist[j];
			}
		} 
		set[u] = 1;//将选出的顶点并入最短路径中
		//这个循环以刚并入的顶点作为中间点,对所有通往剩余顶点的路径进行检测
		for(j = 0; j < g.n; j++) {
			//这个if判断顶点u的加入是否会出现通往顶点j的更短的路径,如果出现,则改变原来路径及其长度,否则什么都不做
			if(set[j] == 0 && dist[u] + g.edges[u][j] < dist[j]){
				dist[j] = dist[u] + g.edges[u][j];//更新路径长度 
				path[j] = u;//更新路径顶点 
			} 
		} 
	} 
}
int main() {
	init();
    int n, m;//n个点,m条边
    int a, x, y, w;
    cin >> m >> n;
    G.e = m;
    G.n = n;
     
    for(int i = 0; i < m; i++){
		cin >> x >> y >> w;
        insert(x, y, w);
    }
    Dijkstra(G, 0, dist, path);
    printfPath(path, 5);
    for(int i = 0; i < n; i++) {
    	cout << dist[i] << " ";
	} 
    return 0;
}

十五.希尔排序

希尔排序又称缩小增量排序,是通过不断分组和插入实现的一种排序,是插入排序的进阶版。希尔排序看上去很复杂,其实是通过每次简单分组使得数列基本有序,这就为最后一步的插入排序省下了时间,从而优化大局。
希尔排序是不稳定的,因为同一个数据有可能被移动多次,希尔排序时间复杂度为 O(n^(1.3-2)) ,空间复杂度O(1)。
函数代码:

void ShellSort(int array[],int length){
	for(int step=length/2;step>0;step=step/2){ 
		for(int i=0;i<step;i++){				
			for(int j=i+step;j<length;j+=step){	
				int temp=array[j];
				int m=j-step;
				for(m=j-step;m>=0&&array[m]>temp;m-=step)										
					array[m+step]=array[m];					
				array[m+step]=temp;				
			}
		}
	}
}

十六.欧拉路

欧拉路:能走完所有边并不重复。
欧拉回路:在欧拉路的基础上回到起点。
无向图:图的度数为偶数的情况下,能够成欧拉回路。
除起点和终点外度数为奇,其余点度数为偶
有向图:出度=入度,能够成欧拉回路。
起点出度比入度大一,终点入度比出度大一,其余点出入度相等

十七.归并排序

归并排序分治的思想,先拆解后合并。将一组数据分为多个子序列,然后在每个子序列内实现有序,最后在合并后进行最后的排序,使数据有序。归并排序也是通过不断分组实现的,和希尔排序不同,归并还需在在组内继续分组,从而实现递归的过程,使得某一部分基本有序。归并排序是稳定的,时间复杂度为O(nlogn)。
函数代码:

void merge(vector<int> & arr, int low, int mid, int high)
{
    int *temp = new int(high - low + 1);
    int i = low, j = mid + 1, k = 0;
    while (i <= mid && j <= high)
        temp[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
while (i <= mid)
        temp[k++] = arr[i++];
    while (j <= high)
        temp[k++] = arr[j++];
    for (i = low, k = 0; i <= high && k<=high - low + 1; i++, k++)
        arr[i] = temp[k];
    delete []temp;
}
void mergeSort(vector<int> & arr, int low, int high)
{
    if (low >= high)
        return;
    int mid = (low + high) / 2;
    mergeSort(arr, low, mid);
    mergeSort(arr, mid + 1, high);
    merge(arr, low, mid, high);
}

十八、分治思想:

分治算法主要用于解决规模较大的问题,通过将大问题“分而治之”有效降低题目难度。又因为分解得到的子问题之间是相互独立且互不影响的,所以可以同时进行。分治法大大提高了时间效率。

十九.二叉树遍历

DLR 先序遍历
LDR 中序遍历
LRD 后序遍历
二叉树遍历除访问根节点外,若访问的节点有自己的子节点且符合当前访问规则,那么继续访问其子节点,以此类推,形成递归过程,最终都从最底部开始访问。
若二叉树顶点各不相同,则知道其前序遍历和中序遍历或中序遍历和后序遍历则能唯一的还原二叉树。

二十.Trie字典树

字典树是以树的方式存储字符串,特点是某些字符串有着公共前缀,节约了存储空间。树的每一条边上都对应着一个字符,从树的根开始存储。每一条路径(不一定从根结点开始,也不一定要访问到底)中经过的字符都是这个字符串的子串。

即获奖概率为2/3,选择转换获奖概率更高。

二十一.平衡二叉查找树

1.平衡二叉查找树是一种绝对平衡的树形结构,其最长高度与最短高度最多相差一个单位长度。
2.平衡二叉查找树任意结点的左结点一定小于此节点,任意结点的右结点一定大于此节点(所以其子树也均为平衡二叉查找树)
3.查找某一数据只需不断对比与当前节点的大小,比当前结点大,则向右访问,比当前结点小,则向左访问,直到下一个结点为空,即查找结束,若没有出现查找的数据,则此数据不存在。

二十二.红黑树

红黑树属于二叉查找树的一种,适用于有频繁插入和删除的数据集合。
1.红黑树根节点是黑色的,叶节点是不存储有效数据的黑色空节点。
2.红黑树相邻两个节点不能同时为红色,也就是红色结点的子节点是黑色的。
3.红黑树任意结点到可到达的任意叶节点都包含了相同数量的黑色结点。
4.有n个结点的红黑树高度《=2*log(n+1)。

二十三.树

是一种有层次关系的集合。每棵树只有根结点没有父节点,其他节点有且只有1个父节点,树中每一个节点都构成一个以他为根的树。

二叉树 每个结点最多有两个孩子
平衡二叉树 除最后一层外每一层都被填满且最后一层从第一个节点开始。

二十四.堆

堆可以看做一种特殊的树,其不同就是有大小排序。从根结点开始依次增大就是最小堆,依次减小就是最大堆。二叉堆即每个结点最多只有两个子节点。

二十五.堆排序

堆排序是一种不稳定的排序算法,堆排序先构建一个堆,将元素放进堆中,然后不断交换根结点与堆尾的值,从而求出当前最值。

二十六.贝叶斯定理

关于随机事件A,B的条件概率定理称为贝叶斯定理,P(a|b),表示在B事件发生的前提下A事件发生的概率,即条件概率。
贝叶斯定理:P(a|b)=P(b|a)*p(a) / p(b).

二十七.三门问题——蒙提霍尔问题

三门问题源自蒙提霍尔主持的美国的电视游戏节目Let’s Make a Deal。参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门中的一扇有羊的门。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门是否会增加参赛者赢得汽车的机率。
有三种可能的情况:
参赛者挑山羊一号,主持人挑山羊二号。转换将赢得汽车。
参赛者挑山羊二号,主持人挑山羊一号。转换将赢得汽车。
参赛者挑汽车,主持人挑羊一号。转换将失败,和“参赛者挑汽车,主持人挑羊二号。转换将失败。”此情况的可能性为:1/31/22=1/2

二十八.快速幂

快速幂适用于求a的n次方类问题,即将a的n次方化简为an/2*an/2,n为奇数时在乘一个,以此类推化简至自己想要的结果。这种方法降低了时间复杂度并提高了效率,是一种很常用的数学类算法。

二十九,矩阵运算

矩阵加法:将同一位置上的元素相加,即:c[1][1]=a[1][1]+b[1][1].
矩阵减法:将同一位置上的元素相减,即:c[1][1]=a[1][1]-b[1][1].
矩阵乘法:必须满足A的行=B的列,C[i[j]a的第i行与b的第j列对应元素乘积之和。即C[i][j]=a[i][1]*b[1][j]+a[i][2]*b[2][j]+…a[i][r]b[r][j]=∑rk=1 ai,kbk,j。矩阵乘法满足结合律不满足交换律。

三十、KMP算法

KMP算法用于解决字符串的匹配问题,即a是不是b的子串,将b称为主串,a称为模式串,也称子串。
KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。

写在最后:今天因为时间仓促,写得不够精细,之后还会优化此篇文章;之前笔记是发在洛谷的,今天做了整理和补充,望支持。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来自八中的小鹿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值