//冒泡排序 从小到大 从前向后
//8, 7, 6, 5, 4 第1个---比较4次 2---3 3---2 4---1
// 1, 2, 3, 4, 5
//有序无序都需要比较4+3+2+1=10次 (n-1)+(n-2)+...+1=[n*(n-1)]/2---> O(n方)
//无序时交换的次数=比较的次数*3 有序时交换的次数=0
//内层循环每次结束都可以确定一个最值 由于 if (arr[j] > arr[j + 1])---》稳定
public static int[] BubbleSort(int[] arr) {
int length = arr.length;
int temp;
int compareCount = 0;
int swapCount = 0;
//比较 i-1 次 第1个---比较4次 2---3 3---2 4---1
for (int i = 0; i < length; i++) {
for (int j = 0; j < length - i - 1; j++) {
compareCount++;
if (arr[j] > arr[j + 1]) {
swapCount += 3;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
//最坏(逆序):比较了:10次 交换了:30次
//最好(有序):比较了:10次 交换了:0次
System.out.println("比较了:" + compareCount + "次 交换了:" + swapCount + "次");
return arr;
}
//选择排序 假设第一个为最小值
//8, 7, 6, 5, 4 1---4 2---3 3---2 4---1
//1, 2, 3, 4, 5 1---4 2---3 3---4 4---1
//最好最坏都比较4+3+2+1=10次 (n-1)+(n-2)+...+1=[n*(n-1)]/2---> O(n方)
/*
每次找到一个最值 交换3n次 和冒泡比较,交换的次数和长度有关,不在和比较的次数挂钩,
所以即使无序,交换的次数会比冒泡少,但是有序时交换的次数会比冒泡多
*/
//由于 if (arr[j] < min) ---》稳定
public static int[] selectSort(int[] arr) {
int compareCount = 0;
int swapCount = 0;
int min, index;
int temp;
for (int i = 0; i < arr.length; i++) {
//默认第一个是最小值
min = arr[i];
index = i;
//遍历确定真正的最小值并记录下标
for (int j = i + 1; j < arr.length; j++) {
compareCount++;
if (arr[j] < min) {
min = arr[j];
index = j;
}
}
//将最小值和arr[i]交换位置后 arr[i]及之前的有序 之后无序
//将arr[i]之后的重复这个过程
swapCount += 3;
temp = arr[i];
arr[i] = min;
arr[index] = temp;
}
//最坏:比较了:10次 交换了:15次
//最好:比较了:10次 交换了:15次
System.out.println("比较了:" + compareCount + "次 交换了:" + swapCount + "次");
return arr;
}
//插入排序
//8, 7, 6, 5, 4 1=0 2---1 3---2 4---3 5---4
//1, 2, 3, 4, 5 1=0 2---1 3---1 4---1 5---1
//最好比较1+1+1+1=4次 O(n) 不需要交换
//最坏比较1+2+...+(n-1)=[n*(n-1)]/2---> O(n方) 交换=比较的次数*3==10*3=30次
/*和冒泡排序类似,在最好情况下, 插入排序只需要比较 不需要交换
在最坏情况下, 每次都要交换
插入和冒泡比较类似的点在于:每次都能找到一个最值,将序列换分为有序和无序两个部分,冒泡排序向后比较,插入排序向前比较
区别是:冒泡排序每次的最值位置确定,不需要再改变 而插入需要
*/
//由于(arr[j] < arr[j - 1])---》稳定
//注: while (j > 0 &&arr[j] < arr[j - 1] )顺序不要变
public static int[] insertSort(int[] arr) {
int swapCount = 0;
//i从0或者1开始都可以 比较次数只相差1 逻辑上默认第一个有序,从第二个开始和前面的比较即可
for (int i = 1; i < arr.length; i++) {
//默认第一个有序
int j = i;
//利用短路 j>0写在前面 依次和前面的元素比较交换
while (j > 0 && arr[j] < arr[j - 1]) {
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
j--;
swapCount += 3;
}
}
//最坏:比较了:10次 交换了:30次
//最好:比较了:4次 交换了:0次
System.out.println("交换了:" + swapCount + "次");
return arr;
}
//希尔排序
//8, 7, 6, 5, 4 87--65--4 6比较1次 4比较2次 5比较了1次 -》45-67-8 所以一共比较了4次 交换了4*3=12次
//1, 2, 3, 4, 5 12--34--5 比较0次
//希尔排序是一种有步长的插入排序 在元素数量较多的情况下,使整体大小分布有种趋向,目的是减少交换的次数
//因为步长不断变化-》不稳定 例如:3 5* 6 1 5->3 1 5 5* 6->1 3 4 5* 6
//注:(k - i) >= 0
public static int[] shellSort(int[] arr) {
int swapCount = 0;
//每次划分的区间 i表示步长
for (int i = arr.length / 2; i > 0; i = i / 2) {
//每个区间有几个元素 就要进行几轮
for (int j = 0; j < i; j++) {
//然后是步长为i的插入排序
for (int k = j + i; k < arr.length; k += i) {
while ((k - i) >= 0 && arr[k] < arr[k - i]) {
int temp = arr[k - i];
arr[k - i] = arr[k];
arr[k] = temp;
k = k - i;
swapCount += 3;
}
}
}
}
//最坏:交换了:12次
//最好:交换了:0次
System.out.println("交换了:" + swapCount + "次");
return arr;
}
//快速排序
//无序: 交换了:8次
//有序:交换了:8次 有序的状态
/*注:if (low > high) return; 8,7,6,5,4->4,7,6,5,8->...如果没有这个判断
会出现right=-1 left=0 -》 while(left<right)不满足 -》 QuickSort(arr, 0, - 1);死循环 -》stackover
*/
//arr[left] < key --》稳定
public static void quickSort(int[] arr, int low, int high) {
if (low > high) return;
int left = low;
int right = high;
//第一个位置的元素作为基准元素
int key = arr[left];
while (left < right) {
while (left < right && arr[right] > key) {
//从右往左扫描,找到第一个比基准值小的元素
right--;
}
//此时key=arr[left] 该处的值已经被key保存 且 表明基准值应该在arr[right]的右侧
arr[left] = arr[right];
swapCount++;
while (left < right && arr[left] < key) {
//从左往右扫描,找到第一个比基准值大的元素
left++;
}
//表明基准值应该在arr[left]的左侧
arr[right] = arr[left];
swapCount++;
}
//left = right 表明第一次快排结束
arr[left] = key;
//对基准值左边的元素进行递归排序
quickSort(arr, low, left - 1);
//对基准值右边的元素进行递归排序。
quickSort(arr, right + 1, high);
}
//归并排序
public static void mergeSort(int[] arr, int left, int right) {
//只有两个直接返回
if (left >= right)
return;
//划分左侧
int mid = (left + right) / 2;
mergeSort(arr, left, mid);
//划分右侧
mergeSort(arr, mid + 1, right);
//合并
merge(arr, left, mid + 1, right);
System.out.println("-------------");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static void merge(int[] arr, int leftstart, int rightstart, int rightend) {
//678 1245
int mid = rightstart - 1;
int leftpos = leftstart;
int rightpos = rightstart;
/*if (rightend % 2 == 0) {//奇数个
rightpos = mid + 1;
} else {
rightpos = mid;
}*/
//指向新的数组的起始位置
int k = 0;
//不要写成arr.length 数组复制的时候会出现越界异常
int[] temp = new int[rightend - leftstart + 1];
while (leftpos <= mid && rightpos <= rightend) {
if (arr[leftpos] <= arr[rightpos]) {
temp[k++] = arr[leftpos++];
} else {
temp[k++] = arr[rightpos];
rightpos++;
}
}
while (leftpos <= mid) {
temp[k++] = arr[leftpos++];
}
while (rightpos <= rightend) {
temp[k++] = arr[rightpos++];
}
//将temp里的元素复制到arr中保持修改
for (int m = 0; m < temp.length; m++) {
arr[leftstart + m] = temp[m];
}
for (int i = 0; i < temp.length; i++) {
System.out.println(temp[i]);
}
}