堆排的思路及实现代码
- 建立大根堆,使得堆顶的元素,也就是数组首元素为当前数组最大值
- 交换最大值到队尾,并且endIndex--
- 将剩余的n-1个元素重新建立大根堆,主要是对刚刚交换到首元素位置的元素进行下沉操作
- 重复执行2,3操作,直到endIndex下标指向首元素
java
public int[] heapSort(int[] arr) {//堆排序
createBigHeap(arr);
int endIndex=arr.length-1;
while(endIndex>=0){
swap(arr,0,endIndex);
siftDown(arr,0,endIndex);
endIndex--;
}
return arr;
}
public static void createBigHeap(int[] arr){
for(int parent=(arr.length-2)/2;parent>=0;parent--){
siftDown(arr,parent,arr.length);
}
}
public static void siftDown(int[]arr,int parent,int end){
int child=parent*2+1;
while(child<end){
if(child+1<end&&arr[child]<arr[child+1]){
//这里的end条件需要用上,不能用arr.length,因为在堆排方法里,需要用到
child++;
}
if(arr[parent]<arr[child]){
swap(arr,parent,child);
parent=child;
child=parent*2+1;
}else{
break;
}
}
}
注:这里shiftDown方法是向下操作,将给定 的parent向下比较,所以一轮交换之后还需要,将被交换的子节点设置为parsent,再次向下比较
快排的思路及代码实现
- 使用hoare/挖坑法选择基准值,将数组分为两部分,左序列都小于基准值,右序列都大于基准值
- 然后左右序列都分别重复1操作,直到序列长度为1时递归结束
- hoare思路:将传过来的数组首元素保存为基准值,right指针后面向前开始比较找到小于基准值停止,再left指针从前向后找大于基准值的数,两个数都找到就交换,完成后继续开始查找,直到两个找元素下表相遇,就将基准值和right left相遇的值交换
- 挖坑法思路: 和hoare的思路差不多少,不同的是right找到小于基准值下标后,直接将值给left,同样left找到之后同样将值给right,最后两个下标相遇之后就将基准值给相遇的下标
java
public int[] quickSort(int[]arr){//hoare/挖坑
quick(arr,0,arr.length);
return arr;
}
public void quick(int[]arr,int start,int end){
if (start>end){
return;
}
int index=partition(arr,start,end);
quick(arr,start,index-1);
quick(arr,index+1,end);
}
public int partition(int[]arr,int left,int right){//hoare法
int i=left;
int pivot=arr[left];
while (left<right){
while (left<right&&arr[right]>=pivot){
right--;
}
while (left<right&&arr[left]<=pivot){
left++;
}
swap(arr,left,right);
}
swap(arr,i,left);
return left;
}
public int partition2(int[]arr,int left,int right) {//挖坑法
int i=left;
int pivot=arr[left];
while (left<right){
while (left<right&&arr[right]>=pivot){
right--;
}
arr[left]=arr[right];//找到直接覆盖到另一个下标
while (left<right&&arr[left]<=pivot){
left++;
}
arr[right]=arr[left];//找到直接覆盖到另一个下标
}
arr[left]=pivot;
return left;
}
优化思路:
- 在最理想的情况下,每次都可以将通过partition方法找到最中间的数返回给index,在通过递归分为两个子数组,可以写一个三数取中法,将排序序列的首、尾和中间三个位置的元素,然后取这三个元素中的中位数作为基准值。这样做的好处是,即使输入序列已经部分有序,也能够得到一个相对较好的划分,从而减少递归的深度和不平衡的可能性,进而提升算法的性能。
- 在递归到数组总长小于10(经验推断值),采用插入排序法方法因为快速排序在处理小数据集时,由于递归调用和划分操作带来的开销,不如插入排序这样的简单排序算法效率高。
优化后的快排
java
public static void quickSort(int[] arr,int start,int end){
if(start>=end){
return;
}
if(start-end-1==10){
insertSort(arr,start,end);
return;
}
int Index=midTreeNum(arr,start,end);
swap(arr,start,Index);
int par=partition(arr,start,end);
quickSort(arr,start,par-1);
quickSort(arr,par+1,end);
}
public static int partition(int[] arr,int left,int right){
int i=left;
int pivot=arr[left];
while(left<right){
while(left<right&&arr[right]>=pivot){
right--;
}
while(left<right&&arr[left]<=pivot){
left++;
}
swap(arr,left,right);
}
swap(arr,i,left);
return left;
}
public static void insertSort(int[] arr,int left,int right) {//插排
for(int i=left;i<right;i++){
int tmp=arr[i];
int j=i-1;
for(;j>=right;j--){
if(arr[j]>tmp){
arr[j+1]=arr[j];
}else{
break;
}
}
arr[j+1]=tmp;
}
}
public static int midTreeNum(int[]arr,int left,int right){
int mid=(left+right)/2;
if(arr[left]<arr[right]){
if(arr[mid]<arr[left]){
return left;
}else if(arr[mid]>arr[right]){
return right;
}else{
return mid;
}
}else{
if(arr[mid]<arr[right]){
return right;
}else if(arr[mid]>arr[left]){
return left;
}else{
return mid;
}
}
}
归并排序思路及代码实现
- 将一个待排序的数组不断地二分,直到每个子数组只剩下一个元素为止。
- 然后将这些子数组视为已排序的数组,并依次将相邻的两个子数组用merge方法合并成一个有序的数组,直到最终合并成一个完整的有序数组。
merage实现思路:需要创建一个临时数组来存放合并后的序列。合并时,通过比较两个子数组中的元素,将较小的元素依次放入临时数组中,直到其中一个子数组的所有元素都放入临时数组。然后将另一个子数组中剩余的元素直接放入临时数组的末尾。最后,将临时数组中的元素复制回原始数组,完成一次合并操作。
java
public int[] Sort(int[]arr){
mergeSort(arr,0,arr.length-1);
return arr;
}
public void mergeSort(int[]arr,int start,int end){
if(start>=end){
return;
}
int midIndex=(end+start-1)/2;//注意是加
mergeSort(arr,start,midIndex);
mergeSort(arr,midIndex+1,end);
merge(arr,start,midIndex,end);
}
public void merge(int[]arr,int start,int midIndex,int end){
int s1=start;
int e1=midIndex;
int s2=midIndex+1;
int e2=end;
int k=0;
int[]tmp=new int[end-start+1];
while (s1<=e1&&s2<=e2){
if(arr[s1]<=arr[s2]){
tmp[k++]=arr[s1++];
}
if(arr[s1]>=arr[s2]){
tmp[k++]=arr[s2++];
}
}
while (s1<=e1){
tmp[k++]=arr[s1++];
}
while (s2<=e2){
tmp[k++]=arr[s2++];
}
for(int i=0;i<k;i++){
//arr[start++]=tmp[i];
arr[i+start]=tmp[i];
}
}