八个经典排序算法Java实现

本文涵盖的排序:

1. 快速排序
2. 归并排序
3. 冒泡排序
4. 插入排序
5. 选择排序
6. 希尔排序
7. 堆排序
8. 计数排序

一个类一个排序,在一个统一的Main类中测试运行
测试用例在注释里直接复制
堆排序我没完全弄懂,所以引用了其他博客里面的

如果有错误请在评论区带测试用例留言

不然我也找不出来是哪出错

备注:想偷懒直接使用其中一种排序的朋友,不必继承父类,但是记得把父类的swap方法复制进去
先是排序类的父类:

public abstract class Sort {

    public abstract void sort(int[] array);

    static void swap(int[] array, int i, int j) {
    	// 该方法用于交换数组第i项和第j项
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

然后是排序部分(继承父类)

快速排序

public class quickSort extends Sort{
    @Override
    public void sort(int[] array) {
        quickSort(array, 0, array.length-1);
    }

    private void quickSort(int[] array, int le, int ri){
        if (le >= ri){
            return;
        }
        int key = array[le];
        int low = le;
        int hi = ri;
        while(low < hi){
            while (low < hi && array[hi] >= key){
                hi--;
            }
            while (low < hi && array[low] <= key){
                low++;
            }
            if (low < hi){
                swap(array, low, hi);
            }
        }
        // low == hi
        swap(array, low, le);
        quickSort(array, le, low-1);
        quickSort(array, low+1, ri);
    }
}

归并排序

public class mergeSort extends Sort{
    @Override
    public void sort(int[] array) {
        int[] temp = new int[array.length];
        // temp用于临时存取数据
        mergeSort(array, 0, array.length - 1, temp);
    }

    private void mergeSort(int[] array, int low, int hi, int[] temp){
        if (low >= hi){
            return;
        }
        int middle = (low + hi)/2;
        mergeSort(array, middle + 1, hi, temp);
        mergeSort(array, low, middle, temp);
        merge(array, low, middle, hi, temp);
    }

    private void merge(int[] array, int low, int middle, int hi, int[] temp) {
        // 运行merge方法时,数组分为两部分
        // 一部分是low~middle(暂时称呼为左数组)
        // 另一部分是middle+1~hi(暂时称呼为右数组)
        int right_start = middle + 1;
        // 右数组第一位,right_start从middle+1到hi
        int left_start = low;
        // 左数组第一位,left_start从low到middle
        int index_for_temp = low;
        // temp用于储存结果,所以temp的index是从low到hi
        // 逐个归并
        while(left_start <= middle && right_start <= hi) {
            // 遍历左右数组
            if (array[left_start] <= array[right_start])
                // 左数组left_start位置上的值比右数组right_start值小
                temp[index_for_temp++] = array[left_start++];
            else
                // 左数组left_start位置上的值比右数组right_start值大
                temp[index_for_temp++] = array[right_start++];
        }
        // 由于左右数组大小不一定相同,此时左右数组可能有一个还没有遍历结束
        // 将左边剩余的归并
        while (left_start <= middle) {
            temp[index_for_temp++] = array[left_start++];
        }
        // 将右边剩余的归并
        while ( right_start <= hi ) {
            temp[index_for_temp++] = array[right_start++];
        }
        // 将此次temp的改变放到array中
        for (int i = low; i <= hi; i++) {
            array[i] = temp[i];
        }
    }
}

冒泡排序

public class bubbleSort extends Sort{
    @Override
    public void sort(int[] array) {
        boolean swaped = true;
        // 判断是否进行过交换操作
        // 是用于稍稍增加效率的办法,可以不用
        for (int i = 0; i < array.length-1; i++) {
            swaped = false;
            for (int j = 0; j < array.length-1; j++) {
                if (array[j] > array[j+1]){
                    swap(array, j, j+1);
                    swaped = true;
                }
            }
            if (!swaped){
                //若该次便利未经过交换,则表示数组已经是从小到大的顺序了,可以退出循环
                break;
            }
        }
    }
}

插入排序

public class insertionSort extends Sort{
    @Override
    public void sort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            // 从第二位开始插入
            int temp = array[i];
            int j = i;
            while (j > 0 && temp < array[j-1]){
                // 要插入的数比它前面的数小,该数往前挪(前面一位数占据该数当前的位置)
                array[j] = array[j-1];
                j--;
            }
            if (j != i){
                // 该数确实比它前面的数字小,且向前挪动了,该数可以插入了
                array[j] = temp;
            }
        }
    }
}

选择排序

public class selectionSort extends Sort{
    @Override
    public void sort(int[] array) {
        int minIndex;
        for (int i = 0; i < array.length; i++) {
            minIndex = i;
            for (int j = i+1; j < array.length; j++) {
                if (array[i] > array[j]){
                    minIndex = j;
                }
            }

            if (minIndex != i){
                // 最小值不是当前第i位
                swap(array, i, minIndex);
                // 将该位与最小位交换
            }
        }
    }
}

