8种排序方法的java实现,均来自于算法导论的伪代码实现。
第一种:插入排序【看注释】
* 插入排序:原址排序,比较排序
*
* 时间复杂度:o(n²)
*
* page-10
*
*
*/
public class InsertSortArray {
public static void main(String[] args) {
int a[] = { 5, 2, 4, 6, 1, 3 };
// 由于Java中除了基本数据类型是按值传递以外,其他的都是按引用传递的,修改的都是源数据。
insertionSort(a);
// 打印输出结果
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
/**
* @param a:待排序的数组
*/
private static void insertionSort(int[] a) {
// 从第二个数开始插入比较
for (int j = 1; j < a.length; j++) {
// key代表即将插入的数字
int key = a[j];
// 从插入位置左边第一个位置开始比较
int i = j - 1;
// 一直向左移动,key小于谁,就放置在谁的左边第一个位置
while (i >= 0 && a[i] > key) {
// key需要插入当前位置的左边,则需要把比他大的向右移动
a[i + 1] = a[i];
i--;
}
// 跳出循环的时候,a[i]<key,a[i+1]=key是必须的
a[i + 1] = key;
}
}
}
第二种:并归排序
/**
* 归并排序:非原址排序,比较排序
*
*
*
* 时间复杂度:o(nlogn)
*
*
*
*/
public class MergeSortArray {
/**
*
* @param array:带排序的数组
* @param start:需要排序数组的开始下标
* @param end:需要排序数组的结束下标,array.length-1
*/
private static void mergeSort(int[] array, int start, int end) {
if (start < end) {
// 1、二分数组
int mid = (start + end) / 2;
mergeSort(array, start, mid);
mergeSort(array, mid + 1, end);
// 2、合并数组
merge(array, start, mid, end);
}
}
/**
* 合并数组
*
* @param array
* @param start
* @param mid
* @param end
*/
private static void merge(int[] array, int start, int mid, int end) {
// 得到左右两边数组的长度
int left = mid - start + 1;
int right = end - mid;
// 创建一个缓存数组,分别存储左右数组的值
// 数组的长度比实际的大1
int left_array[] = new int[left + 1];
int right_array[] = new int[right + 1];
for (int i = 0; i < left; i++) {
left_array[i] = array[start + i];
}
for (int i = 0; i < right; i++) {
right_array[i] = array[mid + 1 + i];
}
// 最后一个对于的位置,存放数组结束表标志
left_array[left] = Integer.MAX_VALUE;
right_array[right] = Integer.MAX_VALUE;
int i = 0;
int j = 0;
for (int k = start; k <= end; k++) {
if (left_array[i] <= right_array[j]) {
array[k] = left_array[i];
i = i + 1;
} else {
array[k] = right_array[j];
j = j + 1;
}
}
}
public static void main(String[] args) {
int a[] = { 5, 2, 4, 7, 1, 3, 2, 6 };
// 把数组指定的下标支架的数据排序
// 如果是整个数组,则传入 0和length-1
mergeSort(a, 3, 4);
// 打印输出结果
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
}
第三种:堆排序
/**
* 堆排序:原址排序,比较排序
*
* @author luopan
*
*/
public class HeapSortArray {
/**
* 维护最大堆的性质函数
*
* 注意:只能调整指定位置
*
* 时间复杂度:o(logn),o(h)【h是树的高度】
*
* @param array:这个数组存储的可能不是一个具有最大堆性质的,但是经过这个函数以后,返回一个具有最大堆性质的堆。
* @param index:默认数组的下表是从0开始的
*/
private static void max_heapify(int array[], int index, int heap_size) {
int left = 2 * index + 1;// index的左孩子
int right = 2 * (index + 1);// index的右孩子
int largest = -1;
// 左孩子比父结点的值大
if (left < heap_size && array[left] > array[index]) {
largest = left;
} else {
largest = index;
}
// 右孩子比largest点的值大
if (right < heap_size && array[right] > array[largest]) {
largest = right;
}
if (largest != index) {
int tmp = array[index];
array[index] = array[largest];
array[largest] = tmp;
max_heapify(array, largest, heap_size);
}
}
/**
* 传入一个数组,得到一个最大堆数组
*
* @param array
*/
private static void build_max_heap(int array[]) {
int heap_size = array.length;
for (int i = array.length / 2; i >= 0; i--) {
max_heapify(array, i, heap_size);
}
}
/**
* 堆排序算法
*
* @param array
*/
private static void heap_sort(int array[]) {
// 第一步,创建一个最大堆
build_max_heap(array);
int heap_size = array.length;
// 数组的最大值一定在array[0]上
for (int i = array.length - 1; i >= 1; i--) {
// 把array[0]中最大数调整到array[i]中
int tmp = array[0];
array[0] = array[i];
array[i] = tmp;
// 然后把array[0...length-2]调整成最大堆
heap_size--;
max_heapify(array, 0, heap_size);
}
}
public static void main(String[] args) {
int a[] = { 1, 2, 3, 4, 7, 8, 9, 10, 14, 16 };
// int a[] = { 35, 10, 20, 9, 8, 19, 17, 7, 6, 5, 4, 16, 15 };
build_max_heap(a);
System.out.println("最大堆如下:");
for (int i = 0; i < a.length; i++) {
if (i == a.length - 1) {
System.out.print(a[i]);
} else {
System.out.print(a[i] + ",");
}
}
heap_sort(a);
System.out.println("\n堆排序以后的:");
for (int i = 0; i < a.length; i++) {
if (i == a.length - 1) {
System.out.print(a[i]);
} else {
System.out.print(a[i] + ",");
}
}
}
}
第四种:快速排序
/**
* 快速排序,原址排序,比较排序
*
* 最坏情况的时间复杂度是o(n²),期望时间复杂度是o(nlogn)
*
* 快速排序也采用分治思想:
*
* A[p..r]===>A[p..q-1]和A[q+1..r]
*
* A[p..q-1]<A[q]且A[q+1..r]>A[q+1]
*
* 中间值的选择是随机的,那么程序执行的时间复杂度就是随机的。
*
*/
public class QuickSortArray {
/**
* 快速排序
*
* @param array
* @param start:需要排序的开始下标
* @param end:需要排序的结束下标
*/
private static void quick_sort(int array[], int start, int end) {
if (start < end) {
int q = quick_partion(array, start, end);
quick_sort(array, start, q - 1);
quick_sort(array, q + 1, end);
}
}
/**
* 计算下标q成为快速排序最关键的一部分
*
* @param array
* @param start
* @param end
* @return 返回分割下标q
*/
private static int quick_partion(int array[], int start, int end) {
// 1、选取用于分割数组为两部分的中间值【真的是随机选取】
int x = array[end];
// 2、选取一个下标
int i = start - 1;
// 3、循环寻找适当的下标q【只能循环到end-1,因为end已经被使用了】
for (int j = start; j < end; j++) {
if (array[j] <= x) {// 找到比中间值x小的值
i = i + 1;
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
// 4、在循环结束的时候,需要把中间值移动到适当的位置
int tmp = array[end];
array[end] = array[i + 1];
array[i + 1] = tmp;
return i + 1;
}
public static void main(String[] args) {
// int a[] = { 1, 2, 3, 4, 7, 8, 9, 10, 14, 16 };
int a[] = { 35, 10, 20, 9, 8, 19, 17, 7, 6, 5, 4, 16, 15 };
quick_sort(a, 0, a.length - 1);
System.out.println("快速排序以后:");
for (int i = 0; i < a.length; i++) {
if (i == a.length - 1) {
System.out.print(a[i]);
} else {
System.out.print(a[i] + ",");
}
}
}
}
第五种:快速排序的随机化版本
/**
*
* 快速排序的随机化版本
*
* 与始终选取array[end]作为中间值不同,我们采用一种随机抽样技术,使得数组分割更加的平衡
*
* i = RANDOM(start,end):从start到end之间随机选取一个下标
*
* 然后,exchange array[i] 和 array[end]
*
* 接下来就直接使用QuickSortArray的程序即可。
*
*/
public class QuickRandomSortArray {
public static void main(String[] args) {
}
}
第六种:计数排序
/**
* 计数排序:假设n个输入元素中的每个都在0-k区间,并且假设数组元素属于一个小区间内的整数
*
* 对于每一个输入元素x,确定小于x的元素个数。
*
* 利用这个信息,就可以直接把x放到它的输出数组的位置上了
*
* 额外的tmpArray浪费了很多的空间,如果分布范围很大,但是很稀疏,则就有空间浪费的问题了。
*
*/
public class CountingSortArray {
/**
* 计数排序
*
* @param inArray:输入待排序数组
* @param outArray:输出结果数组
* @param k:数组的范围
*/
private static void counting_sort(int inArray[], int outArray[], int k) {
// 1、创建一个临时数组,并且全部赋值为0
int tmpArray[] = new int[k + 1];
for (int i = 0; i <= k; i++) {
tmpArray[i] = 0;
}
// 2、统计输入数组的每个元素的频率
for (int i = 0; i < inArray.length; i++) {
tmpArray[inArray[i]] = tmpArray[inArray[i]] + 1;
}
// 3、统计出小于或者等i的个数
for (int i = 1; i <= k; i++) {
tmpArray[i] = tmpArray[i - 1] + tmpArray[i];
}
// 4、按照小于等于i的个数,确定每个元素的位置
for (int i = inArray.length - 1; i >= 0; i--) {
// 【注意】由于outArray在按照0的下标开始的,但是统计次数的时候是从1开始的,所以需要减去1
outArray[tmpArray[inArray[i]] - 1] = inArray[i];
tmpArray[inArray[i]] = tmpArray[inArray[i]] - 1;// 把统计数次减1,是为处理相同元素的手段
}
}
public static void main(String[] args) {
int a[] = { 2, 5, 3, 0, 2, 3, 0, 3 };
int out[] = new int[a.length];
counting_sort(a, out, 5);
System.out.println("计数排序以后:");
for (int i = 0; i < out.length; i++) {
if (i == out.length - 1) {
System.out.print(out[i]);
} else {
System.out.print(out[i] + ",");
}
}
}
}
第七种:基数排序
/**
* 基数排序:
*
* 数组所有元素中位数最高d
*
* 按照最低位有效进行排序,然后合并在一起。
*
*/
public class RadixSortArray {
/**
* 计算得到数值指定位置的数字
*
* @param num
* @param pos:pos=1代表个位,pos=2代表十位以此类推
* @return
*/
private static int getNumInpos(int num, int pos) {
int tmp = 1;
for (int i = 0; i < pos - 1; i++) {
tmp *= 10;
}
return (num / tmp) % 10;
}
/**
*
* @param array
* @param d
*/
private static void radix_sort(int array[], int d) {
int tmp[] = new int[array.length];
// 下标0-9,分别代表0-9这10个数
int radix[] = new int[10];
// 从低位向最高位遍历,1代表个位,2代表十位,以此类推
for (int i = 1; i <= d; i++) {
// 1、把数组复制到缓存数组中
System.arraycopy(array, 0, tmp, 0, array.length);
Arrays.fill(radix, 0);
// 2、遍历数组,获取个十百千万...关键字
for (int j = 0; j < tmp.length; j++) {
int key = getNumInpos(tmp[j], i);
radix[key]++;
}
// 3、统计出小于或者等于的个数
for (int j = 1; j < radix.length; j++) {
radix[j] = radix[j - 1] + radix[j];
}
// System.out.print("第" + i + "位个数统计:");
// for (int j = 0; j < radix.length; j++) {
// System.out.print(radix[j] + " ");
// }
// 4、按照小于等于的个数,更新array的元素位置
for (int m = array.length - 1; m >= 0; m--) {
int key = getNumInpos(tmp[m], i);
// 和计数排序是一样的,调整数组元素该存放的位置上即可
array[radix[key] - 1] = tmp[m];
radix[key]--;// 遇到相同的元素,我们需要把次数减1,然后供下次出现填充
}
// System.out.println();//换行调试
}
}
public static void main(String[] args) {
int array[] = { 329, 457, 657, 839, 436, 720, 355 };
radix_sort(array, 3);
System.out.println("计数排序以后:");
for (int i = 0; i < array.length; i++) {
if (i == array.length - 1) {
System.out.print(array[i]);
} else {
System.out.print(array[i] + ",");
}
}
}
}
第八种:桶排序
import java.util.ArrayList;
import java.util.Collections;
/**
*
* 桶排序:假设输入数据服从均匀分布,这样的话排序的时间复杂度是:o(n)
*
* 也就是说数组元素分布在:[0,1)区间
*
* 桶排序将[0,1)区间划分为n个相同大小的区间,称为桶
*
*
*
*/
public class BucketSortArray {
/**
* 桶排序数组
*
* @param array
*/
private static void bucket_sort(int array[]) {
// 1、计算待排数组的最大值和最小值
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for (int i = 0; i < array.length; i++) {
max = Math.max(max, array[i]);
min = Math.min(min, array[i]);
}
// 2、动态计算桶的数量,并且初始化
int buckets = (max - min) / array.length + 1;
ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<ArrayList<Integer>>(buckets);
for (int i = 0; i < buckets; i++) {
bucketArr.add(new ArrayList<Integer>());
}
// 3、将元素放进桶中,按照元素的大小进桶
for (int i = 0; i < array.length; i++) {
int num = (array[i] - min) / array.length;
bucketArr.get(num).add(array[i]);
}
// 4、对每个桶分别排序
for (int i = 0; i < bucketArr.size(); i++) {
Collections.sort(bucketArr.get(i));
}
// 5、合并桶,并返回数组
ArrayList<Integer> tmp;
int index = 0;
for (int i = 0; i < bucketArr.size(); i++) {
tmp = bucketArr.get(i);
for (int j = 0; j < tmp.size(); j++) {
array[index] = tmp.get(j);
index++;
}
}
}
public static void main(String[] args) {
int a[] = { 102456, 9012, 39867, 68957, 83556, 19702 };
bucket_sort(a);
System.out.println("桶排序以后:");
for (int i = 0; i < a.length; i++) {
if (i == a.length - 1) {
System.out.print(a[i]);
} else {
System.out.print(a[i] + ",");
}
}
}
}
算法导论的前8章主要讲都是这些排序伪代码为例子。仅此记录笔记。一个星期的成果。