1.排序
快速: 任意一个分节点:左边数字都比它小,右边都比它大,递归处理
归并:类似于二叉树,先递归,分到最小,然后从最小开始,排序,归并,成为大数组
2.二分(整数,浮点)
边界问题:
取数:最左边 最后递归时候不能(l,i-1) (i,r)
右边 (l,j) (j+1,r)
特例:1 2
模板:
很好的推导
运用了递归,双指针,二分的思想
适用例子:
2
1 2
#include
using namespace std;
const int N = 1e6 +10;
int n;
int q[N];
void quick_sort(int q[],int l,int r)
{
if(l>=r) return ;
int x = q[l],i = l-1,j = r+1;//x = q[r]
//最好x=q[l+r>>1
while(i<j)//如果是i<=j,例子1 2陷入死循环
{
do i++; while(q[i] < x);
do j--; while(q[j] > x);
//while(q[i] < x) i++;
//while(q[j] > x) j–;
//当q[i]和q[j]都为 x 时, i 和 j 都不会更新,导致 while 陷入死循环
if(i < j) swap(q[i],q[j]);
}
quick_sort(q,l,j);//q,l,i-1
quick_sort(q,j+1,r);//q,i,r
}
int main()
{
scanf("%d",&n);
for (int i=0;i<n;i++) scanf("%d",&q[i]);
quick_sort(q,0,n-1);
for (int i=0;i<n;i++) printf("%d ",q[i]);
return 0;
}
自己写了一遍
#include
using namespace std;
const int N = 100000;
int n;
int q[N];
void quick_sort(int q[],int l,int r)
{
if(l>=r) return;
int x=q[(l+r)>>1],i=l-1,j=r+1;//取中间节点作为分界点
while(i<j)
{
do i++;while(q[i]<x);//找到大于x的数字,停止右移
do j--;while(q[j]>x);//找到小于x的数字,停止左移
if(i<j) swap(q[i],q[j]);//交换
}
quick_sort(q,l,j);//递归排序x左右侧的数字
quick_sort(q,j+1,r);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
quick_sort(q,0,n-1);
for(int i=0;i<n;i++) printf("%d ",q[i]);
return 0;
}
错误代码错因解析:
void quick_sort(int l, int r){
if(l >= r) return;
int mid = l + r >> 1;
int i = l - 1, j = r + 1;
while(i < j){
do i++; while(a[i] < a[mid]);
do j--; while(a[j] > a[mid]);
if(i < j) swap(a[i], a[j]);
}
quick_sort(l, j), quick_sort(j + 1, r);
}
mid位置所在的值(比较参考值)会改变
题解1:排序+print
#include
using namespace std;
const int N = 1e6+10;
int n,k;
int q[N];
void quick_sort(int l,int r)
{
//特殊情况
if(l>=r) return;
//分出左右
int x=q[l+r>>1],i=l-1,j=r+1;
while(i<j)
{
do i++;while(q[i]<x);
do j--;while(q[j]>x);
if(i<j) swap(q[i],q[j]);
}
//递归处理左右
quick_sort(l,j);
quick_sort(j+1,r);
}
int main()
{
scanf("%d %d",&n,&k);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
quick_sort(0,n-1);
printf("%d",q[k-1]);
return 0;
}
题解2:快排+二分
#include
using namespace std;
const int N = 1e6+10;
int n,k;
int q[N];
void quick_sort(int l,int r)
{
//特殊情况
if(l>=r) return;
//分出左右
int x=q[l+r>>1],i=l-1,j=r+1;
while(i<j)
{
do i++;while(q[i]<x);
do j--;while(q[j]>x);
if(i<j) swap(q[i],q[j]);
}
//递归处理左右
if(k<=j) quick_sort(l,j);//if判断
else quick_sort(j+1,r);
}
int main()
{
scanf("%d %d",&n,&k);
k--;//位置减去
for(int i=0;i<n;i++) scanf("%d",&q[i]);
quick_sort(0,n-1);
printf("%d",q[k]);
return 0;
}
void swap(int *a,int i,int j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
void quicksort(int *a,int begin,int end)
{
if(begin >= end)
return ;
int flag = begin;//注意:flag在左边,则从右边开始,反之亦然
int l = begin + 1;
int r = end;
while(l < r)
{
while(l<r&&a[r]>a[flag])//找到比flag小的数,然后停下来
r--;
while(l<r&&a[l]<a[flag])//找到比flag大的数,然后停下来
l++;
if(l<r)//交换两个位置上的数
swap(a,l,r);
}
swap(a,flag,r);//l = r,(注意的原因),此时此位置上的数字一定小于flag,
//因为是从右边开始先走的
quicksort(a,begin,r-1);
quicksort(a,r+1,end); //递归
}
在著名的快速排序中,有一个经典的过程叫做划分。
在此过程中,我们通常选取其中一个元素作为分界值。
将小于分界值的元素移到其左侧,将大于分界值的元素移到其右侧。
给定 N个不同的正整数进行过一次划分后的排列情况。
请你判断,共有多少元素可能是此次划分的分界值。
例如,N=5
,各元素排列为 1,3,2,4,5
,则:
1 可能是分界值,因为它的左侧没有元素,而右侧的元素都比它大。
3一定不是分界值,因为尽管它的左侧的元素都比它小,但是它右侧的 2
也小于它。
2一定不是分界值,因为尽管它的右侧的元素都比它大,但是它左侧的 3
也大于它。
出于类似判断可知 4,5也可能是分界值。
因此,在此样例中,共有 3 个可能的分界值。
输入格式
第一行包含整数 N
第二行包含 N 个不同的正整数。
输出格式
第一行输出可能的分界值数量。
第二行按升序顺序输出所有可能的分界值。
如果分界值数量为 0
,则在输出分界值数量后,输出一个空行即可。
输入样例:
5
1 3 2 4 5
输出样例:
3
1 4 5
最开始的做法:
int sort(int *a,int *b,int n)
{
int i,l,r,k = 0,p = 0;
for(i = 0;i < n;i++)
{
k = 0;
for(l = i - 1;k == 0&&l >= 0 && l < i;l++)
{
if(a[l] > a[i])
{
k = 1;
}
}
for(r = i + 1;k == 0&& r < n;r ++)
{
if(a[r] < a[i])
{
k = 1;
}
}
if(k == 0)
b[p ++] = a[i];
}
return p;
}
超时了,然后
void sort(int *a,int n)
{
int i;
int LeftMax[n],RightMin[n];
LeftMax[0] = 0;
RightMin[n-1] = 1000000;
for(i = 1;i < n;i++)
LeftMax[i] = LeftMax[i - 1] > a[i - 1]?LeftMax[i - 1]:a[i - 1];
for(i = n - 2;i >= 0;i --)
RightMin[i] = RightMin[i + 1] < a[i + 1]?RightMin[i + 1]:a[i + 1];
vector<int> b;
for(i = 0;i < n;i ++)
{
if(a[i] > LeftMax[i]&&a[i] < RightMin[i])
b.push_back(a[i]);
}
int n1 = b.size();
cout << n1 << endl;
if(n1 == 0)
puts(" ");
else
{
for(i = 0;i < n1;i ++)
cout << b[i] << " ";
}
}
这个题解有点懵:
#include
#include
#include
using namespace std;
const int N=100010;
int a[N],b[N];
int n;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
b[i]=a[i];
}
sort(a,a+n);
int lmax=0;
vector<int> res;
for(int i=0;i<n;i++)
{
if(a[i]==b[i]&&a[i]>lmax) res.push_back(a[i]);
if(b[i]>lmax) lmax=b[i];
}
cout<<res.size()<<endl;
if(res.size()==0) puts("");
else
{
for(int i=0;i<res.size();i++) cout<<res[i]<<" ";
}
return 0;
}
//作者:陌若安生
//链接:https://www.acwing.com/solution/content/15523/
//来源:AcWing
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
相关推导
类似于二叉树
1.递归拆分
2.双指针排序回到原来
运用了递归,双指针,二分的思想
#include
using namespace std;
const int N=1e6+10;
int n;
int q[N],res[N];
void merge_sort(int l,int r)
{
if(l>=r) return;
int mid = l+r >> 1;
//递归拆分
merge_sort(l,mid);
merge_sort(mid+1,r);
//双指针排序
//(1)双指针走
int i=l,j=mid+1,k=0;
while(i<=mid&&j<=r)
{
if(q[i]<q[j]) res[k++]=q[i++];
else res[k++] = q[j++];
}
//(2)剩余部分不上
while(i<=mid) res[k++] = q[i++];
while(j<=r) res[k++] = q[j++];
//(3)复制到原数组
for(i=l,j=0;i<=r;i++,j++) q[i] = res[j];
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
merge_sort(0,n-1);
for(int i=0;i<n;i++) printf("%d ",q[i]);
return 0;
}
#include
using namespace std;
const int N=1e5+10;
int n;
long long cnt=0;
int q[N];
void merge_sort(int l,int r)
{
//边界条件
if(l>=r) return ;
int mid = l+r >> 1;
//递归分开
//wrong(1)忘记写递归
merge_sort(l,mid);
merge_sort(mid+1,r);
int i=l,j=mid+1,k=0,res[r-l+1],h;//wrong(2):l-r+1
//双指针对比
while(i<=mid&&j<=r)
{
if(q[i]<=q[j]) {res[k++] = q[i++];}
else {res[k++] = q[j++]; cnt+=mid-i+1;}
}
//0-mid还剩下没有复制完
while(i<=mid)
{
res[k++] = q[i++];
}
//mid+1~r还剩下
//错误(3):改变了i,j
while(j<=r)
{
res[k++] = q[j++];
}
for(i=l,j=0;i<=r;i++,j++) q[i]=res[j];
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
merge_sort(0,n-1);
printf("%lld",cnt);
return 0;
}
写的特别好的优质文章
#include
using namespace std;
const int N=1e6+10;
int n;
int q[N];
void binary_sort(int x)
{
//找到左边界
int mid,l=0,r=n-1;
while(l<r)
{
mid = l+r >>1;
// if(q[mid]
// else r=mid;
if(q[mid]>=x) r=mid;
else l=mid+1;
}
if(q[r] != x )
{
cout<<"-1 -1"<<endl;
return ;
}
cout<<r<<" ";
//找到右边界
l=0,r=n-1;
while(l<r)
{
mid = l+r+1 >>1;
if(q[mid]<=x) l=mid;
else r=mid-1;
}
cout<<r<<endl;
}
int main()
{
int m,x;
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
while(m--)
{
scanf("%d",&x);
binary_sort(x);
}
return 0;
}
解析:
二分:
(1) while(l
(2)mid = l+r >>1还是mid = l+r+1 >>1?
(3)q[mid]模板中:2.1配3.1 2.2配3.2
(4)谁是左边界谁是右边界?
找到左边界 :
例如找数列1 2 2 3 3 4中左边的2
while(l<r)
{
mid = l+r>>1;
//错误情况1:
//改成l+r+1:超时无限循环 例如 1 2 2 3 3 4
//例如:当mid =l+r+1,l=r-1
//mid=r
//若满足q[mid]>=x -> r=mid =r
//r 保持不变,l也是,无限循环
if(q[mid]<x) l=mid+1;
else r=mid;
//if-else 内容可以根据判断条件推导
//因为if语句的条件可以推导出后面的语句
//然而后面的语句,在l+r+1的时候超时
//所以L+r+1不能和q[mid]
//l+r 可以,下面的模板同理
// 上面两句等价于
//if(q[mid]>=x) r=mid;
//else l=mid+1;
//讨论第二个:q[mid]
//q[mid]<=x 时,成立:l=mid 否则:r=mid-1
//q[mid]<=x时此时情况就成为了错误情况2(解释看下面)
}
找到右边界 :
例如找数列1 2 2 3 3 4中右边的2
l=0,r=n-1;
while(l<r)
{
mid = l+r+1 >>1;
//错误情况2
//改成l+r超时无限循环
//例如:当mid =l+r,l=r-1
//mid=l(向下取整)
//若满足q[mid]<=x -> l=mid =l
//l 保持不变,r也是,无限循环
if(q[mid]<=x) l=mid;
else r=mid-1;
}
#include
#include
using namespace std;
int main()
{
double n;
scanf("%lf",&n);
double ans=1e-9,mid=0,flag=1;
//负数
if(n<0)
{
n=fabs(n);
flag=0;
}
double l=0,r;
//小于1的数字
n>1.0?r=n:r=1;
//while(fabs(ans-n)>1e-6)
for(int i=0;i<1000;i++)//改进:while(r-l>1e-6) r=-22 l=22(解决了负数和小数的问题)
{
mid = (l+r)/2;
ans = mid*mid*mid;
if(ans<n) l=mid;
else r=mid;
}
if(flag==1)
{
printf("%.6lf",l);
}
else
{
printf("-%.6lf",l);
}
return 0;
}