往期: JAVA 修炼秘籍第一章:《痛苦的折磨》 JAVA 修炼秘籍第二章:《逐渐魔化》 JAVA 修炼秘籍第三章:《绝地反击》 JAVA 修炼秘籍第四章:《闭关修炼》 JAVA 修炼秘籍第五章:《卧薪尝胆》 JAVA 修炼秘籍第六章:《鏖战》 JAVA 修炼秘籍第七章:《面向对象编程》 JAVA 修炼秘籍第八章:《String类》 JAVA 修炼秘籍第九章:《List / ArrayList / LinkedList 》 JAVA 修炼秘籍第十章:《优先级队列(堆)PriorityQueue》 JAVA修炼秘籍(番外篇)第一章:《这四道代码题,你真的会吗?》 JAVA修炼秘籍(番外篇)第二章:《图书馆管理系统》
——————————————————————生活以痛吻我,我却报之以歌。
一、插入排序
- 时间复杂度:O(n2)。
- 空间复杂度:O(1)。
- 当一组数据,数据量不大且趋近于有序时,推荐使用插入排序更快
public static void InsertSort(int[] arr){
for(int i=1;i<arr.length;i++){
int tmp=arr[i];
int j=i-1;
for(;j>=0;j--){
if(arr[j]>tmp){
arr[j+1]=arr[j];
}else{
break;
}
}
arr[j+1]=tmp;
}
}
二、折半插入排序
- 时间复杂度:O(n2)。
- 空间复杂度:O(1)。
- 通过直接插入排序可以发现,每次比较时,i下标前的数据时有序的。
- 折半插入排序利用此特性将i下标的值与前面有序数据进行二分查找。
- 算是直接插入排序的一种优化。
public static void HalveInsertSort(int[] arr){
for(int i=1;i<arr.length;i++){
int left=0;
int right=i-1;
int tmp=arr[i];
while(right>=left){
int mid=(left+right)/2;
if(arr[mid]>tmp){
right=mid-1;
}else{
left=mid+1;
}
}
for(int j=i-1;j>right;j--){
arr[j+1]=arr[j];
}
arr[right+1]=tmp;
}
}
三、希尔排序
- 时间复杂度:O(n1.3)。
- 空间复杂度:O(1)。
- 希尔排序的巧妙处与插入排序有异曲同工之处。
- 中心思想在于gap的取值,取值方法有很多中,但都大同小异,其余思想与插入排序无太大差别。
public static void shellSort(int[] arr){
int gap=arr.length;
while(gap>0){
mySort(arr,gap);
gap/=2;
}
}
public static void mySort(int[] arr,int gap){
for(int i=gap;i<=arr.length;i++){
int tmp=arr[i];
int j=i-gap;
for(;j>=0;j-=gap){
if(arr[j]>tmp){
arr[j+gap]=arr[j];
}else{
break;
}
}
arr[j+gap]=tmp;
}
}
四、选择排序
- 时间复杂度:O(n2)。
- 空间复杂度:O(1)。
- 顾名思义,选择排序,选择好了再排序。
- 每次从数组中选择一个最大或最小的数据与头或尾交换。
- 再将交换后的边界缩小。
public static void selectSort(int[] arr){
for(int i=0;i<arr.length;i++){
int max=0;
for(int j=1;j<arr.length-i;j++){
if(arr[j]>arr[max]){
max=j;
}
}
int tmp=arr[max];
arr[max]=arr[arr.length-1-i];
arr[arr.length-1-i]=tmp;
}
}
五、双向选择排序
- 时间复杂度:O(n2)。
- 空间复杂度:O(1)。
- 与选择排序中心思想相同,这次是一次找出最大与最小同时交换
- 此算法中而每次取值的最小值下标有头下标交换时,如果头下标的位置刚好时是最大值,此时交换后max下标指向则不是最大值而是最小值,最后的if()判断很重要。
public static void TwoWaySelectSort(int[] arr){
int left=0;
int right=arr.length-1;
while(left<=right){
int min=left;
int max=left;
for(int i=left+1;i<=right;i++){
if(arr[i]>arr[max]){
max=i;
}
if(arr[i]<arr[min]){
min=i;
}
}
swap(arr,min,left);
if(max==left){
max=min;
}
swap(arr,max,right);
left++;
right--;
}
}
六、堆排序
- 时间复杂度:O(logn)。
- 空间复杂度:O(1)。
- 假设要排序升序,首先将数组变为大堆存储形式。
- 再循环从后向前把每个元素与0下标元素交换。
- 每次交换后都要进行一次大堆调整。
public static void shiftDown(int[] arr,int parent,int len){
int child=parent*2+1;
while(child<len){
if(child+1<len&&arr[child]<arr[child+1]){
child++;
}
if(arr[child]>arr[parent]){
swap(arr,child,parent);
parent=child;
child=parent*2+1;
}else{
break;
}
}
}
public static void MySort(int[] arr){
for(int i=(arr.length-1)/2;i>=0;i--){
shiftDown(arr,i,arr.length);
}
}
public static void heapSort(int[] arr){
MySort(arr);
int end=arr.length-1;
while(end>0){
swap(arr,0,end);
shiftDown(arr,0,end);
end--;
}
}
七、冒泡排序
- 时间复杂度:O(n2)。
- 空间复杂度:O(1)。
- 两两比较,最终将数组最大元素放在最后位置。
- 调整边界,因上次循环结束后最后以为已是有序,每次比较长度-1。
- 若一次循环下来,没有任何数据交换,此时证明当前数据集已经有序,可以直接结束循环。
public static void bubbleSort(int[] arr){
for(int i=0;i<arr.length-1;i++){
boolean bool=true;
for(int j=0;j<arr.length-1-i;j++){
if(arr[j+1]<arr[j]){
swap(arr,j+1,j);
bool=false;
}
}
if(bool){
break;
}
}
}
八、快速排序(挖坑)
- 时间复杂度:O(logn)。
- 空间复杂度:O(logn)。
- 顾名思义,选择一个基准值,将小于基准值大放到基准值左,大于基准值放在右。
public static int partition(int[] arr,int start,int end){
int tmp=arr[start];
while(start<end){
while(start<end&&arr[end]>=tmp){
end--;
}
arr[start]=arr[end];
while(start<end&&arr[start]<=tmp){
start++;
}
arr[end]=arr[start];
}
arr[start]=tmp;
return start;
}
public static void quickSorts(int[] arr,int left,int right){
if(left>=right){
return;
}
int pivot=partition(arr,left,right);
quickSorts(arr,left,pivot-1);
quickSorts(arr,pivot+1,right);
}
public static void quickSort(int[] arr){
quickSorts(arr,0,arr.length-1);
}
九、快速排序(Hoare)
- 时间复杂度:O(logn)。
- 空间复杂度:O(logn)。
- 思想于挖坑法大致相同,此方法只是将每次的大于基准值与小于基准值同时交换。
public static int partition(int[] arr,int start,int end){
int i=start;
int j=end;
int tmp=arr[i];
while(i<j){
while(i<j&&arr[j]>=tmp){
j--;
}
while(i<j&&arr[i]<=tmp){
i++;
}
swap(arr,i,j);
}
swap(arr, start,i);
return i;
}
public static void quickSorts(int[] arr,int left,int right){
if(left>=right){
return;
}
int pivot=partition(arr,left,right);
quickSorts(arr,left,pivot-1);
quickSorts(arr,pivot+1,right);
}
public static void quickSort(int[] arr){
quickSorts(arr,0,arr.length-1);
}
十、快速排序(非递归)
- 时间复杂度:O(logn)。
- 空间复杂度:O(logn)。
- 非递归思想需要借助一个栈来记录基准值左右的下标。
- 代码运行流程与递归无太大差别。
public static int partition(int[] arr,int start,int end){
int tmp=arr[start];
while(start<end){
while(start<end&&arr[end]>=tmp){
end--;
}
arr[start]=arr[end];
while(start<end&&arr[start]<=tmp){
start++;
}
arr[end]=arr[start];
}
arr[start]=tmp;
return start;
}
public static void quickSort(int[] arr){
int left=0;
int right=arr.length-1;
Stack<Integer> stack=new Stack<>();
int pivot=partition(arr,left,right);
if(left+1<pivot){
stack.add(left);
stack.add(pivot-1);
}
if(right-1>pivot){
stack.add(pivot+1);
stack.add(right);
}
while(!stack.isEmpty()){
right=stack.pop();
left=stack.pop();
pivot=partition(arr,left,right);
if(left+1<pivot){
stack.add(left);
stack.add(pivot-1);
}
if(right-1>pivot){
stack.add(pivot+1);
stack.add(right);
}
}
}
十一、归并排序(递归)
- 时间复杂度:O(n logn)。
- 空间复杂度:O(n)。
- 把一个大问题拆分成一个一个的小问题,把每一个小问题都排序好,再组合。
public static void merge(int[] arr,int low,int mid,int high,int[] tmp){
int i = 0;
int j = low,k = mid+1;
while(j <= mid && k <= high){
if(arr[j] < arr[k]){
tmp[i++] = arr[j++];
}else{
tmp[i++] = arr[k++];
}
}
while(j <= mid){
tmp[i++] = arr[j++];
}
while(k <= high){
tmp[i++] = arr[k++];
}
for(int t=0;t<i;t++){
arr[low+t] = tmp[t];
}
}
public static void mergeSort(int[] arr,int low,int high,int[] tmp){
if(low<high){
int mid = (low+high)/2;
mergeSort(arr,low,mid,tmp);
mergeSort(arr,mid+1,high,tmp);
merge(arr,low,mid,high,tmp);
}
}
十二、归并排序(非递归)
- 时间复杂度:O(nlogn)。
- 空间复杂度:O(n)。
- 控制好边界就好。
public static void mergeSorts(int[] arr,int gap){
int s1=0;
int e1=s1+gap-1;
int s2=e1+1;
int e2=Math.min(arr.length-1,s2+gap-1);
int[] tmp=new int[arr.length];
int k=0;
while(s2<arr.length){
while(s1<=e1&&s2<=e2){
if(arr[s1]<=arr[s2]){
tmp[k++]=arr[s1++];
}else{
tmp[k++]=arr[s2++];
}
}
while(s1<=e1){
tmp[k++]=arr[s1++];
}
while(s2<=e2){
tmp[k++]=arr[s2++];
}
s1=e2+1;
e1=s1+gap-1;
s2=e1+1;
e2=Math.min(arr.length-1,s2+gap-1);
}
while(s1<=arr.length-1){
tmp[k++]=arr[s1++];
}
for(int i=0;i<k;i++){
arr[i]=tmp[i];
}
}
public static void mergeSort(int[] arr){
for(int gap=1;gap<arr.length;gap*=2){
mergeSorts(arr,gap);
}
}
|