概述
归并:区间一分二,将两个子区间排序,合并 o(nlogn)
快排:选一基准,将小于基准的放左边,大于放右边,然后。。
时间复杂度nlogn~n^2
归并 & 求逆序对
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int ar[500050], br[500050];
ll ans;
int n;
int p1, p2;
void erfen(int l, int r)
{
if(l == r) return ;
int mid = (l + r) >> 1;
int i = l, j = mid + 1, k = l;
erfen(l, mid), erfen(mid + 1, r);
while(i <= mid && j <= r)
{
if(ar[i] <= ar[j]) br[k++] = ar[i++];
else
{
br[k++] = ar[j++];
ans += mid - i + 1;
}
}
while(i <= mid) br[k++] = ar[i++];
while(j <= r) br[k++] = ar[j++];
for(int i = l; i <= r; ++i) ar[i] = br[i];
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", &ar[i]);
erfen(1, n);
printf("%lldn", ans);
return 0;
}
树状数组求逆序对
https://www.luogu.com.cn/problem/solution/P1908
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int tree[500005];
int val[500005];
int n;
ll ans;
struct node
{
int x, pos;
}ar[500050];
int lowbit(int x)
{
return x & -x;
}
bool cmp(node a, node b)
{
if(a.x != b.x) return a.x < b.x;
else return a.pos < b.pos;
}
void build(int p)
{
for(int i = p; i <= n; i += lowbit(i)) ++tree[i];
}
ll query(int p)
{
ll res = 0;
for(int i = p; i > 0; i -= lowbit(i)) res += tree[i];
return res;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
scanf("%d", &ar[i].x);
ar[i].pos = i;
}
sort(ar + 1, ar + n + 1, cmp);
for(int i = 1; i <= n; ++i) val[ar[i].pos] = i;
for(int i = 1; i <= n; ++i)
{
build(val[i]);
ans += i - query(val[i]);
}
printf("%lldn", ans);
return 0;
}
归并模板
(long long ago 写的,效率不如上面的欸)
//归并
#include <bits/stdc++.h>
using namespace std;
int a[100], b[100];//原数组a,数组b用于暂存排好序的原数组
void _merge(int l, int mid, int r)//合并
{
int p1 = l, p2 = mid + 1;//两个指针
for(int i = l; i <= r; ++i)//遍历数组
{
//当前数组b应该取那个数?左半边还是右半边
//当p1小于mid时(即左半边还有数)且 p2 大于右边界时(右半边没数了)或者左边这个数小于右边这个数时
//应该取左边的数,即a[p1],并将p1右移
if((p1 <= mid) && ((p2 > r) || a[p1] <= a[p2]))
{
b[i] = a[p1];
p1++;
}
//否则取右边
else
{
b[i] = a[p2];
p2++;
}
}
for(int i = 1; i <= r; ++i) a[i] = b[i];//还原给原数组
}
void erfen(int l, int r)//分区间
{
int mid = (l + r) / 2;
if(l < r)
{
erfen(l, mid);
erfen(mid + 1, r);
}
_merge(l, mid, r);
}
int main()
{
for(int i = 1; i <= 10; ++i) cin >> a[i] ;
erfen(1, 10);
for(int i = 1; i <= 10; ++i) cout << a[i] << endl;
return 0;
}
快排模板
//快排
#include <bits/stdc++.h>
using namespace std;
int a[105];
void quick_sort(int l, int r)
{
int i = l, j = r;
//i:从左向右找第一个>x的数;j:从右向左找第一个<x的数
int mid = (l + r) / 2;
int x = a[mid];//基准选正中间这个数
while(i <= j)
{
//注意木有等号,因为基准并不一定排好序后也正在中间,所以基准有可能也要被交换
while(a[i] < x) i++;
while(a[j] > x) j--;
if(i <= j)
{
swap(a[i], a[j]);
++i;
--j;
}
}
if(l < j) quick_sort(l, j);
if(i < r) quick_sort(i, r);
}
int main()
{
for(int i = 1; i <= 10; ++i) cin >> a[i];
quick_sort(1, 10);
for(int i = 1; i <= 10; ++i) cout << a[i] << endl;
return 0;
}
归并求逆序对个数
最简单的,冒泡排序中交换的次数就是逆序对个数,但是冒泡时间复杂度过于太高,不用那个法
//归并求逆序对个数
#include <bits/stdc++.h>
using namespace std;
int a[100], b[100];
int cnt;
void _merge(int l, int mid, int r)
{
int p1 = l, p2 = mid + 1;
for(int i = l; i <= r; ++i)
{
if((p1 <= mid) && ((p2 > r) || a[p1] <= a[p2]))
{
b[i] = a[p1];
p1++;
}
//如果取的数来自右半边,那么这就摧毁了mid - p1 + 1个逆序对
//即序列中,由这个数参与的逆序对个数为mid - p1 + 1个
else
{
b[i] = a[p2];
p2++;
cnt += mid - p1 + 1;
//就加了这一句
}
}
for(int i = 1; i <= r; ++i) a[i] = b[i];
}
void erfen(int l, int r)
{
int mid = (l + r) >> 1;
if(l < r)
{
erfen(l, mid);
erfen(mid + 1, r);
}
_merge(l, mid, r);
}
int main()
{
for(int i = 1; i <= 10; ++i) cin >> a[i] ;
erfen(1, 10);
cout << cnt << endl;
return 0;
}
快排求序列第k小的数
不用排序,因为这个序列里有很多数,排序会超时
但是用了快排的思想
随便从题干里取了个快读板子
//快排求序列第k小的数
#include <bits/stdc++.h>
using namespace std;
int ar[5000050];
int t, n, k;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
int finding(int l, int r, int k)
{
if(l == r) return ar[l];
int i = l, j = r;
int mid = (l + r) / 2;
int x = ar[mid];
while(i <= j)
{
while(ar[i] < x) i++;
while(ar[j] > x) j--;;
if(i <= j)
{
swap(ar[i], ar[j]);
++i;
--j;
}
}
if(k <= j) return finding(l, j, k);
else if(i <= k) return finding(i, r, k);
else return ar[k];
}
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; ++i) ar[i] = read();
printf("%dn", finding(1, n, k));
}
return 0;
}
快读
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
求最大子串和
方法一:动态规划
状态转移方程:
if(current <= 0) current = ar[i];
else current += ar[i];
mx = max(current, mx);
//最大子串和 动态规划
#include <bits/stdc++.h>
using namespace std;
int ar[100];
int mx = -999999999, current;
int main()
{
for(int i = 1; i <= 10; ++i) cin >> ar[i];
current = ar[1];
for(int i = 2; i <= 10; ++i)
{
if(current <= 0) current = ar[i];
else current += ar[i];
mx = max(current, mx);
}
cout << mx << endl;
return 0;
}
方法二: 非动态规划
这个最长的字串和可能来自三种情况:
1.左半串最长和
2.右半串最长和
3.横跨中间的某个串的和
用递归求前两种,第三种需要求左半串的后缀和的最大值,右半串的前缀和的最大值,将这两个最大值相加即可。
//最长子串和 fei动态规划
#include <bits/stdc++.h>
using namespace std;
int ar[105];
int maxs(int n, int l, int r)
{
int mid = (l + r) / 2;
if(l == r) return ar[l];//递归中间,这个串就一个元素了
int ans = max(maxs(n, l, mid), maxs(n, mid + 1, r));//分别递归取左半串和右半串的最大值然后对这两个取max
int ll = 0, rr = 0, tmp = 0;
//ll代表左半串后缀和的最大值,rr代表右半串前缀和的最大值
for(int i = mid + 1; i <= r; ++i)
{
tmp += ar[i];
rr = max(rr, tmp);
}
tmp = 0;
for(int i = mid; i >= l; --i)
{
tmp += ar[i];
ll = max(ll, tmp);
}
ans = max(ans, ll + rr);//第三种情况与前两种情况的最大值再取一个max就是答案
return ans;
}
int main()
{
for(int i = 1; i <= 10; ++i) cin >> ar[i];
cout << maxs(10, 1, 10) << endl;
return 0;
}
最后
以上就是兴奋铃铛为你收集整理的归并排序(求逆序对),快速排序(求序列第k小的数,最大子串和)的全部内容,希望文章能够帮你解决归并排序(求逆序对),快速排序(求序列第k小的数,最大子串和)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复