java 基本排序

/**
 * 快排概念:
 * 快排也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的;在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,
 * 比它小的元素移动到数组的另一边,从而将数组拆成两个部分,这种思路叫做分治法
 *
 * 时间复杂度:
 * 原数组在分支法的思想下,没一轮都会被拆分为两部分,所以遍历的轮数为logn,每一轮的比较和交换需要把数组全部遍历一遍,
 * 这个时间复杂度是O(n),所以总的平均时间复杂度是O(nlogn)
 * 但是如果将原本逆序的数列排序成顺序数列,整个数列并没有被分成两半,每一轮都只确定来基准元素的位置,数列的第一个元素要么是最大值要么是最小值,
 * 无法发挥分支法的优势,这个时候快速排序需要进行n轮,时间复杂度退化为O(n平方)。
 * 解决思路是:随机选择一个元素作为基准元素
 *
 */
/**
     * 递归的方式
     * @param arr
     * @param startIndex
     * @param endIndex
     */
    public static void quickSort(int[] arr,int startIndex,int endIndex){
        if(arr == null || startIndex >= endIndex){
            return;
        }
        //得到基准元素位置
        int pivotIndex = partition(arr,startIndex,endIndex);
        //根据基准元素位置分成两部分进行递归
        quickSort(arr,startIndex,pivotIndex - 1);
        quickSort(arr,pivotIndex + 1,endIndex);
    }

    /**
     * 循环方式
     * 引入一个存储Map类型元素的栈,用于存储每一次交换时的起始下标和结束下标
     * 每一次循环,都会让栈顶元素出栈,通过partition方法方法进行分支,并且按照基准元素的位置分成左右两部分,左右两部分再分别入栈。
     * 当栈为空时,说明排序已经完毕,推出循环
     */
    public static void quickSort2(int[] arr,int startIndex,int endIndex){
        Stack<Map<String,Integer>> stack = new Stack<>();
        Map<String,Integer> root = new HashMap<>();
        root.put("startIndex",startIndex);
        root.put("endIndex",endIndex);
        stack.push(root);
        while (!stack.isEmpty()){
            Map<String,Integer> item = stack.pop();
            int pivotIndex = partition(arr,item.get("startIndex"),item.get("endIndex"));
            if(item.get("startIndex")<pivotIndex - 1){
                Map<String,Integer> left = new HashMap<>();
                left.put("startIndex",item.get("startIndex"));
                left.put("endIndex",pivotIndex - 1);
                stack.push(left);
            }
            if(pivotIndex + 1 < item.get("endIndex")){
                Map<String,Integer> right = new HashMap<>();
                right.put("startIndex",pivotIndex + 1);
                right.put("endIndex",item.get("endIndex"));
                stack.push(right);
            }
        }
    }

    /**
     * 选择基准元素位置:(单边循环法)
     * 首先选定基准元素 pivot,同时设置一个mark指针指向数列起始位置,这个mark指针代表小于基准元素的区域边界
     * 接下来从基准元素的下一个位置开始遍历数组
     * 如果遍历到的元素大于基准元素,则继续往下遍历
     * 如果遍历到的元素小于基准元素,需要做两件事情。
     * 第一:将mark元素向右移动1位,因为小于pivot基准元素的区域边界增大了1
     * 第二:将遍历到的最新的元素和mark位置指针所在的元素互换位置,因为最新遍历的元素归属于小于pivot的区域
     * @param arr
     * @param startIndex
     * @param endIndex
     * @return
     */
    public static int partition(int[] arr,int startIndex,int endIndex){
        int pivot = arr[startIndex];
        int mark = startIndex;
        for(int i = startIndex + 1;i <= endIndex ;i++){
            if(arr[i]<pivot){
                mark ++;
                int temp = arr[mark];
                arr[mark] = arr[i];
                arr[i] = temp;
            }
        }
        arr[startIndex] = arr[mark];
        arr[mark] = pivot;
        return mark;
    }



/**
 * 归并排序
 * 分治、递归思想
 * 要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分进行排序,然后将排序好的两部分合并
 * 最好、最坏、平均时间复杂度都为 O(nlogn),需要空间复杂度为O(n),是稳定的非原地算法
 * @param data
 * @param p
 * @param r
 */
