[Daily Practice] -数组排序

1.非比较排序

先找到数组中的最大值和最小值,确定最大值和最小值的差值为n,新建一个长度为n+1的数组,
新建的这个数组用来存放每个值出现的次数,最后按顺序添加回原来的数组。
例: [3,7,4,1,3]
最大值为7,最小值为1
新建数组长度为7-1=6的数组[0,0,0,0,0,0,0]
循环数组后可得:[1,0,2,1,0,0,1]
对原数组重新赋值时,按顺序添加,为0时不添加,即:1,3,3,4,7

	public static int[] dualPivotQuicksort (int[] arr) {
        if (arr == null || arr.length == 0) return arr;
        int max = arr[0],min = arr[0];
        int n = arr.length;
        for (int i = 0; i < n; i++) {
            max = Math.max(arr[i], max);
            min = Math.min(arr[i], min);
        }
        int[] counter = new int[max - min + 1];
        for (int i = 0; i < n; i++) {
        	// 对每个数字出现的次数计数
            counter[arr[i]-min]++;
        }
        int index = 0;
        for (int i = min; i < max; i++) {
            int numCount = counter[i-min];
            while (numCount-->0){
                arr[index++] = i;
            }
        }
        return arr;
    }

2.选择排序

首先,找到数组中最小的元素,将它与第一个元素交换位置
然后,再找出剩下的元素中最小的元素,将它与第二个元素交换位置
直到最后一个元素。

	public static int[] selectSort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n; i++) {
            int minIndex = i;
            for (int j = i + 1; j < n; j++) {
                if (arr[minIndex] > arr[j]) {
                    minIndex = j;
                }
            }
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
        return arr;
    }

3.插入排序

将数组中第二个元素取出,与左边第一个元素比较,如果这个元素比它大,则继续向左比较直到找到比它小的元素,将这个元素放到比他小的元素的右边第一个,如果没找到比他它小的,就放在第一个。

	public static int[] insertSort(int[] arr) {
        if (arr == null || arr.length < 2) return arr;
        int n = arr.length;
        for (int i = 1; i < n; i++) {
            int temp = arr[i];
            int index = i - 1;
            // 找到左边比它小的值的位置 index
            while (index > 0 && arr[index] > temp) {
                index--;
            }
            // 将index开始到循环中的这个值temp中间的这些值全部往右移动1位
            for (int j = i; j > index + 1; j--) {
                arr[j] = arr[j - 1];
            }
            // 将temp的值插入到找到的比它小的值的后面
            arr[index + 1] = temp;

        }
        return arr;
    }

4.冒泡排序

