参考https://blog.youkuaiyun.com/hellozhxy/article/details/79911867
有动图展示排序过程,但有一定的瑕疵。
冒泡排序:
//冒泡排序
//以从小到大为例,比较相邻的元素,如果第一个比第二个大,交换位置
//对每一对相邻元素进行同样的操作,对每一个元素除了最后一个元素,进行上诉操作
// 最佳 n 平均n^2 最坏 n^2
public static int[] bubbleSort(int[] arr){
if(arr.length==0){
return arr;
}
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr.length-1-i;j++){
if(arr[j]>arr[j+1]){
arr[j]^=arr[j+1];
arr[j+1]^=arr[j];
arr[j]^=arr[j+1];
/*arr[j]=arr[j]^arr[j+1];
arr[j+1]=arr[j]^arr[j+1];
arr[j]=arr[j]^arr[j+1];*/
}
}
}
return arr;
}
这里的元素交换我使用了一下位运算的异或交换,即一个数对同一个数连续异或三次为它本身这个规律,但存在一个bug,就是当地址相同的两个数异或的话,异或会归零。
//选择排序
//每次遍历选择最小的一个放在数组起始位置
//最佳情况:T(n) = O(n) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)
public static int[] selectSort(int[] arr){
if(arr.length==0){
return arr;
}
for(int i=0;i<arr.length;i++){
int minIndex=i;
for(int j=i;j<arr.length;j++){
if(arr[j]<arr[minIndex]){
minIndex=j;
}
}
int tem=arr[i];
arr[i]=arr[minIndex];
arr[minIndex]=tem;
//使用异或时,如果地址相同的异或会清零。
/* arr[i]=arr[i]^arr[minIndex];
arr[minIndex]=arr[i]^arr[minIndex];
arr[i]=arr[i]^arr[minIndex];*/
}
return arr;
}
//插入排序
//构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
//最佳情况:T(n) = O(n) 最坏情况:T(n) = O(n2) 平均情况:T(n) = O(n2)
public static int[] InsertionSort(int[] arr){
if(arr.length==0) {
return arr;
}
for(int i=0;i<arr.length;i++){
int current=arr[i+1];
int preIndex=i;
while(preIndex>=0 && current<arr[preIndex]){
arr[preIndex+1]=arr[preIndex];
preIndex--;
}
arr[preIndex+1]=current;
}
return arr;
}
//希尔排序;缩小增量排序
//希尔增量序列:{n/2,(n/2)/2...1}
//增加一个增量序列,然后进行插入排序
//最佳情况:T(n) = O(nlog2 n) 最坏情况:T(n) = O(nlog2 n) 平均情况:T(n) =O(nlog2n)
public static int[] shellSort(int[] arr){
if(arr.length==0){
return arr;
}
int len=arr.length;
int temp,gap=len/2;
while (gap>0){
for(int i=gap;i<len ;i++){
temp=arr[i];
int preIndex=i-gap;
while (preIndex>=0 && arr[preIndex]>temp){
arr[preIndex+gap]=arr[preIndex];
preIndex-=gap;
}
arr[preIndex+gap]=temp;
}
gap/=2;
}
return arr;
}
//归并排序
//最佳情况:T(n) = O(n) 最差情况:T(n) = O(nlogn) 平均情况:T(n) = O(nlogn)
//代价是需要额外的内存空间。
// 把长度为n的输入序列分成两个长度为n/2的子序列;
//对这两个子序列分别采用归并排序;
//将两个排序好的子序列合并成一个最终的排序序列。
// 若将两个有序表合并成一个有序表,称为2-路归并。
public static int[] mergeSort(int[] arr){
if(arr.length<2) return arr;
int mid=arr.length/2;
int[] left=Arrays.copyOfRange(arr,0,mid);
int[] right=Arrays.copyOfRange(arr,mid,arr.length);
return merge(mergeSort(left),mergeSort(right));
}
public static int[] merge(int[] left,int[] right){
int[] result=new int[left.length+right.length];
for(int index=0,i=0,j=0;index<result.length;index++){
if(i>=left.length){
result[index]=right[j++];
}
else if(j>=right.length){
result[index]=left[i++];
}
else if(left[i]>right[j]){
result[index]=right[j++];
}
else {
result[index]=left[i++];
}
}
return result;
}
//快速排序
//最佳情况:T(n) = O(nlogn) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(nlogn)
//从数列中挑出一个元素,称为 “基准”(pivot);
//重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
//重复上诉过程,直到整体有序
public static int[] quickSort1(int[] arr,int left,int right){
if(left<right){
int index=partition1(arr,left,right);
quickSort1(arr,left,index-1);
quickSort1(arr,index+1,right);
}
return arr;
}
public static int partition1(int[] arr,int l,int r){
//默认是第一个
int key=arr[l];
// //可以做一个优化
// int index=l+(int)(Math.random()*(r-l+1));
//// int key=arr[index];
while(l<r){
while(l<r&&key<=arr[r]){
r--;
}
arr[l]=arr[r];
while(l<r&&key>=arr[l]){
l++;
}
arr[r]=arr[l];
}
arr[l]=key;
return l;
}
这个快速排序是教科书上的基本实现,如果使用随机索引的方式,要使用交换。
//堆排序
//最佳情况:T(n) = O(nlogn) 最差情况:T(n) = O(nlogn) 平均情况:T(n) = O(nlogn)
//建堆,满足完全二叉树的性质:左边子节点位置 = 当前父节点的两倍 + 1,右边子节点位置 = 当前父节点的两倍 + 2
//交换堆顶与数组最后一个元素
static int len;
public static int[] heapSort(int[] arr){
len=arr.length;
if(len<1){
return arr;
}
buildMaxHeap(arr);
while(len>0){
swap(arr,0,len-1);
len--;
adjustHeap(arr,0);
}
return arr;
}
public static void buildMaxHeap(int[] arr) {
for(int i=(len-1)/2;i>=0;i--){
adjustHeap(arr,i);
}
}
public static void adjustHeap(int[] arr, int i) {
int maxIndex=i;
if(i*2<len&&arr[i*2]>arr[maxIndex]){
maxIndex=i*2;
}
if(i*2+1<len&&arr[i*2+1]>arr[maxIndex]){
maxIndex=i*2+1;
}
if(maxIndex!=i){
swap(arr,maxIndex,i);
adjustHeap(arr,maxIndex);
}
}