从小到大排序:从第二个数开始,若非递增,则插入前面有序序列中,该位置前面一位小于它,后面一位大于它;
二分插入排序:因为前面序列有序,则可用二分找到该位置;
用了二分可以减少比较大小的次数
撸代码:
#include
int main()
{
int a[10]={56,32,12,43,78,66,23,44,21,58};
/**从小到大 直接插入排序*/
for(int i=1;i<10;i++)
{
if(a[i]<a[i-1])/**若存在逆序,则向前找小于a[i]*/
{
int temp=a[i],j=i-1;
while(j>=0&&temp<a[j])/**在找的过程中,大于a[i]的向后推1位*/
{
a[j+1]=a[j];
j--;
}
a[j+1]=temp;
}
}
printf("直接插入排序后:\n");
for(int i=0;i<10;i++)
printf("%d ",a[i]);
return 0;
}
直接插入排序每次跨度为1,而希尔排序不断改变跨度(增量,在增量下“大致有序”),多次直接插入排序。
撸代码:
#include
void shellPass(int a[],int d)
{
for(int i=d;i<10;i++)
{
if(a[i]<a[i-d])
{
int temp=a[i],j=i-d;
while(j>=0&&temp<a[j])
{
a[j+d]=a[j];
j-=d;/**跨度不同*/
}
a[j+d]=temp;
}
}
}
int main()
{
int a[10]={56,32,12,43,78,66,23,44,21,58};
int len=10;
while(len)
{
len=len/2;
shellPass(a,len);/**多次改变增量*/
}
printf("希尔排序后:\n");
for(int i=0;i<10;i++)
{
printf("%d ",a[i]);
}
return 0;
}
给一个序列 9 1 0 5 4,进行归并排序:(从小到大)
下标 | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
数值 | 9 | 1 | 0 | 5 | 4 |
单个元素为一组,两两合并为有序序列:
下标 | 0 1 | 2 3 | 4 |
---|---|---|---|
数值 | 1 9 | 0 5 | 4 |
2个元素为一组,每两组合并:
下标 | 0 1 2 3 | 4 |
---|---|---|
数值 | 0 1 5 9 | 4 |
最后两组合并:
下标 | 0 1 2 3 4 |
---|---|
数组 | 0 1 4 5 9 |
在合并的过程中,就相当于两个有序序列合成一个有序序列
那么怎么分成两组呢?
递归派上了用场!从1~n开始分,知道分不了,利用回溯进行排序,完全ok
在两个有序序列合并的时候,一个是left序列,一个是right序列,当right序列某个元素跑到left前面了,计算一下,left的长度减去跑的位置,就是当前这个数的逆序啦
撸代码:
#include
#define N 500050
long long ans;
int a[N],n,t[N];
void Sort(int l,int mid,int r)
{
int i=l,j=mid+1;/**将分开的左右两个有序链表合并*/
int k=l;
while(i<=mid&&j<=r)
{
if(a[i]<=a[j])
{
t[k++]=a[i++];
}
else
{
t[k++]=a[j++];
ans+=mid-i+1;/**统计 a[j]的 逆序数*/
}
}
while(i<=mid)
{
t[k++]=a[i++];
}
while(j<=r)
{
t[k++]=a[j++];
}
for(int i=l;i<=r;i++)
{
a[i]=t[i];
}
return ;
}
void departSort(int l,int r)
{
if(l<r)
{
int mid=(l+r)>>1;
departSort(l,mid);
departSort(mid+1,r);/**分完之后,排序*/
Sort(l,mid,r);
}
return ;
}
int main()
{
while(~scanf("%d",&n)&&n)
{
ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
departSort(1,n);/**归并排序*/
printf("%lld\n",ans);
}
return 0;
}
从小到大
思想:先2分块,确定基准元素,保证块之间有序,也就是说 左块任意数字小于右块任意数字,块内无序,再对每一块分2块,层层递归;
优化:减少基准元素值的交换次数,先找到两个位置 ,左边大于基准元素,右边小于基准元素,两个值交换,直至遍历所有数字后,才基准元素交换;
关于递归优化不是太明白,优化前是quickSort函数两次递归,优化后一次递归,应该是在这里减少了堆栈深度。
撸代码:
#include
#include
using namespace std;
int Partition(int a[],int low,int hight)
{
int pivotKey=a[low];/**基准元素值(枢轴)*/
while(low<hight)
{
while(low<hight&&a[hight]>=pivotKey)/**从右开始,若小于基准元素,则交换位置*/
hight--;
swap(a[low],a[hight]);/**小于基准元素的值跑到左边*/
while(low<hight&&a[low]<=pivotKey)/**从左开始*/
low++;
swap(a[low],a[hight]);
}
return low;
}
/*******优化 不必要的交换(还可优化基准元素的选取,防止选到极大极小值)*********/
/*数组中分三次取样,每次取三个数,三个样品中各取出中间数,然后在这三个中枢当中再取一个中间数作为枢轴*/
int Partition1(int a[],int low,int hight)
{
int startIndex=low;/**基准元素只交换一次*/
int pivotKey=a[low];
while(low<hight)
{
while(low<hight&&a[hight]>=pivotKey)
hight--;
while(low<hight&&a[low]<=pivotKey)
low++;
swap(a[low],a[hight]);/**找到左边大的,右边小的 ,交换一次*/
}
/**循环执行完,左小右大,low就是基准元素该去的位置*/
swap(a[low],a[startIndex]);
return low;
}
void quickSort(int a[],int low,int hight)
{
int pivot;
if(low<hight)
{
pivot=Partition(a,low,hight);/**基准元素下标*/
quickSort(a,low,pivot-1);/**分块排序,保证块之间有序*/
quickSort(a,pivot+1,hight);
}
/*********递归优化:减少堆栈深度**********/
/*
while(low
}
int main()
{
int a[10]={5, 1, 9, 3, 7, 4, 8, 6, 2};
int n=9;
quickSort(a,0,n-1);/**从小到大排序*/
for(int i=0;i<n;i++)
printf("%d ",a[i]);
return 0;
}
大根堆,小根堆:所有非叶子节点大于或者小于其孩子节点。
用大根堆进行从小到大的排序
建立大根堆:从下往上,从右往左遍历非叶子节点,判断其是否符合大根堆性质,若不符合,则交换节点位置,直至建出大根堆。
大根堆根节点一定是被排序的这段数值的最大值,交换堆尾堆首数值,堆尾指针前移*(有没有冒泡的感觉?最大值逐渐飘到堆尾)*
当前堆只有根节点不符合大根堆性质,所以从根节点开始,向下找到合适的位置即可
#include
#include
using namespace std;
/**刚还奇怪,为什么从小到大排序用的是大根堆?
仔细看,建好堆以后,根节点是最大值,可是!!!
交换了堆尾和堆首的值,那么最大值就跑到了后面.
交换后,这个堆尾就算最大值了 ,不用管,再排序前面 n-1 个节点
同理,每次最大值都跑到了后面
*/
/**以 k 为根的完全二叉树,分别以2*K 和 2*k+1 为根的左右子树为大根堆*/
/**使 k 为根的树满足堆的性质*/
void sift(int a[],int k,int m)
{
int x=a[k];
int i=k;
int j=k*2;
bool finish=false;
while(j<=m&&!finish)
{
if(j+1<=m&&a[j]<a[j+1])
j=j+1;/**此时 j 指向最大子树根节点*/
if(x>=a[j])
finish=true;
else/**若是比子树根小,交换后还得考虑下层大小关系*/
{
a[i]=a[j];
i=j;
j=2*i;
}
}
a[i]=x;
}
void crateHeap(int a[],int n)
{
/**从最后一个非叶子节点往上筛选建堆*/
for(int i=n/2;i>=1;i--)
sift(a,i,n);
}
void heapSort(int a[],int n)
{
/**建大根堆后 ,保证了下面的数字不会大于上面的数字*/
crateHeap(a,n);
/**对 a 数组进行 从小到大 堆排序*/
for(int i=n;i>=2;i--)
{
/**每次得到的最大值跑到了后面*/
swap(a[i],a[1]);
/**交换以后只有堆顶元素是不合法的,所以下面只是为了
给堆顶元素找到一个合适的位置*/
/**对a[1~i-1]调整成堆*/
sift(a,1,i-1);
}
}
int main()
{
int a[12]={0,88,32,34,12,34,66,52,33,25,20};
heapSort(a,10);
for(int i=1;i<=10;i++)
printf("%d ",a[i]);
return 0;
}
排序思想: 对于数组 a[ ] 排序 ,先用数组c[ a[ i ] ] 记录其中的值出现的次数,然后计算前缀和;得出的值的意义就是 对于c[ a[i] ] 的值就是 对于所有的 a[ i ] 最后一个 a[ i ] 在数组中有序的排名,所以借助 ans[ ] 数组记录下标c[a[i] ] 的值为 a[i] ,- - c[ a [ i ] ] 就是 对于所有a[i] ,a[ i ] 的倒数第二个位置。
/**计数排序 1~10以内的数*/
#include
#include
#include
void printArr(int a[],int n)
{
for(int i=0;i<n;i++)
printf("%d%c",a[i],i==n-1?'\n':' ');
}
void countSort(int a[],int n)
{
int c[12];
for(int i=0;i<=11;i++)
c[i]=0;/**清空*/
for(int i=0;i<n;i++)
c[a[i]]++;/**计数*/
for(int i=1;i<=n;i++)
c[i]+=c[i-1];/**前缀和*/
/**
有了前缀和后,数字最后一个a[j]的排名为c[a[j]]
所以访问每个数字时,只是把a[j]放到对应的名次上
因为可能有多个a[j],所以名次可以从最后一个向前分配
*/
int ans[12];
for(int j=n-1;j>=0;j--)
{
ans[--c[a[j]]]=a[j];
}
printf("排序后:\n");
printArr(ans,10);
}
int main()
{
int a[12];
int n=10;
/*srand函数设定rand函数所用随机数演算法的种子值*/
//srand(time(NULL));
/*time(t) t==NULL 返回当前时间,若非空:返回当前时间的同时,将返回值赋予t 指向的内存空间*/
for(int i=0;i<n;i++)
a[i]=rand()%7+1;
printArr(a,10);
countSort(a,10);
return 0;
}
排序思想: 首先通过最大最小值数据范围 maxx-minn 按照每个桶平均装的数量 得出桶的数量。然后遍历数组 a[ ] ,装入桶中,进行桶内排序。
#include
#include
#include
#include
#include
using namespace std;
void bucketSort(int a[],int n)
{
int maxx=-9999,minn=9999;
for(int i=0; i<n; i++)
{
if(maxx<a[i])
maxx=a[i];
if(minn>a[i])
minn=a[i];
}
/**根据数据范围得出桶的数量(最多10个元素1个桶)*/
int bucketNum=maxx/10-minn/10+1;
vector<int>v[bucketNum];
for(int i=0; i<n; i++)
{
int t=(a[i]-minn)/10;
v[t].push_back(a[i]);
}
printf("Sort Over:\n");
for(int i=0; i<bucketNum; i++)
{
/**
桶内排序,可以插入排序,也可以用排序函数
*/
if(v[i].size())
{
sort(v[i].begin(),v[i].end());
for(int j=0; j<v[i].size(); j++)
printf("%d ",v[i][j]);
}
}
}
int main()
{
int n=10;
int a[12];
for(int i=0; i<n; i++)
a[i]=rand()%7+1;
for(int i=0;i<n;i++)
printf("%d%c",a[i],i==n-1?'\n':' ');
bucketSort(a,n);
return 0;
}
LSD排序思想: 初始化 exp=1 ,先按照低 exp 位进行计数排序,然后按照低 exp+1 位计数排序,直至exp/max=0 ,排序完成。
#include
#include
using namespace std;
/**LSD排序(从右向左定位)*/
void countSort(int a[],int exp,int n)
{
int c[10];
for(int i=0;i<10;i++)
c[i]=0;
/**按照位权计数*/
for(int i=0;i<n;i++)
c[(a[i]/exp)%10]++;
for(int i=1;i<10;i++)
c[i]+=c[i-1];
int b[20];
/**暂时按照低exp位排序*/
for(int i=n-1;i>=0;i--)
b[--c[(a[i]/exp)%10]]=a[i];
for(int i=0;i<n;i++)
a[i]=b[i];
}
void bitSort(int a[],int n)
{
int maxx=a[0];
for(int i=1;i<n;i++)
maxx=max(maxx,a[i]);
for(int exp=1;maxx/exp>0;exp*=10)/**从右向左定位*/
{
/**计数排序*/
countSort(a,exp,n);
}
}
int main()
{
int a[12]={103,9,1,7,15,25,109,209,5};
int n=9;
bitSort(a,n);
printf("基数排序后:\n");
for(int i=0;i<n;i++)
printf("%d ",a[i]);
return 0;
}