冒泡排序是贪心的,每次能在余下部分得到一个最值的位置。一轮冒泡可能多次交换位置。贪心是指每一步能得到最终解的一部分。
插入排序是依次将数值插入到有序部分,寻找插入位置的方式如果是一个一个比较,是直接插入排序,如果是二分查找方式,是二分插入排序。依次增加元素,得到局部解,最终得到整体解,是动态规划的思想。
选择排序是和冒泡一样是贪心的,每次能得到一个最值的位置。但是是每一轮找到余下部分最值的位置,然后和一侧交换位置,每一轮只交换一次位置。
快速排序是JDK默认的排序方式,是平均最快的。使用对撞指针将数值插入到两侧都比之大或者小的位置。是贪心的,每次能找到一个数值的位置。是分治的,在两侧部分递归相同操作。
归并排序是递归分解后递归拼接有序部分,平均速度仅次于快速排序。是分治的。
package sort;
public class SortTest {
/**
* 方式 是否稳定(等值是否换相对位置) 时间复杂度 是否原地排序(不用新增数组临时存储有序元素)
* 冒泡 Y O(n^2) Y
* 插入 Y O(n^2) Y 二分插入排序虽然查找快,但和直接插入排序一样还是得后移相同大小
* 选择 N O(n^2) Y
* 快速 N O(nlogn) Y
* 归并 Y O(nlogn) N
* 堆
* @param args
*/
public static void main(String[] args) {
// 快:quickSort > mergeSort >>> insertSort >> selectSort >>> bubbleSort
int[] array = {3, 5, 1, 2, 6};
// bubbleSort(array);
insertSort1(array);
// selectSort(array);
// quickSort(array);
// mergeSort(array);
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
/**
* 冒泡排序
*
* @param array
*/
private static void bubbleSort(int[] array) {
int n = array.length;
// n - 1个值到达右侧
// i:冒泡的轮数索引,选择最值的轮数索引
for (int i = 0; i < n - 1; i++) {
// 比较n - 1 - j次;i = 0时,比较n - 1次
// j:用于比较的两个元素中左侧元素的索引
for (int j = 0; j < n - 1 - i; j++) {
// > 从小到大:将最大值移动到右侧,然后将次大值移动到最大值左侧...
// < 从大到小:将最小值移动到右侧,然后将次小值移动到最小值左侧...
if (array[j] < array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
/**
* 直接插入排序
*
* @param array
*/
private static void insertSort(int[] array) {
int length = array.length;
// 从第二个值开始,将值插入到应该在的位置,每次插入时前面的已经是有序的
// 插入时一个一个比较是直接插入排序,二分查找是二分插入排序
// i:用于插入的元素的索引
for (int i = 1; i < length; i++) {
int waitInsertValue = array[i];
// j:用于和插入元素比较的元素的索引
int j;
// waitInsertValue < array[j]
// < 从小到大:左侧选择小的位置插入
// > 从大到小:左侧选择大的位置插入
for (j = i - 1;j >= 0 && waitInsertValue > array[j];j--) {
// 原始位置的值后移,空出最终插入的位置
array[j + 1] = array[j];
}
array[j + 1] = waitInsertValue;
}
}
/**
* 二分插入排序
*
* @param array
*/
private static void insertSort1(int[] array) {
int n = array.length;
for(int i = 1;i < n;i++){
int waitInsertValue = array[i];
int waitInsertIndex;
int left = 0;
int right = i - 1;
while(left < right){
int mid = (left + right) / 2;
if(waitInsertValue < array[mid]){
right = mid - 1;
} else if(array[mid] < waitInsertValue){
left = mid + 1;
} else {
// 找到插入位置
// waitInsertIndex = left + 1;
break;
}
}
// waitInsertValue == array[mid;或者插入两个元素组成的有序数组时退出,此时left == right,要插入的位置在left或者left + 1
if(waitInsertValue < array[left]){
waitInsertIndex = left;
} else {
waitInsertIndex = left + 1;
}
for(int j = i - 1;j >= waitInsertIndex;j--){
array[j + 1] = array[j];
}
array[waitInsertIndex] = waitInsertValue;
}
}
/**
* 选择排序 最值放左侧
* 选择排序不稳定,比如6,6,2,从小到大排序,第一个6会和2互换位置
*/
private static void selectSort(int[] array) {
int length = array.length;
// 选择最值的轮数索引
for (int i = 0; i < length - 1; i++) {
// 默认最值索引
int needIndex = i;
for (int j = i + 1; j < length; j++) {
// < 从小到大:选择最小的放到左侧
// > 从大到小:选择最大的放到左侧
if (array[j] > array[needIndex]) {
needIndex = j;
}
}
if (needIndex != i) {
int temp = array[i];
array[i] = array[needIndex];
array[needIndex] = temp;
}
}
}
/**
* 选择排序 最值放右侧
*/
private static void selectSort1(int[] array) {
int n = array.length;
for(int i = 0;i < n - 1;i++){
// 默认最值索引
int needIndex = n - 1 - i;
for(int j = 0;j <= n - 1 - i;j++){
// > 从小到大:选择最大的放到右侧
// < 从大到小:选择最大的放到右侧
if(array[j] > array[needIndex]){
needIndex = j;
}
}
if(needIndex != n - 1 - i){
int temp = array[n - 1 - i];
array[n - 1 - i] = array[needIndex];
array[needIndex] = temp;
}
}
}
/**
* 快速排序
* 将第一个放到中间,并使左侧都比此值小,右侧都比此值大
* 分治:左侧和右侧执行相同操作
* 不稳定:等值元素会交换相对位置,如5,3,3,从小到大排序,因为对撞指针的方式右侧的3会到达5的位置,也就是左侧3的左侧
* @param array
*/
private static void quickSort(int[] array) {
quickSort(array, 0, array.length - 1);
}
private static void quickSort(int[] array, int leftIndex, int rightIndex) {
// key放在数组最右侧时,之后的递归,对于左侧部分,leftIndex == rightIndex,对于右侧leftIndex < rightIndex
if (leftIndex >= rightIndex) {
return;
}
int currentLeftIndex = leftIndex;
int currentRightIndex = rightIndex;
int key = array[currentLeftIndex];
// 对撞指针,从小到大时:比key小的移动到左侧,比key大的移动到右侧,key放到左右指针对撞的位置
while (currentLeftIndex < currentRightIndex) {
// >= 从小到大
// <= 从大到小
while (currentLeftIndex < currentRightIndex && array[currentRightIndex] > key) {
currentRightIndex--;
}
array[currentLeftIndex] = array[currentRightIndex];
// <= 从小到大
// >= 从大到小
while (currentLeftIndex < currentRightIndex && array[currentLeftIndex] < key) {
currentLeftIndex++;
}
array[currentRightIndex] = array[currentLeftIndex];
}
array[currentLeftIndex] = key; // currentLeftIndex == currentRightIndex
quickSort(array, leftIndex, currentLeftIndex - 1);
quickSort(array, currentRightIndex + 1, rightIndex);
}
/**
* 归并排序
* 分治:反复切割成两个部分,直至成单个元素
* 合并有序数组:反复将两个有序数组,合成一个有序数组
* 等值元素不会交换位置;需新增数组存储有序元素
* @param array
*/
private static void mergeSort(int[] array) {
int length = array.length;
int[] temp = new int[length]; // 每次归并有序数组时寄存排序的值
mergeSort(array, 0, length - 1, temp);
}
/**
* 分治
* @param array
* @param leftIndex
* @param rigthIndex
* @param temp
*/
private static void mergeSort(int[] array, int leftIndex, int rigthIndex, int[] temp) {
if(leftIndex >= rigthIndex){ // 只有一个元素时,无需分裂
return;
}
int midIndex = (rigthIndex + leftIndex) / 2;
mergeSort(array, leftIndex, midIndex, temp); // 会使array的leftIndex到midIndex有序
mergeSort(array, midIndex + 1, rigthIndex, temp); // 会使array的midIndex + 1到rigthIndex有序
merege(array, leftIndex, midIndex, rigthIndex, temp);
}
/**
* 合并两个有序数组
* @param array
* @param leftIndex
* @param midIndex
* @param rigthIndex
* @param temp
*/
private static void merege(int[] array, int leftIndex, int midIndex, int rigthIndex, int[] temp) {
int sortedPart1LeftIndex = leftIndex;
int sortedPart1ReightIndex = midIndex;
int sortedPart2LeftIndex = midIndex + 1;
int sortedPart2RightIndex = rigthIndex;
int tempIndex = 0;
while(sortedPart1LeftIndex <= sortedPart1ReightIndex && sortedPart2LeftIndex <= sortedPart2RightIndex){
if(array[sortedPart1LeftIndex] <= array[sortedPart2LeftIndex]){
temp[tempIndex++] = array[sortedPart1LeftIndex++];
} else {
temp[tempIndex++] = array[sortedPart2LeftIndex++];
}
}
while(sortedPart1LeftIndex <= sortedPart1ReightIndex){
temp[tempIndex++] = array[sortedPart1LeftIndex++];
}
while(sortedPart2LeftIndex <= sortedPart2RightIndex){
temp[tempIndex++] = array[sortedPart2LeftIndex++];
}
tempIndex = 0;
while(leftIndex <= rigthIndex){
array[leftIndex++] = temp[tempIndex++];
}
}
}