奇偶排序并不莫名其妙

621 阅读5分钟

"令人迷惑的奇偶排序"

奇偶排序

出处: Java 数据结构与算法课后编程题 简单排序 3.4

原句
3.4还有一种简单排序算法是奇偶排序。
它的思路是在数组中重复两趟扫描。第一趟扫描选择所有的数据项对,a[i]a[j+1],j是奇数(j=135,……)。
如果它们的关键字的值次序颠倒,就交换它们。
第二趟扫描对所有的偶数数据项进行同样的操作(j=246,……)。
重复进行这样两趟的排序直到数组全部有序。
用oddEvenSort()方法替换bubbleSort.java程序(清单3.1)中的bubbleSort()方法。
确保它可以在不同数据量的排序中运行,还需要算出两趟扫描的次数。
解读

原话给出的思路可以看出十分清晰了(前面的几道题也是, 给出的思路也十分清晰明了, 虽然少但是给人感觉就是一下子就能看得懂)

一句话概括

一个数组, 先从奇数位开始比较相邻俩数据, 然后从偶数位开始比较相邻俩数据. 每次都是比较相邻两元素.

先贴代码

public class ArrayBub {
	private long[] a;
	private int nElems;
	//
	public ArrayBub(int max) {
		a = new long[max];
		nElems = 0;
	}
	//
	public void insert(long newV) {
		a[nElems++] = newV;		
	}
	//
	public void display() {
		for (int i = 0 ; i < nElems; i++) {
			System.out.print(a[i] + " ");
		}
		System.out.println();
	}
	
	//
	public void oddEvenSort() {
		int oe;		
		boolean sorted = false;
		int i;
		
		while (!sorted) {
			sorted = true;
			
			for (oe = 0; oe < 2; oe++) {
                                //平行遍历, 先后作为起点
				for (i = oe; i < nElems-1; i+=2) {
					if (a[i] > a[i+1]) {
						sorted = false;
                                                //说明有非升序数对, 修改布尔值表示需要继续排序
						//遇到非升序数对
						swap(i, i+1);
					}
				}
			}
		}
	}
	
	//
	public void swap(int one, int two) {
		long temp = a[one];
		a[one] = a[two];
		a[two] = temp;
	}
	//看似交换坐标, 实际交换对应坐标的值交换
}

//验证
public class ArrayBubApp {
	public static void main(String[] args) {
		int maxSize = 10;	
		//
		ArrayBub arr3;
		arr3 = new ArrayBub(maxSize);
		arr3.insert(7);
		arr3.insert(8);
		arr3.insert(6);
		arr3.insert(5);
		arr3.insert(4);
		arr3.insert(3);
		arr3.insert(2);
		arr3.insert(1);
		arr3.insert(0);
		arr3.insert(-1);
		//
		System.out.println("odd 前");
		arr3.display();
		//
		System.out.println("odd 后");
		arr3.oddEvenSort();
		arr3.display();
	}
}
//控制台输出
odd 前
7 8 6 5 4 3 2 1 0 -1 
odd 后
-1 0 1 2 3 4 5 6 7 8 
代码分析

1.这个代码很特别的地方, 如果是写过冒泡排序, 你以为那个用从 n 开始动态递减的 out 作为遍历终点从而控制遍历的终点已经够奇妙了, 那么这里你肯定也会感觉很新颖.

虽然不是循环终点, 但是这里外循环控制的是起点, 用的是从 0 到 1, 控制的是每次开始的起点.

2.每次 i 的变化跳到下一个奇偶位, 比较的, 还是相邻两个元素, 这个不变.

实际调试时会发现黄色条跳得很莫名其妙, 这是整个调试过程的录制动图

看完后人只想缓缓地在脑门上画一个问号, 完全没有很直观的变化趋势.

这时, 可以在纸上写一遍, 毕竟太莫名其妙了.

实验数据用的是

7 8 6 5 4 3 2 1 
作图

卖家秀

odd-even-sort.png

买家秀

odd-even-sort-date.jpg

这个时候还看不出什么,

虽然从头到尾写了一遍, 但还是不知道它怎么就莫名其妙地变成升序了——缓缓打出一个蛤??

但是至少写一遍后知道了两两对比之后 2 个元素就会变成有序(下括号标出的数对总是有序的), 这一点在第一轮偶数对是最直观的.

还是不能看出它是怎么做到排序的.

这个时候不妨将数据改成人的身高, 看看它们是怎么排序的.(模拟棒球运动员按身高整队)

odd-even-sort-shengao.png

从图中可以看出, 一米八被不知不觉地换到了最后一个位置.

倒数一轮也是, 1.2 被移动到了 1.3 前面, 刚好在 1.1 的后面

说到 1.1 , 它也被从后面换到最前面来了

整体上, 一米八 的位置从左上角作右下角呈现一条 下划线, 一米七 也是. 一米六 也是先 下划线 然后 折回来

回想冒泡排序的原理, 可以知道, 它是比较相邻两个元素, 然后, 最大的被调到了最后面. (通俗来说就是一直找更大的)

要实现那样的话, 肯定数组内所有元素两两都比较过才能确定最后是升序排列.

这道题目和冒泡之间的联系就是它肯定也经过两两之间的比较.

这个时候再来会看上面的 一米八:

原来是在奇数位, 然后是偶数, 相邻之间两两比较, 不断比较不断被换到右边, 直到来到最后一个位置.

然后, 会发现, 其它所有的元素都是这样, 原来的奇数位的数字, 下一个会出现在偶数位, 然后两两比较.

就这样, 每两两个元素都会先后经过相互之间的比较. 最后总是得到更大(更小)然后被移动到了前面或者后面.

结束语

原来, 奇偶数排序的原理实际上和冒泡是一样的, 通过让所有元素两两之间经过相互比较, 最后确定最大值, 最小值也会经过比较被换到最前面.

这也是为什么有博客会说

Odd-Even Sort

This is basically a variation of bubble-sort.

所以说, 奇偶排序写起来并不困难, 2 个 01 作为起点作不同的遍历. 比较 相邻 的元素.

是怎么做到排序的呢? 实际上和冒泡的原理一样, 也是让相邻元素两两经过不同顺序的相互比较, 直到最小的被移到最前, 最大的被移到了最后.

参考

数据结构和算法(Java) - 奇偶排序 - Bluesky的文章 - 知乎

Odd–even sort - Wikipedia