排序--java实现
java中含有很多排序算法,我在这里只列举出来了几种我们常见的排序算法。
在讲解排序算法之前,需要先声明几个词语:
1、稳定性:是指有两个相同的值在排序后其先后顺序是否发生了变化,若没有发生变化则是稳定的排序
2、时间复杂度与空间复杂度:这里对其不进行讲解,请大家自行查询其他博客,明白其计算方法。
一、插入排序
1、直接插入算法(InsertSort):这种排序算法是最简单但也是相比较为复杂的一种排序算法。
思想:这个排序区间可以划分为待排序区间和已排序区间(本人习惯左边作为已排序区间,右边作为待排序区间),每次选择待排序区间的第一个元素作为待插入元素,有序插入到已排序区间中,最开始未排序区间设置为1,因为默认包含有一个元素的区间是有序的。
插入过程:
(1)从已排序区间的最大值开始(若你左边是待排序区间,则是与已排序区间的最小值开始比较,下面亦是反着比较大小),将待插入元素依次与其比较大小
(2)若待插入元素较小,则将被比较的元素后移一位,待插入元素与其前一位继续比较,重复该过程,直到被比较元素小于待插入元素,将该待插入元素插入此位置。
实现代码如下:
1 public static void insertSort(int[] array){ 2 int index = 1; 3 while(index < array.length){ 4 int tmp = array[index]; 5 int i = index - 1; 6 for(; i>=0; i--){ 7 if(array[i] > tmp) 8 array[i+1] = array[i]; 9 else 10 break; 11 } 12 array[i + 1] = tmp; 13 index++; 14 } 15 }
最好 | 平均 | 最坏 | 空间复杂度 | 稳定性 | |
时间复杂度 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
2、希尔排序(ShellSort):
思想:是对直接插入排序的一种优化。
排序过程:根据所给的seq(分组数)来进行分组执行直接插入排序,
代码实现如下:
1 public static void shellSort(int[] arr){ 2 int step = arr.length/2; 3 while(step >= 1){ 4 for(int i = step; i<arr.length; i++){ 5 int index = i - step; 6 while(index >= 0){ 7 if(arr[index] > arr[index+step]){ 8 int tmp = arr[index]; 9 arr[index] = arr[index+step]; 10 arr[index+step] = tmp; 11 index-=step; 12 }else 13 break; 14 } 15 } 16 step/=2; 17 } 18 19 }
最好 | 平均 | 最坏 | 空间复杂度 | 稳定性 | |
时间复杂度 | O(n) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
二、选择排序
思想:同样也分为已排序区间和未排序区间,每次从未排序区间中选择一个最小的值,插入到已排序区间的最后。已排序区间长度的初始值设为0。
代码实现如下:
1 public static void selectSort(int[] arr){ 2 for(int i = 0; i < arr.length-1; i++){ 3 for(int j = i;j < arr.length;j++){ 4 if(arr[j] < arr[i]){ 5 int tmp = arr[i]; 6 arr[i] = arr[j]; 7 arr[j] = tmp; 8 } 9 } 10 } 11 }
最好 | 平均 | 最坏 | 空间复杂度 | 稳定性 | |
时间复杂度 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
三、堆排序
思想:对整个区间进行堆排序,升序建立大根堆,降序建立小根堆,每次将堆顶与堆的最后一个元素进行交换,删除堆的最后一个元素后进行堆调整。再重复上述过程,直到堆里只剩下一个元素。
代码实现过程:
// 创建堆(堆排序) private void createHeap(int[] heap) { // 堆创建,自下向上进行向下调整 int index = heap.length / 2 - 1; for(int i = index; i >= 0; i--){ UpToDown(heap,heap.length, i); } } // 向下调整 public static void UpToDown(int[] heap, int size, int index){ int child = index*2+1; while(child < size){ if(child+1 < size && heap[child+1]>heap[child]) child+=1; if(heap[child] > heap[index]){ int tmp = heap[index]; heap[index] = heap[child]; heap[child] = tmp; } index = child; child = index*2+1; } } // 删除堆顶 public Integer poll(int[] heap){ if(heap.length == 0) return null; int result = heap[0]; heap[0] = heap[heap.length-1]; UpToDown(heap,heap.length-1,0); return result; }
最好 | 平均 | 最坏 | 空间复杂度 | 稳定性 | |
时间复杂度 | O(n*log(n)) | O(n*log(n)) | O(n*log(n)) | O(1) | 不稳定 |
四、冒泡排序
思想:在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有 序
代码实现过程:
1 public static void BubbleSort(int[] arr){ 2 for(int i = 0; i < arr.length-1; i++){ 3 for(int j = 1; j < arr.length-i; j++){ 4 if(arr[j-1]> arr[j]){ 5 int tmp = arr[j-1]; 6 arr[j-1] = arr[j]; 7 arr[j] = tmp; 8 } 9 } 10 } 11 }
最好 | 平均 | 最坏 | 空间复杂度 | 稳定性 | |
时间复杂度 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
五、快速排序
思想:
(1) 从待排序区间选择一个数,作为基准值(pivot);
(2) Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基 准值大的(可以包含相等的)放到基准值的右边;
(3)采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序, 或者小区间的长度 == 0,代表没有数
代码实现过程:
1 public static void FastSort(int[] arr, int begin, int end){ 2 if(begin >= end) 3 return; 4 int left = begin; 5 int right = end; 6 while(left < right){ 7 while(left < right && arr[right] >= arr[begin]) 8 right--; 9 while(left < right && arr[left] <= arr[begin]) 10 left++; 11 int tmp = arr[left]; 12 arr[left] = arr[right]; 13 arr[right] = tmp; 14 } 15 int tmp = arr[left]; 16 arr[left] = arr[begin]; 17 arr[begin] = tmp; 18 FastSort(arr, begin, left-1); 19 FastSort(arr, left+1, end); 20 }
最好 | 平均 | 最坏 | 空间复杂度 | 稳定性 | |
时间复杂度 | O(n*log(n)) | O(n*log(n)) | O(n^2) | O(log(n)) | 不稳定 |
优化:
(1)三数取中法:选取基准值时有可能会每次取到的都是待排序区间的最值,此时快速排序的效率比直接插入效率还低,因此为了避免这类情况发生,每次从待排序区间中选取三个数,取中间的值,这样至少可以保证这个值有一个大于它的值或者小于它的值。
(2)当递归后的待排序区间的长度较小后,可以采用直接插入排序,降低递归的深度,在区间较小的时候直接插入排序效率相对较高。
(3)当递归达到一定深度后,但其待排序区间长度仍然较长,可以考虑停止递归,而对此时的待排序区间采用堆排序。
六、归并排序
思想:是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子 序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
代码实现如下:
1 private static void merge(int[] array, int low, int mid, int high) { 2 int i = low; 3 int j = mid; 4 int length = high - low; 5 int[] extra = new int[length]; 6 int k = 0; 7 8 // 选择小的放入 extra 9 while (i < mid && j < high) { 10 // 加入等于,保证稳定性 11 if (array[i] <= array[j]) { 12 extra[k++] = array[i++]; 13 } else { 14 extra[k++] = array[j++]; 15 } 16 } 17 18 // 将属于元素放入 extra 19 while (i < mid) { 20 extra[k++] = array[i++]; 21 } 22 23 while (j < high) { 24 extra[k++] = array[j++]; 25 } 26 27 // 从 extra 搬移回 array 28 for (int t = 0; t < length; t++) { 29 // 需要搬移回原位置,从 low 开始 30 array[low + t] = extra[t]; 31 } 32 } 33 34 public static void mergeSort(int[] array) { 35 mergeSortInternal(array, 0, array.length); 36 } 37 // 待排序区间为 [low, high) 38 private static void mergeSortInternal(int[] array, int low, int high) { 39 if (low >= high - 1) { 40 return; 41 } 42 43 int mid = (low + high) / 2; 44 mergeSortInternal(array, low, mid); 45 mergeSortInternal(array, mid, high); 46 47 merge(array, low, mid, high); 48 }
最好 | 平均 | 最坏 | 空间复杂度 | 稳定性 | |
时间复杂度 | O(n*log(n)) | O(n*log(n)) | O(n*log(n)) | O(n) | 不稳定 |
注:若有不对的地方,多谢各位大佬指正。