public static void mergeSort(int[] data,int p,int r){
    //终止条件
    if(p>=r) return;
    //防止 p和r过大,(p+r)溢出
    int q = p +(r- p)/2;
    //分治递归
    mergeSort(data,p,q);
    mergeSort(data,q + 1,r);
    // 将A[p...q]和A[q+1...r]合并为A[p...r]
    merge(data,p,q,r);
}

private static void merge(int[] data,int p,int q,int r){
    int i= p;
    int j = q +1;
    int k = 0;
    int[] temp = new int[r-p + 1];
    while (i<=q && j<=r){
        if(data[i]<=data[j]){
            temp[k++] = data[i++];
        }else {
            temp[k++] = data[j++];
        }
    }
    // 判断哪个子数组中有剩余的数据
    int start = i;
    int end = q;
    if(j<=r){
        start = j;
        end = r;
    }
    // 将剩余的数据拷贝到临时数组tmp
    while (start <= end){
        temp[k++]=data[start ++];
    }
    //将临时变量拷贝回a[p..r]
    for (i=0;i<=r-p;i++){
        data[p+i] = temp[i];
    }
}

/**
 * 冒泡排序,基本优化
 * 冒泡排序思想:把相邻两个元素两两比较,当一个元素大于右侧相邻元素时,交换它们的位置,当一个元素小于或等于右侧相邻元素时,位置不变。一次冒泡会让至少一个元素移动到它应该在的位置,重复n次,就完成了n个数据的排序工作
 * 为原地稳定算法,时间复杂度为n²,最好时间复杂度为n,平均和最坏情况为n²
 * 采用双循环进行排序,外部循环控制所有回合,内部循环实现每一轮的冒泡处理,先进行元素比较,再进行元素交换
 *
 * @param arr
 */
public static void bubbleSort(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        //有序标记,每一轮的初始致都为true
        boolean isSorted = true;
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                //因为有元素交换,所以是无序的
                isSorted = false;
            }
        }
        if (isSorted) {
            break;
        }
    }
}

/**
 * 冒泡排序优化
 * sortBorder是无序数列的边界,在每一轮排序过程中,处于sortBorder之后的元素不需要进行比较了,肯定是有序的
 * @param arr
 */
public static void bubbleSort2(int[] arr) {
    //记录最后一次交换的位置
    int lastExchangeIndex = 0;
    //无序数列的边界,每次比较只需要比较到此为止
    int sortBorder = arr.length - 1;
    for (int i = 0; i < arr.length; i++) {
        //有序标记,每一轮的初始致都为true
        boolean isSorted = true;
        for (int j = 0; j < sortBorder; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                //因为有元素交换,所以是无序的
                isSorted = false;
                //更新为最后一次交换元素的位置
                lastExchangeIndex = j;
            }
        }
        sortBorder = lastExchangeIndex;
        if (isSorted) {
            break;
        }
    }
}
/**
 * 插入排序
 * 首先我们将数组中数据分为两个区间,已排序和未排序区间。初始化时已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序算法中的元素,在已排序算法中找到合适的插入位置将其插入
 * 原地稳定算法,时间复杂度为O(n2),但它赋值次数比冒泡的少(插入赋值一次,冒泡3次),所以效率比冒泡的高
 * @param data
 */
public static void insertSort(int[] data){
    if(data == null || data.length <= 1) return;
    for (int i=1;i<data.length;i++){
        int val = data[i];
        int j = i - 1;
        //查找插入的位置
        for(; j>=0 ;j--){
            if(data[j]>val){
                //数据移动
                data[j+1] = data[j];
            }else {
                break;
            }
        }
        //找到位置插入
        data [j + 1] = val;
    }
}

public static void main(String[] args) {
    int[] data ={6,11,3,9,8};
    quickSort(data);
    System.out.println("快排结果为"+ Arrays.toString(data));
    data =new int[]{6,11,3,9,8};
    mergeSort(data,0,data.length-1);
    System.out.println("归并排序结果为"+ Arrays.toString(data));
    data =new int[]{6,11,3,9,8};
    bubbleSort(data);
    System.out.println("冒泡排序结果为"+ Arrays.toString(data));
    data =new int[]{6,11,3,9,8};
    insertSort(data);
    System.out.println("插入排序结果为"+ Arrays.toString(data));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值