快排
主要思想:
选取数列中的一个数x作为标准,将小于x的元素丢在左边,大于x的元素丢在右边;
递归处理将数组不断划分,在子数组里面重复上述步骤
具体措施:
1.定义两个指针(数组下标)分别从左右两端推进,与x对比。左边找大于x的数,右边找小于x的数,找到之后互换位置。
2.递归左右两个数组
具体代码:
void quick_sort(int l,int r)
{
if(l>=r) return;
int i=l-1,j=r+1,mid=q[l+r>>1];//这里可以随机取,左右端取,中间取都可以
//左边大于x,右边小于x的元素,找到了互换位置
while(i<j)
{
while(q[++i]<mid);
while(q[--j]>mid);
if(i<j)
{ int temp;
temp= q[i];
q[i]=q[j];
q[j]=temp;
}
}
//左右数组递归
quick_sort(l,j),quick_sort(j+1,r);
}
代码里面要注意的是左右数组递归时,j要么与i相等,要么j在i前面一个位置。
划分标准为j时,mid取(l+r>>1);
划分标准为i时,mid取(l+r+1>>1);
若以i为标准,mid取(l+r>>1)两个元素时递归会被写死。可以自己举个例子。
归并
主要思想:
数组先左右划分成子问题,递归处理两个数组。将两个子数组每个元素进行比较,较小的存入临时数组,最后将数组合并。
具体措施:
设置两个指针(数组下标),分别指向左右两个数组,两个数组元素比较,较小的存入临时数组。
最后将临时数组内容放回。
具体代码:
int temp[N];
void merge_sort(int l, int r,int q[]) // 归并排序
{
int mid=l+r>>1;
int i=l,j=mid+1,k=0;
if(l>=r) return;
merge_sort(l,mid,q),merge_sort(mid+1,r,q);//划分子问题
while(i<=mid&&j<=r)//两个数组元素比较
{
if(q[i]<q[j]) temp[k++]=q[i++];
else temp[k++]=q[j++];
}
//剩余元素全部输出,因为这个代码是在递归处理的,这里的输出是有序的数组
while(i<=mid) temp[k++]=q[i++];
while(j<=r) temp[k++]=q[j++];
//还原到原数组
for(int i=l,j=0;i<=r;i++,j++) q[i]=temp[j];
}
双指针算法
可以用作双重循环的优化,减少时间复杂度
例子1:最长连续的不重复子序列 【1 2 2 3 5-> 3】
主要思想:
i,j两个指针,i向前记录数组元素,额外开一个数组num记录每个元素出现次数
若出现次数大于1则说明出现重复元素,通过j指针向前移动弹出元素,知道num里面该元素的值小于1.最后取最大i,j之间的长度。
具体措施:
一层for循环让指针i向前移动记录数组,让num数组记录元素出现次数;
内部嵌套while循环,让j指针向前移动,直至弹出重复元素。
具体代码:
int res;
for(int i=0,j=0;i<n;i++)
{
s[q[i]]++;
while(s[q[i]]>1)
{
s[q[j]]--;
j++;
}
res= max(res,i-j+1);
}
例子2:给两个升序数组一个目标值,求数组中哪两个元素和等于目标值(数据保证答案唯一)
主要思想:
用暴力解法写两层for循环即可。用双指针算法进行优化,这里双指针算法就是在暴力解法结合题目升序的性质优化。指针i指向A数组前端,指针j指向B数组末端。(防止指针回溯是降低复杂度的关键)
具体措施:
在开始时将j指针指到末尾,若二者和小于x ,继续移动j仍然会小于,此时移动i(增大),
若大于x继续移动j
考虑移动了i之前的j里面会有解吗?
不会 之前的小i加上原来的j都比x大 ,更何况现在是大i了
for(int i=0,j=m-1;i<n;i++)
{
while(A[i]+B[j]>x) j--;
if(A[i]+B[j]==x) {cout<<i<<" "<<j<<endl;break;}
}