最近整理了一些常用的排序算法,主要是下面几个排序算法:
- 冒泡排序
- 快速排序
- 选择排序
- 插入排序
- 希尔排序
- 归并排序
- 堆排序
- 基数排序
- 桶排序
1.冒泡排序
每次确定一个元素放在最后,然后从头开始下一轮
/**
* 稳定排序
* 冒泡排序
* @param a
*/
public static void buddleSort(int[] a) {
System.out.print("排序前数组:");
System.out.println(Arrays.toString(a));
if (a == null) {
return;
}
int len = a.length;
for (int i = 0; i < len - 1; i++) {
for (int j = 0; j < len - i -1; j++) {
if (a[j] < a[j+1]) {
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
System.out.print("排序后数组:");
System.out.println(Arrays.toString(a));
}
2.快速排序
同样每次确定一个元素,但是确定的是第一个元素所在的位置,然后递归对该位置左右两部分排序
/**
* 不稳定排序
* 快速排序
* @param a
* @param left
* @param right
*/
public static void quickSort(int[] a, int left, int right) {
if (a == null || left > right) {
return;
}
if (right - left <= 1) {
return;
}
int temp = a[left];
int start = left;
int end = right;
while (left != right) {
while (a[right] > temp && right > left) {
right--;
}
if (left < right) {
a[left] = a[right];
left++;
}
while (a[left] < temp && left < right) {
left++;
}
if (left < right) {
a[right] = a[left];
right--;
}
}
a[left] = temp;
quickSort(a, start, left-1);
quickSort(a, left+1, end);
}
3.选择排序
每次遍历选择最小的元素放在第一个位置
/**
* 不稳定排序
* 选择排序
* @param a
*/
public static void selectSort(int[] a) {
if (a == null || a.length == 0) {
return;
}
System.out.print("排序前数组:");
System.out.println(Arrays.toString(a));
int len = a.length;
for (int i = 0; i < len -1; i++) {
for (int j = i + 1; j < len; j++) {
if (a[i] > a[j]) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
System.out.print("排序后数组:");
System.out.println(Arrays.toString(a));
}
4.插入排序
第一个是每次往前插入都交换位置,第二个是先保存,然后后移元素,最后放入到合适位置。后面的希尔插入就是用的第二种
/**
* 稳定排序
* @param a
*/
public static void insertSort(int[] a) {
if (a == null || a.length == 0) {
return;
}
System.out.print("排序前数组:");
System.out.println(Arrays.toString(a));
int len = a.length;
for (int i = 0; i < len - 1; i++) {
int j = i;
while(j >= 0 && a[j+1] < a[j]) {
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
j--;
}
}
System.out.print("排序后数组:");
System.out.println(Arrays.toString(a));
}
/**
* 稳定排序
* 每次插入先把元素后移,而不是每次交换
* @param a
*/
public static void insertSort2(int[] a){
for(int i=1;i= 1 && temp
5.希尔排序
/**
* 不稳定
* 希尔排序
* @param a
*/
public static void shellSort(int[] a) {
if (a == null || a.length == 0) {
return;
}
System.out.print("排序前数组:");
System.out.println(Arrays.toString(a));
int len = a.length;
int inc = len / 2;
while (inc > 0) {
for (int i = inc; i < len-1; i++) {
int temp = a[i];
int j = i;
while (j >= inc && a[j - inc] > temp) {
a[j] = a[j-inc];
j = j - inc;
}
a[j] = temp;
}
inc = inc / 2;
}
System.out.print("排序后数组:");
System.out.println(Arrays.toString(a));
}
6.归并排序
归并排序分为递归和迭代两个版本。递归版本比较好理解。迭代版本主要是要理解第二个for循环,是遍历每一对要合并的子数组
/**
* 稳定
* 递归 Top-to-Down(维基百科)
* 1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
* 2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
* 3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
* 4.重复步骤3直到某一指针到达序列尾
* 5.将另一序列剩下的所有元素直接复制到合并序列尾
* @param a
*/
public static void mergeSort1(int[] a) {
System.out.print("归并排序前数组:");
System.out.println(Arrays.toString(a));
int len = a.length;
int[] result = new int[len];
mergeSortRecursive(a, result, 0, len-1);
System.out.print("归并后数组:");
System.out.println(Arrays.toString(a));
}
private static void mergeSortRecursive(int[] arr, int[] result, int start, int end) {
if (start >= end) {
return;
}
//int len = end - start;
//int mid = (len >> 1) + start;
int mid = (start + end) / 2;
int start1 = start;
int end1 = mid;
int start2 = mid + 1;
int end2 = end;
mergeSortRecursive(arr, result, start1, end1);
mergeSortRecursive(arr, result, start2, end2);
int k = start;
while (start1 <= end1 && start2 <= end2) {
result[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
}
while (start1 <= end1) {
result[k++] = arr[start1++];
}
while (start2 <= end2) {
result[k++] = arr[start2++];
}
for (k = start; k <= end; k++) {
arr[k] = result[k];
}
}
/**
* 迭代 Down-to-Top(维基百科)
* 1.将序列每相邻两个数字进行归并操作,形成 {ceil(n/2)} {ceil(n/2)}个序列,排序后每个序列包含两/一个元素
* 2.若此时序列数不是1个则将上述序列再次归并,形成 {ceil(n/4)} {ceil(n/4)}个序列,每个序列包含四/三个元素
* 3.重复步骤2,直到所有元素排序完毕,即序列数为1
* @param a
*/
public static void mergeSort2(int[] a) {
int len = a.length;
int[] result = new int[len];
int block;
int start;
System.out.print("归并排序前数组:");
System.out.println(Arrays.toString(a));
for (block = 1; block < len; block *= 2) {
for (start = 0; start < len; start += 2 * block) {
int low = start;
int mid = (start + block) < len ? (start + block) : len;
int high = (start + 2 * block) < len ? (start + 2 * block) : len;
//
int start1 = low;
int end1 = mid;
int start2 = mid;
int end2 = high;
while (start1 < end1 && start2 < end2) {
result[low++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
}
while (start1 < end1) {
result[low++] = a[start1++];
}
while (start2 < end2) {
result[low++] = a[start2++];
}
}
int[] temp = a;
a = result;
result = temp;
}
System.out.print("归并后数组:");
System.out.println(Arrays.toString(a));
}
7.堆排序
/**
* 不稳定
* 堆节点的访问:通常堆是通过一维数组来实现的。在数组起始位置为0的情形中:
* 1.父节点i的左子节点在位置 {(2i+1)}
* 2.父节点i的右子节点在位置 {(2i+2)}
* 3.子节点i的父节点在位置 {floor((i-1)/2)}
*
* 堆的操作:在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:
* 1.最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
* 2.创建最大堆(Build_Max_Heap):将堆所有数据重新排序
* 3.堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
*/
private static void heapSort(int[] a) {
/*
* 第一步:将数组堆化
* beginIndex = 第一个非叶子节点。
* 从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。
* 叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。
*/
int len = a.length - 1;
int beginIndex = (len -1) >> 1;
for (int i = beginIndex; i >= 0; i--) {
maxHeapify(a, i, len);
}
/*
* 第二步:对堆化数据排序
* 每次都是移出最顶层的根节点A[0],与最尾部节点位置调换,同时遍历长度 - 1。
* 然后从新整理被换到根节点的末尾元素,使其符合堆的特性。
* 直至未排序的堆长度为 0。
*/
for (int i = len; i > 0; i--) {
swap(a, 0, i);
maxHeapify(a, 0, i -1);
}
}
private static void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
private static void maxHeapify(int[] a, int index, int len) {
// 左子节点索引
int li = (index << 1) + 1;
// 右子节点索引
int ri = li + 1;
// 子节点值最大索引,默认左子节点。
int cMax = li;
if(li > len) {
// 左子节点索引超出计算范围,直接返回。
return;
}
if(ri <= len && a[ri] > a[li]) {
// 先判断左右子节点,哪个较大。
cMax = ri;
}
if(a[cMax] > a[index]){
// 如果父节点被子节点调换,
// 则需要继续判断换下后的父节点是否符合堆的特性。
swap(a, cMax, index);
maxHeapify(a, cMax, len);
}
}
另:如果在调用maxHeapify时不想递归调用,也可以换成迭代,只要控制好出口条件即可,如下代码所示:
private static void maxHeapify(int[] a, int index, int len) {
/*// 左子节点索引
int li = (index << 1) + 1;
// 右子节点索引
int ri = li + 1;
// 子节点值最大索引,默认左子节点。
int cMax = li;
if(li > len) {
// 左子节点索引超出计算范围,直接返回。
return;
}
if(ri <= len && a[ri] > a[li]) {
// 先判断左右子节点,哪个较大。
cMax = ri;
}
if(a[cMax] > a[index]){
// 如果父节点被子节点调换,
// 则需要继续判断换下后的父节点是否符合堆的特性。
swap(a, cMax, index);
maxHeapify(a, cMax, len);
}*/
while (2 * index <= len) {
// 左子节点索引
int li = (index << 1) + 1;
// 右子节点索引
int ri = li + 1;
// 子节点值最大索引,默认左子节点。
int cMax = li;
if(li > len) {
// 左子节点索引超出计算范围,直接返回。
return;
}
if(ri <= len && a[ri] > a[li]) {
// 先判断左右子节点,哪个较大。
cMax = ri;
}
if(a[cMax] > a[index]){
// 如果父节点被子节点调换,
// 则需要继续判断换下后的父节点是否符合堆的特性。
swap(a, cMax, index);
index = cMax;
} else {
break;
}
}
}
8.基数排序
/**
* 稳定排序
* 基数排序
* @param data
* @param n
*/
private static void radixSort(int[] data, int n) {
System.out.print("基数排序前数组:");
System.out.println(Arrays.toString(data));
int d = maxbit(data, n);
int[] tmp = new int[n];
//计数器
int[] count = new int[10];
int i, j, k;
int radix = 1;
for(i = 1; i <= d; i++) {
//进行d次排序
for(j = 0; j < 10; j++) {
//每次分配前清空计数器
count[j] = 0;
}
for(j = 0; j < n; j++) {
//统计每个桶中的记录数
k = (data[j] / radix) % 10;
count[k]++;
}
for(j = 1; j < 10; j++) {
//将tmp中的位置依次分配给每个桶
count[j] = count[j - 1] + count[j];
}
for(j = n - 1; j >= 0; j--) {
//将所有桶中记录依次收集到tmp中
k = (data[j] / radix) % 10;
tmp[count[k] - 1] = data[j];
count[k]--;
}
for(j = 0; j < n; j++) {
//将临时数组的内容复制到data中
data[j] = tmp[j];
}
radix = radix * 10;
}
System.out.print("基数排序后数组:");
System.out.println(Arrays.toString(data));
}
private static int maxbit(int data[], int n) //辅助函数,求数据的最大位数
{
///< 最大数
int maxData = data[0];
/// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。
for (int i = 1; i < n; ++i)
{
if (maxData < data[i]) {
maxData = data[i];
}
}
int d = 1;
int p = 10;
while (maxData >= p)
{
//p *= 10; // Maybe overflow
maxData /= 10;
++d;
}
return d;
}
9.桶排序
/**
* 稳定排序
* 桶排序
* @param arr
*/
public static void bucketSort(int[] arr){
System.out.print("桶排序前数组:");
System.out.println(Arrays.toString(arr));
int max = arr[0],min = arr[0];
for (int a : arr) {
if (maxa) {
min=a;
}
}
//该值也可根据实际情况选择
int bucketNum = max/10 - min/10 + 1;
List buckList = new ArrayList>();
//create bucket
for (int i = 1; i <= bucketNum; i++) {
buckList.add(new ArrayList());
}
//push into the bucket
for (int i = 0; i < arr.length; i++) {
int index = indexFor(arr[i],min,10);
((ArrayList)buckList.get(index)).add(arr[i]);
}
ArrayList bucket = null;
int index = 0;
for (int i = 0;i < bucketNum; i++){
bucket = (ArrayList)buckList.get(i);
insertSort(bucket);
for (int k : bucket) {
arr[index++]=k;
}
}
System.out.print("桶排序后数组:");
System.out.println(Arrays.toString(arr));
}
/**
* @param a 待排序数组元素
* @param step 步长(桶的宽度/区间),具体长度可根据情况设定
* @return 桶的位置/索引
*/
private static int indexFor(int a, int min, int step) {
return (a-min) / step;
}
/**
* 把桶内元素插入排序
*/
private static void insertSort(List bucket) {
for (int i=1;i=0 && bucket.get(j)>temp; j--){
bucket.set(j+1, bucket.get(j));
}
bucket.set(j+1, temp);
}
}