冒泡排序
算法描述
- 比较相邻的元素。如果第一个比第二个大,则交换。
- 对每一对相邻的元素做同样的工作,从开始的第一对到结尾的最后一对,这样最后的元素就是最大的数
- 针对所有元素重复以上的步骤,除了最后一个
- 重复1-3的步骤,直到排序完成
动画演示
算法实现
@Slf4j
public class BubbleSort {
public static void main(String[] args) {
int[] nums = {9,3, 1, 4, 2, 7,8,6,5};
buddle(nums);
for (int number : nums) {
System.out.println(number);
}
}
private static void buddle(int[] nums) {
for (int i = 0; i < nums.length - 1; i++) {
for (int j = 0; j < nums.length - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j + 1];
nums[j + 1] = nums[j];
nums[j] = temp;
}
}
log.info("第{}轮冒泡结束,数组为{}",i+1, Arrays.toString(nums));
}
}
}
// 第1轮冒泡结束,数组为[3, 1, 4, 2, 7, 8, 6, 5, 9]
// 第2轮冒泡结束,数组为[1, 3, 2, 4, 7, 6, 5, 8, 9]
// 第3轮冒泡结束,数组为[1, 2, 3, 4, 6, 5, 7, 8, 9]
// 第4轮冒泡结束,数组为[1, 2, 3, 4, 5, 6, 7, 8, 9]
// 第5轮冒泡结束,数组为[1, 2, 3, 4, 5, 6, 7, 8, 9]
// 第6轮冒泡结束,数组为[1, 2, 3, 4, 5, 6, 7, 8, 9]
// 第7轮冒泡结束,数组为[1, 2, 3, 4, 5, 6, 7, 8, 9]
// 第8轮冒泡结束,数组为[1, 2, 3, 4, 5, 6, 7, 8, 9]
//优化点1:如果某一轮冒泡没有发生交换,则表示所有数据有序,可以结束外层循环
private static void buddle_v1(int[] nums) {
for (int i = 0; i < nums.length - 1; i++) {
boolean swapped = false;
for (int j = 0; j < nums.length - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
swapped = true;
int temp = nums[j + 1];
nums[j + 1] = nums[j];
nums[j] = temp;
}
}
log.info("第{}轮冒泡结束,数组为{}",i+1, Arrays.toString(nums));
if (!swapped) {
break;
}
}
}
//第1轮冒泡结束,数组为[3, 1, 4, 2, 7, 8, 6, 5, 9]
//第2轮冒泡结束,数组为[1, 3, 2, 4, 7, 6, 5, 8, 9]
//第3轮冒泡结束,数组为[1, 2, 3, 4, 6, 5, 7, 8, 9]
//第4轮冒泡结束,数组为[1, 2, 3, 4, 5, 6, 7, 8, 9]
//第5轮冒泡结束,数组为[1, 2, 3, 4, 5, 6, 7, 8, 9]
//每轮冒泡时,最后一次交换索引可以作为下一轮冒泡的比较次数,减少每轮冒泡次数
private static void buddle_v2(int[] nums) {
int n = nums.length - 1;
//== while(true)
for (int i = 0; i < nums.length - 1; i++) {
int last = 0;
for (int j = 0; j < n; j++) {
if (nums[j] > nums[j + 1]) {
last = j;
int temp = nums[j + 1];
nums[j + 1] = nums[j];
nums[j] = temp;
}
}
n = last;
if (n == 0) {
break;
}
}
}
选择排序
算法描述
- 将数组分为两个子集,排序的和未排序的,每一轮从未排序的子集中选出最小的元素,放入排序子集
- 重复以上步骤,直到整个数组有序
动画演示
算法实现
@Slf4j
public class SelectionSort {
public static void main(String[] args) {
int[] nums = {9,3, 1, 4, 2, 7,8,6,5};
selection(nums);
log.info("最后的数组为{}", Arrays.toString(nums));
}
private static void selection(int[] nums) {
for (int i = 0; i < nums.length - 1; i++) {
int min = i;
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] < nums[min]) {
min = j;
}
}
if (min != i) {
//交换
int temp = nums[min];
nums[min] = nums[i];
nums[i] = temp;
}
}
}
}
与冒泡排序比较
-
二者平均时间复杂度都是 O(n^2)
-
选择排序一般要快于冒泡,因为其交换次数少
-
但如果集合有序度高,冒泡优于选择
-
冒泡属于稳定排序算法,而选择属于不稳定排序
稳定排序指,按对象中不同字段进行多次排序,不会打乱同值元素的顺序
不稳定排序则反之
插入排序
算法描述
- 从第一个元素开始,该元素可以认为已经被排序
- 取出下一个元素,在已经排序的元素序列中比较(从后向前扫描)
- 如果当前元素小于已排序比较的元素,已排序往后移动一位
- 已排序的比较元素往前移动,重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
- 新元素插入当前位置
- 重复2-5的步骤
动画演示
算法实现
@Slf4j
public class InsertionSort {
public static void main(String[] args) {
int[] nums = {9,3, 1, 4, 2, 7,8,6,5};
insertion(nums);
log.info("最后的数组为{}", Arrays.toString(nums));
}
private static void insertion(int[] nums) {
for (int i = 1; i < nums.length; i++) {
int current = nums[i];
int j = i;
while (j > 0) {
if (current < nums[j - 1]) {
nums[j] = nums[j - 1];
j--;
} else {
break;
}
}
nums[j] = current;
}
}
}
希尔排序
算法描述
希尔排序是插入排序的一种,又称“缩小增量排序”,是插入排序算法的一种更高效的改进版本。
-
首先选取一个间隙序列,如 (n/2,n/4 … 1),n 为数组长度
-
每一轮将间隙相等的元素视为一组,对组内元素进行插入排序,目的有二
① 少量元素插入排序速度很快
② 让组内值较大的元素更快地移动到后方
-
当间隙逐渐减少,直至为 1 时,即可完成排序
动画演示
算法实现
@Slf4j
public class ShellSort {
public static void main(String[] args) {
int[] nums = {9, 3, 1, 4, 2, 7, 8, 6, 5};
shell(nums);
log.info("最后的数组为{}", Arrays.toString(nums));
}
private static void shell(int[] nums) {
int length = nums.length;
for (int gap = length / 2; gap >= 1; gap = gap / 2) {
// 注意:这里和动图演示的不一样,动图是分组执行,实际操作是多个分组交替执行
for (int i = gap; i < nums.length; i++) {
int current = nums[i];
int j = i;
while (j > 0) {
if (nums[j] < nums[j - gap]) {
nums[j] = nums[j - gap];
j = j - gap;
} else {
break;
}
}
nums[j] = current;
}
}
}
}
归并排序
算法描述
- 尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是1为止。
- 将相邻的两个子组进行合并成一个有序的大组。
- 不断的重复步骤2,直到最终只有一个组为止。
动画演示
算法实现
@Slf4j
public class MergeSort {
public static void main(String[] args) {
int[] nums = {9, 3, 1, 4, 2, 7, 8, 6, 5};
sort(nums);
log.info("最后的数组为{}", Arrays.toString(nums));
}
public static void sort(int[] nums) {
int[] temp = new int[nums.length];
sort(nums, 0, nums.length - 1, temp);
}
private static void sort(int[] nums, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) >>> 1;
sort(nums, left, mid, temp);
sort(nums, mid + 1, right, temp);
merge(nums, left, mid, right, temp);
}
}
//3个指针的实现
private static void merge(int[] nums, int left, int mid, int right, int[] temp) {
int lo = left;//左序列指针
int hi = mid + 1;//右序列指针
int tempIndex = left;//临时数组指针
while (lo <= mid && hi <= right) {
if (nums[lo] < nums[hi]) {
temp[tempIndex++] = nums[lo++];
} else {
temp[tempIndex++] = nums[hi++];
}
}
//上面的循环结束后,如果退出循环的条件是i<=mid,则证明左边小组中的数据已经归并完毕,如果退出循环的条件是j<=right,则证明右边小组的数据已经填充完毕;
//如果左边还有剩下的,将左边剩余元素填充进temp中
while (lo <= mid) {
temp[tempIndex++] = nums[lo++];
}
//如果右边还有剩下的,将右边剩余元素填充进temp中
while (hi <= right) {
temp[tempIndex++] = nums[hi++];
}
log.info("temp数组为{}", Arrays.toString(temp));
//将temp中的元素全部拷贝到原数组中
for (int index = left; index <= right; index++) {
nums[index] = temp[index];
}
}
}
快速排序
算法描述
- 首先设定一个分界值,通过该分解值将数组分成左右两部分
- 将大于或等于分界值的数据放在数据右边,小于分界值的数据放到数组的左边。
- 左边和右边的数据各自做排序。对于左侧的数组,又可以取一个分界值,将数组分成左右两部分,左边放较小值,右边放较大值。右边也同样
- 重复上面过程。
动画演示
算法实现
@Slf4j
public class QuickSort {
public static void main(String[] args) {
int[] nums = {9, 3, 1, 4, 2, 7, 8, 6, 5};
quick(nums, 0, nums.length - 1);
log.info("最后的数组为{}", Arrays.toString(nums));
}
public static void quick(int[] a, int l, int h) {
if (l >= h) {
return;
}
int p = partition(a, l, h); // p 索引值
quick(a, l, p - 1); // 左边分区的范围确定
quick(a, p + 1, h); // 左边分区的范围确定
}
private static int partition(int[] a, int l, int h) {
int pv = a[l];//左边作为基本点
int left = l;
int right = h;
while (left < right) {
// right 从右找小的
while (left < right && a[right] > pv) {
right--;
}
// left 从左找大的
while (left < right && a[left] <= pv) {
left++;
}
swap(a, left, right);
}
swap(a, l, right);
log.info(Arrays.toString(a) + " right=" + right);
return right;
}
}
参考资料
www.cnblogs.com/onepixel/p/… 十大经典排序算法(动图演示)
www.cnblogs.com/chengxiao/p… 图解排序算法
www.bilibili.com/video/BV1iJ… java 数据结构和算法
www.bilibili.com/video/BV15b… java 面试宝典