赞
踩
对一序列对象根据某个关键字进行排序(通俗点来说就是吧一堆数据按照从小到大(升序),或者从大到小排列(降序)),我们下面都基于升序来讲解。
冒泡排序(bulleSort)这里就不在多说了,非常经典的一种排序,我们接触的第一种排序啦。
算法描述:
算法分析
时间复杂度:
空间复杂度O(1)
不稳定
实现代码
public static void bullSort(int[] array){ for(int i=0;i<array.length;i++) { int flag=0; for(int j=0;j<array.length-1-i;j++) { if(array[j]>array[j+1]) { int tmp=array[j]; array[j]=array[j+1]; array[j+1]=tmp; flag=1; } } if(flag==0) { break; } } }
选择排序(selectSort)是一种简单直观的排序算法。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
算法描述
算法分析
时间复杂度:
空间复杂度O(1)
稳定
实现代码
public static void selectSort(int[] array) {
for(int i=0;i<array.length-1;i++)
{
for(int j=i+1;j<array.length;j++)
{
if(array[j]<array[i])
{
int tmp=array[i];
array[i]=array[j];
array[j]=tmp;
}
}
}
}
希尔排序(shellSort)也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,它与插入排序的不同之处在于,它会优先比较距离较远的元素(将小的数据尽可能的放到前面)希尔排序是把数据按照增量数组中得值分成好多个组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的数据越来越多,当增量减至1时,整个数据恰被分成一组,其实此时也就是插入排序了。(增量数组当中的值为素数,但最后一个是1)
算法描述
算法分析
算法分析
时间复杂度:
空间复杂度O(1)
不稳定
实现代码
public static void shellSort(int[] array) { int[] drr = {5,3,1};//增量数组 for (int i = 0; i < drr.length; i++) { shell(array,drr[i]); } } public static void shell(int[] array ,int gap) { int j,tmp; for(int i=gap;i<array.length;i++) { tmp=array[i]; j=i-gap; for(;j>=0;j-=gap) { if(array[j]>tmp) { array[j+gap]=array[j]; }else{ break; } } array[j+gap]=tmp; } }
举例数组为[12,5,9,16,6,8,27,58,80,0,7,4,33,55,77]
增量序列为[5,3,1]
归并排序(mergeSort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。(原理-合并两个有序数组)
算法描述
来分析一下吧,对于归并算法,有两个部分组成,分解和合并。首先讲讲分解,我们需要将待排序的序列不停地进行分解,通过两个索引变量控制,一个初始索引(low),一个结尾索引(high)。只有当两索引重合才结束分解。接下来是合并,合并操作也是最麻烦的,也是通过两个索引变量s1,s2。开始s1在左边序列的第一个位置,s2在右边序列的第一个位置,然后就是寻找左右两个序列中的最小值(合并两个数组),放到新序列中,这时可能会出现一边的元素都放置完毕了,而另外一边还存在元素,此时只需将剩余的元素按顺序放进新序列即可,因为这时左右两边的序列已经是有序的了,最后将新序列复制到旧序列。这里也特别需要注意,因为合并的过程是分步的,而并非一次合并完成,所以数组的索引是在不断变化的。(所以需要加strat保证位置的正确性)
算法分析
时间复杂度:
空间复杂度 O(n)
稳定
实现代码
public static void mergeSort(int[] array){ mergeSortInternal(array,0,array.length-1); } public static void mergeSortInternal(int[] array,int low,int high){ if(low>=high) return ; int mid=(low+high)/2; //分解 mergeSortInternal(array,low,mid); mergeSortInternal(array,mid+1,high); //合并 merge(array,low,mid,high); } //核心:两数组进行合并 public static void merge(int[] array,int start,int mid,int end){ int s1=start; int s2=mid+1; int[] tmp=new int[end-start+1]; int k=0; while(s1<=mid&&s2<=end) { if(array[s1]<=array[s2]) { tmp[k++]=array[s1++]; }else{ tmp[k++]=array[s2++]; } } //有可能第一个段还有数据 有可能第2个段也有数据 while(s1<=mid) { tmp[k++]=array[s1++]; } while(s2<=end) { tmp[k++]=array[s2++]; } for(int i=0;i<end-start+1;i++) { //一定要加上start array[i+start]=tmp[i]; } } // 归并排序 private static void merge_sort(int[] arr, int low, int high) { if (low>=high) return; int mid = low+high>>1; merge_sort(arr, low, mid); merge_sort(arr, mid + 1, high); int[] tmp = new int[high - low + 1]; int i = low, j = mid + 1, k = 0; // 进行归并 while (i <= mid && j <= high) { if (arr[i] <= arr[j]) tmp[k++] = arr[i++]; else tmp[k++] = arr[j++]; } while (i <= mid) tmp[k++] = arr[i++]; while (j <= high) tmp[k++] = arr[j++]; // 进行赋值 for (i = low, j = 0; i <=high; i++, j++) arr[i] = tmp[j]; }
//非递归版本 public static void mergeSort2(int[] array) { for (int i = 1; i < array.length; i*=2) { merge(array,i); } } public static void merge(int[] array,int gap) { int s1 = 0; int e1 = s1+gap-1; int s2 = e1+1; int e2 = s2+gap-1 < array.length ? s2+gap-1 : array.length-1; int[] tmp = new int[array.length]; int k = 0;//下标 //当有两个归并段的时候 while (s2 < array.length) { //当当有两个归并段 且 这两个段内都要数据 while (s1 <= e1 && s2<= e2) { if(array[s1] <= array[s2]) { tmp[k++] = array[s1++]; }else{ tmp[k++] = array[s2++]; } } while (s1 <= e1) { tmp[k++] = array[s1++]; } while (s2 <= e2){ tmp[k++] = array[s2++]; } //从这里开始的时候,每个下标都可能越界 s1 = e2+1; e1 = s1+gap-1; s2 = e1+1; e2 = s2+gap-1 < array.length ? s2+gap-1 : array.length-1; } //退出上面循环后, // 那么把s1段内的数据给拷贝下来,因为 有可能e1已经越界了 while (s1 < array.length) { tmp[k++] = array[s1++]; } //拷贝tmp到原数组当中 for (int i = 0; i < tmp.length; i++) { array[i] = tmp[i]; } }
直接插入排序(insertSort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
生活中打扑克牌就是插入排序的体现
算法描述
算法分析
时间复杂度:
空间复杂度 O(1)
稳定
实现代码
public class insertSort { public static void insertSort(int[] array) { for (int i = 1; i < array.length; i++) { int tmp = array[i]; int j = i-1; for (j; j >= 0 ; j--) { //如果这里是一个大于等于号 此时这个排序就不稳定了 if(array[j] > tmp) { array[j+1] = array[j]; }else { break; } } array[j+1] = tmp; } }
直接插入排序数据越有序,排序越快。当数据大部分有序时,使用直接插入排序最合适,直接插入排序也会用在其他一些排序的优化上。
快速排序(quickSort)的基本思想:通过一趟排序将待排记录分成两部分,其中一部分数据比某一个值均大,另一部分比这个值均小,(称这个值为基准pivot)然后分别对这两部分记录继续进行排序,以达到整个序列有序。
算法描述
算法分析
时间复杂度:
空间复杂度 O(nlogn)
不稳定
实现代码
//递归写法及其优化 public static void quickSort1(int[] array){ quick(array,0,array.length-1); } //找基准 public static int pivot(int[] array,int low,int high) { int tmp = array[low]; while (low < high) { while (array[high]>=tmp&&low<high) { high--; } //把high数据赋值给low array[low]=array[high]; while (array[low]<=tmp&&low<high) { low++; } //把low下标的值给high array[high]=array[low]; } array[low] = tmp; return low; } public static void insertSort(int[] array,int low,int high) { int tmp,j; for(int i=low;i<=high;i++) { tmp=array[i]; j=i-1; for(;j>=low;j--) { if(array[j]>tmp) { array[j+1]=array[j]; }else{ break; } } array[j+1]=tmp; } } public static void quick(int[] array,int low,int high){ if(low>=high) return ; if(high-low + 1 <= 50) { //使用插入排序 insertSort(array,low,high); return;//记着这里一定要return 这里说明 这个区别范围有序了 直接结束 } //优化1 medianOfThree(array,low,high); int piv=pivot(array,low,high); quick(array,low,piv-1); quick(array,piv+1,high); } //三数取中法优化(找基准) public static void swap(int[] array,int i,int j) { int tmp = array[i]; array[i] = array[j]; array[j] = tmp; } public static void medianOfThree(int[] array,int low,int high) { int mid = (low+high)/2; //array[mid] <= array[low] <= array[high] if(array[low] < array[mid]) { swap(array,low,mid); }//array[mid] <= array[low] if(array[low] > array[high]) { swap(array,low,high); }//array[low] <= array[high] if(array[mid] > array[high]) { swap(array,mid,high); }//array[mid] <= array[high] } //2 递归实现 private static void quickSort(int[] arr, int low, int high) { if(low>=high) return ; int i=low,j=high; while (i< j) { while (i < j && arr[j] >= arr[low]) j--; while (i < j && arr[i] <= arr[low]) i++; swap(arr, i, j); } swap(arr, i, low); quickSort(arr, low, i - 1); quickSort(arr, i + 1, high); } private static void swap(int[] arr, int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
//非递归写法 //找基准 public static int pivot(int[] array,int low,int high) { int tmp = array[low]; while (low < high) { while (array[high]>=tmp&&low<high) { high--; } //把high数据赋值给low array[low]=array[high]; while (array[low]<=tmp&&low<high) { low++; } //把low下标的值给high array[high]=array[low]; } array[low] = tmp; return low; } public static void quickSort2(int[] array) { Stack<Integer> stack = new Stack<>(); int low = 0; int high = array.length-1; int piv = pivot(array,low,high);// if(piv > low + 1) { stack.push(low); stack.push(piv-1); } if(piv < high-1) { stack.push(piv+1); stack.push(high); } while (!stack.empty()) { high = stack.pop(); low = stack.pop(); piv = pivot(array,low,high); if(piv > low + 1) { stack.push(low); stack.push(piv-1); } if(piv < high-1) { stack.push(piv+1); stack.push(high); } } }
堆排序(heapSort)在学堆排序之前我们需要了解大根堆、小根堆、如何构造堆等相关的一些知识(不了解的话,可以看看博主之前写的堆的文章)。
算法描述
我们默认是升序从小到大,我们分为三个步骤
算法分析
时间复杂度:
空间复杂度O(1)
稳定的
实现代码
代码
//向下调整 public static void adjustDown(int[] array,int parent,int len) { int child = 2*parent+1; while (child < len) { if(child+1 < len && array[child] < array[child+1]) { child++;// } if(array[child] > array[parent]) { int tmp = array[child]; array[child] = array[parent]; array[parent] = tmp; parent = child; child = 2*parent+1; }else { break; } } } //建大堆 public static void crateBigHeap(int[] array) { for(int i = (array.length-1-1) /2; i>= 0;i--) { adjustDown(array,i,array.length); } } //堆排序 public static void heapSort(int[] array) { crateBigHeap(array); int end=array.length-1; while(end>0) { int tmp=array[0]; array[0]=array[end]; array[end]=tmp; adjustDown(array,0,end); end--; } }
外部排序:排序过程需要在磁盘等外部存储进行的排序前提:内存只有 1G,需要排序的数据有 100G
因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序
上面这些排序都是基于比较然后进行交换的思想,但也有不需要交换的方法,那便是计数排序、桶排序等等,大家感兴趣的话可以下去看看,这里就不做过多描述了。
好了,如果有错误和不懂的地方的话留言评论即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。