排序主要分为两类:内部排序和外部排序。java
一般所说的各类排序算法其实指的是内部排序算法。内部排序是基于内存的,整个排序过程都是在内存中完成的,而外部排序指的是因为数据量太大,内存不能彻底容纳,排序的时候须要借助外存才能完成(经常是算计着某一部分已经计算过的数据移出内存让另外一部分未被计算的数据进入内存)。算法
在一个有序的集合中,将当前值插入到适合位置上,使得插入结束以后整个集合依然是有序的。数组
一、直接插入排序ui
将第 i 个记录插入到前面 i-1 个已经有序的集合中。spa
i=1,一个元素确定是有序的,code
i =2 ,将第二个元素插入到前 i-1个有序集合中;blog
i =3,将第三个元素插入到前 i-1(2)集合中,等等。排序
直到去插入最后一个元素时,前面的 i-1 个元素构成的集合已经有序,因而找到第 i 个元素的合适位置插入便可,整个插入排序完成。递归
下面是具体的实现代码:索引
@Test public void test1() { int[] array = {1,13,72,9,22,4,6,781,29,2}; InsertSort(array); System.out.println(Arrays.toString(array)); } private void InsertSort(int[] array) { for(int i=1;i<array.length;i++){ int temp = array[i]; int j=i-1; while(j>=0&&array[j]>temp){ //须要移动位置,将较大的值array[j]向后移动一个位置 array[j+1]=array[j]; j--; } //循环结束说明找到适当的位置了,是时候插入值了 array[j+1]=temp; } }
整个程序的逻辑是从数组的第二个元素开始,每一个元素都以其前面全部的元素为基本,找到合适的位置进行插入。对于这种按照从小到大的排序原则,程序使用一个临时变量temp保存当前须要插入的元素的值,从前面的子序列的最后一个元素开始,循环的与temp进行比较,一旦发现有大于temp的元素,让它顺序的日后移动一个位置,直到找到一个元素小于temp,那么就找到合适的插入位置了。
由于咱们使用的判断条件是,key>array[j]。因此来讲,插入排序算法也是稳定的算法。对于值相同的元素并不会更改他们原来的位置顺序。至于该算法的效率,最好的状况是全部元素都已有序,比较次数为n-1,最坏的状况是全部元素都是逆序的,比较次数为(n+2)(n-1)/2,因此该算法的时间复杂度为O(n*n)。
既然咱们每次要插入的序列是有序的,咱们彻底可使用二分查找到合适位置再进行插入,这显然要比直接插入的效率要高一些。
@Test public void test1() { int[] array = {1,13,72,9,22,4,6,781,29,2}; halfInsertSort(array); System.out.println(Arrays.toString(array)); } private void halfInsertSort(int[] array) { for(int i=1;i<array.length;i++){ int temp = array[i]; //找到合适的位置 int low =0,high=i-1,mid; while(low<=high){ mid=(low+high)/2; if(temp==array[mid]){ break; }else if(temp>array[mid]){ low=mid+1; }else{ high=mid-1; } } //low的索引位置就是即将插入的位置 //移动low索引位置后面的全部元素 for(int j=i-1;j>=low;j--){ array[j+1]=array[j]; } array[low]=temp; } }
虽然,折半插入改善了查找插入位置的比较次数,可是移动的时间耗费并无获得改善,因此效率上优秀的量可观,时间复杂度仍然为O(n*n)。
二、希尔排序
它将待排序序列拆分红多个子序列,保证每一个子序列的组成元素相对较少,而后经过对子序列使用直接排序。
希尔排序算法使用一个距离增量来切分子序列。
如图,咱们初始有一个序列,按照距离增量为4来拆分的话,能够将整个序列拆分红四个子序列,咱们对四个子序列内部进行直接插入排序获得结果以下:
修改距离增量从新划分子序列:
很显然,当距离增量变小的时候,序列的个数也会变少,可是这些子序列的内部都基本有序,当对他们进行直接插入排序的时候会使得效率变高。一旦距离增量减小为1,那么子序列的个数也将减小为1,也就是咱们的原序列,而此时的序列内部基本有序,最后执行一次直接插入排序完成整个排序操做。
/*渐减delete的值*/ public static void ShellSort(){ int[] array = {46,55,13,42,94,17,5,70}; int[] delets = {4,2,1}; for (int i=0;i<delets.length;i++){ oneShellSort(array,delets[i]); } //遍历输出数组内容 for(int value : array){ System.out.print(value + ","); } } /*根据距离增量的值划分子序列并对子序列内部进行直接插入排序*/ public static void oneShellSort(int[] array,int delet){ int temp; for(int i=delet;i<array.length;i++){ //从第二个子序列开始交替进行直接的插入排序 //将当前元素插入到前面的有序队列中 if(array[i-delet] > array[i]){ temp = array[i]; int j=i-delet; while(j>=0 && array[j] > temp){ array[j+delet] = array[j]; j -= delet; } array[j + delet] = temp; } } }
利用两个元素之间的值的大小进行比较运算,而后移动外置实现的,这类排序算法主要有两种:冒泡排序+快速排序
一、冒泡排序
冒泡排序经过两两比较,每次将最大或者最小的元素移动到整个序列的一端:
冒泡排序的算法实现以下:【排序后,数组从小到大排列】
@Test public void test1() { int[] array = {49,38,65,97,76,13,27,49}; bubbleSort(array); System.out.println(Arrays.toString(array)); } public void bubbleSort(int[] array){ for(int i=0;i<array.length-1;i++){ for(int j=0;j<array.length-1-i;j++){ if(array[j]>array[j+1]){ int temp=array[j]; array[j]=array[j+1]; array[j+1]=temp; } } } }
2.快速排序
经过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另外一部分关键字小,则分别对这两部分继续进行排序,直到整个序列有序。
@Test public void test1() { int[] array = {49, 38, 65, 97, 76, 13, 27, 49}; quickSort(array, 0, array.length - 1); System.out.println(Arrays.toString(array)); } public void quickSort(int[] a, int low, int high) { //1,找到递归算法的出口 if (low > high) { return; } //2, 存 int i = low; int j = high; //3,key int key = a[low]; //4,完成一趟排序 while (i < j) { //4.1 ,从右往左找到第一个小于key的数 while (i < j && a[j] > key) { j--; } // 4.2 从左往右找到第一个大于key的数 while (i < j && a[i] <= key) { i++; } //4.3 交换 if (i < j) { int p = a[i]; a[i] = a[j]; a[j] = p; } } // 4.4,调整key的位置 int p = a[i]; a[i] = a[low]; a[low] = p; //5, 对key左边的数快排 quickSort(a, low, i - 1); //6, 对key右边的数快排 quickSort(a, i + 1, high); }