原题出处: Java 数据结构与算法(第二版) Robert Lafore
因为看到这个泡反向走的思路挺有意思的, 又有可行性, 苦于没找到课后答案, 于是就调试并整理了出来 hh.
简介
是否可以在 in 索引从左移动, 直到比较到最大的更大值并移动到右边的 out 变量**后**,
修改 bubbleSort() 方法, 使它称为双向移动的.
这样, in 索引先向以前一样, 将最大的数据项从左移到右, 当它到达 out 变量的位置时, 它掉头并将最小的数据项从右移到左.
提示: 需要 2 个外部索引变量, 一个在右边(之前的 out 变量), 另一个在左边.
分析
方案上是可行的!
复盘原版冒泡的过程:
in
经过相邻2个比较后将更大值移动到最右边, 经过一轮后, 更大的那个值经过反复拉扯, 总会移动到最右边的位置, 也就是 out
索引的位置.
那么在反向走的时候, 也可以将最小值移动到最左边嘛, 同样的道理.
过程疏通
在经过调试后, 有了下面的实现.
修改过程:
在 in 走到 out 位置的时候, 此时, in 已经结束了一轮的循环. 总是达到当前轮的最右边.
所以, 这个时候让 in 往回走是最合适的.
引入动态变量 back, 初始值为 in, 终点值为 1, 每次递减.
比较相邻2个数, 当遇到更小值的时候, 往左移动(交换)
直到最小值被交换到最左边.
同理, 反向一轮后, 左边部分也默认升序(无需进行冒泡)
修改 in 的初始值为 back+1, 下一轮从 back+1, 也就是左边已经升序部分的下一位开始.
代码实现(Java语言实现)
1.在 in
一轮循环后的位置, 添加 1 个以 in
为"起点"往回走的循环.
2.更新 in
从左边已升序序列的下一位开始.
//当 in 走到最右边, 往回走, 往回走的同时将最小值移动到最左边
public void bubbleSort() {
int out, back, in;
for (out = nElems - 1; out > 1; out--) {
for (in = 0; in < out; in++) {
if (a[in] > a[in+1]) {
swap(in, in+1);
}
}
for (back = in-1; back > 1; back--) {
if (a[back] < a[back-1]) {
swap(back, back-1);
}
}
in = back+1;
//优化: 使in等于back+1, 由于back及其左边也已升序排列, 无需再继续冒泡
}
}
调试图解
为了更加直观的感受, 这里只录制第一轮来回走的过程.
同样为了更加直观的体验, 推荐重点关注👉
观察黄色部分的 99 从上面移动到最下面的过程.
观察黄色部分的 0 从下面移动到最上面的过程.
实验数据
[77 99 44 55 22 88 11 0 66 33 ]
冒泡算法的优化
1.冒泡在什么时候才交换?
答: 发现更大值的时候. 换句话说也就是非升序的时候.
2.有没有一种情况, 中间有一轮已经升序了? 后面的循环还有必要的吗?
答: 确实! 比如 [90, 1, 2, 3, 4, 5, 6, 7, 8]
.
我们知道, 冒泡只有在非升序的时候才进行交换. 那么, 如果没有发生交换的话, 则说明序列已经升序.
3.实现
增加一个布尔变量. 在交换过程修改. 当有一轮冒泡后发现布尔值没有被修改, 则直接返回序列.
public void bubbleSort() {
int out, back, in;
for (out = nElems - 1; out > 1; out--) {
boolean hasSwap = false;
for (in = 0; in < out; in++) {
if (a[in] > a[in+1]) {
hasSwap = true; //*
swap(in, in+1);
}
}
for (back = in-1; back > 1; back--) {
if (a[back] < a[back-1]) {
hasSwap = true; //*
swap(back, back-1);
}
}
in = back+1;
//*
if (hasSwap == false)
return;
//冒泡只有在非升序序列中发生交换
}
}
贴上完整代码
ArrayBub2.java
package chap4.srt;
/**
* @name ArrayBub2
* @layer chap4.srt
* @intro 这个例子是根据书后面的编程练习题改编的
* @function 让 in 向右移动时把最大值移动最右边, 返回的时候把最小值移动到最左边.
* */
public class ArrayBub2 {
private long[] a;
private int nElems;
//
public ArrayBub2(int max) {
a = new long[max];
nElems = 0;
}
//
public void insert(long newV) {
a[nElems++] = newV;
}
//反向冒泡
public void bubbleSort() {
int out, back, in;
for (out = nElems - 1; out > 1; out--) {
boolean hasSwap = false;
for (in = 0; in < out; in++) {
if (a[in] > a[in+1]) {
hasSwap = true;
swap(in, in+1);
}
}
for (back = in-1; back > 1; back--) {
if (a[back] < a[back-1]) {
hasSwap = true;
swap(back, back-1);
}
}
in = back+1;
//优化下一轮 in 的起点
if (hasSwap == false)
return;
//优化冒泡的轮数
}
}
//看似交换坐标, 实则交换元素
public void swap(int one, int two) {
long temp = a[one];
a[one] = a[two];
a[two] = temp;
}
public void display() {
for (int i = 0 ; i < nElems; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
}
ArrayBub2App.java
package chap4.srt;
/**
* @name ArrayBub2App
* @layer chap4.srt
* @intro
* @function
* */
public class ArrayBub2App {
public static void main(String[] args) {
ArrayBub2 arr3;
int maxSize = 10;
arr3 = new ArrayBub2(maxSize);
arr3.insert(77);
arr3.insert(99);
arr3.insert(44);
arr3.insert(55);
arr3.insert(22);
arr3.insert(88);
arr3.insert(11);
arr3.insert(00);
arr3.insert(66);
arr3.insert(33);
//77 99 44 55 22 88 11 0 66 33
arr3.display();
arr3.bubbleSort();
arr3.display();
//0 11 22 33 44 55 66 77 88 99
}
}
2021-04-07
原题出处: 第三章简单排序 课后编程题 3.1 P79