八大排序算法的java实现

目录

测试80000数据排序的时间的代码: 耗时在标题上写着,这个和个人计算机硬件有关,仅参考

一、冒泡排序:9521ms

二、选择排序:2444ms

三、插入排序:542ms

四、希尔排序:118ms

五、快速排序:15ms

六、归并排序:13ms

七、基数排序(桶排序):31ms

八、堆排序  :10ms



相关术语解释:

1) 稳定 :如果 a 原本在 b 前面,而 a=b ,排序之后 a 仍然在 b 的前面;
2) 不稳定 :如果 a 原本在 b 的前面,而 a=b ,排序之后 a 可能会出现在 b 的后面;
3) 内排序 :所有排序操作都在内存中完成;
4) 外排序 :由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
5) 时间复杂度:  一个算法执行所耗费的时间。
6) 空间复杂度 :运行完一个程序所需内存的大小。
7) n: 数据规模
8) k: 桶”的个数
9) In-place:    不占用额外内存
10) Out-place: 占用额外内存

测试80000数据排序的时间的代码: 耗时在标题上写着,这个和个人计算机硬件有关,仅参考

int arr[] = new int[80000];

        for (int i = 0;i< 80000;i++){
            arr[i] = (int) (Math.random()*800000);
        }

        long l = System.currentTimeMillis();
        BubbleSort.bobbleSort(arr);
        long l1 = System.currentTimeMillis();
        System.out.println("花费时间:"+(l1-l)+ "ms");

一、冒泡排序:9521ms

public static void bobbleSort(int arr[]) {//冒泡排序时间复杂度为O(n^2)
        //下标指向第一个数,把他和下一个数比较,大的放后面,小的放前面,然后下标向后挪一位,继续这样比较,一轮下来确定了一个最大的,
        // 然后下标从新指向第一个数,再比较一轮,这里的结束条件是上一轮的基础上减去1.然后这样下去即可)
        boolean flag = false;
        for (int j = 0; j < arr.length - 1; j++) {//当有5个数时,需要遍历4组
            for (int i = 0; i < arr.length - 1 - j; i++) {//每次遍历比上一次要少遍历一个数
                int temp = 0;
                if (arr[i] > arr[i + 1]) {
                    flag = true;//判断一组数据是否已经是顺序的
                    temp = arr[i];
                    arr[i] = arr[i + 1];
                    arr[i + 1] = temp;
                }
            }
            if (flag == true) {//如果一轮循环解说后发生了交换,则再吧flag改回去,如果这一轮没有发生交换,证明已经是顺序的了,则可以减少循环直接退出即可
                flag = false;
            } else {
                break;
            }
        }
    }

二、选择排序:2444ms

public static void selectSort(int[] arr) {//先把第一个当作最小的,和后面的比,比一圈下来,最小的那个数和下标记录下来,
                                              //和第一个数交换位置,然后就群定了一个数,依次向后一个一个确定下来即可)
        for (int j = 0; j < arr.length - 1; j++) {//总共循环 arr.length-1 次
            int min = arr[j];
            int minIndex = j;
            for (int i = j + 1; i < arr.length; i++) {//这里找到最小的元素
                if (min > arr[i]) {
                    min = arr[i];
                    minIndex = i;
                }
            }
            //最小元素放在最前面
            if (minIndex != j) {//这里的判断会节省时间
                arr[minIndex] = arr[j];
                arr[j] = min;
            }
        }
    }

三、插入排序:542ms

public static void insertSort(int[] arr) {

        for (int i = 1; i < arr.length; i++) {

            int insertVal = arr[i];//带插入的数
            int insertIndex = i - 1;//带插入的数的前一个数的下标

            //给insertVal找位置,这个太厉害了 (把要插入的数拿出来和前面的一个一个比,
            // 前面的数要是大,就往后挪一位,前面的数要是小或者相等要是小,那就跟你后面了)
            while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
            //退出while时表示找到了
            arr[insertIndex + 1] = insertVal;
//            System.out.println(Arrays.toString(arr));
        }
}

