选择排序:
算法是:
① 第1趟排序
在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]交换。
③第i趟排序
第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换。
这种算法也是不稳定的:可对数组{a1 = 49,a2 = 38, a3 = 65, a4 = 49, a5 = 12, a6 = 42} 排序就可以看出,排序完成后 a1 和 a4的相对位置改变了。
时间复杂度是 O(n^2)
交换次数O(n),最好情况是,已经有序,交换0次;最坏情况是,逆序,交换n-1次。交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。
下面是代码:/*
选择排序
*/
#include <stdio.h>
#include <malloc.h>
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
/*
下边这种不需要中间数就可以交换数据
但是要注意,如果a和b中存储的数据一样的话
那么这个函数就会导致a和b中存储的数都变成了零
可以在a和b中的数不等的时候调用这个函数或者可以
使用c++中的引用来进行交换
*/
void swap_1(int *a, int *b)
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
void selectsort(int a[], int s, int e)
{
if (a != NULL && s <= e && s >= 0)
{
int i,j,min,ptr;
for (i=s; i<=e; i++)
{
// min = a[i]; 开始设置了这个 但后来发现没必要 直接跟a[ptr]比较即可
ptr = i;
for (j = i; j<=e; j++)
{
if (a[j] < a[ptr])
{
ptr = j;
}
}
if (ptr != i)
{
//这里注意交换的是a[ptr],不能写成a[j]
//开始写成了a[j] 导致半天才发现问题
//因为j在跳出for循环后永远都是等于e的
swap(&a[i], &a[ptr]);
}
}
}
}
void main()
{
int i = 0;
int a[] = { 21,25,49,25,16,8 };
selectsort(a, 0, 5);
for (; i <= 5; i++)
{
printf("%d\n", a[i]);
}
printf("Press Enter to exit...\n");
getchar();
}
插入排序:
基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。
代码如下:
/*
插入排序
*/
#include <stdio.h>
#include <malloc.h>
void swap(int *a, int *b)
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
void InsertSort(int a[], int s, int e)
{
int i = s, j = e;
int key;
for (i = s+1; i <= e; i++)
{
key = a[i];
for (j = i-1; j >= s; --j)
{
if (key < a[j])
// swap(&a[j], &a[j + 1]); 这种效率没有另一种效率高
a[j + 1] = a[j]; //这样没必要每次都要进行三次变换
else
break;
}
a[j + 1] = key;//在最后的时候才把数据拷贝进来应在的地方
}
}
void main()
{
int i = 0;
int a[] = { 21,25,49,25,16,8 };
InsertSort(a, 0, 5);
for (; i <= 5; i++)
{
printf("%d\n", a[i]);
}
printf("Press Enter to exit...\n");
getchar();
}
上面的三个算法中:虽然都是,但是还是有区别的,首先是冒泡的速度是最慢的,因为要进行太多次的交换了,然后是选择排序,交换次数比冒泡少,但是比较次数和冒泡是一样的,插入排序就比前两个好多了,比较次数不固定。
希尔排序:
希尔排序其实就是插入排序的演变,只不过是进行了多次插入排序,然后每次的排序是分组进行,这样把数组的大致顺序弄好,然后再进行一次真正的插入排序,就可以了。
这里注意,希尔排序虽然也是插入排序并且进行了多次插入排序,但是时间复杂度降下来的原因是因为前边的插入排序每次把数组进行一些微调,所以导致最后一次的真正的时间复杂度不会是O(n2)。
基本方法:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
最后一个增量必须为1;
希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差
由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
时间复杂度:最好的是O(n1.3)平均是O(nlgn)(不过这个也跟序列中的gap取的值有关系)
代码如下:
/*
希尔排序
*/
#include <stdio.h>
#include <malloc.h>
void swap(int *a, int *b)
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
/*
下面的代码是我自己写的,这里的gap是用户自己输入的
并且每次gap--这样的效率不是最高的 然后看了国嵌的视频
才感觉到
*/
void ShellSort1(int a[], int s, int e, int gap)
{
while (gap)
{
int i,j,key;
for (i = s + gap; i <= e; i++)
{
key = a[i];
for (j = i - gap; j >= s && key < a[j]; j -= gap)
{
if (key < a[j])
a[j + gap] = a[j];
}
a[j+gap] = key;
}
--gap;
}
}
/*
这里传入的不是gap而是数组的长度
然后每次gap = gap / 3 + 1 这样的效率据研究
表明是最高的
*/
void ShellSort2(int a[], int s, int e, int len)
{
int gap = len;
do
{
gap = gap / 3 + 1;
int i, j, key;
for (i = s + gap; i <= e; i += gap)
{
key = a[i];
for (j = i - gap; j >= s && key < a[j]; j -= gap)
{
if (key < a[j])
a[j + gap] = a[j];
}
a[j + gap] = key;
}
} while (gap>1);
}
void main()
{
int i = 0;
int a[] = { 21,25,49,25,16,8 };
ShellSort2(a, 0, 5, 6);
for (; i <= 5; i++)
{
printf("%d\n", a[i]);
}
printf("Press Enter to exit...\n");
getchar();
}
归并排序:
先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。
归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(N*logN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)也是效率比较高的。
并且归并排序是稳定的,但是代价是依赖辅助空间,如果有一次辅助空间的申请不成功,那么归并排序就失败
代码如下:/*
归并排序
*/
#include <stdio.h>
#include <malloc.h>
void swap(int *a, int *b)
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
void MergeSort(int a[], int s, int e, int tmp[])
{
if (s < e)
{
int mid = (s + e) / 2;
int i=s, j=mid+1,p=s;
MergeSort(a, s, mid, tmp);
MergeSort(a, mid + 1, e, tmp);
while (i <= mid && j <= e)
{
if (a[i] < a[j])
tmp[p++] = a[i++];
else
tmp[p++] = a[j++];
}
while (i <= mid)
tmp[p++] = a[i++];
while (j <= e)
tmp[p++] = a[j++];
for (i = s; i <= e; i++)
a[i] = tmp[i];
}
}
void main()
{
int i = 0;
int a[] = { 21,25,49,25,16,8 };
int *p = (int*)malloc(sizeof(int)* 6);
MergeSort(a, 0, 5, p);
for (; i <= 5; i++)
{
printf("%d\n", a[i]);
}
free(p);
printf("Press Enter to exit...\n");
getchar();
}
快速排序:
采用的是分治思想
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]赋给A[i];
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]赋给A[j];
5)重复第3、4步,直到i=j;
这个时候,就把第一个元素放在了对的位置,因为它左边的元素,都比它小,而右边的元素都逼它大,所以然后再将它两边的数组分别递归的进行快排,最后数组就排完了。
关于快排的基本属性:
首先是这种排序是不稳定的;
最坏的时间复杂度为O(n2),但就平均性能而言,平均时间复杂度为O(nlgn),快速排序由于排序效率在同为O(nlgn)的几种排序方法中效率较高。
这里需要说明的一点是:关于最坏的情况是由于输入的数据决定的,所以为了避免出现这种情况,进行了优化,就是对选择的关键数据进行随机性选择,这样就可以尽量避免了,但是最坏的情况仍是不变的
并且快排的速度比归并快3倍多
下面是代码:
/*
快速排序
*/
#include <stdio.h>
#include <malloc.h>
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void quicksort(int a[], int s, int e)
{
if (a != NULL && s < e && s >= 0)
{
int i = s, j = e;
int key = a[s];
while (i != j)
{
//这里的条件一开始写成了1就是一直循环
//忘了有肯能这个队列可能已经排好,j--到最后肯定会
//出现数组越界的情况
while (i < j)
{
if (a[j] < key)
{
swap(&a[i], &a[j]);
++i;
break;
}
--j;
}
while (i <j)
{
if (a[i] > key)
{
swap(&a[i], &a[j]);
--j;
break;
}
++i;
}
}
quicksort(a, s, i-1);
quicksort(a, i + 1, e);
}
}
void main()
{
int i = 0;
int a[] = { 21,25,49,25,16,8 };
quicksort(a, 0, 5);
for (; i <= 5; i++)
{
printf("%d\n", a[i]);
}
printf("Press Enter to exit...\n");
getchar();
}