一:插入排序
思想:在对整个数组循环for(i = 0;i < n;i++)
的过程中,保证此时走到的i
位置的前面已经是一个有序序列,所以对于i
位置的处理就是由i
位置依次向前,找到第一个不满足大于此时i
位置的元素的位置,插入即可。
#include<stdio.h>
void Insert(int a[],int n) //a是待排序数组,n是数组大小
{
int i,j;
int tmp;
for(i = 0;i < n;i++) { //循环处理每一个元素
tmp = a[i]; //先将a[i]保存
for(j = i;j>0 && a[j-1]>tmp; j--) { //为a[i]在0-i的区间找合适的位置
a[j] = a[j-1];
}
a[j] = tmp; //将a[i]放到合适的位置上
}
}
二:选择排序
思想:外层循环for(i = 0;i < n;i++)
将每个位置轮询,保证每个位置都与其后面的位置上的元素比较确定出目前最大或者最小的,然后通过交换放置此位置,与插入不同的地方是,插入的过程中子过程
中可能会出现某个时刻不是所有的元素都在数组中,但是选择每次交换都是不会改待排序数组中元素的值的。即任何状态下数组中元素值都与原始一样,只是可能位置不同。
void Choice(int a[],int n)
{
int i,j;
for(i = 0;i < n;i++) { //外层循环轮询每一个位置
for(j = i+1;j < n;j++) { //通过与后面的比较找出最大或者最小放到此位置.
if(a[i] > a[j]) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
三:冒泡排序
思想:外层循环将数组的每个位置轮询,内层循环每次从开始找到n-i-1
的位置,其实每次都是从1
位置找到n-i-1
的位置找到最大的,然后放到n-i-1
的位置,每次都是找出大的,所以冒泡。
void MaoPao(int a[],int n)
{
int i,j;
for(i = 0;i < n;i++) {
for(j = 0;j < n-i-1;j++) {
if(a[j] > a[j+1]) {
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
四:桶排序
思想:在一个很大的空间将要排序的数字依次直接放到应该放置的地方,这个地方就是我们初始化的桶,它的大小必须比待排元素的最大值大,所以对于1,2,3,4,100
这种序列排序就比较浪费空间,你必须初始化100个桶,但是使用它的前提就是你对空间没有严格要求,只求时间上最小。
void Bump(int a[],int n)
{
int i;
int bump[100] = {0}; //初始化桶的值全为0
for(i = 0;i < n;i++) { //让数组中的元素依次找到对应的桶,并将桶的值+1
bump[a[i]]++;
}
for(i = 0;i < 100;i++) {
if(bump[i] >= 1) { //只要桶里面的值不为0,就表示次桶对应有值,值就是桶的序号
printf("%d ",i); //这种输出剔除了重复,若不剔除重复,就循环输出
}
/*for(j = bump[i];j >= 1;j--) {//这是不剔除重复输出
printf("%d ",i);
}*/
}
printf("\n");
}
五:快速排序
思想:快速排序采用的是分治思想
,将问题的规模一步步缩小,快排的关键在于中枢元的选择,我们一般会优化中枢元的选择,经过一次排序我们会将比中枢元小的放到它的左边,将中枢元大的放到它的右边,依次重复这个动作就好。
void QuickSort(int s[],int l,int r)
{
if(l < r) {
int i = l,j = r,x = s[i];
while(i < j) {
while(i < j && s[j] >= x) { //从后向前找最大
j--;
}
if(i < j) { //找到符合条件的值
s[i++] = s[j];
}
while(i < j && s[i] < x) { //从前向后找最小
i++;
}
if(i < j) { //找到符合条件的值
s[j--] = s[i];
}
}
s[i] = x; //将中枢元放到i的位置
QuickSort(s,l,i-1); //再将左边排序
QuickSort(s,i+1,r); //再将右边排序
}
}
六:归并排序
思想:归并也是分治
的思想,首先是分
的过程,就是将元素区间从1增加到2再到4一直循环处理下去,区间中的元素个数依次减少,然后治
,将元素合并,也是一个相反的过程,首先合并的是小区间,然后合并大区间,一直到合并成一个区间。
void MergeArray(int a[],int first,int mid,int last,int temp[])
{
int i = first,j = mid+1;
int m = mid,n = last;
int k = 0;
int p;
while(i <= m && j <= n) { //从给定区间一半开始比较合并两个区间
if(a[i] <= a[j]) {
temp[k++] = a[i++];
} else {
temp[k++] = a[j++];
}
}
while(i <= m) {
temp[k++] = a[i++];
}
while(j <= n) {
temp[k++] = a[j++];
}
for(i = 0;i < k;i++) { //将temp[]的值更新到a[]中,temp数组是临时数组
a[first+i] = temp[i];
}
}
void MergeSort(int a[],int first,int last,int temp[])
{
if(first < last) { //将问题分解到最小直到first>=last,即每一个元素本身
int mid = (first+last)/2; //就是一个区间
MergeSort(a,first,mid,temp); //每次都是先处理左边
MergeSort(a,mid+1,last,temp); //再处理右边
MergeArray(a,first,mid,last,temp); //在将数组合并
}
}
七:堆排序
思想:变治思想
就是将问题变化成另一种易于解决的形式,堆排的过程有两个关键部分,一个是建堆的过程,一个是排序的过程,实际上我觉得建堆的过程比排序难理解,因为我们一旦建好堆后,排序就是取出数组的第一个元素,然后再将堆中的最后一个元素给第一个元素,调整堆的形态就好了,那么建堆的过程是我们输入元素一个一个建立吗?实际上下面的算法采用的是堆化数组
,就是先给数组中存值,然后再将数组做相应调整,使其的存储序列刚好是一个堆。
void MinHeapFixdown(int a[],int i,int n)
{
int j,temp;
temp = a[i]; //根
j = 2*i+1; //左孩子
while(j < n) {
if(j+1<n && a[j+1]<a[j]) { //从左右孩子中找到最小的
j++;
}
if(a[j] >= temp) { //左右孩子都比根大直接break
break;
}
a[i] = a[j]; //否则将最小的值给根
i = j; //然后只要目前i还有孩子,就应该再次检测
j = 2*i+1; //找到孩子,下次循环进来还是检测temp的关系。
}
a[i] = temp; //无论怎样,每次temp即使移动多次,最后还是放到i的位置。
}
void MakeMinHeap(int a[],int n) //将普通数组堆化的过程
{
int i;
for(i = n/2-1;i >= 0;i--) { //从n/2-1开始保证所有的根都会检测
MinHeapFixdown(a,i,n); //从底部开始调整,到顶部时候,底部已经被调整好
}
}
void HeapSort(int a[],int n)
{
int i;
int p;
for(i = n-1;i >= 0;i--) {
printf("%d ",a[0]); //输出目前堆顶的元素也就是数组首元素
a[0] = a[i]; //每次将最后一个元素置为数组首元素
MinHeapFixdown(a,0,i); //每次都从0开始到n调整
}
printf("\n");
}