四、希尔排序:118ms

希尔排序:交换法


    public static void shellSort(int arr[]) {

        int temp = 0;
        //共有gap组,
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                for (int j = i - gap; j >= 0; j -= gap) {
                    if (arr[j] > arr[j + gap]) {
                        temp = arr[j + gap];
                        arr[j + gap] = arr[j];
                        arr[j] = temp;
                    }
                }
            }
        }
}

对希尔排序优化:移位法 


    public static void shellSort2(int arr[]) {
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {//设置步长,依次除以2
            for (int i = gap; i < arr.length; i++) {//此时i指向每一个小组中的第二个元素,i不断增长,一直是两两比较,
                                                    // 即使一组有很多个元素也是和自己减去步长的那个元素比较
                int j = i;//记录循环到的位置的下标
                int temp = arr[j];//保存遍历到的这个数,当从while循环了一次出来以后,temp就换了,换成了这一组的前一个
                while (j - gap >= 0 && temp < arr[j - gap]) {//和本组的前一个比较,寻找正确的插入的位置
                    arr[j] = arr[j - gap];//相当于把元素后移
                    j -= gap;//当找到了位置以后,需要和前一个位置继续比较
                }
            }
        }
    }

五、快速排序:15ms

public static void quickSort(int[] arr, int left, int right) {
        /*
        步骤:
        快速排序,就是找一个中轴点,左右各有一个下标向中间遍历,当左边的大于中轴点,右边的小于中轴点时,交换两个元素,如果两个下标
    重合,表示本轮已经是左小右大,需要注意的是如果交换完以后发现arr[l] == pivot 的值,那么r要前移一位,同理如果交换完以后发现
    arr[r] == pivot 的值,那么l要后移一位,此时一次循环完美结束。(顺序不能乱了,这里我也搞不太清楚,先记住吧)
        递归时l和r必然相等,左右都要递归。需要注意的是如果l == r ,必须l++,r-- ,否则会栈溢出     
         */
        int l = left;//下标
        int r = right;
        int temp = 0;//临时变量,用于交换
        //pivot 中轴
        int pivot = arr[(left+right) / 2];//这里!!!!!!我本来写的arr[(arr.length-1)/2],,会报错!!!!!!离谱


        //while循环是为了让比pivot小的放在左边,大的放到右边
        while (l < r) {
            while (arr[l] < pivot) {//在pivot左边一直找,找到比pivot要大的退出
                l++;
            }
            while (arr[r] > pivot) {//在pivot右边一直找,找到比pivot要小的退出
                r--;
            }
            if (l >= r) {//说明第一轮已经排好了,左边都小于pivot,右边都大于pivot
                break;
            }
            //交换
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;


            //如果交换完以后发现arr[l] == pivot 的值,那么r要前移一位
            if (arr[l] == pivot) {
                r--;
            }
            //如果交换完以后发现arr[r] == pivot 的值,那么l要后移一位
            if (arr[r] == pivot) {
                l++;
            }


        }

        //如果l == r ,必须l++,r-- ,否则会栈溢出
        if (l == r) {
            l++;
            r--;
        }
        //向左递归
        if (left < r) {
            quickSort(arr, left, r);
        }
        //向右递归
        if (l < right) {
            quickSort(arr, l, right);
        }
    }

六、归并排序:13ms

归并排序是一个分+合的过程,为区分清楚各部分的功能,有两段代码

//合并的方法
    /**
     * @param arr   带排序的数组
     * @param left  最左边的索引
     * @param mid   中间的索引
     * @param right 最右边的索引
     * @param temp  中转的数组
     */
    public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        int i = left;//左边初始索引
        int j = mid + 1;//右边的初始索引
        int t = 0;//temp的索引

        //把两组有序的数据按规则放到temp中,直到一侧全放进去
        while (i <= mid && j <= right) {
            if (arr[i] <= arr[j]) {
                temp[t] = arr[i];
                t++;
                i++;
            } else {
                temp[t] = arr[j];
                t++;
                j++;
            }
        }
        //把另一组数据全放进去
        while (j<=right) {
            temp[t] = arr[j];
            t++;
            j++;
        }

        while (i<=mid) {
            temp[t] = arr[i];
            t++;
            i++;
        }

        //把temp中的数据再放回数组中,并不是每次都把所有的拷进去,只有最后一次是全拷进去了,前面的都不是,可能是2个,4个等等
        t = 0;
        int tempLeft = left;
        //从头到尾都是一组下标,传入数据时,left为0,right为arr.length-1。所以设置临时左下标,右下标自己会变
        //第一次tempLeft=0,right = 1
        //第二次tempLeft =2,right = 3
        //。。。
        //最后一次tempLeft=0,right= 7
