一、七大排序算法
#include <iostream>
#include <vector>
using namespace std;
void swap(int &a, int &b) {
int tmp;
tmp = a;
a = b;
b = tmp;
}
//一、插入排序
//1.1 直接插入排序 时间复杂度O(n^2) 空间复杂度O(1) 稳定
//构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
void insert_sort(vector<int> &nums) {
for (int i = 0; i < nums.size(); i++){
for (int j = i; j > 0; j--){
if (nums[j] < nums[j - 1])
swap(nums[j], nums[j - 1]);
}
}
}
//1.2 希尔排序 时间复杂度O(n^1.3) 空间复杂度O(1) 不稳定
//按照一定步长分成子序列进行排序,然后逐步递减步长来完成最终排序。增量 d = 1时, 就是插入排序
void shell_sort(vector<int> &nums) {
for (int gap = nums.size() >> 1; gap>0; gap >>= 1){
for (int i = gap; i < nums.size(); i++)
{
int tmp = nums[i];
int j = i - gap;
for (; j >= 0 && nums[j] > tmp; j -= gap){
nums[j + gap] = nums[j];
}
nums[j + gap] = tmp;
}
}
}
//二、选择排序
//2.1 选择排序 时间复杂度O(n^2) 空间复杂度O(1) 不稳定
//先进行第一轮循环比较相邻两项,找到最小值得下标,将最小值与第一项交换,然后重复排序剩余部分。
void select_sort(vector<int> &nums) {
for (int i = 0; i < nums.size(); i++) {
int min = i;
for (int j = i + 1; j < nums.size(); j++){
if (nums[j] < nums[min])
min = j;
}
swap(nums[i], nums[min]);
}
}
//2.2 堆排序 时间复杂度O(nlgn) 空间复杂度O(1) 稳定
//构造一个最大堆(完全二叉树),父结点大于左右子结点,然后取出根结点(最大值)与最后一个结点交换,重复调整剩余的结点成最大堆,得到有序的序列。
void max_heapify(vector<int> &nums, int l, int r) {
int curr = l;
int child = curr * 2 + 1;
while (child < r) {
if (child + 1 < r &&nums[child] < nums[child + 1])
child++;
if (nums[curr] < nums[child]) {
swap(nums[curr], nums[child]);
curr = child;
child = 2 * curr + 1;
}
else
break;
}
}
void heap_sort(vector<int> &nums) {
for (int i = nums.size() / 2 - 1; i >= 0; i--)
{
max_heapify(nums, i, nums.size());
}
for (int i = nums.size() - 1; i > 0; i--)
{
swap(nums[0], nums[i]);
max_heapify(nums, 0, i);
}
}
//三、交换排序
//3.1 冒泡排序 时间复杂度O(n^2) 空间复杂度O(1) 稳定
//每次通过两两比较交换位置,选出剩余无序序列里最大(小)的数据元素放到队尾。
void bubble_sort(vector<int> &nums) {
for (int i = 0; i < nums.size() - 1; i++){
for (int j = 0; j < nums.size() - i - 1; j++){
if (nums[j] > nums[j + 1])
swap(nums[j], nums[j + 1]);
}
}
}
//3.2快速排序 时间复杂度O(nlgn) 空间复杂度O(1) 不稳定
//将无序序列分隔成两部分,左边均比右边小,分别对这两部分进行排序
void quick_sort(vector<int> &nums, int l, int r) {
if (l < r) {
int i = l, j = r;
while (i < j) {
while (nums[j] >= nums[l] && i < j)
j--;
while (nums[i] <= nums[l] && i < j)
i++;
swap(nums[i], nums[j]);
}
swap(nums[l], nums[i]);
quick_sort(nums, l, i-1);
quick_sort(nums, i + 1, r);
}
}
//四、归并排序 时间复杂度O(nlgn) 空间复杂度O(n) 稳定
//将序列递归分成单位为1的小序列,然后两两合并排序,最终得到有序的序列。
void merge_sort(vector<int> &nums, int l, int r, vector<int> &tmp) {
if (l >= r) return;
int m = (l + r) / 2;
merge_sort(nums, l, m, tmp);
merge_sort(nums, m + 1, r, tmp);
int i = l, j = m + 1, index = l;
while (i <= m && j <= r) {
if (nums[i] < nums[j])
tmp[index++] = nums[i++];
else
tmp[index++] = nums[j++];
}
while (i <= m)
tmp[index++] = nums[i++];
while (j <= r)
tmp[index++] = nums[j++];
for (int i = l; i <= r; i++)
nums[i] = tmp[i];
}
int main() {
cout << "原始数列:8, 3, 1, 4, 11, 2, 2, 1, 5, 2, 7, 9, 1, 6 " << endl;
cout << "请选择排序算法:" << endl;
cout << "1 插入排序,2 希尔排序,3 选择排序,4 堆排序,5 冒泡排序,6 快速排序,7 归并排序" << endl;
int index;
while (cin >> index){
vector<int> nums{ 8, 3, 1, 4, 11, 2, 2, 1, 5, 2, 7, 9, 1, 6 };
int len = nums.size();
switch (index){
case 1:
insert_sort(nums); //插入排序
break;
case 2:
shell_sort(nums); //希尔排序
break;
case 3:
select_sort(nums); //选择排序
break;
case 4:
heap_sort(nums); //堆排序
break;
case 5:
bubble_sort(nums); //冒泡排序
break;
case 6:
quick_sort(nums, 0, len - 1); //快速排序
break;
case 7:
vector<int> tmp(len);
merge_sort(nums, 0, len - 1, tmp); //归并排序
break;
}
for (int i = 0; i<nums.size(); i++){
cout << nums[i] << ' ';
}
cout << endl;
}
return 0;
}
二、大整数计算
#include <iostream>
#include <string>
using namespace std;
//大数相加
string addstr(string s1, string s2) {
if (s1.size()>s2.size()) return addstr(s2, s1); //s1短
s1.insert(s1.begin(), s2.size() - s1.size(), '0');
string res(s2.size() + 1, '0');
for (int i = s2.size() - 1; i >= 0; i--){
int t = (s1[i] - '0') + (s2[i] - '0') + (res[i + 1] - '0');
res[i + 1] = t % 10 + '0';
res[i] = t / 10 + '0';
}
if (res[0] == '0') return res.substr(1);
return res;
}
//大数相乘
string multiply(string num1, string num2) {
int len1 = num1.size(), len2 = num2.size();
string res(len1 + len2, '0');
for (int i = len1 - 1; i >= 0; i--){
for (int j = len2 - 1; j >= 0; j--){
int t = (res[i + j + 1] - '0') + (num1[i] - '0')*(num2[j] - '0');
res[i + j + 1] = t % 10 + '0';
res[i + j] += t / 10;
}
}
for (int i = 0; i<len1 + len2; i++){
if (res[i] != '0') return res.substr(i);
}
return "0";
}
int main(){
string s1, s2;
cin >> s1 >> s2;
cout << "相加:" << addstr(s1,s2) << endl;
cout << "相乘:" << multiply(s1, s2) << endl;
return 0;
}
三、编辑距离问题
编辑距离,又称Levenshtein距离,是指两个子串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。请尝试写出一个算法来计算两个字符串的编辑距离。
1、当min(i,j)=0时,f(i,j)=max(i,j),一个字符串的长度为0,编辑距离是另一个字符串的长度
2、当a[i]=b[j]时,f(i,j)=f(i−1,j−1),比如xxcz和xyz的距离=xxc和xy的距离
3、否则,lev(i,j)为如下三项的最小值:
f(i−1,j)+1(在a中删除ai);
f(i,j−1)+1(在a中插入bj);
f(i−1,j−1)+1(在a中把ai替换bj),
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
//编辑距离
int EditDistance(string s1, string s2) {
int m = s1.size(), n = s2.size();
vector<vector<int>> arr(m + 1, vector<int>(n + 1));
for (int i = 0; i <= m; i++){
for (int j = 0; j <= n; j++){
if (i == 0 || j == 0){
arr[i][j] = max(i, j);
}
else if(s1[i-1] == s2[j-1]){ //注意
arr[i][j] = arr[i - 1][j - 1];
}
else{
arr[i][j] = min(arr[i - 1][j] + 1, min(arr[i][j-1] + 1, arr[i - 1][j - 1] + 1));
}
}
}
return arr[m][n];
}
int main(){
string s1, s2;
cin >> s1 >> s2;
cout << EditDistance(s1, s2) << endl;
return 0;
}
时间复杂度O(nm), 空间复杂度O(nm)
四、kmp算法
给定两个字符串str和match,长度分别为N和M。实现一个算法,如果字符串str中含有子串match,则返回match在str中的开始位置,不含有则返回-1
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 暴力匹配算法
int ViolentMatch(string s, string p) {
int i = 0, j = 0, slen = s.size(), plen = p.size();
while (i < slen && j < plen)
{
if (s[i] == p[j]){
i++;
j++;
}
else{
i = i - j + 1;
j = 0;
}
}
if (j == plen) return i - j;
else return -1;
}
// KMP算法
int KMP(string s, string p, vector<int>next) {
int i = 0, j = 0, slen = s.size(), plen = p.size();
while (i < slen && j < plen)
{
if (j == -1 || s[i] == p[j]){
i++;
j++;
}
else{
j = next[j];
}
}
if (j == plen) return i - j;
else return -1;
}
// MultiKMP算法
vector<int> MultiKMP(string s, string p, vector<int>next) {
vector<int> res;
int i = 0, j = 0, slen = s.size(), plen = p.size();
while (i < slen)
{
if (j == -1 || s[i] == p[j]){
i++;
j++;
}
else{
j = next[j];
}
if (j == plen){
res.push_back(i - j);
j = 0;
}
}
return res;
}
// MultiKMP2算法
vector<int> MultiKMP2(string s, string p, vector<int>next) {
vector<int> res;
int i = 0, j = 0, slen = s.size(), plen = p.size();
while (i < slen)
{
if (j == -1 || s[i] == p[j]){
i++;
j++;
}
else{
j = next[j];
}
if (j == plen){
res.push_back(i - j);
i = i - j + 1;
j = 0;
}
}
return res;
}
vector<int> GetNext(string p)
{
vector<int> next(p.size());
next[0] = -1;
int k = -1, j = 0;
while (j < p.size() - 1)
{
//p[k]表示前缀,p[j]表示后缀
if (k == -1 || p[j] == p[k]){
++k;
++j;
if (p[j] != p[k]) next[j] = k;
else next[j] = next[k];
}
else{
k = next[k];
}
}
return next;
}
void printvec(vector<int> vec) {
if (!vec.empty()){
for (int i = 0; i < vec.size(); i++){
cout << vec[i] << ' ';
}
cout << endl;
}
else{
cout << -1 << endl;
}
}
int main(){
string s1, s2;
cin >> s1 >> s2;
vector<int> next = GetNext(s2);
// 暴力匹配第一次出现位置
cout << ViolentMatch(s1, s2) << endl;
// KMP匹配第一次出现位置
cout << KMP(s1, s2, next) << endl;
// KMP匹配无重复所有出现位置
vector<int> res = MultiKMP(s1, s2, next);
printvec(res);
// KMP匹配可重复所有出现位置
vector<int> res2 = MultiKMP2(s1, s2, next);
printvec(res2);
return 0;
}
算法详解 时间复杂度为O(m + n)
扩展:BM算法、Sunday算法。
五、dijkstra算法
迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。它解决的是带权重的有向图上单源最短路径问题。
#include <iostream>
#include <string>
using namespace std;
const int maxn = 100; // 最大顶点数
int map[maxn][maxn]; // 邻接矩阵存图
int dis[maxn]; // dist[i]表示距离源点的最短距离
int vis[maxn]; // vis[i] = 1表示已加入集合S
int path[maxn]; // 记录路径
int n, m, r;
void dijk(int s) // 求顶点s到其他顶点的最短路径
{
//初始化
memset(path, -1, sizeof(path));
/*INF使用0x3f3f3f3f的好处:
* 1:满足无穷大加一个有穷的数依然是无穷大(在DijKstra算法松弛操作中避免了溢出而出现负数)
* 2:满足无穷大加无穷大依然是无穷大(两个0x3f3f3f3f相加并未溢出)
* 3:初始化时,由于每一个字节为0x3f,所以只需要memset(buf,0x3f,sizeof(buf))即可
*/
memset(dis, 0x3f, sizeof(dis)); //初始化为无穷大
memset(vis, 0, sizeof(vis));
dis[s] = 0; //自身到自身的距离为0
while (1)
{
int k = 0;
for (int j = 1; j <= n; j++)
{
if (!vis[j] && dis[j]<dis[k])//找未收录顶点中dis值最小的
k = j; //这里第一次找到的是起点
}
if (!k) return; //没有未收录的点,则返回
vis[k] = 1;
//松弛操作
for (int j = 1; j <= n; j++)
{
//第一次循环只有起点的邻接点距离被更新,每次都更新新找到的点的邻接点
if (dis[j]>dis[k] + map[k][j])
{
dis[j] = dis[k] + map[k][j];
path[j] = k;//路径被改变,重新记录前驱,最短路是由最短路+某一条固定路组成,所以前驱是有效的
}
}
}
}
void printPath(int x)
{
if (x == -1)
return;
//递归
printPath(path[x]);
if (x == r)
cout << x;
else
cout << " -> " << x;
}
/*问题描述:
* 输入n,m和r,代表n个节点,m条边,出发点r,然后是m行输入,每行有x,y,z,代表x到y的路距离为z。
* 问题:从r出发到各点的最短路径。
*/
int main()
{
cin >> n >> m >> r;
memset(map, 0x3f, sizeof(map));
int x, y, z;
for (int i = 1; i <= m; i++)
{
cin >> x >> y >> z;
if (map[x][y] > z){ // 防止1->1的环
map[x][y] = z;
//map[y][x] = z; // 无向图
}
}
dijk(r);
for (int i = 1; i <= n; i++){
printPath(i);
cout<< " "<< dis[i];
cout<< endl;
}
return 0;
}
六、0/1背包问题
0/1背包问题:满足总重量<=背包重量限制,使价值最大。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int ZeroOnePack(int m, int s, vector<int> weight, vector<int> value) {
// F[i ][ v] 表示 前i件物品恰放入一个容量恰为v的背包可以获得的最大价值
vector<vector<int> > F(m + 1, vector<int>(s + 1));
for (int i = 0; i <= s; i++) {
F[0][i] = 0; //如果要求恰好装满背包,除了F[0]为0,其余F[1 .. s]均设为-INF
}
for (int i = 1; i <= m; i++){
for (int j = 0; j <= s; j++){
if (j >= weight[i - 1])
F[i][j] = max(F[i - 1][j], F[i - 1][j - weight[i - 1]] + value[i - 1]);
else
F[i][j] = F[i - 1][j];
}
}
//打印哪几样物品能够获得最大价值
vector<bool> isAdd(m,false);
int j = s;
for (int i = m; i >= 1; i--){
if (F[i][j] != F[i - 1][j]){
isAdd[i - 1] = true;
j = j - weight[i - 1];
}
}
for (int i = 0; i < m; i++){
cout << isAdd[i] << " ";
}
cout << endl;
return F[m][s];
}
int main() {
int m, s; // 物品种类m,背包总空间t,求最大价值
cin >> m >> s;
vector<int> weight;
vector<int> value;
for (int i = 0; i < m; i++){
int t1, t2;
cin >> t1 >> t2;
weight.push_back(t1);
value.push_back(t2);
}
int res = ZeroOnePack(m, s, weight, value);
cout << res << endl;
return 0;
}