Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法......感兴趣就关注我吧!你定不会失望。
🌈个人主页:主页链接
🌈算法专栏:专栏链接
现已更新完KMP算法,之后我会继续往里填充内容哒。
🌈LeetCode专栏:专栏链接
目前在刷初级算法的LeetBook 。若每日一题当中有力所能及的题目,也会当天做完发出
🌈代码仓库:Gitee链接
🌈点击关注=收获更多优质内容🌈
用一篇Blog来讲解下最近学到的快速排序,归并排序,二分查找等处理数据的方式,为日后的刷题打下坚实的基础。
快速排序:
不同于之前所见到的冒泡、插入等排序方式,快速排序的时间复杂度最好的情况为O(nlogn),最坏的情况为O(n^2),所以通常状况下,我们描述他的复杂度为O(n)。
void quick_sort(int* a,int l,int r) { if(l>=r)return; int pri=a[(l+r)>>1]; int i=l-1,j=r+1; while(i<j) { while(a[++i]<pri); while(a[--j]>pri); if(i<j) swap(a[i],a[j]); } quick_sort(a,l,i-1); quick_sort(a,j+1,r); }
思想:分治思想
对一串数字进行排序(这里以31254为例),选择基准数为中间(l+r>>1)那一个(其实选择任意一个都可以(例如第一个a[0]或者最后一个a[n-1])),将左右边界设置为i与j,这样我们就得到了下面的情况
接下来开始,当a[i]<=pri时,i++。直到不满足这个条件时,i停下
此时当a[j]>pri时,j--。直到不满足这个条件时,j停下
这时候判断i<j,若满足则交换,若不满足则退出循环
第一次制作动图,属实难看,我会努力改进的(doge
交换完成后继续进行i++,j--的循环,直到不满足i<j这个循环条件
此时我们可以看到有left--[i-1]这个区级内满足<=pri,[j+1]--right满足>pri这一个规律,所以接下来,我们就以这两个区间为划分,进行下一阶段的快速排序。即【2 1】【5 4】这两个区间。细心的你肯定发现了,那3去哪了?3存在的位置是最开始基准数的位置,这个位置的数(要么是基准数,要么是已经排完序的数)所以不需要将他加入区间。
至此快排结束
归并排序:
#include <cstdio> #include<iostream> using namespace std; const int N=1000010; int n; int q[N]; int tmp[N]; void merge_sort(int *q,int left,int right) { if(left>=right)return ; int mid=(left+right)>>1;//(left+right)/2 merge_sort(q,left,mid);//左半边拆成两个小块 merge_sort(q,mid+1,right);//右半边拆成两个小块 int k=0,i=left,j=mid+1;// while(i<=mid&&j<=right) { if(q[i]<=q[j])tmp[k++]=q[i++]; else tmp[k++]=q[j++]; } while(i<=mid)tmp[k++]=q[i++]; while(j<=right)tmp[k++]=q[j++]; for(int i=left,j=0;i<=right;i++,j++)q[i]=tmp[j]; } int main() { cin>>n; for(int i=0;i<n;i++)cin>>q[i]; merge_sort(q, 0,n-1); for(int i=0;i<n;i++)cout<<q[i]; getchar(); }
思想:
与快速排序相同,归并排序利用的也是分治思想,但由于其稳定性,时间复杂度为O (nlogn)。
首先将数据从中间分开(类似快排取pri的过程)此时的mid是中间数组的下标
然后将数据以【left,mid】【mid+1,right】这样的规律分开,无限递归下去,最后就会得到一个只有两个数字的数组,这时取两个区间的首作为两个指针,即i=left,j=mid+1,也就是比较这两个部分,然后将较小的那一个放入tmp临时数组,最后判断这两部分是否都完成了比较。
若有一部分没比较完,即可证明这一部分存在的数都比已经比较完的数大,依次放入tmp数组即可。最后将tmp数组从头开始放入原数组【left,right】这一区间,即完成并这一操作。
至此归并排序结束。
整数二分查找:
二分查找可以用来解决两种问题,假设有一组数122334,求出>=2的区间以及<=2的区间
这时候就需要两个模板来解决这两种问题。
模板一:<=target
#include<iostream> using namespace std; // 整数对分查找模板 int BinarySearchInt() { int n=100/*array size*/,target=0/*search target*/; int left=0,right=n-1,q[n]; while(left<right)//模板1 { int mid=(left+right+1)>>1; if(q[mid]<=target)/*边界为mid--right*/ left=mid; else right=mid-1; } }
有序的数列可以用二分查找,可以用二分查找的数列不一定需要有序。当需要找一个数(也可以是条件)的时候,取这组数的中间值mid,这时候会出现两种情况。注意模板一的mid一定要+1,若不加一当只有两个数的时候,/会向下取整,陷入无限循环。
1 q[mid]<=target
这时是如下图的这种情况,那么下回寻找的区间就是【mid,right】,也就是令left=mid
2 q[mid]>target
这时是如下图的这种情况,那么下回寻找的区间就是【left,mid-1】,也就是令right=mid-1,这里为什么要-1呢?是因为q[mid]已经大于target了此时的mid已经确定不是要找的target,所以可以减一,而上一种情况中,q[mid]可以等于target,也就是q[mid]可能是我们要的答案,所以不可以减一。
模板二:>=target
#include<iostream> using namespace std; // 整数对分查找模板 int BinarySearchInt() { int n=100/*array size*/,target=0/*search target*/; int left=0,right=n-1,q[n]; while (left<right) { int mid=(left+right)>>1; if(q[mid]<target)left=mid+1; else right=mid; } }
1 q[mid]<target
这时是如下图的这种情况,那么下回寻找的区间就是【mid+1,right】,也就是令left=mid+1
2 q[mid]>=target
这时是如下图的这种情况,那么下回寻找的区间就是【left,mid】,也就是令right=mid。
浮点数二分查找:
浮点数二分查找就没有那么多的问题了,整体思想和整数一样,但是少了边界的问题。就显得很简单。
这里引用acwing上的一道题来解释
这里的边界就设置为题目所给的-10000<=n<=10000,也就是说left=-10000 right=10000,然后用mid的三次方去看是否>=x或者<x
若>=x 则说明mid的值取大了,答案区间应该为【left,mid】反之。
注:因为计算机存储数据的原因,很难精确的表示出一个浮点数。所以根据题设,只要左右边界相减的值小于10^-6,就认为他满足条件。
#include<iostream> using namespace std; const double N=1e-7; int main() { double x; cin>>x; double left=-10000,right=10000,mid; while(right-left>N) { mid=(left+right)/2; if(mid*mid*mid<=x)left=mid; else right=mid; } printf("%lf",mid); }
至此浮点数二分查找结束。
完结撒花:
🌈本篇博客的内容【【快速排序、归并排序、整数&浮点数二分查找、】模板与解析】已经结束。
🌈若对你有些许帮助,可以点赞、关注、评论支持下博主,你的支持将是我前进路上最大的动力。
🌈若以上内容有任何问题,欢迎在评论区指出。若对以上内容有任何不解,都可私信评论询问。
🌈诸君,山顶见