最全的数据结构排序算法实现及比较

冒泡排序

类似暴力破解,1 – n 个,每个都比较一次。完成排序

public void sort(int[] arr) {
    int len = arr.length;
    for (int i = 0; i < len; i++) {
        for (int j = i + 1; j < len - 1; j++) {
            if (arr[i] > arr[j]) {
                SwapUtil.swap(arr, i, j);
            }
        }
    }
}

选择排序

[ 排序完毕 | 未排序] 每次从未排序的找一个最小的放入到排序完毕的后边

public void sort(int[] arr) {
    int len = arr.length;
    for (int i = 0; i < len; i++) {
        int index = findMinIndex(arr, i);
        if (index !=  -1) {
            SwapUtil.swap(arr, i, index);
        }
    }
}
 
public int findMinIndex(int[] arr, int begin) {
    int len = arr.length;
    int index = -1, elem = arr[begin];
    for (; begin < len; begin++) {
        if (arr[begin] < elem) {
            index = begin;
            elem = arr[begin];
        }
    }
    return index;
}
堆排序

堆排序

先进行堆的构建,再进行堆的调整

@Override
public void sort(int[] arr) {
    int len = arr.length;
    sort(arr, len);
}
 
private void sort(int[] arr, int len) {
    buildMaxHeap(arr, len);
    adjust(arr, len);
}
 
private void adjust(int[] arr, int len) {
    for (int i = len - 1; i > 0; i--) {
        SwapUtil.swap(arr, 0, i);
        adjustHeap(arr, 0, i);
    }
}
 
private void buildMaxHeap(int[] arr, int len) {
    // 这里索引从 0 开始,所以最后一个非叶子子节点 array.length / 2 - 1;
    for (int i = len / BUILD_HEAP_SEPARATED - 1; i >= 0; i--) {
        adjustHeap(arr, i, len);
    }
    System.out.println("build heap : " + Arrays.toString(arr));
}
 
private void adjustHeap(int[] arr, int i, int length) {
    // 先把当前元素取出来,因为当前元素可能要一直移动
    int temp = arr[i];
    // 从左孩子开始遍历 ( 2i + 1)
    for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
        // 若右孩子存在,且右孩子大于左孩子
        if (k + 1 < length && arr[k] < arr[k + 1]) {
            // 转移到右孩子
            k++;
        }
        // 如果发现结点(左右子结点)大于根结点,则进行值的交换
        if (arr[k] > temp) {
            SwapUtil.swap(arr, i, k);
            // 如果子节点更换了,则以子节点为根的子树会受到影响,
     // 所以,循环对子节点所在的树继续进行判断
            i  =  k;
        } else {
            //不用交换,直接终止循环
            break;
        }
    }
}

希尔排序

这里采用数组长度一半作为希尔增量进行排序,每次减少一半,知道增量为1.排序完毕。

 @Override
public void sort(int[] arr) {
    int step = arr.length / 2;
    for ( ; step > 0; step /= 2) {
        for (int i = step; i < arr.length; i++) {
            int value = arr[i];
            int j;
            // 跳跃的插入, 因为增量的存在,所以,后移时,是当前增量后移
            for (j = i - step; j >= 0 && arr[j] > value; j -= step) {
                arr[j + step] = arr[j];
            }
            arr[j + step] = value;
        }
    }
 
}

插入排序

类似于选择排序,分为【排序 | 未排序区域】每次从未排序区域选取一个,再排序区域寻找位置,然后后移交换。

@Override
public void sort(int[] arr) {
    int len = arr.length;
    for (int i = 1; i < len; i++) {
        int elem = arr[i];
        int index = getIndex(arr, i, elem);
        if (index != i) {
            retreat(arr, index, i);
            arr[index] = elem;
        }
    }
}
 
/**
 * 获取插入排序所在索引
 * @param arr 数组
 * @param limit 已排序区间上界
 * @param elem 待排序元素
 * @return 要插入的数组
 */
private int getIndex(int[] arr, int limit, int elem) {
    for (int i = 0; i < limit; i++) {
        if (elem < arr[i]) {
            return i;
        }
    }
    return limit;
}
 
/**
 * 将数组内一段元素后移
 * @param arr 数组
 * @param begin 区间下界
 * @param end 区间上界
 */
private void retreat(int[] arr, int begin, int end) {
    while(end > begin) {
        arr[end] = arr[end - 1];
        end--;
    }
}

二路归并排序

@Override
public void sort(int[] arr) {
    mergeSort(arr, 0, arr.length - 1);
}
 
