常见排序算法及Java实现及相关比较
一:冒泡排序
1. 算法
- 比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。 - 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
2.特点
平均情况:O(n^2)
最好情况:O(n)
最坏情况:O(n^2)
辅助空间:O(1)
稳定性:稳定
3.代码
package Sort;
import java.util.Arrays;
public class BubbleSort {
public static void bubbleSort(int[] arr){
if(arr == null || arr.length < 2){
return;
}
for(int i = arr.length-1 ; i > 0 ;i--){
for(int j = 0; j < i ;j++){
if(arr[j] > arr[j+1]){
swap(arr,j,j+1);
}
}
}
}
private static void swap(int[] arr,int i ,int j){
//异或运算:1.X^X = 0;2.X^0 = X
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
//for test
private static void comparator(int[] arr){
Arrays.sort(arr);
}
private static int[] generateRandomArr(int maxLength,int maxValue){
int[] arr = new int[(int)(Math.random() * (maxLength+1))];//[0,maxLength]长度的数组
for(int i = 0; i < arr.length ;i++){
arr[i] = (int)(Math.random() * (maxValue+1)) - (int)(Math.random() * maxValue);
}
return arr;
}
private static int[] copyArr(int[] arr){
if(arr == null){
return null;
}
int[] newArr = new int[arr.length];
for(int i = 0; i < arr.length ;i++){
newArr[i] = arr[i];
}
return newArr;
}
private static boolean isEqual(int[] arr1,int[] arr2){
if(arr1 == null && arr2 == null){
return true;
}
if(arr1 == null || arr2 == null){
return false;
}
if(arr1.length != arr2.length){
return false;
}
for(int i = 0; i < arr1.length ;i++){
if(arr1[i] != arr2[i]){
return false;
}
}
return true;
}
private static void printArr(int[] arr){
if(arr == null){
System.out.println("数组为空");
}
System.out.println("数组为:");
for(int i = 0; i < arr.length ;i++){
if(i == arr.length-1){
System.out.print(arr[i]);
break;
}
System.out.print(arr[i] + ",");
}
System.out.println();
}
public static void main(String[] args) {
int maxLength = 100;
int maxValue = 100;
int testTime = 500000;
boolean equal = true;
for(int i = 0; i < testTime ;i++){
int[] arr1 = generateRandomArr(maxLength, maxValue);
int[] arr2 = copyArr(arr1);
bubbleSort(arr1);
comparator(arr2);
equal = isEqual(arr1,arr2);
if(equal){
continue;
}else{
printArr(arr1);
printArr(arr2);
System.out.println("Error");
break;
}
}
System.out.println(equal ? "没问题" : "出错了");
}
}
4.改进:鸡尾酒排序/定向冒泡排序
先从前往后把最大的放在最后,然后从后往前把最小的放在最前面
public static void bubbleSortPro(int[] arr){
if(arr == null || arr.length < 2){
return;
}
int n = arr.length;
int left = 0;
int right = n-1;
while(left < right){
for(int i = left; i < right; i++){
if(arr[i] > arr[i+1]){
swap(arr,i,i+1);
}
}
right--;
for(int i = right; i > left; i--){
if(arr[i] < arr[i-1]){
swap(arr,i-1,i);
}
}
left++;
}
}
private static void swap(int[] arr,int i ,int j){
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
二:选择排序
1.算法
找到数组中最小元素,和数组第一个元素交换位置。
然后在剩下的元素中找到最小元素,和数组第二个元素交换位置。
如此往复,直到数组有序。
2.特点
分类 -------------- 内部比较排序
最差时间复杂度 ---- O(n^2)
最优时间复杂度 ---- O(n^2)
平均时间复杂度 ---- O(n^2)
所需辅助空间 ------ O(1)
稳定性 ------------ 不稳定
3.代码
package test;
import java.util.Arrays;
public class SelectionSort {
public static void selectSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
swap(arr, i, minIndex);
}
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
/*这里若按照如下方式交换arr[i] 和 arr[j] 的值会报错
原因在于:我们选择排序的时候选的是从i到arr.length-1中的最小的一个和i位置上的元素进行交换
若i位置上的元素就是最小元素:
那么在做arr[i] = arr[i] ^ arr[j] 的时候,arr[i]和arr[j] 是同一个元素会出现 a ^ a = 0的情形,
使得arr[i]变成了0.
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];*/
}
// for test
public static void comparator(int[] arr) {
Arrays.sort(arr);
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
// for test
public static int[] copyArray(int[] arr) {
if (arr == null) {
return null;
}
int[] res = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
res[i] = arr[i];
}
return res;
}
// for test
public static boolean isEqual(int[] arr1, int[] arr2) {
if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
return false;
}
if (arr1 == null && arr2 == null) {
return true;
}
if (arr1.length != arr2.length) {
return false;
}
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
// for test
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// for test
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 100;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr1 = generateRandomArray(maxSize, maxValue);
int[] arr2 = copyArray(arr1);
selectSort(arr1);
comparator(arr2);
if (!isEqual(arr1, arr2)) {
succeed = false;
printArray(arr1);
printArray(arr2);
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
int[] arr = generateRandomArray(maxSize, maxValue);
printArray(arr);
selectSort(arr);
printArray(arr);
}
}
三:插入排序
1.算法
当前索引左边的所有元素是有序的,把当前索引和之前的元素进行比较
- 如果当前索引上的数小,就把之前的数往右移动
- 如果当前索引上的数大,结束
2. 特点
插入排序所需要的的时间取决于输入中元素的初始顺序
3. 代码
import java.util.Arrays;
public class InsertionSort {
public static void insertionSort(int[] arr){
if(arr == null || arr.length < 2){
return ;
}
insertionSort(arr,0,arr.length-1);
}
private static void insertionSort(int[] arr,int l,int r){
for(int i = 1; i < arr.length ;i++){
int num = arr[i];
for(int j = i-1; j >= 0; j--){
if(num < arr[j]){
swap(arr,j,j+1);
}
}
}
}
private static void swap(int[] arr,int i ,int j){
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
//for test
private static void comparator(int[] arr){
Arrays.sort(arr);
}
private static int[] generateRandomArr(int maxLength,int maxValue){
int[] arr = new int[(int)(Math.random() * (maxLength+1))];//[0,maxLength]长度的数组
for(int i = 0; i < arr.length ;i++){
arr[i] = (int)(Math.random() * (maxValue+1)) - (int)(Math.random() * maxValue);
}
return arr;
}
private static int[] copyArr(int[] arr){
if(arr == null){
return null;
}
int[] newArr = new int[arr.length];
for(int i = 0; i < arr.length ;i++){
newArr[i] = arr[i];
}
return newArr;
}
private static boolean isEqual(int[] arr1,int[] arr2){
if(arr1 == null && arr2 == null){
return true;
}
if(arr1 == null || arr2 == null){
return false;
}
if(arr1.length != arr2.length){
return false;
}
for(int i = 0; i < arr1.length ;i++){
if(arr1[i] != arr2[i]){
return false;
}
}
return true;
}
private static void printArr(int[] arr){
if(arr == null){
System.out.println("数组为空");
}
System.out.println("数组为:");
for(int i = 0; i < arr.length ;i++){
if(i == arr.length-1){
System.out.print(arr[i]);
break;
}
System.out.print(arr[i] + ",");
}
System.out.println();
}
public static void main(String[] args) {
int maxLength = 100;
int maxValue = 100;
int testTime = 500000;
boolean equal = true;
for(int i = 0; i < testTime ;i++){
int[] arr1 = generateRandomArr(maxLength, maxValue);
int[] arr2 = copyArr(arr1);
insertionSort(arr1);
comparator(arr2);
equal = isEqual(arr1,arr2);
if(equal){
continue;
}else{
printArr(arr1);
printArr(arr2);
System.out.println("Error");
break;
}
}
System.out.println(equal ? "没问题" : "出错了");
}
}
四:希尔排序
1.算法
一种基于 插入排序 的排序算法
- 插入排序:对于大规模、乱序数组的速度会很慢,因为它只交换相邻元素
- 希尔排序:交换不相邻的元素
希尔排序的思想是使数组中任意间隔 h 的元素都是有序的。这样的数组成为 h 有序数组。
对于任意以 1 结尾的 h 序列,我们都能将数组排序。
2. 特点
3.代码
import java.util.Arrays;
public class ShellSort {
public static void shellSort(int[] arr){
if(arr == null || arr.length < 2){
return ;
}
int h = 1;
while(h < arr.length / 3){
h = h*3+1;
}
while(h >= 1){
for(int i = h; i < arr.length ;i++){
for(int j = i; j >= h && arr[j-h] > arr[j]; j -= h){
swap(arr,j-h,j);
}
}
h /= 3;
}
}
private static void swap(int[] arr,int i ,int j){
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
//for test
private static void comparator(int[] arr){
Arrays.sort(arr);
}
private static int[] generateRandomArr(int maxLength,int maxValue){
int[] arr = new int[(int)(Math.random() * (maxLength+1))];//[0,maxLength]长度的数组
for(int i = 0; i < arr.length ;i++){
arr[i] = (int)(Math.random() * (maxValue+1)) - (int)(Math.random() * maxValue);
}
return arr;
}
private static int[] copyArr(int[] arr){
if(arr == null){
return null;
}
int[] newArr = new int[arr.length];
for(int i = 0; i < arr.length ;i++){
newArr[i] = arr[i];
}
return newArr;
}
private static boolean isEqual(int[] arr1,int[] arr2){
if(arr1 == null && arr2 == null){
return true;
}
if(arr1 == null || arr2 == null){
return false;
}
if(arr1.length != arr2.length){
return false;
}
for(int i = 0; i < arr1.length ;i++){
if(arr1[i] != arr2[i]){
return false;
}
}
return true;
}
private static void printArr(int[] arr){
if(arr == null){
System.out.println("数组为空");
}
System.out.println("数组为:");
for(int i = 0; i < arr.length ;i++){
if(i == arr.length-1){
System.out.print(arr[i]);
break;
}
System.out.print(arr[i] + ",");
}
System.out.println();
}
public static void main(String[] args) {
int maxLength = 100;
int maxValue = 100;
int testTime = 500000;
boolean equal = true;
for(int i = 0; i < testTime ;i++){
int[] arr1 = generateRandomArr(maxLength, maxValue);
int[] arr2 = copyArr(arr1);
shellSort(arr1);
comparator(arr2);
equal = isEqual(arr1,arr2);
if(equal){
continue;
}else{
printArr(arr1);
printArr(arr2);
System.out.println("Error");
break;
}
}
System.out.println(equal ? "没问题" : "出错了");
}
}
五、快排
1.算法
2.特点
有一个partition
过程,作用是以最后一个元素为基准,把小于这个元素的都放在左边,大于的都放在右边,等于的都放在中间。
3.代码
class Solution {
public int[] sortArray(int[] nums) {
//快排
if(nums == null || nums.length < 2){
return nums;
}
sortArray(nums,0,nums.length-1);
return nums;
}
private void sortArray(int[] nums,int l,int r){
if(l < r){
int[] p = partition(nums,l,r);
sortArray(nums,l,p[0]);
sortArray(nums,p[1],r);
}
}
private int[] partition(int[] nums,int l,int r){
int less = l-1;
int more = r;
while(l < more){
if(nums[l] < nums[r]){
swap(nums,++less,l++);
}else if(nums[l] > nums[r]){
swap(nums,--more,l);
}else{
l++;
}
}
swap(nums,r,l);
return new int[]{less,l+1};
}
private void swap(int[] nums,int i ,int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
六、归并排序
1. 算法
2. 特点
3.代码
class Solution {
public int[] sortArray(int[] nums) {
//归并排序
if(nums == null || nums.length == 0){
return nums;
}
mergeSort(nums,0,nums.length-1);
return nums;
}
private void mergeSort(int[] nums,int l,int r){
if(l == r){
return ;
}
int m = l + ((r-l)>>1);
mergeSort(nums,l,m);
mergeSort(nums,m+1,r);
merge(nums,l,m,r);
}
private void merge(int[] nums,int l,int m ,int r){
int[] help = new int[r-l+1];
int index = 0;
int p1 = l;
int p2 = m+1;
while(p1 <= m || p2 <= r){
if(p1 > m ){
help[index++] = nums[p2++];
continue;
}
if(p2 > r ){
help[index++] = nums[p1++];
continue;
}
help[index++] = nums[p1] > nums[p2] ? nums[p2++] : nums[p1++];
}
for(int i = 0; i < help.length ;i++){
nums[l+i] = help[i];
}
}
}