顾名思义,该排序过程类似水开时大气泡不断上升的过程,即当前元素不断与后一元素比较,较大是交换,一直交换到数组末尾的过程
代码实现
/**
* 冒泡排序
* 把第一个元素与第二个元素比较,如果第一个比第二个大,则交换他们的位置。
* 接着继续比较第二个与第三个元素,如果第二个比第三个大,则交换他们的位置….
*/
public class BubbleSort {
public int[] bubbleSort(int[] arrs){
int n=arrs.length;
if(n<2)return arrs;
for(int i=0;iarrs[j+1]) {
swap(arrs, j, j + 1);
}
}
}
return arrs;
}
private void swap(int[] arrs,int i,int j){
int temp=arrs[i];
arrs[i]=arrs[j];
arrs[j]=temp;
}
public static void main(String[] args) {
BubbleSort bubbleSort=new BubbleSort();
int[] arrs={45,5,68,6,59,755,69,21,456};
System.out.println(bubbleSort.bubbleSort(arrs));
}
}
代码改进
当当前数组已经有序时,则后序比较都为无效比较,可增加标识位flag,若第i趟比较没有产生交换,则表示此时已经有序
public int[] bubbleSort(int[] arrs){
int n=arrs.length;
if(n<2)return arrs;
for(int i=0;iarrs[j+1]) {
swap(arrs, j, j + 1);
flag=false;
}
}
if(flag)break;
}
return arrs;
}
通俗讲即在每趟遍历未排序数组中,选择 出最大(最小)的值,并将其与第i个值交换
代码实现
/**
* 简单选择排序
* 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
* 然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
*/
public class SimpleSelectionSort {
public int[] selectionSort(int[] arrs){
int n=arrs.length;
if(arrs==null||n<2)return arrs;
for (int i=0;i
插入排序,即是假设数组的前 i-1个元素有序(i从1开始),当轮到第i个元素时,只需要找到第i个数在前i-1个元素中的位置,并将这个数值插入,一言以蔽之即为 找准位置插入
代码实现
/**
* 简单插入排序
* 从第一个元素开始,该元素可以认为已经被排序;
* 取出下一个元素,在已经排序的元素序列中从后向前扫描;
* 如果该元素(已排序)大于新元素,将该元素移到下一位置;
* 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
* 将新元素插入到该位置后;
* 重复步骤2~5。
*/
public class SimpleInsertionSort {
public int[] insertionSort(int[] arrs) {
int n = arrs.length;
if (arrs == null || n < 2) return arrs;
for (int i = 1; i < n; i++) {
int cur = arrs[i];
int j = i - 1;
while (j >= 0 && arrs[j] > cur) {
arrs[j + 1] = arrs[j];
j--;
}
arrs[j+1]=cur;
}
return arrs;
}
public static void main(String[] args) {
SimpleInsertionSort simpleInsertionSort=new SimpleInsertionSort();
int[] arrs={45,5,68,6,59,755,69,21,456};
System.out.println(simpleInsertionSort.insertionSort(arrs));
}
}
嘛是希尔排序,上面说的简单插入排序在面对大致有序的数组时还是很不错的,但是当数组顺序混乱甚至逆序时,比较次数实在太多,那么可以对上面的简单插入算法进行改进,改进方向为使无序的数组变得大致有序,即对数组进行划分分组,对组内元素进行插入排序,且不断缩小分组大小,直到分组大小为1
代码实现
/**
* 希尔排序
* 是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
* 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
* 按增量序列个数k,对序列进行k 趟排序;
* 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
*/
public class ShellSort {
public int[] shellSort(int[] arrs){
int n=arrs.length;
if(arrs==null||n<2)return arrs;
//进行分组 初始分组长度为n/2,依次为上一次的1/2
int d=n;
while (d>0){
d/=2;
//循环插入排序d组
for(int i=0;i=0&&arrs[t]>temp){
arrs[t+d]=arrs[t];
t-=d;
}
arrs[t+d]=temp;
}
}
}
return arrs;
}
public static void main(String[] args) {
ShellSort shellSort=new ShellSort();
int[] arrs={45,5,68,6,59,755,69,21,456};
System.out.println(shellSort.shellSort(arrs));
}
}
排序过程:
代码实现
/**
* 快速排序
* 快速排序的基本思想:
* 通过一趟排序将待排记录分隔成独立的两部分,
* 其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
*/
public class QuickSort {
public int[] quickSort(int[] arrs){
arrs= quickSort(arrs,0,arrs.length-1);
return arrs;
}
private int[] quickSort(int[] arrs,int low,int hight){
if(low=midValue) j--;
if(i>=j)
break;
swap(arrs,i,j);
}
arrs[low]=arrs[j];
arrs[j]=midValue;
return j;
}
private void swap(int[] arrs,int i,int j){
int temp=arrs[i];
arrs[i]=arrs[j];
arrs[j]=temp;
}
public static void main(String[] args) {
QuickSort quickSort=new QuickSort();
int[] arrs={45,5,68,6,59,755,69,21,456};
System.out.println(quickSort.quickSort(arrs));
}
}
堆排序,关键字:堆,即利用大顶堆(小顶堆)这个特殊的数据结构进行排序
大顶堆数据结构特点:
1:一个完全二叉树
2:父节点的值要大于左子树和右子树的值
因为堆是个完全二叉树,立即推:最后一个非叶子节点在数组中的下标为,第i个元素的叶子节点的下标为2i+1,2i+2。
排序过程:
代码实现
/**
* 堆排序
* 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。
* 堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
*
* 1将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
* 2将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
* 3重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
*/
public class HeapSort {
public int[] heapSort(int[] arrs) {
int n = arrs.length;
if (arrs == null || n < 2) return arrs;
for (int i = n / 2 - 1; i >= 0; i--) {
adjustHeap(arrs, i, n);
}
for (int j = n - 1; j > 0; j--) {
swap(arrs, 0, j);
adjustHeap(arrs, 0, j);
}
return arrs;
}
public void adjustHeap(int[] arrs, int i, int len) {
int temp = arrs[i];
for (int k = i * 2 + 1; k < len; k = k * 2 + 1) {
if (k + 1 < len && arrs[k] < arrs[k + 1]) {
k++;
}
if (arrs[k] > temp) {
arrs[i] = arrs[k];
i = k;
} else {
break;
}
}
arrs[i] = temp;
}
private void swap(int[] arrs, int i, int j) {
int temp = arrs[i];
arrs[i] = arrs[j];
arrs[j] = temp;
}
public static void main(String[] args) {
HeapSort heapSort=new HeapSort();
int[] arrs={45,5,68,6,59,755,69,21,456};
heapSort.heapSort(arrs);
}
}
归并排序思想,即数组长度太长,那就将数组不断进行“1/2”式的切分,分别对各“1/2”子数组进行排序,最后将两个有序的数组合并为一个有序的数组.(二分法)
代码实现
/**
* 二路归并排序
* 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
* 若将两个有序表合并成一个有序表,称为2-路归并。
*/
public class TwowayMergeSort {
public int[] mergeSort(int[] arrs) {
int n = arrs.length;
if (arrs == null || n < 2) return arrs;
arrs = sort(arrs, 0, n - 1);
return arrs;
}
public int[] sort(int[] arrs, int left, int right) {
if (left == right) return arrs;
int mid = left + (right - left) / 2;
sort(arrs, left, mid);
sort(arrs, mid + 1, right);
merge(arrs, left, mid, right);
return arrs;
}
public void merge(int[] arrs, int left, int mid, int right) {
int[] temp = new int[right - left + 1];
int i = left, j = mid + 1, t = 0;
while (i <= mid && j <= right) {
if (arrs[i] < arrs[j]) {
temp[t++] = arrs[i++];
} else {
temp[t++] = arrs[j++];
}
}
while (i <= mid)
temp[t++] = arrs[i++];
while (j <= right)
temp[t++] = arrs[j++];
for (int k = 0; k < right - left + 1; k++) {
arrs[left + k] = temp[k];
}
}
public static void main(String[] args) {
TwowayMergeSort twowayMergeSort = new TwowayMergeSort();
int[] arrs = {45, 5, 68, 6, 59, 755, 69, 21, 456};
arrs = twowayMergeSort.mergeSort(arrs);
for (int arr : arrs) {
System.out.print(arr + " ");
}
}
}
关键词:计数。
计嘛数?数组中各个元素出现的个数。
排序过程:
适用范围(局限性)
通过上述描述可知,计数排序是根据数组中最大值来确定新建数组长度,则立即推:当数组值较为分散,例如{0,10000}时,则需要新建10001长度的数组,且只有两个元素被使用,造成极大的空间浪费。并且当数组元素存在负数时,亦不适用,即找不到下标为负数的数组。
代码实现
/**
* 计数排序
* 将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
* 找出待排序的数组中最大和最小的元素;
* 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
* 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
* 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
*
*
* 适用于数组大小范围确定且不大
*/
public class CountSort {
public int[] countSort(int[] arrs){
if(arrs==null||arrs.length<2)return arrs;
//找到arrs中的最大值
int max=arrs[0];
for(int v:arrs){
max=v>max?v:max;
}
//创建长度为max+1(0~max)的数组
int[] countArr=new int[max+1];
//累加结果
for(int v:arrs){
countArr[v]++;
}
//遍历结果
int j=0;
for(int i=0;i0){
arrs[j++]=i;
countArr[i]--;
}
}
return arrs;
}
public static void main(String[] args) {
CountSort countSort=new CountSort();
int[] arrs={0, 1, 1, 2, 3, 3, 3, 4, 8, 5, 5, 6, 7, 7, 8, 9, 9, 9, 9, 10};
countSort.countSort(arrs);
for (int arr : arrs) {
System.out.print(arr + " ");
}
}
}
上述的计数排序存在其局限性,桶排序为计数排序的改进。
桶排序中,可创建若干个桶(List获取其他容器类,类似计数排序中new的数组),每个桶中装有一定数量的数组元素,分别对每个桶中的元素进行排序,而后根据桶的顺序依次输入。
问题来了:
应该创建多少个桶?
个人理解,桶的数量与数组元素的分布情况有关,例如,在元素范围为(-50,100),每个桶的装取范围为20,则桶的数量应为(100-(-50))/20。当然,这与映射函数有关。
桶应该装哪些元素?
这个问题则与映射函数有关系,桶排序的高效与否与映射函数直接相关,差的映射函数会将所有元素统统装入同一桶中,好的映射函数则会将数据均匀的映射如不同的桶中。
排序过程
代码实现
/**
* 桶排序
*将数据分段放入不同的桶中
*对桶中元素进行排序
* 根据桶顺序依次输入
* 需要额外的空间
*/
public class BucketSort {
public int[] bucketSort(int[] arrs){
if(arrs==null||arrs.length<2)return arrs;
bucketSort0(arrs,10);
return arrs;
}
public void bucketSort0(int[] arrs,int bucketSize){
//1.获取数组最大值和最小值
int max=arrs[0],min=arrs[0];
for(int v:arrs){
max=max>v?max:v;
min=min> buckets=new ArrayList<>(bucketCount);
for(int i=0;i());
}
//将数组数据映射到不同的桶中
for(int v:arrs){
int idx=(v-min)/bucketSize;
buckets.get(idx).add(v);
}
//对各桶进行排序
for(List bucket:buckets){
if(bucket.size()>0){
Collections.sort(bucket);
}
}
//将桶中数据依次写回;
int j=0;
for(List bucket:buckets){
for(Integer value:bucket){
arrs[j++]=value;
}
}
}
public static void main(String[] args) {
BucketSort bucketSort=new BucketSort();
int[] arrs={-7, 51, 3, 121, -3, 32, 21, 43, 4, 25, 56, 77, 16, 22, 87, 56, -10, 68, 99, 70};
bucketSort.bucketSort(arrs);
for (int arr : arrs) {
System.out.print(arr + " ");
}
}
}
适用范围(局限性)
数组元素符合均匀分布
基数排序是借助“分配”和“收集”两种操作对单逻辑关键字进行排序的一种内部排序方法。
排序过程
代码实现
/**
* 基数排序
* 取得数组中的最大数,并取得位数;
* arr为原始数组,从最低位开始取每个位组成radix数组;
* 对radix进行计数排序(利用计数排序适用于小范围数的特点);
*/
public class RadixSort {
public int[] radoxSort(int[] arrs){
if(arrs==null||arrs.length<2)return arrs;
int max=arrs[0];
int mod=10;
int div=1;
//获取最大值
for(int v:arrs){
max=max>v?max:v;
}
int maxDigit=0;
while (max>0){
max/=10;
maxDigit++;
}
//初始化桶
List> buckets=new ArrayList<>(10);
for(int i=0;i<10;i++) buckets.add(new ArrayList<>());
for(int i=0;i bucket:buckets){
for(int val:bucket){
arrs[j++]=val;
}
bucket.clear();
}
}
return arrs;
}
public static void main(String[] args) {
RadixSort radixSort=new RadixSort();
int[] arrs={45,5,68,6,59,755,69,21,456};
radixSort.radoxSort(arrs);
for (int arr : arrs) {
System.out.print(arr + " ");
}
}
}
各个排序算法比较
如图