希尔排序

public class shellSort extends Sort{
    @Override
    public void sort(int[] array) {
        int gap = array.length/2;
        // 将array分成gap组,同一组相邻的两个数间隔距离为gap
        while (gap > 0){
            for (int i = gap; i < array.length; i++) {
                int j = i;
                while (j - gap >= 0 && array[j] < array[j-gap]){
                    // array[j]和array[j-gap]是同一组相邻的两个数
                    // 对同一组进行排序
                    swap(array, j, j-gap);
                    j -= gap;
                    // 由于第j位比j-gap位小,所以将第j位换到了j-gap位
                    // 但是换到了j-gap位的数没有参与前面的比较,所以要比较j-gap和j-gap-gap
                    // 所以令j = j-gap并循环执行
                }
            }
            gap /= 2;
        }
    }
}

堆排序(这个我还没完全弄懂所以先搜集了一个别人的可行的代码)

public class heapSort extends Sort{
    /***
     * 堆排序部分为转载内容
     * 原文:https://blog.csdn.net/m0_46975599/article/details/112125116
     */
    @Override
    public void sort(int[] array) {
        //1.构建大顶堆
        for (int i = array.length/2-1; i >= 0; i--) {
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(array, i, array.length);
        }
        //然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
        //2.调整堆结构+交换堆顶元素与末尾元素
        for (int j = array.length - 1; j > 0; j--) {
            swap(array, 0, j);//将堆顶元素与末尾元素进行交换
            adjustHeap(array, 0, j);//重新对堆进行调整
        }
    }
    /**
     * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上, 也就是说只调用一次,并没有得到大顶堆)
     * 就是将arr[i] 的值放到本次 调整过程中适当的位置。
     * @param arr    : 数组
     * @param i      : 非叶子节点的索引
     * @param length : 对多少个元素进行调整,这个length是逐渐减少的..
     */
    public void adjustHeap(int[] arr, int i, int length) {
        int temp = arr[i];//先取出当前元素i
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {//从i结点的左子结点开始,也就是2*i+1处开始
            if (k + 1 < length && arr[k] < arr[k + 1]) {//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if (arr[k] > temp) {//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];//把较大的值,赋给当前节点
                i = k;//i 指向k,继续循环比较
            } else {
                break;
            }
        }
        arr[i] = temp;//将temp值放到最终的位置
    }
}

计数排序

public class countSort extends Sort{
    @Override
    public void sort(int[] array) {
        int maxValue = array[0];
        int minValue = array[0];
        for (int i : array) {
            maxValue = Math.max(i, maxValue);
            minValue = Math.min(i, minValue);
        }
        int[] temp = new int[maxValue - minValue + 1];
        // temp用于储存最大最小值之间的数出现的次数
        // temp[0]是minValue出现的次数
        for (int i :
                array) {
            temp[i-minValue]++;
        }
        int index = 0;
        for (int i = 0; i < temp.length; i++) {
            // 遍历temp
            for (int j = 0; j < temp[i]; j++) {
                // temp[i]每有一位,便向array中放入一次数字
                array[index] = i+minValue;
                index++;
            }
        }
    }
}

最后是测试用的Main类

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    /**
     * 测试数据
     * 1 5 9 6 8 3 5 4 6 2 7 5 9 6
     * 99 66 66 55 88 22 55 55 55 66 9 4 3 55 16 84 91 77
     * 3
     * 5 8 3
     */

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        Sort sort = null;
        while (true) {
            System.out.println("请输入待测试算法");
            System.out.println("1. 快速排序");
            System.out.println("2. 归并排序");
            System.out.println("3. 冒泡排序");
            System.out.println("4. 插入排序");
            System.out.println("5. 选择排序");
            System.out.println("6. 希尔排序");
            System.out.println("7. 堆排序");
            System.out.println("8. 计数排序");
            switch (br.readLine()) {
            	// 好像自Java7之后switch就可以直接用String了,
            	// 版本不兼容的话这里也可以用Integer.parseInt()转换成int类型
            	// 如果想无限排序,自己在外面再套一个循环
                case "1":
                    sort = new quickSort();
                    break;
                case "2":
                    sort = new mergeSort();
                    break;
                case "3":
                    sort = new bubbleSort();
                    break;
                case "4":
                    sort = new insertionSort();
                    break;
                case "5":
                    sort = new selectionSort();
                    break;
                case "6":
                    sort = new shellSort();
                    break;
                case "7":
                    sort = new heapSort();
                    break;
                case "8":
                    sort = new countSort();
                    break;
                default:
                    System.out.println("请从列表中选出序号!");
                    continue;
            }
            break;
        }
        System.out.println("请输入测试用例:");
        String[] s = br.readLine().split(" ");
        int[] array = new int[s.length];
        for (int i = 0; i < s.length; i++) {
            array[i] = Integer.parseInt(s[i]);
        }
        sort.sort(array);
        for (int j : array) {
            System.out.print(j + " ");
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值