文章中的图片是网上找的(偷点懒哈),不过代码都是本人自己写的,绝对原创。
算法一:冒泡排序法
对一组等待排序的数字,重复走访所有的元素,每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
具体流程如下:
1、从第一个数开始向后走,比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2、一轮排序之后,最后的元素应该会是最大的数。
3、针对所有的元素重复以上的步骤,除了最后一个。
4、持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
在基本的冒泡排序基础上,还可以进行进一步改进。
改进1:设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。
改进2:利用在每趟排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半。
相对应的java代码实现:
public class bubble_sort {
public static void sort(int[] arr, int n) {
for (int i = 0; i < n - 1; i++)
for (int j = 0; j < n - 1 - i; j++)
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
/* 改进1: 通过添加一个标志变量,观察某一趟排序是否有数据交换,如果没有则说明已经排好序,可以立即结束 */
public static void sort1(int[] arr1, int n) {
int i = n - 1; // 初始时,最后位置保持不变
while (i > 0) {
int pos = 0; // 每趟开始时,交换记录清零
for (int j = 0; j < i; j++)
if (arr1[j] > arr1[j + 1]) {
pos = j;
int temp1 = arr1[j];
arr1[j] = arr1[j + 1];
arr1[j + 1] = temp1;
}
i = pos; // 为下一趟排序做准备
}
}
/* 改进2;每一趟排序同事得到最大、最小值,使排序趟数减少一半 */
public static void sort2(int[] arr2, int n) {
int low = 0;
int high = n - 1; // 设置变量初始值
int temp2, j;
while (low < high) {
for (j = low; j < high; j++)
if (arr2[j] > arr2[j + 1]) {// 正向冒泡,找到最大值
temp2 = arr2[j];
arr2[j] = arr2[j + 1];
arr2[j + 1] = temp2;
}
high--; // 修改high值,前移一位
for (j = high; j > low; j--)
if (arr2[j] < arr2[j - 1]) {// 反向冒泡,找到最小值
temp2 = arr2[j];
arr2[j] = arr2[j - 1];
arr2[j - 1] = temp2;
}
low++; // 修改low值,后移一位
}
}
}
方法二:选择排序
选择排序的基本思想就是在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0zhOLCXq-1632647366788)(http://img2.imgtn.bdimg.com/it/u=3962173368,312192950&fm=21&gp=0.jpg)]
方法改进:跟上面冒泡排序一样,选择排序也可以从数组两边同时开始排序,及同时选出最小的和最大的元素,这样一来排序的趟数可以减少一半。
程序实现代码:
public class selection_sort {
public static int getminkey(int[] a, int n, int i) {
int mink = i;
for (int j = i + 1; j < n-i; j++)
if (a[j] < a[mink])
mink = j;
return mink; //返回最小值的下标
}
public static int getmaxkey(int[] a, int n, int i) {
int maxk = n-1-i;
for (int j =n-2-i; j > i-1; j--)
if (a[j] > a[maxk])
maxk = j;
return maxk; //返回最大值的下标
}
public static void sort(int[] arr, int n) {
for(int i=0;i<=n/2;i++){ //采用最大值、最小值同时插入的方法,讲的时间耗费
int minkey=getminkey(arr,n,i);
if(minkey!=i){ //如果最小值下标不等于i,将这两个位置的内容互换
int temp1=arr[i];
arr[i]=arr[minkey];
arr[minkey]=temp1;
}
int maxkey=getmaxkey(arr,n,i);
if(maxkey!=n-1-i){ //如果最小值下标不等于i,将这两个位置的内容互换
int temp2=arr[n-1-i];
arr[n-1-i]=arr[maxkey];
arr[maxkey]=temp2;
}
// Main.print(arr, n);
}
}
}
方法三:插入排序
直接插入排序就是将一个记录插入到已排序好的有序表中,从而得到一个新的,记录数增1的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。方法的关键就是要设立一个作为临时存储和判断数组边界之用的哨兵。
假设数组为array[n]。
1、初始时,array[0]为有序表,准备将array[1…n-1]逐个插入有序表中,令i=1。
2、将array[i]并入当前的有序区array[0…i-1]中形成array[0…i]的有序表间。
3、i++并重复第二步直到i==n-1。排序完成。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yXLcnCI5-1632647366790)(http://ettc.sysu.edu.cn/2005wlkc/shujujiegou/teaching/chapter10/images/9-1.gif)]
程序代码:
public class insertion_sort {
public static void sort(int[] arr, int n) {
for (int i = 1; i < n; i++) {
if (arr[i] < arr[i - 1]) { // 若第i个元素小于i-1元,移动前面的有续表后插入
int j = i - 1;
int temp = arr[i]; // 复制为哨兵,即为
while (j >= 0 && temp < arr[j]) {// 此处特别注意要把j>=0放在前面,否则会报错,因为java编译是从左到右的
arr[j + 1] = arr[j];
j--; // 元素后移
}
arr[j + 1] = temp; // 插入到正确位置
}
}
}
}
方法四:快速排序
快速排序可以说是对冒泡排序的一种改进。通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
基本过程就是:
1、选择一个基准元素,通常选择第一个元素或者最后一个元素,
2、通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。
3、此时基准元素在其排好序后的正确位置
4、然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
这里在网上还看到了一张比较形象的动态图,顺便贴出来了
快速排序还可以跟插入排序相结合,只对长度大于k的子序列递归调用快速排序,让原序列基本有序,然后再对整个基本有序序列用插入排序算法排序。实践证明,改进后的算法时间复杂度有所降低。
算法代码:
public class quick_sort {
public static int partition(int[] a, int low, int high) {
int pivotkey = a[low]; // 基准元素
while (low < high) {
// 从表的两端交替向中间扫描
while (low < high && a[high] >= pivotkey)
high--;// 从high所指位置向前扫描,至多到low+1位置
// 将比基准元素小的交换到低端
int temp1 = a[low];
a[low] = a[high];
a[high] = temp1;
while (low < high && a[low] <= pivotkey)
low++;
int temp2 = a[low];
a[low] = a[high];
a[high] = temp2;
}
return low;
}
public static void sort(int[] arr, int low, int high) {
if (low < high) {
int pivotloc = partition(arr, low, high);// 将表一分为二
sort(arr, low, pivotloc - 1);// 递归排序基准元素左边的序列
sort(arr, pivotloc + 1, high);// 递归排序基准元素右边的序列
}
}
/* 改进:当子序列长度不大于一个指定数k后,不再进行递归排序,而是对整个序列进行直接插入排序 */
public static void sort_improve(int[] a, int low, int high, int k) {
if (high - low > k) {
int pivot = partition(a, low, high);
sort_improve(a, low, pivot - 1, k);
sort_improve(a, pivot + 1, high, k);
}
}
public static void sort1(int[] arr, int n, int k) {
sort_improve(arr, 0, n, k);// 先使序列基本有序
// 对基本有序序列进行插入排序
for (int i = 1; i < n; i++)
if (arr[i] < arr[i - 1]) {
int j = i - 1;
int temp = arr[i];
while (j >= 0 && temp < arr[j]) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
}
}
方法五:希尔排序
希尔排序是在直接插入排序的基础上改进而来的,又称为缩小增量排序。其基本思想是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
基本步骤:
1、选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
2、按增量序列个数k,对序列进行k 趟排序;
3、每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。
4、增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gwhab7kq-1632647366793)(http://bcs.duapp.com/xpython07/spider/img/acmtuiku/http___img0.tuicool.com_Mv2Eje.jpg)]
算法程序代码:
public class shell_sort {
public static void shellsort(int[] a, int n, int d) {
for (int i = d; i < n; i++) {
if (a[i] < a[i - d]) { //将以d为间距的序列进行插入排序
int j = i - d;
int temp = a[i];
while (j >= 0 && temp < a[j]) {
a[j + d] = a[j];
j -= d;
}
a[j + d] = temp;
}
}
}
public static void sort(int[] arr, int n) {
int d=Math.round(n/2);
while(d>=1){
shellsort(arr,n,d);
d=Math.round(d/2); //缩小d的值重新进行排序
// Main.print(arr, n);
}
}
}
方法六:堆排序
说实话,在所有的这些排序方法中,我感觉堆排序是最难理解的一种了。
我们首先来了解一下堆的定义:
堆通常是用树形结构表示的,如下是一个最大堆(即堆顶元素是最大的)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-41RNvWux-1632647366795)(http://img5.imgtn.bdimg.com/it/u=3965217777,286576496&fm=21&gp=0.jpg)]
实现堆排序的过程主要有两部分,第一就是要先将一个数组heap[n]建成堆,然后才能进行堆排序。下面我就这两个过程具体的说明一下:
建最大堆过程:
1、给定一个数组,首先根据该数组元素构造一个完全二叉树。
2、最后一个非终端元素的下标是[n/2]向下取整,所以从第[n/2]向下取整个元素开始,从后往前进行调整。
3、每次都是从父结点、左孩子、右孩子中进行比较交换,交换可能会引起孩子结点不满足堆的性质,所以每次交换之后需要重新对被交换的孩子结点进行调整。
建立过程实例如图:
堆排序过程:
1、建好堆以后,将heap[0]和heap[n-1]交换,输出heap[0]。
2、对剩下的数组heap[n-1]调堆(此时的数组长度减1),重新进行堆排序。
3、重复进行上述2个过程,直到输出所有的数组元素。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uB7aXkGO-1632647366796)(http://img5.imgtn.bdimg.com/it/u=843746713,217716790&fm=21&gp=0.jpg)]
最终得到的结果如下:
特别说明,此处三张图皆来自于http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html,在此特别感谢。
总结
在排序算法中有一个比较关键的词汇:稳定性。即若待排序的序列中,存在多个具有相同关键字的记录,经过排序, 这些记录的相对次序保持不变,则称该算法是稳定的;若经排序后,记录的相对 次序发生了改变,则称该算法是不稳定的。
稳定的排序算法可以避免多余的比较,而且如果排序算法是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。
上述的6个算法中:
稳定的算法:冒泡排序、插入排序
不稳定的算法:选择排序、快速排序、希尔排序、堆排序
转载引用请注明地址:http://blog.csdn.net/u010896929/article/details/44677291