(堆、快速、归并)排序总结及java实现

1.堆排序(Heapsort)

1.1算法描述
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。是一种不稳定的排序算法。
排序过程描述:
1.将待排序数组(R1,R2….Rn)构建成大根堆,此堆为初始的无序区
2.将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
3.由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)向下调整为新的大根堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
动图演示:
在这里插入图片描述
1.2算法实现

import java.util.Arrays;

public class HeapSort {
    public static void swap(int[] arr, int a, int b){
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

    public static void shiftDownBig(int[] arr, int length, int parent){// 向下调整
        int child = parent * 2 + 1;
        while(child < length ){
            if(child + 1 < length && arr[child + 1] > arr[child]){
                child++;
            }
            if(arr[child] > arr[parent]){
                swap(arr, child, parent);
                parent = child;
                child = parent * 2 + 1;
            }else{
                break;
            }
        }
    }

    public static void heapSort(int[] arr){//堆排序
        int length = arr.length;
        for(int i = (length - 2) / 2; i >= 0; i--){//建堆
            shiftDownBig(arr, length, i);
        }
        int end = length - 1;//最后一个下标
        while(end > 0){//一直调整到数组第一个
            swap(arr, 0, end);//交换堆顶和未排序范围最后一个(堆顶是最大的,这样就放在了未排序的最后一个,完成这个元素的排序)
            shiftDownBig(arr, end, 0);//将堆顶换上去的元素在未排序范围内向下调整
            end--;
        }
    }

    public static void main(String[] args) {
        int[] a = {24, 63, 15, 33, 51, 35, 76, 33};
        heapSort(a);
        System.out.println(Arrays.toString(a));
    }
}

1.3复杂度分析
最佳时间复杂度:O(nlogn)
最坏时间复杂度:O(nlogn)
平均时间复杂度:O(nlogn)
空间复杂度:O(1)

2.快速排序(Quicksort)

2.1算法描述
快速排序就是先找一个基准值,将待排序序列分为两部分,一部分中所有数字大于基准值,另一部分小于基准值,再对两部分分别进行排序,直到整个序列有序。是一种不稳定的排序算法。
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。
具体算法过程如下:
1.从数列中挑出一个元素,称为 “基准”(pivot);

2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
分区具体过程:

  1. 首先选择此分区最左边的元素(也可三数取中法)作为基准值,记录此基准值的位置
  2. 然后从此分区最右边开始遍历找到第一个比基准值小的元素,再从左边找第一个大于基准值的元素,交换两个元素
  3. 再次从刚才的右边位置向左找第一个比基准值小的元素,再从刚才的左边位置找第一个大于基准值的元素,交换两个元素
  4. 重复步骤4,直到左右相遇
  5. 将基准值位置(最左边开始的位置)与相遇点元素交换

这样就将比基准值小全部划分到基准值左边分区,比基准值大的全部划分到基准值右边分区

3.递归地(recursive)把分出的两个分区再次进行排序。

2.2算法实现
1)递归实现:

import java.util.Arrays;

public class QuickSort {
    public static void swap(int[] arr, int a, int b){
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
    //快速排序一次排序过程
    public static int quick(int[] arr, int left, int right){
        //保留基准值
        int value = arr[left];
        //基准值所在下表
        int start = left;
        //只要左指针和右指针没有相遇进行循环
        while(left < right){
            //从右向左找第一个小于基准值的位置(这里也必须保证left < right),
            // 注意这里必须先从右边找第一个小于基准值的数,如果先从左边找的话会产生错误
            while(left < right && arr[right] >= value){
                right--;
            }
            //从左到右找第一个大于基准值的数位置
            while(left < right && arr[left] <= value){
                left++;
            }
            //找到之后,交换两个数
            swap(arr, left, right);
        }
        //遍历完整个数组后,交换基准值和两个指针相遇位置的值
        swap(arr, start, left);
        return left;
    }
    //快速排序
    public static void quickSort(int[] arr, int left, int right){
        if(left < right){
            //取出排序相遇位置的坐标
            int mid = quick(arr, left, right);
            //从上次相遇位置进行分组,对左右组分别进行快速排序
            quickSort(arr, left, mid - 1);
            quickSort(arr, mid + 1, right);
        }
    }

    public static void main(String[] args) {
        int[] a = {24, 63, 15, 33, 51, 35, 76, 33};
        quickSort(a, 0, a.length - 1);
        System.out.println(Arrays.toString(a));
    }
}

2)非递归实现:

//快速排序(非递归实现)
import java.util.Arrays;
import java.util.Stack;

public class QuickSort {
    public static void swap(int[] arr, int left, int right){
        int temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
    }
    //双指针进行一次排序过程
    public static int quick(int[] arr, int left, int right){
        int value = arr[left];
        int prev = left;//慢指针
        int cur = left + 1;//快指针
        while(cur <= right){
            if(arr[cur] < value && prev++ != cur){//找到小于基准值的位置,如果prev走向下一个位置不是cur的位置,说明此时prev指向的值大于基准值
                swap(arr, prev, cur);
            }
            cur++;
        }
        swap(arr, left, prev);
        return prev;
    }
    //非递归快速排序
    public static int[] quickSort(int[] arr, int left, int right){
        Stack<Integer> stack = new Stack<>();
        if(left < right){//先将数组左右下标压入栈中(注意这里你的压栈顺序,等会取出时要先取后压入的)
            stack.push(left);//先压左
            stack.push(right);//右
        }
        while(!stack.isEmpty()){
            //每次取出两个元素,作为要排序区间的左右范围
            int right1 = stack.pop();//先取右
            int left1 = stack.pop();//左
            int mid = quick(arr, left1, right1);//对当前区间进行排序,并记录中间位置的下标
            if(mid - 1 > left1){//如果中间位置向左的区间元素数量大于1,将左区间的左右下标压入栈
                stack.push(left1);//先压左
                stack.push(mid - 1);//右
            }
            if(mid + 1 < right1){//同理
                stack.push(mid + 1);
                stack.push(right1);
            }
        }
        return arr;
    }

