冒泡排序
基本思想:两两相邻记录的关键字 ,如果反序则交换,直到没有反序记录为止。
void BubbleSort(int k[], int n) {
//冒泡排序是从下往上冒泡,相邻两个比较
int temp, flag;
flag = 1;//这里标志, 是对排序的优化
for (int i = 0; i < n - 1 && flag; ++i)
for (int j = n - 1; j > i; j--) {
flag = 0;
//如果到最上面两个比较的时候不进入下面的交换,冒泡可以提前结束
if (k[j - 1] > k[j]) {
temp = k[j - 1];
k[j - 1] = k[j];
k[j] = temp;
flag = 1;
}
}
}
选择排序
通过n-i次关键字比较,从n-i+1个记录中选出关键字最小的记录,
并和第i(i<=i<=n)个记录交换。
void SelectSort(int k[],int n){
int temp,min;
for (int i = 0; i < n; ++i) {
min=i;//min起始下标为i
for (int j = i+1; j < n; ++j)
if(k[j]<k[min])
min=j;//找到最小元素的下标
//交换
temp=k[min];
k[min]=k[i];
k[i]=temp;
}
}
直接插入排序
· 操作是将一个记录插入到已经排好序的有序表中,
从而得到一个新的、记录增加1的有序表。
适合于 待排序数组基本有序,且记录较少。
void InsertSort(int k[], int n) {
int temp, j;
for (int i = 1; i < n; i++) {
if (k[i] < k[i - 1]) {
temp = k[i];
for (j = i - 1; k[j] > temp&&j>=0; --j) {
k[j + 1] = k[j];
}
k[j + 1] = temp;
}
}
}
希尔排序
是对直接插入排序的改进。O(n*logn)
void ShellInsert(int k[], int n) {
int temp, j;
int gap=n;
do{
gap=gap/3+1;
for (int i = gap; i < n; i++) {
temp = k[i];
for (j = i - gap; k[j] > temp&&j>=0; j-=gap) {
k[j + gap] = k[j];
}
//上面一步已经j-gap过了
k[j + gap] = temp;
}
}while (gap>1);
}
堆排序
是对选择排序的改进。O(n*logn)
· 是一棵完全二叉树。
· 根结点一定是堆中所有结点最大者或最小者,如果按照层序遍历的方式给结点从
1开始编号,则结点之间满足如下关系:
· 利用堆进行的排序算法,它的基本思想是:
-将待排序的序列构成一个大顶堆(或小顶堆)
-此时,整个序列的最大值就是堆顶的根结点。将它移走(就是将其与堆数组的末尾
元素交换,此时末尾元素就是最大值)。
-然后将剩余n-1个序列重新构成一个堆,这样就会得到n个元素中的最大值。
-如此反复执行,便能得到一个有序序列了。
void swap(int k[], int i, int j) {
int temp;
temp = k[i];
k[i] = k[j];
k[j] = temp;
}
void HeapAdjust(int k[], int s, int n) {
//k表示数组,s表示双亲结点,n表示元素个数
int i, temp;
temp = k[s];//存放需要调整的双亲结点
//i = 2*s 为s的左孩子
for (i = 2 * s; i <= n; i *= 2){
//左孩子<右孩子,且i不超过n(待排长度)
if (k[i] < k[i + 1] && i < n)
i++;
//如果双亲结点大,提前结束循环
if(temp>=k[i])
break;
//两个孩子结点较大的到双亲结点
k[s]=k[i];
//交换的孩子结点的下标给s
s=i;
}
k[s]=temp;
}
void HeapSort(int k[], int n) {
//为了更好体现树的概念,k[0]不参与排序
int i;
//从整个堆最下面开始,构建大顶堆
for (i = n / 2; i > 0; --i)
HeapAdjust(k, i, n);
for (i = n; i > 1; --i) {
swap(k, 1, i);//堆顶元素与 i交换(最后一个元素)
HeapAdjust(k, 1, i - 1);
}
}
归并排序
· 假设初始序列有n个记录,则可以看成n个有序子序列,每个子序列长度为1,
然后两两归并,得到[n/2]个长度为2或1的有序子序列;再两两归并,...,如此重复
直到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。
#define MAXSIZE 10
//实现归并,并把最后结果放到list1,list1是待排序数组的头指针
void Merging(int *list1, int list1_size, int *list2, int list2_size) {
int i, j, k, m;
int temp[MAXSIZE];
i = j = k = 0;
while (i < list1_size && j < list2_size) {
//比较大小,放入临时数组中
if (list1[i] < list2[j])
temp[k++] = list1[i++];
else
temp[k++] = list2[j++];
}
//把剩下的都放入临时数组中
while (i < list1_size) {
temp[k++] = list1[i++];
}
while (j < list2_size) {
temp[k++] = list2[j++];
}
//临时数组覆盖 list1
for (m = 0; m < list1_size + list2_size; m++)
list1[m] = temp[m];
}
void MergeSort(int k[], int n) {
//归并排序的递归算法
if (n > 1) {
int *list1 = k;//左半部分头指针
int list1_size = n / 2;//左半部分大小
int *list2 = k + n / 2;//右半部分头指针
int list2_size = n - list1_size;//右半部分大小
//递归拆分
MergeSort(list1, list1_size);
MergeSort(list2, list2_size);
//逐一合并
Merging(list1, list1_size, list2, list2_size);
}
}
快速排序
· 是对冒泡排序的一种改进。基本思想是通过一趟排序将待排记录分割成独立的两部分,
其中一部分记录的关键字都比另一部分的关键字小,则可分别对这两部分记录继续进行
排序,以达到整个序列有序。
int Partition(int k[], int low, int high) {
int point;
//优化方案,选取三个元素比较大小,把中间的放到k[low]位置
// int m=(low+high)/2;
// if(k[low]>k[high])
// swap(k,low,high);
// if(k[m]>k[high])
// swap(k,m,high);
// if(k[m]>k[low])
// swap(k,m,low);
point = k[low];
while (low < high) {
while (low < high && k[high] >= point) {
//比point大的都放后面,否则互换位置
high--;
}
if(low==high)
break;//提前结束
swap(k, low, high);//将元素调换
while (low < high && k[low] <= point) {
//比point小的都放前面,否则互换位置
low++;
}
swap(k, low, high);//将元素调换
}//找到point在数组中的位置
return low;//返回时,low = high
}
void QSort(int k[], int low, int high) {
int point;
if (low < high) {
point = Partition(k, low, high);
//对左边部分递归调用
QSort(k, low, point - 1);
//对右边部分递归调用
QSort(k, point + 1, high);
}
}
void QuickSort(int k[], int n) {
QSort(k, 0, n - 1);//初始位置,最后的位置
}
排序总结
如何判断一个排序稳定与否,举个栗子:
排序前:5,6(1),1,4,3,6(2),(第一个6在第二个6之前)
排序后:如果排序后的结果是1,2,3,4,5,6(1),6(2)那么就说此排序算法是稳定的,即是稳定的排序。
如果排序后的结果是1,2,3,4,5,6(2),6(1),即6(1)和6(2)相比较排序前,即是不稳定排序。