//        System.out.println("tempLeft= " + tempLeft +",right="+right);
        while (tempLeft <= right) {

            arr[tempLeft] = temp[t];
            t++;
           tempLeft++;
        }

    }
//分+合的方法
    public static void mergeSort(int[] arr,int left,int right,int[] temp){
        if (left< right){
            int mid = (left+right)/2;
            //向左递归进行分解
            mergeSort(arr,left,mid,temp);
            //向右递归进行分解
            mergeSort(arr,mid+1,right,temp);
            //递归一次节合并一次
//            System.out.println("********");
            merge(arr,left,mid,right,temp);
        }
    }

七、基数排序(桶排序):31ms

public static void radixSort(int[] arr) {
        //得到带排序数组中最大数
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (max < arr[i]) {
                max = arr[i];
            }
        }
        //得到最大值是几位数
        int maxLength = (max + "").length();

        //先造好十个桶,又因为每个桶中有元素,所以用二维数组
        //考虑到每个桶要有arr.length的空间,不难发现基数排序是一个用空间换时间的方法
        int[][] tong = new int[10][arr.length];

        //声明一个一维数组表示每个桶有几个数
        int[] num = new int[10];

        //  i表示第几个桶
        //  m为0表示看这一轮看个位,m为1表示看十位,依次类推
        //  n的出现方便取模,来获取想要看的位置的数字
        //  numIndex表示在个位、十位、百位...上的数字
        //  temp表示原来数组的下标,方便赋值
//元素放入相应的桶中
        for (int m = 0, n = 1; m < maxLength; m++, n = n * 10) {
            //第一次看个位,第二次看十位,第三次看百位。。。
            for (int i = 0; i < arr.length; i++) {
                int numIndex = arr[i] / n % 10;
                tong[numIndex][num[numIndex]] = arr[i];
                num[numIndex]++;
            }
//元素取出来,放到原来的数组中
            //需要每个桶都有指针来遍历
            int temp = 0;
            for (int i = 0; i < num.length; i++) {
                if (num[i] != 0) {
                    for (int j = 0; j < num[i]; j++) {
                        arr[temp] = tong[i][j];
                        temp++;
                    }
                }
                num[i] = 0;//重置桶中的数据!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!很重要
            }
//            System.out.println(Arrays.toString(arr));
        }

八、堆排序  :10ms

public static void heapSort(int[] arr) {

        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }

        int temp;
        for (int k = arr.length - 1; k > 0; k--) {
            temp = arr[k];
            arr[k] = arr[0];
            arr[0] = temp;
            adjustHeap(arr, 0, k);//每次都从0开始
        }
        System.out.println("升序数组:" + Arrays.toString(arr));
    }
/**
     * 将局部的树变成大顶堆
     *
     * @param arr    带排序数组
     * @param i      非叶子节点的下标
     * @param length 带排序数组的长度
     */
    public static void adjustHeap(int[] arr, int i, int length) {
        int temp = arr[i];

        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {//直接挪到最左下的那个非叶子节点
            if (k + 1 < length && arr[k] < arr[k + 1]) {
                k++;
            }
            if (arr[k] > temp) {
                arr[i] = arr[k];//把大的数放在了堆顶(局部的)
                i = k;//角标也挪上去,继续循环比较
            } else {
                break;
            }
        }
        arr[i] = temp;//temp放到调整后的位置,完成交换
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java塑造中...

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值