    public static void main(String[] args) {
        int[] a = {24, 63, 15, 33, 51, 35, 76, 33};
        System.out.println(Arrays.toString(quickSort(a, 0, a.length - 1)));
    }
}

2.3复杂度分析
最佳时间复杂度:O(nlogn)
最坏时间复杂度:O(n^2)
平均时间复杂度:O(nlogn)
最佳空间复杂度:O(logn)
最坏空间复杂度:O(n)
平均空间复杂度:O(logn)

3.归并排序

3.1算法描述
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并排序是一种稳定的排序算法
具体算法过程如下:
整个过程分为划分和归并两个过程
划分过程:
1.把长度为n的输入序列分成两个长度为n/2的子序列;
2.把长度为n/2的输入序列分成两个长度为n/4的子序列;
3.直到将输入序列划分成一个个长度为1的子序列
归并过程:
1.将两个长度为1的子序列进行归并(也就是将长度为1的子序列合并成长度为2的子序列,合并时排序)
2.接着在上一步的基础上归并长度为2的数组,直到全部归并完毕
动图演示:
在这里插入图片描述
3.2算法实现
1)递归实现

//归并排序实现
import java.util.Arrays;

public class MergeSort {
    //合并一次数组
    public static void merge(int[] arr, int left, int mid, int right, int[] temp){
        int start1 = left, end1 = mid;//合并的一个数组的左右区间
        int start2 = mid + 1, end2 = right;//合并的另一个数组的左右区间
        int index = left;//记录合并数组最开始的下标
        while(start1 <= mid && start2 <= end2){//合并
            if(arr[start1] <= arr[start2]){//先存小的
                temp[index++] = arr[start1++];
            }else{
                temp[index++] = arr[start2++];
            }
        }
        //判断是否还有剩余元素,如果有将剩余元素添加
        while(start1 <= end1){
            temp[index++] = arr[start1++];
        }
        while(start2 <= end2){
            temp[index++] = arr[start2++];
        }
        for(int i = left; i <= right; i++){//拷贝
            arr[i] = temp[i];
        }
    }

    //进行递归归并排序
    public static void mSort(int[] arr, int left, int right, int[] temp){
        if(left >= right){
            return;
        }
        int mid = (left + right) / 2;//中间坐标
        mSort(arr, left, mid, temp);//左边数组进行排序
        mSort(arr, mid + 1, right, temp);//右边数组进行排序
        merge(arr, left, mid, right, temp);//进行合并
    }
    //归并排序
    public static int[] mergeSort(int[] arr){
        int[] temp = new int[arr.length];
        mSort(arr, 0, arr.length - 1, temp);
        return arr;
    }

    public static void main(String[] args) {
        int[] a = {24, 63, 15, 33, 51, 35, 76, 33};
        System.out.println(Arrays.toString(mergeSort(a)));
    }
}

2)非递归实现

import java.util.Arrays;

public class MergeSort {
    //合并一次数组
    public static void merge(int[] arr, int left, int mid, int right, int[] temp){
        int start1 = left, end1 = mid;//合并的一个数组的左右区间
        int start2 = mid + 1, end2 = right;//合并的另一个数组的左右区间
        int index = left;//记录合并数组最开始的下标
        while(start1 <= mid && start2 <= end2){//合并
            if(arr[start1] <= arr[start2]){//先存小的
                temp[index++] = arr[start1++];
            }else{
                temp[index++] = arr[start2++];
            }
        }
        //判断是否还有剩余元素,如果有将剩余元素添加
        while(start1 <= end1){
            temp[index++] = arr[start1++];
        }
        while(start2 <= end2){
            temp[index++] = arr[start2++];
        }
        for(int i = left; i <= right; i++){//拷贝
            arr[i] = temp[i];
        }
    }
   
   public static int[] mergeSort(int[] arr){
       int[] temp = new int[arr.length];
       for(int i = 1; i < arr.length; i *= 2){//外层循环i是每次归并的元素个数
           for(int j = 0; j < arr.length; j += i * 2){//内层循环j是下次归并的起始位置下标
               int left = j;
               int mid = left + i - 1;
               if(mid >= arr.length - 1){//如果右半部分没有数据,不用进行归并
                   continue;
               }
               int right = left + 2 * i - 1;
               if(right > arr.length - 1){//如果右区间下标越界
                   right = arr.length - 1;
               }
               merge(arr, left, mid, right, temp);
           }
       }
       return arr;
   }


    public static void main(String[] args) {
        int[] a = {24, 63, 15, 33, 51, 35, 76, 33};
        System.out.println(Arrays.toString(mergeSort(a)));
    }
}

3.3复杂度分析
最佳时间复杂度:O(nlogn)
最坏时间复杂度:O(nlogn)
平均时间复杂度:O(nlogn)
空间复杂度:O(n)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值