把第一个元素与第二个元素比较,如果第一个比第二个大,则交换他们的位置。接着继续比较第二个与第三个元素,如果第二个比第三个大,则交换他们的位置….
我们对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样一趟比较交换下来之后,排在最右的元素就会是最大的数。除去最右的元素,我们对剩余的元素做同样的工作,如此重复下去,直到排序完成。

 	public static int[] bubbleSort(int[] arr) {
        if (arr == null || arr.length < 2) return arr;
        int n = arr.length;
        for (int i = 0; i < n; i++) {
            boolean flag = true;
            // 一次遍历完成后,最后一位肯定比前面的值都大,所以这里循环的范围是0~n-i-1
            for (int j = 0; j < n - i - 1; j++) {
                if (arr[j + 1] < arr[j]) {
                    flag = false;
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
            // 如果flag为true,说明在刚才的循环中没有发现右边的值比左边的小的情况,
            // 也就是说已经完成了排序,后面的就不用循环了
            if (flag) {
                break;
            }
        }
        return arr;
    }

5.希尔排序

希尔排序的思想是采用插入排序的方法,先让数组中任意间隔为 m 的元素有序,
刚开始 m 的大小可以是 m = n / 2,接着让 m = n / 4,
让 m 一直缩小,当 m = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了。

	public static int[] hillSortSort(int[] arr) {
        if (arr == null || arr.length < 2) return arr;
        int n = arr.length;
        // 对每组间隔为 h的分组进行排序,刚开始 h = n / 2;
        for (int h = n / 2; h > 0; h /= 2) {
            //对各个局部分组进行插入排序
            for (int i = h; i < n; i++) {
                // 将arr[i] 插入到所在分组的正确位置上
                insertI(arr, h, i);
            }
        }
        return arr;
	}

    /**
     * 将arr[i]插入到所在分组的正确位置上
     * 分组当中每个元素之间的下标差值为h
     */
    private static void insertI(int[] arr, int h, int i) {
        int temp = arr[i];
        int k;
        for (k = i - h; k > 0 && temp < arr[k]; k -= h) {
            arr[k + h] = arr[k];
        }
        arr[k + h] = temp;
    }

6.归并排序

将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},
第二次归并后:{6,100,202,301},{1,8,38},
第三次归并后:{1,6,8,38,100,202,301}

	public static int[] mergeSort(int[] arr) {
        int n = arr.length;
        // 子数组的大小分别为1,2,4,8...
        // 刚开始合并的数组大小是1,接着是2,接着4....
        for (int i = 1; i < n; i += i) {
            //进行数组进行划分
            int left = 0;
            int mid = left + i - 1;
            int right = mid + i;
            //进行合并,对数组大小为 i 的数组进行两两合并
            while (right < n) {
                merge(arr, left, mid, right);
                left = right + 1;
                mid = left + i - 1;
                right = mid + i;
            }
            // 还有一些被遗漏的数组没合并,千万别忘了
            // 因为不可能每个字数组的大小都刚好为 i
            if (left < n && mid < n) {
                merge(arr, left, mid, n - 1);
            }
        }
        return arr;
    }

    // 合并函数,把两个有序的数组合并起来
    // arr[left..mif]表示一个数组,arr[mid+1 .. right]表示一个数组
    private static void merge(int[] arr, int left, int mid, int right) {
        //先用一个临时数组把他们合并汇总起来
        int[] a = new int[right - left + 1];
        int i = left;
        int j = mid + 1;
        int k = 0;
        while (i <= mid && j <= right) {
            if (arr[i] < arr[j]) {
                a[k++] = arr[i++];
            } else {
                a[k++] = arr[j++];
            }
        }
        while (i <= mid) a[k++] = arr[i++];
        while (j <= right) a[k++] = arr[j++];
        // 把临时数组复制到原数组
        for (i = 0; i < k; i++) {
            arr[left++] = a[i];
        }
    }

7.快速排序

随机选取一个元素作为基准元素,为了方便,这里取第一个,将数组中小于基准元素的元素放在基准原始的左边,将大于基准元素的元素放到基准元素的右边,然后递归对两遍的元素分别进行此操作,直到数组的元素为1。

	public static int[] quickSort(int[] arr) {
        if (arr == null || arr.length == 0) return arr;
        return quickSort(arr, 0, 1);
    }

    public static int[] quickSort(int[] arr, int left, int right) {
        if (left < right) {
            //获取中轴元素所处的位置
            int mid = partition(arr, left, right);
            //进行分割
            arr = quickSort(arr, left, mid - 1);
            arr = quickSort(arr, mid + 1, right);
        }
        return arr;
    }

    private static int partition(int[] arr, int left, int right) {
        //选取基准元素
        int base = arr[left];
        int i = left + 1;
        int j = right;
        while (true) {
            // 向右找到第一个小于等于 base 的元素位置
            while (i <= j && arr[i] <= base) i++;
            // 向左找到第一个大于等于 pivot 的元素位置
            while (i <= j && arr[j] >= base) j--;
            if (i >= j)
                break;
            //交换两个元素的位置,使得左边的元素不大于pivot,右边的不小于pivot
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        arr[left] = arr[j];
        // 使基准元素处于有序的位置
        arr[j] = base;
        return j;
    }

8.堆排序

将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。
将其与末尾元素进行交换,此时末尾就为最大值。
然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。
如此反复执行,便能得到一个有序序列了

	public static int[] headSort(int[] arr) {
        //构建大顶堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr, i, arr.length);
        }
        //调整堆结构+交换堆顶元素与末尾元素
        for (int j = arr.length - 1; j > 0; j--) {
            //将堆顶元素与末尾元素进行交换
            int temp = arr[0];
       		arr[0] = arr[j];
        	arr[j] = temp;
            //重新对堆进行调整
            adjustHeap(arr, 0, j);
        }

        return arr;
    }

    public static void adjustHeap(int[] arr, int i, int length) {
        //先取出当前元素i
        int temp = arr[i];
        //从i结点的左子结点开始,也就是2i+1处开始
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
            //如果左子结点小于右子结点,k指向右子结点
            if (k + 1 < length && arr[k] < arr[k + 1]) {
                k++;
            }
            //如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
            if (arr[k] > temp) {
                arr[i] = arr[k];
                i = k;
            } else {
                break;
            }
        }
        //将temp值放到最终的位置
        arr[i] = temp;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值