java数据结构:排序

稳定性:

内部排序:数据元素全部放在内存中的排序

外部排序:由于数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序

排序的运用:

1.商品价格

2.学校名次

常见的排序:

1.插入排序:将新的记录按照大小插入到已经排好的序列之中。

   1)直接插入排序

    /**
     *直接插入排序
     * 时间复杂度:
     *     最好情况:数据完全有序的时候:1,2,3,4,5  O(N)
     *     最坏情况:数据完全逆序的时候:5,4,3,2,1 O(N^2)
     * 结论:当所给数据    越有序   排序越快
     *
     * 空间复杂度:O(1)
     * 稳定性:稳定
     *      一个本身稳定的排序,可以变为不稳定排序
     *      本身稳定的排序,不能变成稳定的排序
     *
     */
    public static void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int tmp = array[i];
            int j = i-1;//每一次都让j等于i后面的那一个
            for (;j>=0;j--) {
                if(array[j] > tmp) {
                    array[j+1] = array[j];
                }else {
                    break;//不用再j--了
                }
            }
            array[j+1] = tmp;//前面的j--
        }
    }
   //顺序数组
    public static void orderArray(int[] array) {
        for (int i = 0; i < array.length; i++) {
            array[i] = i;
        }
    }

    //逆序数组
    public static void notOrderArray(int[] array) {
        for (int i = 0; i < array.length; i++) {
            array[i] = array.length-i;
        }
    }

    //随机数组
    public static void notOrderArrayRandom(int[] array) {
        Random random = new Random();
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(10_0000);//100000以内
        }
    }

    //对直接插入排序进行测试
    public static void testInsertSort(int[] array) {
        int[] tmpArray = Arrays.copyOf(array,array.length);//拷贝一份,不对原数组进行操作
        long startTime = System.currentTimeMillis();//开始的时间
        Sort.insertSort(array);
        long endTime = System.currentTimeMillis();//结束的时间
        System.out.println("直接插入排序耗时:" + (endTime-startTime));
    }


    //直接插入排序
    public static void main1(String[] args) {
//        int[] array = {10,5,2,1,4};
//        Sort.insertSort(array);
//        System.out.println(Arrays.toString(array));

        //测试
        int[] array = new int[10_0000];
        orderArray(array);
        notOrderArray(array);
        notOrderArrayRandom(array);
        testInsertSort(array);
    }

2)希尔排序:对直接插入排序的优化

    /**
     * 希尔排序
     * 时间复杂度:n^1.3 - n^1.4   不固定
     * 空间复杂度:O(1)
     * 稳定性:不稳定
     */
    public static void shellSort(int[] array) {
        int gap = array.length;
        while(gap > 1) {
            gap = gap/2;
            shell(gap,array);
        }
    }

    private static void shell(int gap, int[] array) {
        for (int i = gap; i < array.length; i++) {
            //跟直接插入排序差不多
            int j = i-gap;
            int tmp = array[i];
            for (; j>=0; j-=gap) {//j是以gap个单位相减
                if(array[j] > tmp) {
                    array[j+gap] = array[j];//将大的放在了i的位置
                }else{
                    break;
                }
            }
            array[j+gap] = tmp;
        }
    }

2.选择排序:每一次从待排序的数据中选出最小(最大)的元素,放在序列的起始位置。

   1)直接选择排序

         方法一:

   /**
     * 直接选择排序
     * 时间复杂度: O(N^2)
     * 空间复杂度: O(1)
     * 稳定性: 不稳定
     */
    public static void selectSort(int[] array) {
        for (int i = 0; i < array.length; i++) {
            int minIndex = i;
            for (int j = i+1; j < array.length; j++) {
                if(array[j] < array[minIndex]) {
                    minIndex = j;//更新
                }
            }
            swap(array,minIndex,i);
        }
    }
    private static void swap(int[] array, int j,int i) {
        int tmp = array[j];
        array[j] = array[i];
        array[i] = tmp;
    }

          方法二:

  public static void selectSort2(int[] array) {
        int left = 0;
        int right = array.length-1;
        while(left < right) {
            int minIndex = left;
            int maxIndex = left;
            for (int i = left+1; i <= right ; i++) {
                if(array[i] < array[minIndex]) {
                    minIndex = i;
                }
                if(array[i] > array[maxIndex]) {
                    maxIndex = i;
                }
            }
            swap(array,minIndex,left);//将最小的放在left位置
            //当left存放的是最大值时,需进行以下if判断
            if(maxIndex == left) {
                maxIndex = minIndex;
            }
            swap(array,right,maxIndex);//将最大的放在right位置
            left++;//此时left放最小,right放最大,区间缩小
            right--;
        }
    }

      2)堆排序

  /**
     * 堆排序
     * 时间复杂度:O(N*logN)
     * 空间复杂度:O(1)
     * 稳定性:不稳定
     *      数据量非常大的时候   堆排一定比希尔快
     */

    public static void heapSort(int[] array) {
        //创建一个大根堆
        createBigHeap(array);
        int end = array.length-1;
        while(end > 0) {
            swap(array,end,0);//将栈顶元素与最后一个元素交换
            siftDown(array,0,end);//向下调整为大根堆
            end--;//最大值已经放在最后了,调整前面的就可
        }
    }
    private static void createBigHeap(int[] array) {
        for (int parent = (array.length-1-1)/2; parent >= 0 ; parent--) {
            siftDown(array,parent,array.length);//向下调整
        }
    }

    private static void siftDown(int[] array,int parent,int end) {
        int child = 2*parent+1;
        while (child < end) {
            if(child+1 < end && array[child] < array[child+1]) {
                child++;
            }
            if(array[child] > array[parent]) {
                swap(array,parent,child);
                parent = child;//想下调整
                child = 2*parent+1;
            }else {
                break;
            }
        }
    }

