Java排序算法
排序算法
对一序列对象根据某个关键字进行排序
一、冒泡排序(Bubble Sort)
1.原理
1.比较相邻的元素,如果第一个比第二个大,就交换他们两个;
2.对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数;
3.针对除了最后一个之外的所有的元素重复以上的步骤;
4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
2.代码实现
public static long[] bubblesort(long[] array) {
if(array.length==0)
return array;
for (int i = 0; i <array.length; i++) {
for (int j = 0; j <array.length-1-i ; j++) {
if (array[j]>array[j]){
long temp= array[j];
array[j]=array[j];
array[j]=temp;
}
}
}
return array;
}
二、快速排序(Quick Sort)
1.原理
1.在数组中选一个基准数pivot(我选取了数字的最后一个数字作为基准数);
2.通过“遍历的方式”,比较给定元素和区间内其他元素的大小关系,形成<=pivot,pivot,>=pivot三个区间;
3.对于基准数左、右两边的数组,不断重复以上两个过程,直到每个子集只有一个元素,即为全部有序。
2.代码实现
public static void quickSort(long[] array) {
quickSortRange(array, 0, array.length - 1);
}
// 为了代码书写方便,我们选择使用左闭右闭的区间表示形式
// 其中,from,to 下标的元素都算在区间的元素中
// 左闭右闭的情况下,区间内的元素个数 = to - from + 1;
private static void quickSortRange(long[] array, int from, int to) {
if (to - from + 1 <= 1) {
// 区间中元素个数 <= 1 个
return;
}
// 挑选中区间最右边的元素 array[to]
int pi = partitionMethodA(array, from, to);
quickSortRange(array, from, pi - 1);
// 针对小于等于 pivot 的区间做处理
quickSortRange(array, pi + 1, to);
// 针对大于等于 pivot 的区间做处理
}
private static int partitionMethodA(long[] array, int from, int to) {
// 1. 先把 pivot 找出来
long pivot = array[to];
// 2. 通过定义 left 和 right 两个下标,将区间划分出来
int left = from;
int right = to;
// [from, left) 都是 <= pivot 的
// [left, right) 都是未参与比较的
// [right, to] 都是 >= pivot 的
while (left < right) {
// 随着 left 在循环过程中一直在 left++,请问 left < right 的条件不一定能保证,所以,我们时刻进行 left < right 条件的保证
while (left < right && array[left] <= pivot) {
left++;
}
while (left < right && array[right] >= pivot) {
right--;
}
// 循环停止时,说明 array[right] < pivot
// 两边都卡住时,交换 [left] 和 [right] 位置的元素
long t = array[left];
array[left] = array[right];
array[right] = t;
}
long t = array[to];
array[to] = array[left];
array[left] = t;
// 返回 pivot 最终所在下标
return left;
}
三、选择排序(Selection Sort)
1.原理
1.遍历所有数据,先在数据中找出最大或最小的元素,放到序列的起始;
2.再从余下的数据中继续寻找最大或最小的元素,依次放到序列中;
3.直到所有数据有序。
2.代码实现
public static long[] selectionsort(long[] array){
if (array.length == 0)
return array;
for (int i = 0; i < array.length; i++) {
int minIndex = i;
for (int j = i+1; j < array.length; j++) {
if (array[j] < array[minIndex])
//找到最小的数
minIndex = j;
//将最小数的索引保存
}
long temp = array[minIndex];
array[minIndex] = array[i];
array[i] = temp;
}
return array;
}
四、插入排序(Insertion Sort)
1.原理
将前i个(初始为1)数据假定为有序序列,依次遍历数据,将当前数据插入到前述有序序列的适当位置,形成前i+1个有序序列,依次遍历完所有数据,直至序列中所有数据有序。
2.代码实现
// [from, to] 是左闭右闭的
private static void insertSortRange(long[] array, int from, int to) {
int size = to - from;
for (int i = 0; i < size; i++) {
// 有序区间 [from, from + i]
// 无序区间 [from + i + 1, to]
// 选中的无序区间的第一个元素 array[from + i + 1]
long key = array[from + i + 1];
int j;
for (j = from + i; j >= from && array[j] > key; j--) {
array[j + 1] = array[j];
}
array[j + 1] = key;
}
}
五、希尔排序(Shell Sort)
1.原理
先将整个数据序列分割成若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时,再对全部数据进行依次直接插入排序,直至所有数据有序。
2.实现代码
public static long[] shellsort(long[] array,int gap){
for (int i = 0; i < array.length-gap; i++) {
//[0,i+gap)为有序区间,[i+gap,n)为无序区间
long k=array[i+gap];
//无序区间的第一个
int j;
for (j = i; j >0&&k<array[j] ; j=j-gap) {
array[j+gap]=array[j];
}
array[j+gap]=k;
}
return array;
}
六、堆排序(Heap Sort)
1.原理
堆排序利用堆这种近似完全二叉树的数据结构进行排序。堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。以最大堆为例,堆中的最大值总是位于根节点。首先将待排序的n个数据构造为大根堆,将顶端数据与末尾数据进行交换并将堆的尺寸减一,然后剩余n-1个数据再次构造为大根堆,再次交换,再次缩减,直至所有数据有序。
2.实现代码
public static void heapSort(long[] array) {
// 建立大堆
createBigHeap(array);
for (int i = 0; i < array.length - 1; i++) {
//交换之前的无序区间 [0, n - i)
swap(array, 0, array.length - i - 1);
// 交换之后的无序区间 [0, n - i - 1),元素个数 n - i - 1 个
// 对堆的 [0] 进行向下调整,堆里的元素个数就是无序区间的元素个数
shiftDown(array, array.length - i - 1, 0);
}
}
private static void shiftDown(long[] array, int size, int index) {
while (2 * index + 1 < size) {
int maxIdx = 2 * index + 1;
int right = maxIdx + 1;
if (right < size && array[right] > array[maxIdx]) {
maxIdx = right;
}
if (array[index] >= array[maxIdx]) {
return;
}
swap(array, index, maxIdx);
index = maxIdx;
}
}
private static void createBigHeap(long[] array) {
// 从最后一个元素的双亲开始
for (int i = (array.length - 2) / 2; i >= 0; i--) {
shiftDown(array, array.length, i);
}
}
private static void swap(long[] array, int i, int j) {
long t = array[i];
array[i] = array[j];
array[j] = t;
}
七、归并排序(Merge Sort)
1.原理
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序
2.实现代码
public static void mergeSort(long[] array) {
mergeSortRange(array, 0, array.length);
}
//这里的 [from, to) 是左闭右开来表示区间的
private static void mergeSortRange(long[] array, int from, int to) {
int size = to - from;
if (size <= 1) {
return;
}
int mid = from + (size / 2);
//优先对左 [from, mid) 和右 [mid, to) 两个小区间先进行排序(使用同样的方式处理:分治思想)
mergeSortRange(array, from, mid);
mergeSortRange(array, mid, to);
//有了两个分别各自有序的小区间 [from, mid) 和 [mid, to)
//通过一定的方式,将 [from, mid) [mid, to) 合并到 [from, to) 都是有序的
//两个有序数组区间合并到一个有序数组区间(需要结果放回原地去)
merge(array, from, mid, to);
}
private static void merge(long[] array, int from, int mid, int to) {
// 先计算出来额外空间需要多个,计算两个区间加起来多少个元素
int size = to - from;
// 申请一个额外的数组作为临时保存的地方
long[] other = new long[size];
int left = from; // 左边小区间的下标
int right = mid; // 右边小区间的下标
int dest = 0; // 临时空间的下标
// 只要左右两个小区间还有元素要参与比较
while (left < mid && right < to) {
if (array[left] <= array[right]) {
other[dest++] = array[left++];
} else {
other[dest++] = array[right++];
}
}
// 其中一个区间的元素取完了,另一个区间里一定还有元素,再把剩余的元素,统统放入 other
while (left < mid) {
other[dest++] = array[left++];
}
while (right < to) {
other[dest++] = array[right++];
}
// 把 other 中的有序元素,复制回 array 中,要注意下标的问题
for (int i = 0; i < size; i++) {
array[from + i] = other[i];
}
}
八、桶排序(Bucket Sort)
桶排序属于计数排序的升级强化版,同时利用了分治的思想,将待排数据划分到一定数量的有序的桶里,然后再对每个桶中的数据进行排序(桶排序的稳定性取决于桶内排序算法是否稳定),最后再将各个桶里的数据有序的合并到一起。最理想的情况下,输入数据可以被均匀的分配在每一个桶中,时间复杂度可以降到O(n);最坏的情况为所有数据在同一个桶中进行排序,且使用了复杂度较高的排序算法,此时时间复杂度会变为O(n²)。为了使桶排序更加高效,需要做到以下两点:①在额外空间充足的情况下,尽量增加桶的数量。②使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中。