冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
冒泡排序可以说是最简单的排序算法,想必大多数人都已经熟练掌握,这里分享自己在学习过程中觉得挺不错的两种实现。
写法一:基于常规冒泡写法,加了是否发生交换的判断,来减少冒泡次数
/**
* 冒泡排序 升序
* 优化冒泡次数:通过标记是否发生交换来判断目前数组是否有序
* @param array 待排序数组
*/
public static void bubbleSort(int[] array) {
for (int i = array.length - 1; i > 0; i--) {
// 是否发生交换
boolean swapped = false;
for (int j = 0; j < i; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
swapped = true;
}
}
if (!swapped) {
// 没有发生交换,则说明数组已有序,停止冒泡
break;
}
}
}
写法二:通过上一次冒泡中最后一次发生交换的下标,来决定下一次冒泡结束的索引位置;同时也进行是否发生交换的判断,更好的优化排序效率(这个思路本人也第一次接触,感觉非常妙)
/**
* 冒泡排序 升序 较优法
* 优化冒泡次数:冒泡结束的索引位置为0则直接结束
* 优化比较次数:通过上一次冒泡中最后一次发生交换的下标,来决定下一次冒泡结束的索引位置
* @param array 待排序数组
*/
public static void bubbleSort2(int[] array) {
// 冒泡结束的索引位置
int end = array.length - 1;
do {
// 冒泡最后一次发生交换的下标
int lastSwapIndex = 0;
for (int j = 0; j < end; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
lastSwapIndex = j;
}
}
end = lastSwapIndex;
} while (end != 0);
}
选择排序(Selection sort)是一种简单直观的排序算法。
它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
我这里选择排序的实现就算是比较中规中矩的,只要理解了它的工作原理思想,想必实现也不在话下。
/**
* 选择排序 升序
* @param array 待排序数组
*/
public static void selectionSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
int min = i;
for (int j = i + 1; j < array.length; j++) {
if (array[min] > array[j]) {
min = j;
}
}
if (min != i) {
int temp = array[i];
array[i] = array[min];
array[min] = temp;
}
}
}
插入排序(Insertion Sort),一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。
插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。
/**
* 插入排序 升序
* @param array 待排序数组
*/
public static void insertionSort(int[] array) {
// i:待插入的元素下标
for (int i = 1; i < array.length; i++) {
int temp = array[i];
// j:已排序区域尾元素的下标
int j;
for (j = i - 1; j >= 0; j--) {
if (temp > array[j]) {
break;
} else {
array[j + 1] = array[j];
}
}
array[j + 1] = temp;
}
}
快速排序(Quick Sort)通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
快速排序的递归实有两种方式,①单边循环 ②双边循环。下面就来看看两种方式是如何实现快排的吧。
① 单边循环:
pivot
选择最右元素作为基准点元素j
指针负责找到比基准点小的元素,一旦找到则与i
进行交换i
指针维护小于基准点元素的边界,也是每次交换的目标索引i
交换,i
即为分区位置 /**
* 递归快排 单边循环
* @param array 待排序数组
* @param left 待排序分区起始索引
* @param right 待排序分区结束索引
*/
public static void quickSort(int[] array, int left, int right) {
if (left >= right) {
return;
}
int pivot = array[right];
int i = left;
for (int j = left; j < right; j++) {
if (array[j] < pivot) {
if (j != i) {
int tem = array[i];
array[i] = array[j];
array[j] = tem;
}
i++;
}
}
if (i != right) {
array[right] = array[i];
array[i] = pivot;
}
quickSort(array, left, i - 1);
quickSort(array, i + 1, right);
}
② 双边循环:
pivot
选择最左元素作为基准点元素rear
指针负责从右向左找比基准点小的元素,front
指针负责从左向右找比基准点大的元素,front
,rear
相交front
(此时front
与rear
相等)交换,front
即为分区位置 /**
* 递归快排 双边循环
* @param array 待排序数组
* @param left 待排序分区起始索引
* @param right 待排序分区结束索引
*/
public static void quickSort2(int[] array, int left, int right) {
if (left >= right) {
return;
}
int pivot = array[left];
int front = left;
int rear = right;
while (front < rear) {
while (front < rear && array[rear] > pivot) {
rear--;
}
while (front < rear && array[front] <= pivot) {
front++;
}
if (front < rear) {
int tem = array[front];
array[front] = array[rear];
array[rear] = tem;
}
}
if (front != left) {
array[left] = array[front];
array[front] = pivot;
}
quickSort2(array, left, front - 1);
quickSort2(array, front + 1, right);
}
双边循环注意点:
1.基准点在左边,前后指针查找,需要先rear
指针再front
指针
2.循环实现前后指针查找时,循环条件一定要加上front < rear
欢迎大家讨论交流,如有错误不当之处,还请读者批评指正。
觉得有收获的话帮忙点个赞吧,