交换排序:

1)冒泡排序:

    /**
     * 冒泡排序
     * 时间复杂度:O(N^2)   优化之后   最好情况下 O(N)
     * 空间复杂度:O(1)
     * 稳定性:稳定的排序
     */

    public static void bubbleSort(int[] array) {
        //总共有多少趟,比如五个元素要四趟
        for (int i = 0; i < array.length-1; i++) {
            //每一趟比较的次数
            boolean flg = false;//进行优化
            for (int j = 0; j < array.length-1-i; j++) {
                if(array[j] > array[j+1]) {
                    swap(array,j,j+1);
                    flg = true;//只要进行了交换,flg就变为true
                }
            }
            if(!flg) {//flg==false,没有进行交换,那么结束这个循环
                return;
            }
        }
    }

      

2)   快速排序:

  /**
     * 快速排序
     * 时间复杂度:
     *        最好的情况下:O(N*logN)  满二叉树/完全二叉树
     *        最坏的情况下:O(N^2)   单分支的树
     * 空间复杂度:
     *        最好的:O(logN)  层数   满二叉树/完全二叉树
     *        最坏的: O(N) 层数 单分支的树
     * 稳定性:不稳定
     */

    public static void quickSort(int[] array) {
        quick(array,0,array.length-1);
    }
    private static void quick(int[] array, int start, int end) {
        if(start >= end) return; //左边是一个节点或左边没有一个节点
        int pivot = partition(array,start,end);
        quick(array,start,pivot-1);//递归左子树
        quick(array,pivot+1,end);//递归右子树
    }

    private static int partition(int[] array, int left, int right) {
        int i = left;
        int key = array[left];//将第一个元素设为比较的对象
        while (left < right) {
            //为什么不是left先动?举例说明达不到效果,刚交换的在最后又会被交换走
            while(left < right && array[right] >= key) {//防止数组越界,right--会越界
                right--;//找到比key小的才停止
            }
            while(left < right && array[left] <= key) {
                left++;//找到比key大的才停止
            }
            swap(array,left,right);
        }
        swap(array,i,left);//left与right相遇之后,将key与相遇点交换(也就是基准点)
        return left;
    }

快排的挖坑法:

//挖坑法:
    private static int waKenPartition(int[] array, int left, int right) {
        int tmp = array[left];//坑位里填的是第一个left元素
        while (left < right) {
            if(left < right && array[right] >= tmp) {
                right--;
            }
            array[left] = array[right];//right停下,将小于tmp的值放到前面去
            if(left < right && array[left] <= tmp) {
                left++;
            }
            array[right] = array[left];//left停下,将大于tmp的值放到后面去
        }
        array[left] = tmp;//基准
        return left;
    }

快排的前后指针法:

    //前后指针法:
    private static int zhiZhenPartition(int[] array, int left, int right) {
        int prev = left;
        int cur = left+1;
        int tmp = array[left];
        while(cur <= right) {//取等号因为right是length-1
            if(array[cur] < tmp && array[++prev] != array[cur]) {//prev先++
                swap(array,cur,prev);
            }
            cur++;
        }
        swap(array,prev,left);
        return prev;
    }

快排的优化:

 private static void quickA(int[] array, int start, int end) {
        if(start >= end) return; //左边是一个节点或左边没有一个节点

        //再优化:小区间使用插入排序,越有序越快
        if( (end-start) <= 7) {
            insertSortRange(array,start,end);
            return;
        }


        int midIndex = midTree(array,start,end);//三数取中
        swap(array,midIndex,start);//start存放着基准的值
        int pivot = partition(array,start,end);
        quick(array,start,pivot-1);//递归左子树
        quick(array,pivot+1,end);//递归右子树
    }

    private static void insertSortRange(int[] array, int start, int end) {
        for (int i = start; i <= end; i++) {
            int tmp = array[i];
            int j = i-1;//每一次都让j等于i后面的那一个
            for (;j>=start;j--) {
                if(array[j] > tmp) {
                    array[j+1] = array[j];
                }else {
                    break;//不用再j--了
                }
            }
            array[j+1] = tmp;//前面的j--
        }
    }

    //快排的优化:优化空间,不一定优化时间,递归次数减少,改变树的高度,将单分支树变成满二叉树或完全二叉树
    private static int midTree(int[] array, int left, int right) {//返回三个数值位于中间的那个值的下标
        int mid = (left + right)/2;
        if(array[left] < array[right]) {//left > right
            if(array[mid] < array[left]) {
                return left;
            }else if(array[mid] > array[right]) {
                return right;
            }else {
                return mid;
            }
        }else {
            if(array[mid] > array[left]) {
                return left;
            }else if(array[mid] < array[right]) {
                return right;
            }else {
                return mid;
            }
        }
    }

