前言:
1、这篇博客基本上是写给自己看的,不过本人特别喜欢打注释,所以有兴趣看的朋友也可以往下看看我的理解...
2、本文的代码都基于《啊哈!算法》一书。
(可能在更新中......)
目录
快速排序
大致思路:
1、我们令首位为基准数,而在其后面的数都要一直与此基准数做比较。
2、使用双指针,使得一个指针 j 先从最后一位开始向左移动,一个指针 i 从第二位开始往左移动。一直到右指针 j 找到一个小于等于基准数的数,左指针 i 找到一个大于基准数的数,此时交换二者的位置。(一定是 j 先移动,因为数列原先就有序,i 必会移动到最后一位并与 j 相遇,此时首位与末尾交换位置,序列会被打乱)
3、重复2的操作直到 i , j 相遇,此时让首位(基准数)与i j 所指的元素交换位置,则保证了基准数左边的数都小于等于基准数,右边的数都大于基准数。此时我们认定的基准数在数列中已经到了它该到的位置。
4、然后将序列分成基准数左右两边的两个子序列分别重复1~3操作,直到拆不出新的子序列为止。
时间复杂度:
最差O(n^2),平均O(nlogn)
拓展:
某stl函数: nth_element()实现的应该是一次快排,所以若你只想要找到第 n 个元素,使用这个函数花费的时间是O(n)的。
#include <bits/stdc++.h>
using namespace std;
//这个是快速排序
int a[101], n;
//实际上是递归函数
void quicksort(int left, int right)
{
int i, j, t, temp;
//直到拆不出新的子序列为止
if(left > right) return;
//先找一个基准数,也就是当前数列最左端的数字
temp = a[left];
i = left;
j = right;
//
while(i != j)
{
//一定是先让右端点的指针往左移动,找到一个小于基准数的数
while(a[j] >= temp && i < j) j--;
//找到后左指针往右移动,找到一个大于基准数的数
while(a[i] <= temp && i < j) i++;
//如果二者没相遇,就交换左右指针所指的数
if(i < j)
{
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
//让基准数归位
a[left] = a[i];
a[i] = temp;
//基准数左右边的数列再进行快排
quicksort(left, i-1);
quicksort(i+1, right);
}
//
int main()
{
int n;cin>> n;
for(int i = 1;i <= n;++i) cin >> a[i];
//
quicksort(1, n);
//
for(int i = 1;i <= n;++i) cout << a[i] << " ";
return 0;
}
等等!
突然发现后面有点不好写了,因为学习栈/队列/bfs/图论貌似都是融合着学的,所以现在先写一点前置知识
对了我们一般用uvw分别表示从点 u 到 点 v 的权值是 w。
栈和队列简介
栈
可以把栈看成一个竖着放的木桶,我们把手上的东西依次放进木桶内,若想取出物品,必然只能逆序取出。所以栈是先进后出的(FILO),仅栈顶元素出栈。
队列
可以把队列看成一个横着放的羽毛球桶,我们把手上的东西依次从右边放进羽毛球桶内,羽毛球只能从左边取出,所以队列是先进先出的(FIFO),即从队尾入队,队首出队。
图的两种表示法
邻接矩阵表示法
定义:
1、我们建立一个二维数组e[i][j],表示的是从点 i 到点 j 的距离(权值),并规定自己到自己为0, 两点不连通为inf(这里我们令inf = 99999999)。
读入:
1、无向图的话显然 i 到 j 和 j 到 i 都可以,所以两个都要读入哦。
//矩阵的初始化
for(int i = 1;i <= n;++i)
for(int j = 1;j <= n;++j)
if(i == j) e[i][j] = 0;
else e[i][j] = 99999999;
//读入无向图
int a, b;
for(int i = 1;i <= m;++i)
{
cin >> a >> b;
e[a][b] = 1;
e[b][a] = 1;
}
邻接表表示法
定义:
1、大致意思就是以每个顶点u建立一个链表。
(这个有点难解释,会链表的话应该会比较好理解。看代码+注释吧)
#include <bits/stdc++.h>
using namespace std;
//邻接表adjacency list
//我们把m远小于n^2的图称为稀疏图,而m相对较大的图称为稠密图
//对于稀疏图我们用邻接表代替邻接矩阵,时间复杂度O(m+n)logn
//_first数组存的是1~n号顶点最后一条边的编号
//_next数组存的是编号为i的边的上一条边的编号
int main()
{
int n, m;cin >> n >> m;
//uvw要比m的最大值大1
int u[6], v[6], w[6];
//_first要比n的最大值大1, _next要比m的最大值大1
int _first[5], _next[5];
//
for(int i = 1;i <= n;++i) _first[i] = -1;
for(int i = 1;i <= m;++i)
{
cin >> u[i] >> v[i] >> w[i];
_next[i] = _first[u[i]];
_first[u[i]] = i;
}
return 0;
}
图的遍历(邻接矩阵表示法)
用DFS遍历图
就是选定一个顶点然后进行dfs即可。
#include <bits/stdc++.h>
using namespace std;
//使用dfs来遍历图
//我们用e[i][j]数组来存放i到j的关系,0表示自己到自己,1表示有边,INF表示无边
int book[1001], n, m, e[101][101];
int sum = 0;
//
void dfs(int cur)
{
//打印访问过的点
printf("%d ", cur);
sum++;
//如果n个点都被访问过了
if(sum == n) return ;
for(int i = 1;i <= n;++i)
{
//如果当前点与遍历到的点有边且没有访问过
if(e[cur][i] == 1 && book[i] == 0)
{
book[i] = 1;
dfs(i);
}
}
return ;
}
//
int main()
{
//n个点以及m条边
cin >> n >> m;
//初始化图的数组,用99999999表示INF
for(int i = 1;i <= n;++i)
for(int j = 1;j <= n;++j)
if(i == j) e[i][j] = 0;
else e[i][j] = 99999999;
//读入边
int a, b;
for(int i = 1;i <= m;++i)
{
cin >> a >> b;
//因为是无向图所以i-j与j-i都要赋值为1
e[a][b] = 1;
e[b][a] = 1;
}
//选择一个顶点出发
book[1] = 1;
dfs(1);
return 0;
}