Java实现八大排序算法
JAVA排序
1、冒泡排序
实现原理: 从序列左边开始比较相邻位置两个元素的大小,如果前一个大于后一个值就交换位置,这样较大的元素经过交换就慢慢’浮动’到序列的右端。
- 如果有n个数据进行排序,总共需要比较n-1次
- 每一次比较完毕,下一次的比较就会少一个数据参与
代码实现:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
//定义一个数组
int[] arr = {7, 6, 18, 5, 4, 3};
System.out.println("排序前:" + Arrays.toString(arr));
// 冒泡排序
// 外层循环控制比较的轮数
for (int i = 0; i < arr.length - 1; i++) {
// 内层循环控制比较的次数
for (int j = 0;j<arr.length-1-i;j++){
// 比较判断 arr[j]与arr[j+1]
if (arr[j] > arr[j+1]){
// 交换
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println("排序后:" + Arrays.toString(arr));
}
}
2、选择排序
选择排序概述
实现原理:从待排序的数据中寻找最小值,将其与序列最左边的数字进行交换。
- 如果有n个数据进行排序,总共需要比较n-1轮
- 每一轮比较完毕,下一轮的比较就会少一个数据参与
public static int [] selectSort(int [] arr){
int length = arr.length;
int temp,minIndex;
for (int i = 0; i < length-1; i++) {
minIndex = i;
for (int j = i+1; j < length ; j++) {
if (arr[minIndex] > arr[j]){ //如果当前最小值元素大于 其它元素
minIndex = j; // ,就将其它元素下标给 minIndex
}
}
temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
return arr;
}
3、插入排序
实现原理:默认第一个元素为已排序元素,取出下一个元素,在已排序队列中从后向前扫描,如果该元素(已排序元素)大于新元素,就将该元素移动到后一位置,重复此操作,直至已排序队列的元素小于或等于该元素停止。
public static int[] insertSort(int [] array){
// 插入排序:从第一个元素开始,该元素默认已经被排序,取出下一个元素,在已经排序的序列中
//从后向前扫描,如果该元素 大于新元素,将该元素移动到下一位置,重复以上步骤.
int preIndex,current;
for (int i = 1,j = array.length ; i < j; i++) {
preIndex = i - 1;
current = array[i];
while (preIndex >= 0 && array[preIndex] > current){
array[preIndex+1] = array[preIndex];
preIndex--;
}
array[preIndex + 1] = current;
}
return array;
}
以上三种排序时间复杂度为O(n^2)
常见几种java排序算法
4、归并排序
归并排序,简单的说把一串数,从中平等分为两份,再把两份再细分,直到不能细分为止,这就是分而治之的分的步骤。再从最小的单元,两两合并,合并的规则是将其按从小到大的顺序放到一个临时数组中,再把这个临时数组替换原数组相应位置,这就是治。图解如下:
public static void mergeSort(int[] a,int s,int e){
int m = (s + e) / 2;
if (s < e){
mergeSort(a,s,m);
mergeSort(a,m+1,e);
//归并
merge(a,s,m,e);
}
}
private static void merge(int[] a, int s, int m, int e) {
//初始化一个从起始s到终止e的一个数组
int[] temp = new int[(e - s) + 1];
//左起始指针
int l = s;
//右起始指针
int r = m+1;
int i = 0;
//将s-e这段数据在逻辑上一分为二,l-m为一个左边的数组,r-e为一个右边的数组,两边都是有序的
//从两边的第一个指针开始遍历,将其中小的那个值放在temp数组中
while (l <= m && r <= e){
if (a[l] < a[r]){
temp[i++] = a[l++];
}else{
temp[i++] = a[r++];
}
}
//将两个数组剩余的数放到temp中
while (l <= m){
temp[i++] = a[l++];
}
while (r <= e){
temp[i++] = a[r++];
}
//将temp数组覆盖原数组
for (int n = 0; n < temp.length; n++) {
a[s+n] = temp[n];
}
}
5、分治排序法,快速排序法
简单的说, 就是设置一个标准值, 将大于这个值的放到右边(不管排序), 将小于这个值的放到左边(不管排序), 那么这样只是区分了左小右大, 没有排序, 没关系, 左右两边再重复这个步骤.直到不能分了为止.
详细说就是:
1、选择待排数列的首部第一个元素为基准元素x,设置两指针,分别指向数列首尾部位置,假设两指针分别设为i和j。
2、每次遍历的过程是这样的,首先从右到左遍历指针j所指向的元素,直到j指向的元素值小于基准元素x时,停止遍历,将其放到i的位置(因为i的值已经拷贝成了基准x腾出了位置)
3、 i往右挪一步, i++,接着轮到指针i从左到右遍历,直到i所指向的元素值大于基准元素x时,停止遍历,将其放到j的位置(因为上面一步j的值已经占用到了i的位置,腾出位置了)
4、 依此类推,两边轮流遍历, 直到指针i与指针j相等或者大于(实际肯定是i==j)时,停止外部循环。此时必定左边都是比x小的, 右边是比x大的.
5、最后直接将基准元素x直接放置于指针i所指向的位置即可
6、 完成分区操作, 从i的位置一分为二, 左边和右边再递归执行上面的操作. 层层细分
接下来,我们通过示图来展示上述分区算法思路的过程:
public class QuickSort {
public static void sort(int[] arr,int begin,int end) {
//先定义两个参数接收排序起始值和结束值
int a = begin;
int b = end;
//先判断a是否大于b
if (a >= b) {
//没必要排序
return;
}
//基准数,默认设置为第一个值
int x = arr[a];
//循环
while (a < b) {
//从后往前找,找到一个比基准数x小的值,赋给arr[a]
//如果a和b的逻辑正确--a<b ,并且最后一个值arr[b]>x,就一直往下找,直到找到后面的值大于x
while (a < b && arr[b] >= x) {
b--;
}
//跳出循环,两种情况,一是a和b的逻辑不对了,a>=b,这时候排序结束.二是在后面找到了比x小的值
if (a < b) {
//将这时候找到的arr[b]放到最前面arr[a]
arr[a] = arr[b];
//排序的起始位置后移一位
a++;
}
//从前往后找,找到一个比基准数x大的值,放在最后面arr[b]
while (a < b && arr[a] <= x) {
a++;
}
if (a < b) {
arr[b] = arr[a];
//排序的终止位置前移一位
b--;
}
}
//跳出循环 a < b的逻辑不成立了,a==b重合了,此时将x赋值回去arr[a]
arr[a] = x;
//调用递归函数,再细分再排序
sort(arr,begin,a-1);
sort(arr,a+1,end);
}
}
分别对1k,1w,10w,20w大小的随机数组排序,结果如下:
得到综合结果是:
速度: 快速排序>>归并排序>>>>>插入排序>>选择排序>>冒泡排序
并且可以看到,选择排序,冒泡排序在数据量越来越大的情况下,耗时已经呈指数型上涨,而不是倍数上涨,在50w的时候已经需要足足5分钟以上
6、希尔排序
希尔排序视频 插入排序的算法复杂度为O(n2),但如果序列为正序可提高到O(n),而且直接插入排序算法比较简单,希尔排序利用这两点得到了一种改进后的插入排序。
希尔排序,也称 递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是 非稳定排序算法。
希尔排序:将无序数组分割为若干个子序列,子序列不是逐段分割的,而是相隔特定的增量的子序列,对各个子序列进行插入排序;然后再选择一个更小的增量,再将数组分割为多个子序列进行排序…最后选择增量为1,即使用直接插入排序,使最终数组成为有序。
增量的选择:在每趟的排序过程都有一个增量,至少满足一个规则 增量关系 d[1] > d[2] > d[3] >…> d[t] = 1 (t趟排序);根据增量序列的选取其时间复杂度也会有变化,这个不少论文进行了研究,在此处就不再深究; 本文采用首选增量为n/2,以此递推,每次增量为原先的1/2,直到增量为1;
/********************************************************
*函数名称:ShellInsert
*参数说明:pDataArray 无序数组;
* d 增量大小
* iDataNum为无序数据个数
*说明: 希尔按增量d的插入排序
*********************************************************/
void ShellInsert(int* pDataArray, int d, int iDataNum)
{
for (int i = d; i < iDataNum; i += 1) //从第2个数据开始插入
{
int j = i - d;
int temp = pDataArray[i]; //记录要插入的数据
while (j >= 0 && pDataArray[j] > temp) //从后向前,找到比其小的数的位置
{
pDataArray[j+d] = pDataArray[j]; //向后挪动
j -= d;
}
if (j != i - d) //存在比其小的数
pDataArray[j+d] = temp;
}
}
/********************************************************
*函数名称:ShellSort
*参数说明:pDataArray 无序数组;
* iDataNum为无序数据个数
*说明: 希尔排序
*********************************************************/
void ShellSort(int* pDataArray, int iDataNum)
{
int d = iDataNum / 2; //初始增量设为数组长度的一半
while(d >= 1)
{
ShellInsert(pDataArray, d, iDataNum);
d = d / 2; //每次增量变为上次的二分之一
}
}
希尔排序中对于增量序列的选择十分重要,直接影响到希尔排序的性能。我们上面选择的增量序列{n/2,(n/2)/2…1}(希尔增量),其最坏时间复杂度依然为O(n2),一些经过优化的增量序列如Hibbard经过复杂证明可使得最坏时间复杂度为O(n3/2)。