快排的非递归实现:

 //快排的非递归实现:
    public static void quickNor(int[] array) {
        Stack<Integer> stack = new Stack<>();
        int left = 0;
        int right = array.length-1;
        int pivot = partition(array,left,right);
        if(pivot-1 > left) {//左边不止一个节点
            stack.push(left);//新的left
            stack.push(pivot-1);//新的right
        }
        if(pivot+1 < right) {//右边不止一个节点
            stack.push(pivot+1);//新的left
            stack.push(right);//新的right
        }

        while (!stack.isEmpty()) {
            right = stack.pop();
            left = stack.pop();
            pivot = partition(array,left,right);
            if(pivot-1 > left) {
                stack.push(left);
                stack.push(pivot-1);
            }
            if(pivot+1 < right) {
                stack.push(pivot+1);
                stack.push(right);
            }
        }
    }

归并排序:

 /**
     * 归并排序:
     * 时间复杂度: O(N*logN)
     * 空间复杂度: O(N)
     * 稳定性:稳定 -> 插入,归并,冒泡
     */

    public static void mergeSort(int[] array) {
        mergeSortFunc(array,0,array.length-1);
    }
    //拆分
    private static void mergeSortFunc(int[] array, int left, int right) {
        if(left >= right) return;//左边只剩一个或不剩
        int mid = (right+left)/2;
        mergeSortFunc(array,left,mid);//左子树
        mergeSortFunc(array,mid+1,right);//右子树
        merge(array,left,right,mid);
    }

    //合并
    private static void merge(int[] array,int left, int right, int mid) {
        int s1 = left;
        int s2 = mid+1;
        //用一个大小合适的数组来装
        int[] tmp = new int[right-left+1];
        int k = 0;
        while(s1 <= mid && s2 <= right) {
            if(array[s2] <= array[s1]) {
//                tmp[k] = array[s2];
//                k++;
//                s2++;
                tmp[k++] = array[s2++];
            }else {
//                tmp[k] = array[s1];
//                k++;
//                s1++;
                tmp[k++] = array[s1++];
            }
        }
        //s2 > right
        while (s1 <= mid) {
//            tmp[k] = array[s1];
//            k++;
//            s1++;
            tmp[k++] = array[s1++];
        }
        //s1 > mid
        while(s2 <= right) {
//            tmp[k] = array[s2];
//            k++;
//            s2++;
            tmp[k++] = array[s2++];
        }
        //最后将2个tmpArray合并,因为下标都是从0开始,但右边那个数组下标本身是mid+1
        for (int i = 0; i < tmp.length; i++) {
            array[i+left] = tmp[i];
        }
    }

归并的非递归实现:

//归并排序的非递归实现
    public static void mergeSortNor(int[] array) {
        int gap = 1;
        while(gap < array.length) {
            for (int i = 0; i < array.length; i+=2*gap) {
                int left = i;
                int mid = left + gap - 1;
                int right = mid + gap;
                if (mid >= array.length) {
                    mid = array.length - 1;
                }
                if (right >= array.length) {
                    right = array.length - 1;
                }
                merge(array,left,right,mid);
            }
            gap *= 2;
        }
    }

基数排序:

桶排序:https://blog.csdn.net/qq_27124771/article/details/87651495

计数排序:

    //计数排序:与给定范围有关
    //时间复杂度:O(N + 范围)
    //空间复杂度:O(范围)
    //稳定性:不稳定
    public static void countSort(int[] array) {
        int minval = array[0];
        int maxval = array[0];

        //求当前数组的最大值和最小值
        for (int i = 1; i < array.length; i++) {
            if(array[i] < minval) {
                minval = array[i];
            }
            if(array[i] > maxval) {
                maxval = array[i];
            }
        }

        //计数数组
        int[] count = new int[maxval-minval+1];

        //遍历原来的数组
        for (int i = 0; i < array.length; i++) {
            count[array[i]-minval]++;
        }

        //将计数数组写回array
        int index = 0;//表示array的下标
        for (int i = 0; i < count.length; i++) {
            while(count[i] > 0) {
                array[index] = i + minval;
                index++;
                count[i]--;
            }
        }
    }

这个是稳定的:

排序相关的练习题:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值