一:选择排序
1.将元素分为已排序区和待排序区。
2.每一轮从待排序中选择最小值放到已排序区,直到待排序区没有元素为止。
void select_sort(int *arr, int l, int r) {
for (int i = l; i < r-1 ; i++) {//注意最后剩下的元素,如果输入是l=0;r=6;排序0-6的元素,那么
第一个是<r-1;因为剩下一个不用排序(但要注意下面有(i+1);第二个是<=;因为都要参与排序
int ind = i;
for (int j = i + 1; j <=r; j++) {
if (arr[j] < arr[ind])ind = j;
}
swap(arr[ind],arr[i]);
}
return ;
}
int main() {
int a[10]={4,3,1,9,8,7,2};
select_sort(a,0,6);
for(int i=0;i<=6;i++)cout<<a[i]<<' ';
}
如何计算时间复杂度?第一次扫描n次,第二次扫描n-1次...第n次扫描1次,所以复杂度是o(n²)
二.插入排序:
void insert_sort(int *arr,int l,int r){
for(int i=l+1;i<=r;i++){//注意这个和选择是倒过来的,第一位不需要排,后面的每一个都要排
int j=i;//j代表当前要处理的元素。
while(j>l&&arr[j-1]>arr[j]){//进入循环,开始向左边比较,找到自己的位置
swap(arr[j-1],arr[j]);
j--;
}
}
}
也和选择排序一样,分为已排序区和待排序区,但不同的是插入元素的时候放到合适的位置。
时间复杂度:对每一个插入的元素,插入调整次数的期望应该是n/2;
无监督的希尔排序:也即,我们开头就把最小值找出来并且放最左边,这样就不用判断i>l这个过程。
void insert_sort(int *arr,int l,int r){
int min=l;
for(int i=l+1;i<=r;i++){
if(arr[i]<arr[min])min=i;
}
swap(arr[l],arr[min]);
for(int i=l+1;i<=r;i++){
int j=i;
while(arr[j-1]>arr[j]){
swap(arr[j-1],arr[j]);
j--;
}
}
return;
}
也就是说,最后一个元素调整(n-1)/2......所以 其实数量级都一样,都是o(n²)。
三.希尔排序:(基于插入排序的算法):(步长为分的组数)
那我们该如何设置步长序列呢?
void insert_sort(int arr[],int l,int r,int step){
int min=l;
for(int i=l+step;i<=r;i+=step){//这里要注意,for循环是在每次循环结束后进行变量增减操作,判断是在下一次循环开始,所以不会出现i越过r的问题。
if(arr[i]<arr[min])min=i;
}
swap(arr[l],arr[min]);
for(int i=l+step;i<=r;i+=step){
int j=i;
while(arr[j-step]>arr[j]){
swap(arr[j-step],arr[j]);
j-=step;
}
}
return;
}
void shell_sort(int arr[],int l,int r){
int step=1,n=r-l;
while(step*2+1<n)step=step*2+1;//思考一下什么意思,事实上,我们的步长不需要大于n/2(否则就有1个人一组的,没意义,但因为在每次开头都会除以2,所以就改为<n;)
do {
step/=2;//
for(int i=l;i<l+step;i++){
insert_sort(arr,i,r,step);
}
}while(step>1);
四.冒泡排序
也将排序序列分区域。
冒泡排序其实 是每一轮将待排序区的最大值放到待排序区的最后一位,也就是已排序区的第一位。
void bubble_sort(int *arr,int l,int r) {
for(int i=r-1;i>=l+1;i--){
for(int j=l;j<i;j++){
if(arr[j]>arr[j+1])swap(arr[j],arr[j+1]);
}
}
}
五.快速排序:
1.选定基准值。
2.左右两指针交叉排序。
3.得到小于基准值的左边序列,大于基准值的右边序列。
时间复杂度:
如果每次左右区间都是平分,就是nlogn,如果都左边只有一个,那就是n。
void quick_sort(int *arr,int lx,int rx){
int l=lx,r=rx,ind=arr[l];
if(r-l<=1)return;
while(l<r){
while(l<r&&arr[r]>=ind)r--;
if(l<r)arr[l++]=arr[r];
while(l<r&&arr[l]<=ind)l++;
if(l<r)arr[r--]=arr[l];
}
arr[l]=ind;
quick_sort(arr,lx,l-1);
quick_sort(arr,l+1,rx);
}
快速排序的优化:
①基于减少判断条件的优化:
②基于为了尽量平衡左右两边数量的优化(选取合适的基准值):
三点取中法
③:单边递归法:
左半部分递归操作,右半部分本层循环。
六.归并排序:
#include <stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
void Merge(int *arr,int l,int r){
if(l<r){
int mid=(l+r)/2;
Merge(arr,l,mid);
Merge(arr,mid,r);
int p1=l,p2=mid,k=0;
int *temp=malloc(sizeof (int)*(r-l));
while(p1<mid||p2<r){
if(p2==r||(p1<mid&&arr[p1]<=arr[p2]))temp[k++]=arr[p1++];
else temp[k++]=arr[p2++];
}
for(int i=l;i<r;i++)arr[i]=temp[i-l];
free(temp);
return;
}
}
int main(){
int a[10]={5,3,2,1,9,7,6,0,0,0};
Merge(a,0,6);
for(int i=0;i<7;i++)printf("%d",a[i]);
return 0;
}
————————————————
排序算法总结:
1.稳定排序:相同值的元素的相对顺序排序之后是否改变。
插入排序:因为比如说,我有前5和后5,当我把后5放入已排序区时,我是可以做到后5放在前5的后面的。
冒泡排序: 直接前后相等时,不交换。
归并排序: 当我们对左右数组归并时候,我们可以规定将相同值中左边数组的放到前面的位置。
2.非稳定排序:
选择排序:
...5||5...3...待排序区中的最小值3与排序区第一位5交换,改变了两个5的相对位置。
希尔排序:
5 5 3 8
快速排序:
假设基准值是5:5 ......3........3
堆排序:
1 3 2 2 4 5 4.
在线性排序时意味着3和第二个2发生了交换。
3.内外部排序:所谓的内外部排序,说的是是否一定要把数据全部读入到内存中才能排序。
内部排序:插入 冒泡 快速排序
外部排序:归并(归并的过程)
c++中sort函数的使用技巧:(一定要注意sort是左闭右开的!)
1.sort对数组进行从大到小的排序:
sort(arr,arr+23,greater<int>());
2.sort对vector进行排序
sort(arr.begin(),arr.end(),greater<int>());
3.对一些自己规定的数据类型排序.
struct Data{
int t,p;
};
vector<Data>arr;
sort(arr.begin(),arr.end(),cmp);
bool cmp(const Data &a,const Data &b){
if(a.t!=b.t)return a.t<b.t;
return a.p<b.t;}
4.不改变原数组,只给出原数组排序的索引:
int arr[6]={7,4,5,1,3,2};
int ind[6]={0,1,2,3,4,5};//这是arr数组排序的索引值
sort(ind,ind+6,[&](int i,int j)->bool{return arr[i]<arr[j];});//这句话的意思是,按照arr数组中相应元素的大小对ind数组进行排序。
LC01:两数之和:
我超太妙了!!用杨氏矩阵可以证明其正确性!
如果直接遍历整个矩阵,找到等于x的值,那么时间复杂度就是o(n²),
但是,我们如果先从右上角开始,这个元素有什么特点?他是第一行的最大值,是最后一列的最小值。所以如果右上角的值都比x小了,那么最后一列直接都不可能,所以左移动一位,倒数第二列。表现为右指针向左移动一位。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> a = {0, 0};
for (int i = 0; i <nums.size(); ++i) {
for (int j = i + 1; j <nums.size(); ++j) {
if (nums[i] + nums[j] == target) {
a[0] = i;
a[1] = j;
return a;
}
}
}
return a;
}
};
//时间复杂度n²,没意思。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int n=nums.size();
vector<int>ind(n);
for(int i=0;i<n;i++)ind[i]=i;
sort(ind.begin(),ind.end(),[&](int i,int j)->bool{return nums[i]<nums[j];});
int p1=0;int p2=n-1;
while(nums[ind[p1]]+nums[ind[p2]]!=target){
if(nums[ind[p1]]+nums[ind[p2]]<target)p1++;
else p2--;
}
vector<int>ans(2);
ans[0]=ind[p1];ans[1]=ind[p2];
return ans;
};
};