public void mergeSort(int[] arr, int left, int right) {
    if (left < right) {
        int mid = (left + right) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}
 
public void merge(int[] arr, int left, int mid, int right) {
    int[] temp = new int[arr.length];
    int leftIndex = left, rightIndex = mid + 1, tempIndex = left;
    while (leftIndex <= mid && rightIndex <= right) {
        if (arr[leftIndex] < arr[rightIndex]) {
            temp[tempIndex++] = arr[leftIndex++];
        } else {
            temp[tempIndex++] = arr[rightIndex++];
        }
    }
    while (leftIndex <= mid) {
        temp[tempIndex++] = arr[leftIndex++];
    }
    while(rightIndex <= right) {
        temp[tempIndex++] = arr[rightIndex++];
    }
    // 将归并结果归坏到原数组中
    while (left <= right) {
        arr[left] = temp[left++];
    }
}

快速排序

设数组第一个元素为基准,

第一步:数组中所有元素,小于 基准 放到左边,大于 基准 放到右边,此时数组分为两部分,对于基准来说已经处于它应处的位置

第二步,对左右两半数组继续执行第一步,递归完毕,数组有序

时间复杂度: O(n*log2n) – 空间复杂度: good: O(log2n) – bad: O(n)

@Override
public void sort(int[] arr) {
    int len = arr.length;
    sort(arr, 0, len - 1);
}
 
private void sort(int[] arr, int left, int right) {
    if (left < right) {
        int mid = partition(arr, left, right);
        sort(arr, left, mid - 1);
        sort(arr, mid + 1, right);
    }
}
 
private int partition(int[] arr, int left, int right) {
    int sentry = arr[left];
    while (left < right) {
        while (left < right && arr[right] >= sentry) {
            right--;
        }
        arr[left] = arr[right];
        while(left < right && arr[left] <= sentry) {
            left++;
        }
        arr[right] = arr[left];
    }
    arr[left] = sentry;
    return left;
}

排序比较

排序方法 最好情况 最坏情况 平均情况 空间复杂度 稳定性
直接插入排序 O(n) O(n^2) O(n^2) O(1) 稳定
折半插入排序 O(nlong2n) O(n^2) O(n^2) O(1) 稳定
希尔排序 O(n^1.3) O(1) 不稳定
冒泡排序 O(n) O(n^2) O(n^2) O(1) 稳定
简单选择排序 O(n) O(n^2) O(n^2) O(1) 不稳定
快速排序 O(nlog2n) O(n^2) O(nlog2n) O(nlog2n) 不稳定
堆排序 O(nlog2n) O(nlog2n) O(nlog2n) O(1) 不稳定
归并排序 O(nlog2n) O(nlog2n) O(nlog2n) O(n) 稳定
基数排序 O(d(n+rd)) O(d(n+rd)) O(d(n+rd)) O(n+rd) 稳定

125jz网原创文章。发布者:江山如画,转载请注明出处:http://www.125jz.com/8811.html

(1)
江山如画的头像江山如画管理团队
上一篇 2020年10月12日 上午9:35
下一篇 2020年10月14日 上午9:13

99%的人还看了以下文章

  • 手机上可以python编程吗,手机如何开发python

    想在手机上编写python代码? python语言应用很广泛,很多小伙伴想利用空闲时间学习Python,但是有时候身边没有电脑,今天就给大家分享两种手机编写运行python的方法,让你可以直接在自己的手机上做python开发学习。 一、直接在线编写运行(免安装) 手机通过浏览器打开 https://c.runoob.com/compile/9/ 推荐:★★★…

    2022年3月16日 编程开发
    16.4K0
  • Java Vector可实现自动增长的对象数组

    java.util.vector提供了向量类(Vector)以实现类似动态数组的功能。创建了一个向量类的对象后,可以往其中随意插入不同类的对象,即不需顾及类型也不需预先选定向量的容量,并可以方便地进行查找。 对于预先不知或者不愿预先定义数组大小,并且需要频繁地进行查找,插入,删除工作的情况,可以考虑使用向量类。 向量类提供了三种构造方法: public ve…

    2020年8月20日
    5.7K0
  • JSP分页思想2—核心代码

    一、通过order by id desc limit ?,? 控制当前页面显示的记录 显示第几页,共几页,上一页 下一页 <% int PageSize=2; //一页显示的记录数 int RowCount=0; //记录总数 int PageCount=0; //总页数 int intPage; //待显示页码 int i;//循环变量 String…

    2019年10月30日
    15.7K0
  • java实现上位机与下位机串口通信实例(含java串口通信jar包下载及代码)

      串口通信在工程应用中很常见。 上位机与下位机 在上位机与下位机通讯过程中常通过有线的串口进行通信,在低速传输模式下串口通信得到广泛使用。 通常上位机指的是PC,下位机指的是单片机或者带微处理器的系统。下位机一般是将模拟信号经过AD采集将模拟量转换为数字量,下位机再经过数字信号处理以后将数字信号通过串口发送到上位机,相反上位机可以给下位机发送一些指令或者信…

    2023年1月7日 编程开发
    5.9K0
  • 一个完整的servlet验证登录用户名和密码实例

    servlet验证登录用户名和密码实例项目结构 登录页面login.html servlet验证通过后,欢迎页面 登录页面login.html: <body> <form action=”LoginCheck” method=”post”> 用户名:<input type=”text” name=”username” />…

    2020年12月6日
    4.0K0
  • JSP+MySQL MVC综合案例:完整的分页查询

    JSP+MySQL数据库开发教程,MVC综合案例,完整的分页查询代码。

    2018年2月22日
    7.2K0

发表回复

登录后才能评论