冒泡排序
基本思想:两两相邻记录的关键字 ,如果反序则交换,直到没有反序记录为止。
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)相比较排序前,即是不稳定排序。