具体思想可看视频理解
排序舞蹈
1. 插入排序
- 原理
整个区间被分为- 有序区间
- 无序区间
每次选择无序区间的第一个元素,在有序区间内选择合适的位置插入
- 实现
private void insertsort(int[] arr){
for (int i = 1; i < arr.length; i++){
int j = i-1;
int num = arr[i];
for (;j>=0;j--){
if (arr[j] <= num){ //如果比i位置的值大或等于就退出
break;
}
arr[j+1] = arr[j]; //都是比i位置小的情况
}
arr[j+1] = num;
}
}
2. 希尔排序
- 原理
先选定一个整数gap,把待排序文件中所有记录分成gap个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后,取重复上述分组和排序的工作。当到达gap=1时,所有记录在统一组内排好序。
- 希尔排序是对直接插入排序的优化。
- 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
- 实现
public void hillsort(int[] arr){
//插入的进阶版,实质和插入差不多,但效率明显不一样
for (int gap = arr.length/2;gap>0;gap/=2){
for (int i = 1;i < arr.length;i++){
int j = i - gap;
int num = arr[i];
for (; j >= 0&& arr[j] > num; j -= gap){
arr[j + gap] = arr[j];
}
arr[j + gap] = num;
}
}
}
3. 选择排序
- 原理
每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到全部待排序的数据元素排完
- 实现
//了解了思想,这儿就别跟我扯你看不懂代码
private void selectsort(int[] arr){
for (int i = 0;i < arr.length; i++){
int min = i;
for (int j = i;j<arr.length;j++){
if (arr[j] < arr[min]){
min = j;
}
}
exange(arr,i,min);
}
}
private void exange(int[] arr,int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
4. 堆排序
- 原理
基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
注意: 排升序要建大堆;排降序要建小堆。
- 实现
private void heapsort(int[] arr){
createheap(arr); //先建个大堆
for (int i=0;i<arr.length;i++){
exange(arr,0,arr.length-i-1);
shiftdown(arr,arr.length-i-1,0);
}
}
private void createheap(int[] arr) {
for (int i=(arr.length-1)/2;i>=0;i--){ //一个一个往上走
shiftdown(arr,arr.length,i); //一层一层往下走
}
}
private void shiftdown(int[] arr, int size, int index) {
int left = 2 * index+1;
while (left<size){
int max = left;
int right = 2*index+2;
if (size>right){
if (arr[right] > arr[left]){
max = right;
}
}
if (arr[index]>=arr[max]){ //大堆的子树肯定没有根的值大
break;
}
exange(arr,index,max); //有必要时就找最大子根节点与其替换
index=max;
left=2*index+1;
}
}
private void exange(int[] arr,int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
5. 冒泡排序
- 原理
在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序
- 实现
private void bubblesort(int[] arr){
for (int i = 0; i < arr.length; i++){
boolean issorted = true;
for (int j = 0; j < arr.length-i-1; j++){
if (arr[j]>arr[j+1]){
exange(arr,j,j+1);
issorted = false;
}
}
if (issorted){
return;
}
}
}
6. 快速排序
- 原理
- 从待排序区间选择一个数,作为基准值(pivot);
- Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;
- 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,或者小区间的长度 == 0,代表没有数据。
- 实现
递归版本
private void quicksort(int[] arr){
quickedhead(arr,0,arr.length-1);
}
private void quickedhead(int[] arr, int left, int right) {
if (left>=right){
return;
}
int i = left;
int j = right;
int par = arr[right];
while (i<j){
while (i<j&&arr[i]<=par){
i++;
}
while (i<j&&arr[j]>=par){
j--;
}
exange(arr,i,j);
}
exange(arr,i,right); //前面有
quickedhead(arr,left,i-1);
quickedhead(arr,i+1,right);
}
//--第二种
public static void quickSort(int[] array){
quickSortHelp(array,0,array.length-1);
}
public static void quickSortHelp(int[] a,int left,int right){
if (left >= right)//如果左边的值大于右边的值说明已经完成了一组的执行
{
return;
}
int temp = a[left];//将第一个元素设为对比元素(参考值)
int i = left;
int j = right;
while (i < j)
{
while (i<j && a[j]>temp)//如果这个参考值小于右边所比较的元素,则j--,进行右边的下一个元素与参考值比较
{
j--;
}
a[i] = a[j]; //找到了一个比参考值小的数,把这个数放在前面 (原来取参考值的位置)
while (i <j && a[i] <= temp)//如果参考值大于等于右边所比较的元素,则i++;进行下一个元素与参考值的比较
{
i++;
}
a[j] = a[i]; //找到了一个比参考值大的数,把这个数放到后面(刚刚放再前面那个数的位置)
}
a[i] = temp; //当在当组内找完一遍以后就把中间数temp回归
quickSortHelp(a, left, i - 1); //最后用同样的方式对分出来的左边的小组进行同上的做法
quickSortHelp(a, j + 1, right); //用同样的方式对分出来的右边的小组进行同上的做法
}
非递归版本
private void quickheap(int[] arr){
Stack<Integer> stack = new Stack<>();
stack.push(arr.length-1);
stack.push(0);
while (!stack.isEmpty()){
int left = stack.pop();
int right = stack.pop();
if (left>=right){
continue;
}
int mid = parheap(arr,left,right);
stack.push(mid-1);
stack.push(left);
stack.push(right);
stack.push(mid+1);
}
}
private int parheap(int[] arr, int left, int right) {
if (left>=right){
return -1;
}
int i = left;
int j = right;
int par = arr[right];
while (i<j){
while (i<j&&arr[i]<=par){
i++;
}
while (i<j&&arr[j]>=par){
j--;
}
exange(arr,i,j);
}
exange(arr,i,right);
return i;
}
7. 归并排序 (外部排序)
- 原理
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子
序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
- 实现
递归版本
private void addarray(int[] arr, int left, int mid, int right){
if (left>=right){
return;
}
int i = left, j=mid, k=0;
int length = right-left;
int[] extra = new int[length];
while (i < mid && j < right){
extra[k++] = (arr[i]<=arr[j])?arr[i++]:arr[j++];
}
while (i<mid){
extra[k++] = arr[i++];
}
while (j<right){
extra[k++] = arr[j++];
}
for (int t=0;t<extra.length;t++){
arr[t+left] = extra[t];
}
}
private void mergesort(int[] arr){
mergesortheap(arr,0,arr.length);
}
private void mergesortheap(int[] arr, int left, int right) {
if (right-left<=1){
return;
}
int mid = (left+right)/2;
mergesortheap(arr,left,mid);
mergesortheap(arr,mid,right);
addarray(arr,left,mid,right);
}
非递归版本
private void addarray(int[] arr, int left, int mid, int right){
if (left>=right){
return;
}
int i = left, j=mid, k=0;
int length = right-left;
int[] extra = new int[length];
while (i < mid && j < right){
extra[k++] = (arr[i]<=arr[j])?arr[i++]:arr[j++];
}
while (i<mid){
extra[k++] = arr[i++];
}
while (j<right){
extra[k++] = arr[j++];
}
for (int t=0;t<extra.length;t++){
arr[t+left] = extra[t];
}
}
private void mergeedsort(int[] arr){
for (int i = 1;i<arr.length;i*=2){
for (int j = 0;j<arr.length;j=j+2*i){
int left = j;
int mid =left+i;
if (mid>=arr.length){
continue;
}
int right = mid+i;
if (right>arr.length){
right = arr.length;
}
addarray(arr,left,mid,right);
}
}
}
排序总结 (重点)
排序方法 | 最好 | 平均 | 最坏 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
插入排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
希尔排序 | O(n) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
堆排序 | O(n * log(n)) | O(n * log(n)) | O(n * log(n)) | O(1) | 不稳定 |
快速排序 | O(n * log(n)) | O(n * log(n)) | O(n^2) | O(log(n)) ~ O(n) | 不稳定 |
归并排序 | O(n * log(n)) | O(n * log(n)) | O(n * log(n)) | O(n) | 稳定 |