排序的稳定性:排序算法保证一定不会交换相等元素的相对位置;
冒泡排序:主要思想:在数组中一组数据经过两两比较得到最大的元素到数组最后一个位置;
经过双层循环:外层循环主要是考虑要经过多少次冒泡过程,再每一次冒泡过程中得到无序数组中的最大元素,内层循环表示一次冒泡过程中无需区间的相邻元素,需要两两比较一次;
在这里用到了交换的方法所以先写了一个交换方法的代码;
public static void swap(long[] array, int i, int j) {
long t = array[i];
array[i] = array[j];
array[j] = t;
}
冒泡排序:如果一组数据中有8个元素,需要经历7次冒泡过程,每一次冒泡过程需要两两比较得到无序区间中最大的元素;
在这里面无序区间的大小和元素个数一定要看明白;
public static void bubbleSort(long[] array) {
// 要排好整个数组,一共需要多少次冒泡过程
for (int i = 0; i < array.length - 1; i++) {
// 无序区间:[0, array.length - i) 元素个数 array.length - i
// 内部的循环整体,表示一次冒泡过程
// 冒泡过程:
// 无序区间的相邻的两个元素,需要两两比较一次
for (int j = 0; j < array.length - i - 1; j++) {
// 永远保证,相邻两个元素,最大的在后边
if (array[j] > array[j + 1]) {
Swap.swap(array, j, j + 1);
}
}
}
}
选择排序:是双层循环和冒泡排序是一样的,外层排序控制需要多少次选择过程,内层排序遍历无需区间,找到最大元素的下标,这里需要注意的是,假设数组长度是8,数组下标是0——7。
外部循环:第一次选择:无序区间是从[0,7)开始的,i=0;
内不循环:需要找下标从0到7,元素中最大元素的下标,j<array.length(8) - 0,找到最大下标与最后一个进行交换;
外部循环:第二次选择:无序区间是从[0,6)开始的,i=1;
内不循环:需要找下标从0到6,元素中最大元素的下标,j<array.length(8) - 1,找到最大下标与最后一个进行交换;
依次循环;
选择排序代码:
public static void selectSort(long[] array) {
// 要排好整个数组,一共需要多少次选择过程
for (int i = 0; i < array.length - 1; i++) {
// 无序区间:[0, array.length - i) 元素个数 array.length - i
// 内部的循环整体,遍历无序区间,找到最大的元素的过程 => 最大元素所在的下标
int maxElementIdx = -1; //先设定最大下标为-1(无效数值)
for (int j = 0; j < array.length - i; j++) {
if (maxElementIdx == -1 || array[j] > array[maxElementIdx]) {
maxElementIdx = j;
}
}
// 交换,最大的元素 和 无序区间最后一个元素的位置
// 要交换,需要最大元素的下标,和最后一个元素的下标
Swap.swap(array, maxElementIdx, array.length - i - 1);
}
}
插入排序:主要思想是以第一个元素为有序区间,后面的元素为无序区间,找到无序区间内第一个元素插入有序区间内合适的位置,依次循环;下面代码主要分为两步,第一是遍历整个有序区间查找放元素的合适位置,第二部是对有序区间内部元素进行搬移。
这里重点的地方时内部循环中的两个for 不容易看出来,自己可以试个序列。
public static void insertSort1(long[] array) {
// 要排好整个数组,一共需要多少次选择过程
//外部循环控制次数
for (int i = 1; i < array.length; i++) {
// 有序区间:[0, i)
// 待插入的元素:无序区间的第一个元素
//
long toInsert = array[i];
// 在有序区间内查找要插入的位置 —— 从后往前遍历查找
// 有序区间反向表述: [i - 1, 0]
//当分为大小进行比较时,出循环时不要忘记j--;
int j;
for (j = i - 1; j >= 0; j--) {
if (toInsert >= array[j]) {
break;
}
}
//这里出循环找到了要插入的位置
int toInsertIdx = j + 1;
// 把元素插入到合适的位置
//对有序区间下标在 toInsertIdx 后的元素进行搬移,依次向后移,把 toInsertIdx 位置空
//出来把 toInsert 代表的元素放进去;
for (int k = i - 1; k >= toInsertIdx; k--) {
array[k + 1] = array[k];
}
array[toInsertIdx] = toInsert;
}
}
插入排序的优化算法,原理和上面的相同,就是更简化了一些;
public static void insertSort(long[] array) {
for (int i = 1; i < array.length; i++) {
long toInsert = array[i];
int j;
for (j = i - 1; j >= 0 && toInsert < array[j]; j--) { // 不加等号,具备稳定性
array[j + 1] = array[j];
}
//在不满足的情况下直接跳出循环执行下面的,带入一组数据比较好看
array[j + 1] = toInsert;
}
}
希尔排序(shell sort)原理图如下所示,需要对数组进行分组再插入排序;
public static void shellSort(long[] array) {
int group = array.length / 2;
//当group=1,而且已经完成依次排序之后停止循环;
while (true) {
groupInsertSort(array, group);
if (group == 1) {
// 只有 1 组,并且已经完成排序时,停止循环
return;
}
group = group / 2;
}
}
//下面代码和插入排序代码有些相似,可以有一组数据带入试一下
private static void groupInsertSort(long[] array, int group) {
for (int i = group; i < array.length; i++) {
long toInsert = array[i];
int j;
for (j = i - group; j >= 0 && toInsert < array[j]; j -= group) {
array[j + group] = array[j];
}
array[j + group] = toInsert;
}
}
快速排序的思想:
public static void quickSort(long[] array) {
quickSortRange(array, 0, array.length - 1);
}
private static void quickSortRange(long[] array, int from, int to) {
int size = to - from + 1;
// 3. 直到,待排序区间的元素个数 <= 1
if (size <= 1) {
return;
}
// 1. 选择基准值 array[from]
// 2. 做 partition,返回基准值跑到哪里去了 [pivotIdx]
int pivotIdx = partition1(array, from, to);
// 左边的区间如何表示:[from, pivotIdx - 1]
// 右边的区间如何表示:[pivotIdx + 1, to]
// 3. 分别对左右两个小区间按照相同的方式处理(递归调用)
quickSortRange(array, from, pivotIdx - 1);
quickSortRange(array, pivotIdx + 1, to);
}
partition1
private static int partition1(long[] array, int from, int to) {
long pivot = array[from];
int left = from;
int right = to;
// 只要还有未比较的元素,循环就得继续
// (left, right] 的元素个数 right - left > 0
while (left < right) {
// 因为我们基准值在左边,所以先走右边
while (left < right && array[right] >= pivot) {
right--;
}
while (left < right && array[left] <= pivot) {
left++;
}
Swap.swap(array, left, right);
}
Swap.swap(array, from, left);
return left;
}
partition2
private static int partition2(long[] array, int from, int to) {
long pivot = array[from];
int left = from;
int right = to;
while (left < right) {
while (left < right && array[right] >= pivot) {
right--;
}
array[left] = array[right];
while (left < right && array[left] <= pivot) {
left++;
}
array[right] = array[left];
}
array[left] = pivot;
return left;
}
partition3
private static int partition3(long[] array, int from, int to) {
long pivot = array[from];
int d = from + 1;
for (int i = from + 1; i <= to; i++) {
if (array[i] < pivot) {
Swap.swap(array, d, i);
d++;
}
}
Swap.swap(array, from, d - 1);
return d - 1;
}