【排序算法】——冒泡排序、选择排序、插入排序、Shell排序等排序原理及Java实现
排序
1.定义: 所谓排序,即是整理文件中的内容,使其按照关键字递增或递减的顺序进行排列。 输入:n个记录,n1,n2……,其对应1的关键字为k1,k2…… 输出:n(i1),n(i2)……,使得k(i1)<=k(i2)…… (形象点讲就是排排坐,调座位,高在前低在后;或者低在前高在后) 2.排序算法的评价的专业术语 (1)稳定性 稳定:在排序的文件中,若存在多个关键字相同的记录,经过排序后,这些具有相同 关键字的记录之间的相对顺序不会发生改变。 不稳定:若具有相同关键字记录之间的相对次序发生改变,则成为不稳定的排序算法。 (2)复杂度 时间复杂度:一个算法执行所需消耗的时间。 空间复杂度:运行完该程序所需要的内存大小 (3)排序方式 内排序:在排序过程中,整个文件放在内存中进行处理,不涉及数据的内外存交换。 (适用于记录个数不是很多的小文件) 外排序:排序过程中要进行数据的内、外存交换。 (适用于记录个数多,不能将其一次性放入内存中的大文件)
交换排序
思想:对排序记录的关键字进行两两比较,发现次序相反时则进行交换直至没有次序相反的记录。
主要排序方法:冒泡排序、选择排序
冒泡排序
(1)算法描述:
冒泡排序是一种简单的排序,每次访问要排序的数组序列,一次比较相邻两个元素的大小,
若顺序错误就进行交换。(这个方法名的由来是小的元素慢慢“浮”在数组顶端,也可称为沉底(大的元素))
(2)算法的实现
a.比较相邻的两个元素大小,如果第一个大于第二个元素的大小就进行交换
b.对每一对相邻的元素都进行同样的操作,从第一对到最后一对,经过此步骤
最大的元素将是最后的元素。
c.针对所有的元素重复上述操作,除过最后一个。
d.重复a-c步骤,直至排序完成
1 /** 2 *@Description: 排序算法 3 *@Author: dyy 4 */ 5 public class Sort { 6 public static void main(String[] args) { 7 int [] arr = new int[]{4,2,1,6,3,6,0,-5,1,1}; 8 bubbleSort(arr); 9 //遍历打印排序后的数组![image](https://note.youdao.com/favicon.ico) 10 for(int i = 0; i < arr.length;i++){ 11 System.out.print(arr[i]+" "); 12 } 13 } 14 /** 15 *冒泡排序 16 * @date: 2018/8/15 11:27 17 */ 18 public static void bubbleSort(int[] array){ 19 //对数组进行临界判断 20 if(array==null||array.length==0){ 21 System.out.println("array is NULL"); 22 } 23 for(int i = 0; i < array.length - 1; i++){ 24 //进行相邻元素额大小比较(也可循环至array.length-1, 25 // 循环至array.length-i-1是因为没经过一圈的排序后面的元素则就是有序的) 26 for(int j = 0;j < array.length-i-1;j++){ 27 if(array[j]>array[j+1]){ 28 swap(array,j,j+1); 29 } 30 } 31 } 32 } 33 /** 34 *实现数组中两个数的交换 35 * target为要交换的数组,x,y为要交换数字的下标 36 * @date: 2018/8/15 11:30 37 */ 38 public static void swap(int[] target,int x,int y){ 39 int temp = target[x]; 40 target[x] = target[y]; 41 target[y] = temp; 42 } 43 }
选择排序
(1)算法描述:选择排序是一种简单直观的排序算法。
它的工作原理是首先在未排序的序列中找到最大(小)的元素放置序列起始位置。
再从未排序序列中继续寻找元素放置已排序序列的尾部直至所有元素都有序。
(2)算法的实现:
a.初始状态:R[1……n]无序,有序数组为空
b.经过第i次扫描:R[1……i-1]有序,R[i……n]无序
c.经过n-1趟排序,为有序序列
1 /** 2 *选择排序 3 * @date: 2018/8/15 12:26 4 */ 5 public static void selectSort(int[] array){ 6 //临界条件判断 7 if(array==null||array.length==0){ 8 System.out.println("array is NULL"); 9 } 10 //做第i趟排序 11 for(int i = 0; i < array.length-1;i++){ 12 for(int j = i+1; j < array.length;j++){ 13 //选出最小的记录进行交换 14 if(array[i]>array[j]){ 15 swap(array,i,j); 16 } 17 } 18 } 19 }
插入排序
(1)算法描述
插入排序也是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对已未排序
的序列的数据,在已经有序的序列中从后向前找并进行插入。但是在进行插入的过程中需
要不断的将已经排序好的数据进行移位操作。
(2)算法实现
a.从第一个数据开始,将该元素认为已经排序好的序列。
b.取出下一个元素,在已经排序好的元素序列中从后向前进行扫描。
c.如果该元素(排序好的元素)大于新的元素,则将该元素移动到下一个位置
d.重复步骤c,知道找到已排序好的元素小于或者等于新元素的位置
e.将新的元素插入到该位置
f.重复步骤b
1 /** 2 *插入排序 3 * @date: 2018/8/15 19:42 4 */ 5 public static void insertSort(int[] array){ 6 //临界条件判断 7 if(array==null||array.length==0){ 8 System.out.println("array is null"); 9 } 10 for(int i = 0; i < array.length;i++){ 11 //从下一个元素进行遍历,若新元素小于已经排序好的序列的元素进行交换 12 for(int j = i ;(j>0)&&(array[j-1]>array[j]);j--){ 13 swap(array,j-1,j); 14 } 15 } 16 }
Shell排序(希尔排序)
(1)算法描述 希尔排序是插入排序的一种,其中心思想是将数据进行分组,然后对每一组 数据进行排序,在每一组数据有序后,可以利用插入排序对所有分组进行最 后一次排序。这样可以减少数据交换的次数,加快排序速度。 (2)算法实现 a.选择一个增序序列t(1),t(2)……t(n),其中t(n)=1 b.按照增量序列个数k,对序列进行k此排序 c.每次排序,根据对应的增量,分为若干个长度为m的子序列,分别对子序列 进行插入排序。当增量为1时一个序列来进行处理,表长即为整个序列的长度。
1 /** 2 *Shell排序(希尔排序) 3 * @date: 2018/8/15 20:07 4 */ 5 public static void shellSort(int[] array){ 6 if(array==null||array.length==0){ 7 System.out.println("array is null"); 8 } 9 //分隔集合之间的长度(第一次假设为集合长度的一半) 10 int increment = 0; 11 //分隔集合之间的长度,每次为前一次的一半(最后一次为1) 12 for(increment = array.length/2;increment>0;increment = increment>>1){ 13 for(int i = increment;i<array.length;i++){ 14 for(int j = i - increment;j >= 0;j = j - increment){ 15 if(array[i] < array[j]){ 16 swap(array,i,j); 17 } 18 } 19 } 20 } 21 }
快速排序
(1)算法描述
快速排序是一种划分交换排序,采用分治的策略(分治法)。通过一趟排序
将待排序序列划分为两个独立的部分,其中一部分的记录的关键字均小于另
外一部分的关键字。
分法法的基本思想:将原问题分解为若干个规模更小但是结构和原问题相似
的子问题,递归地解决这些子问题,然后将这些子问题的解组合成原问题的解。
(2)算法实现
a.从数组序列中选择一个“基准元素”(此文中以每个序列第一个元素为准)
b.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准
值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,
该基准就处于数列的中间位置。这个称为分区操作
c.递归地把小于基准值元素的子数列和大于基准值元素的子数列排序。
1 public class Sort { 2 public static void main(String[] args) { 3 int [] arr = new int[]{4,2,1,6,3,6,0,-5,1,1}; 4 quickSort(arr,0,arr.length-1); 5 //遍历打印排序后的数组 6 for(int i = 0; i < arr.length;i++){ 7 System.out.print(arr[i]+" "); 8 } 9 } 10 /** 11 *快速排序 12 * @exception: array为待排序序列,low为要排序序列的下界,high为要排序序列的上界 13 * @date: 2018/8/16 9:41 14 */ 15 public static void quickSort(int[] array,int low,int high){ 16 if(array==null||array.length==0){ 17 System.out.println("array is null"); 18 } 19 //划分后的基准元素 20 int standard; 21 //区间长度小于1不需要再进行排序 22 if(low>=high){ 23 return; 24 } 25 //对low……high进行划分 26 standard = partition(array,low,high); 27 //对左区间进行递归排序 28 quickSort(array,low,standard-1); 29 //对右区间进行递归排序 30 quickSort(array,standard+1,high); 31 } 32 /** 33 *对区间进行划分 34 * @date: 2018/8/16 9:49 35 */ 36 private static int partition(int[] array, int low, int high) { 37 //进行临界判断 38 if(array==null||array.length==0){ 39 return 0; 40 } 41 //基准元素,以区间第一个元素为准 42 int standardValue = array[low]; 43 //区间长度小于1不需要进行划分 44 if(low>=high){ 45 return low; 46 } 47 //区间两端交替向中间扫描,直至low=high为止 48 while(low<high){ 49 //(standardValue相当于在位置low上) 50 while((low<high)&&(standardValue<=array[high])){ 51 //从右向左扫描,查找第一个关键字小于基准元素standardValue值的记录 52 high--; 53 } 54 //表示找到小于基准元素standardValue值的记录 55 if(low<high){ 56 //相当于交换low和high位置的值 57 array[low++] = array[high]; 58 } 59 //(standardValue相当于在位置high上) 60 while((low<high)&&(array[low]<=standardValue)){ 61 //从左向右扫描,查找第一个关键字大于standardValue值的记录 62 low++; 63 } 64 //表示找到大于基准元素standardValue值的记录 65 if(low<high){ 66 //交换low和high的值 67 array[high--] = array[low]; 68 } 69 } 70 //基准位置被最后定位 71 array[low] = standardValue; 72 return low; 73 } 74 }
归并排序
(1)算法描述
归并算法是在归并操作上的一种有效的排序算法,同样采用分治法。是将已经有序
的子序列进行合并,得到完全的有序序列,即先使得子序列有序,再使子序列段有
序,将两个有序序列合并为一个有序序列,称为“二路合并”。
(2)算法实现
a.将长度为n的序列划分为长度为n/2的两个子序列。
b.分别对这两个子序列采用归并排序。
c.将这两个已经有序的子序列进行合并。
1 public class Sort { 2 public static void main(String[] args) { 3 int [] arr = new int[]{4,2,1,6,3,6,0,-5,1,1}; 4 quickSort(arr,0,arr.length-1); 5 mergeSort(arr); 6 //遍历打印排序后的数组 7 for(int i = 0; i < arr.length;i++){ 8 System.out.print(arr[i]+" "); 9 } 10 } 11 /** 12 *归并排序 13 * array为待排序序列,left、right为序列的左右边界,temp为和原序列长度相等的临时序列 14 * @date: 2018/8/16 11:31 15 */ 16 public static void mergeSort(int[] array){ 17 //在排序前创建与原数组大小相等的临时数组,避免递归过程中频繁开辟空间 18 int[] temp = new int[array.length]; 19 mergeSort(array,0,array.length-1,temp); 20 } 21 private static void mergeSort(int[] array,int left,int right,int[] temp){ 22 if(left<right){ 23 int mid = left + (right-left)/2; 24 //左半边序列进行归并排序,使得其有序 25 mergeSort(array,left,mid,temp); 26 //有半边序列进行归并排序,使得其有序 27 mergeSort(array,mid+1,right,temp); 28 //将两个子序列进行合并 29 merge(array,left,mid,right,temp); 30 } 31 } 32 private static void merge(int[] array,int left,int middle,int right,int[] temp){ 33 if(array==null||array.length==0){ 34 System.out.println("array is null"); 35 } 36 //左序列指针指向 37 int leftPoint = left; 38 //有序列指针指向 39 int rightPoint = middle+1; 40 //临时数组的指针指向 41 int tempPoint = 0; 42 while (leftPoint<=middle&&rightPoint<=right){ 43 if(array[leftPoint]<=array[rightPoint]){ 44 temp[tempPoint++] = array[leftPoint++]; 45 }else{ 46 temp[tempPoint++] = array[rightPoint++]; 47 } 48 } 49 //将左半序列中的元素添加至temp数组中 50 while (leftPoint<=middle){ 51 temp[tempPoint++] = array[leftPoint++]; 52 } 53 //将右半序列中的元素添加至temp数组中 54 while(rightPoint<=right){ 55 temp[tempPoint++] = array[rightPoint++]; 56 } 57 tempPoint = 0; 58 //将temp中元素拷贝至原数组中 59 while (left<=right){ 60 array[left++] = temp[tempPoint++]; 61 } 62 } 63 }