目录
1.插入排序
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。 按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序
代码如下:
public static void insertSort(long[] array) {
// 一共要取多少个元素来进行插入过程(无序区间里有多少个元素)
for (int i = 0; i < array.length - 1; i++) {
// 有序区间 [0, i] 至少在 i == 0 的时候得有一个元素
// 无序区间 [i + 1, n)
// 先取出无序区间的第一个元素,记为 k
long k = array[i + 1];
// 从后往前,遍历有序区间{
// 找到合适的位置退出
// 所谓合适的位置,就是第一次 k >= array[j] 的位置
int j;
for (j = i; j >= 0 && k < array[j]; j--) {
array[j + 1] = array[j]; // 将不符合条件的数据往后搬一格
}
array[j + 1] = k;
}
}
2. 希尔排序
希尔排序可以说是一种特殊的插入排序,具体体现在希尔排序是将数据按照固定的长度分组,然后每个组内的数据进行插排。分组排序的操作可以称之为预排序,虽然不能直接将数据排列有序,但是可以让数据解决有序,提高跨度而进行插排。
例如下图:十个数据,每四个划分为一组,分的小组之间进行插入排序。而这个间隔多少我们可以用一个变量gap来表示,使用比较大的gap更有利于排序,gap会逐渐趋于1,而当gap区域1时,这组数据就是有序的。一般gap取gap= array.length/2 gap= gap/2 ——> 1
代码如下:
private static void insertSortWithGap(long[] array, int gap) {
// 外围的循环次数是 n - gap 次
for (int i = 0; i < array.length - gap; i++) {
// 一共有 gap 个分组
// 认为一开始 [0, i + gap) 有序
// [i + gap, n) 无序
long k = array[i + gap];
int j;
// j 下标只去找同一组的元素比较,所以每次跳过 gap
for (j = i; j >= 0 && k < array[j]; j = j - gap) {
array[j + gap] = array[j];
}
array[j + gap] = k;
}
}
public static void shellSort(long[] array) {
int gap = array.length / 2;
while (gap != 1) {
insertSortWithGap(array, gap);
gap = gap / 2;
}
// 最后再执行一次插排
insertSort(array);
}
3.冒泡排序
冒泡排序是一组数据从第一个元素开始和后面的每一个元素比较大小,如果小于后面的数,位置不变,如果大于后面的数,两者交换位置即可
public static void bubbleSort(long[] array) {
for (int i = 0; i < array.length - 1; i++) {
boolean sorted = true;
for (int j = 0; j < array.length - i - 1; j++) {
if (array[j] > array[j + 1]) { // 关注这条语句的执行次数 和 array.length 之间的关系
sorted = false;
swap(array, j, j + 1);
}
}
if (sorted) {
return;
}
}
}
private static void swap(long[] array, int i, int j) {
long t = array[i];
array[i] = array[j];
array[j] = t;
}
4.选择排序
直接选择排序 —— 每次选出最小的数放在最前面来进行实现
代码如下:
public static void selectSort(long[] array) {
// 每次选择出最大的数,放到最后去
// 一共要选择出 n - 1 个数
for (int i = 0; i < array.length - 1; i++) {
// 通过遍历无序区间,只需要找到最大的数最在的位置就行(以下标的形式体现)不要做交换
// 无序 [0, n - i)
int maxIndex = 0; // 假设最大的数放在一开始
for (int j = 1; j < array.length - i; j++) {
if (array[j] > array[maxIndex]) {
// 说明,找到了无序区间的新的最大的数
// 所以,记录最大数的下标
maxIndex = j;
}
}
// 遍历完成之后,无序区间的最大的数就放在 maxIndex 所在下标处
// array[maxIndex] 是最大数
// 交换 [maxIndex] 和 无序区间的最后一个元素 [n - i - 1]
swap(array, maxIndex, array.length - i - 1);
}
}
private static void swap(long[] array, int i, int j) {
long t = array[i];
array[i] = array[j];
array[j] = t;
}
5.堆排序 (选择排序)
堆排序是指利用堆这种数据结构所设计的一种排序算法.堆是一个近似于完全二叉树的结构,在此结构中,例如大堆,总有双亲结点大于或者等于孩子结点,右孩子大于左孩子的特性。所以我们可以把待排序的数据抽象成一个完全二叉树,然后根据此特性不断向下调整,直到成为一个大堆,此时的数据就有序。 首先要建堆(对整个数组建堆)然后进行如下步骤:
此时第一个树是将所给数据写成二叉树,然后每个小的二叉树的双亲结点和左右孩子结点比较,将如果双亲结点大于等于孩子结点,则不动;反之讲左右孩子较大的和其交换
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;
}
如此一直循环比较,直到该树成为一个大堆,此时该组数也成为了有序数组。
代码如下:
public static void heapSort(long[] array) {
// 1. 建立大堆
createBigHeap(array); // O(n * log(n)) 甚至 O(n)
// 2. 遍历 n - 1 次
for (int i = 0; i < array.length - 1; i++) { // 循环 n // O(n*log(n))
// 2.1 交换之前的无序区间 [0, n - i)
swap(array, 0, array.length - i - 1); // O(1)
// 交换之后的无序区间 [0, n - i - 1),元素个数 n - i - 1 个
// 2.1 对堆的 [0] 进行向下调整,堆里的元素个数就是无序区间的元素个数
shiftDown(array, array.length - i - 1, 0); // O(log(n))
}
}
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;
}
}