基础排序算法实现,主要参照程序员小灰的《漫画算法》
冒泡排序
/**
* O(n^2)
* @param arr
* @return
*/
private static int[] bubbleSort(int[] arr) {
int sortBorder = arr.length - 1;
int lastChange = 0;
for (int i=0; i<arr.length-1; i++) {
boolean changeFlag = true;
for (int j=0; j<sortBorder; j++) {
if (arr[j] > arr[j+1]) {
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
changeFlag = false;
//最后一个需要交换的元素,后面都已经有序
lastChange = j;
}
}
sortBorder = lastChange;
if (changeFlag) {
break;
}
}
return arr;
}
桶排序
/**
* O(n)
* O(n*k) k个桶,每个桶里是逆序 ??
* @param arr
* @return
*/
public static double[] bucketSort(double[] arr) {
//1、获取最值
double max = arr[0];
double min = arr[0];
for (int i=1, len=arr.length; i<len; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
//2、初始化桶 简单设计桶的数量等于原始数据的数量
int bucketNum = arr.length;
ArrayList<LinkedList<Double>> bucketList = new ArrayList<>(bucketNum);
for (int i=0; i<bucketNum; i++) {
bucketList.add(new LinkedList<>());
}
//3、遍历数组 将原始数据放在合适的桶内
//区间跨度 (max - min) / (bucketNum - 1)
double d = max - min;
for (int i=0, len=arr.length; i<len; i++) {
int num = (int) ((arr[i] - min) * (bucketNum - 1) / d);
bucketList.get(num).add(arr[i]);
}
//4、对每个桶排序
for (int i=0, len=bucketList.size(); i<len; i++) {
Collections.sort(bucketList.get(i));
}
//5、输出所有元素即可
double[] sortArr = new double[arr.length];
int index = 0;
for (LinkedList<Double> linkedList : bucketList) {
for (double e : linkedList) {
sortArr[index++] = e;
}
}
return sortArr;
}
计数排序
/**
* 元素都是比较小的数
* @param arr
* @return
*/
private static int[] countSort1(int[] arr) {
int max = arr[0];
//遍历一遍,找到最大值,为了确定数组的长度
for (int i=0,len=arr.length; i<len; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//用来计数排序的数组
int[] countArr = new int[max + 1];
//遍历待排序数组,记录每个数出现的次数
for (int i=0,len=arr.length; i<len; i++) {
countArr[arr[i]]++;
}
//遍历countArr输出统计结果
int index = 0;
int[] sortArr = new int[arr.length];
for (int i=0,len=countArr.length; i<len; i++) {
for (int j=0; j<countArr[i]; j++) {
sortArr[index++] = i;
}
}
return sortArr;
}
/**
* 元素比较大 按照最大元素分配 浪费空间
* @param arr
* @return
*/
public static int[] countSort2(int[] arr) {
//获得数组中的最值,确定统计数组的大小
int max = arr[0];
int min = arr[0];
for (int i=1,len=arr.length; i<len; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
int d = max - min + 1;
//创建统计数组
int[] countArr = new int[d];
for (int i=0,len=arr.length; i<len; i++) {
countArr[arr[i] - min]++;
//元素-偏移量,例countArr[99 - 90]++;
}
// 对统计数组做变形 小于等于countArr[i]的数字出现的次数
// 主要是为了稳定,计数排序本来是不稳定的排序算法
for(int i=1,len=countArr.length; i<len; i++) {
countArr[i]+=countArr[i - 1];
}
//倒序遍历原始数据,从统计数组找到该元素的正确位置
int[] sortArr = new int[arr.length];
for (int i=arr.length-1; i>=0; i--) {
// countArr[arr[i]-min] 确定arr[i]前面有几个不大于它的元素
//(包含它自己,这也就是确定arr[i]所在的位置)
// countArr[arr[i]-min] - 1 例如:第三大的,应该放置在index为2的位置
sortArr[ countArr[arr[i]-min] - 1] = arr[i];
// 不大于它的数已经放置了一个,对应的数量减一
countArr[arr[i]-min]--;
}
return sortArr;
}
// 基数排序 先以个位进行排序,之后十位,从低位到高位 稳定排序
堆排序
/**
* 下沉操作
* @param array
* @param parentIndex
* @param length
*/
private static void downAdjust(int[] array, int parentIndex, int length) {
int temp = array[parentIndex];
int childIndex = 2 * parentIndex + 1;
while (childIndex < length) {
//有右孩子, 并且右孩子更大
if(childIndex + 1 < length && array[childIndex + 1] > array[childIndex]) {
childIndex++;
}
//父亲节点 不小于子节点 跳出
if (temp >= array[childIndex]) {
break;
}
array[parentIndex] = array[childIndex];
parentIndex = childIndex;
childIndex = 2 * childIndex + 1;
}
array[parentIndex] = temp;
}
/**
* 不稳定
* O(nlogN)
*
* 插入删除 logN n个节点 nlogN
* 很少和响铃元素进行比较交换 快排慢慢趋于有序,堆排则不然
* @param array
*/
public static void heapSort(int[] array) {
// 构建大顶堆
// 如果一个节点的两个子节点已经有序了,那么他的子节点的子节点是否有序,就不需要关心了,因此值调整一半即可
for (int i=(array.length - 2) / 2; i>=0; i--) {
downAdjust(array, i, array.length);
}
System.out.println("最大堆构建"+Arrays.toString(array));
//循环删除堆顶元素
for (int i=array.length-1; i>0; i--) {
//最后一个元素与第一个交换
int temp = array[i];
array[i] = array[0];
array[0] = temp;
downAdjust(array, 0, i);
}
}
插入排序
/**
* 第一个元素有序 后面的和前面已经有序的进行比较 逆序则调整
* 稳定
* O(n) O(n^2)
* @param arr
* @return
*/
private static int[] insertSort(int[] arr) {
for (int i=1; i<arr.length-1; i++) {
for (int j=i; j>0; j--) {
if (arr[j] < arr[j-1]) {
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
}
return arr;
}
归并排序
private static void merge(int[] arr, int low, int mid, int high) {
int[] temp = new int[arr.length];
int i = low, j = mid + 1;
int k = low;
while (i <= mid && j <= high) {
if(arr[i] < arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= high) {
temp[k++] = arr[j++];
}
for (int t=low; t<=high; t++) {
arr[t] = temp[t];
}
}
/**
* 非原地
* 稳定
* O(nlogn)
* @param arr
* @param low
* @param high
*/
private static void sort(int[] arr, int low, int high) {
if (low < high) {
int mid = low + (high - low) / 2;
sort(arr, low, mid);
sort(arr, mid + 1, high);
merge(arr, low, mid, high);
}
}
快排
/**
* O(nlogN) 对半分
* O(n^2) 每次切分都选择了最值元素作为基准元素
* @param arr
* @param startIndex
* @param endIndex
* @return
*/
private static int partition(int[] arr, int startIndex, int endIndex) {
//获取第一个元素,作为基准元素
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;
while (left < right) {
//控制right指针,比较并且左移
while (left < right && arr[right] > pivot) {
right--;
}
//控制left指针,比较并且右移
while (left < right && arr[left] <= pivot) {
left++;
}
//当right<=pivot && left>pivot 停止移动,交换
if (left < right) {
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
}
//左右指针重合了,与基准元素就行交换
//基准元素 。。。。。。。小于 left == right 大运 基准元素 。。。。。
arr[startIndex] = arr[left];
arr[left] = pivot;
//返回基准元素下标
return left;
}
private static void quickSort(int[] arr, int startIndex, int endIndex) {
//递归结束条件
if (startIndex >= endIndex) {
return;
}
//获得基准元素位置
int pivotIndex = partition(arr, startIndex, endIndex);
//根据基准元素,分治
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
选择排序
/**
* 不稳定
* O(n^2)
* 选择最小元素 与第一个元素交换
* 选择第二小元素 与第二个元素进行交换
* @param arr
* @return
*/
private static int[] selectSort(int[] arr) {
for (int i=0; i<arr.length-1; i++) {
for (int j=i+1; j<arr.length; j++) {
if(arr[j] < arr[i]) {
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
return arr;
}
希尔排序
private static void shellSort(int[] arr) {
int N = arr.length;
int h = 1;
while (h < N / 3) {
h = 3 * h + 1;
}
while (h >= 1) {
for (int i=h; i<N; i++) {
for (int j=i; j>=h; j-=h) {
if(arr[j] < arr[j-h]) {
int temp = arr[j];
arr[j] = arr[j-h];
arr[j-h] = temp;
}
}
}
h = h / 3;
}
}