算法4 2.1 初级排序算法

排序

排序就是将一组对象按照某种逻辑顺序重新排列的过程。
我们关注的主要对象是重新排列数组元素的算法。其中每个元素都有一个主键,排序算法的目标就是将所有元素的主键按照某种方式排列(通常是按照大小或是字母顺序)。排序后索引较大的主键>=索引较小的主键。

我们将排序代码放在Example类的sort()方法中,Example类中还包含辅助函数 less() 和 exch()。

排序算法类模板如下:

// Java
public class Example
{
	public static void sort(Comparable[] a)
	{
		//请见算法2.1、2.2、2.3、2.4、2.5 这是排序代码
	}
	private static boolean less(Comparable v, Comparable w)
	{
		return v.compareTo(w) < 0; /*Java自带的一种比较v和w两个数据的大小顺序的方法,
								less(a,b) = 1 意味着 a < b*/
	}
	private static void exch(Comparable[] a, int i, int j)
	{
		Comparable t = a[i]; a[i] = a[j]; a[j] = a[i];
		//交换数组a中两个元素的位置
	}
	private static void show(Comparable[] a)
	{ // 在单行中打印数组
		for(int i = 0; i < a.length; i++)
		{
			StdOut.print(a[i] + " "); //这是《算法4》自己定义的库函数中的方法,就是输出打印
		}
		StdOut.println(); 这是《算法4》自己定义的库函数中的方法
	}
	public static boolean isSorted(Comparable[] a)
	{ // 测试数组元素是否有序
		for (int i = 1; i < a.length; i++)
		{
			if (less(a[i], a[i-1]) 
			{
				return false;
			}
		}
		return true;
	}
	public static void main(String[] args)
	{ // 从标准输入读取字符串,将它们排序并输出
		String[] a = In.readStrings(); // 输入读取
		sort(a); // 排序
		assert isSorted(a); /*这是Java中的断言,IDEA中默认是关闭的,需要设置jvm的参数为-ea
							(1)assert [boolean 表达式]
							如果[boolean表达式]为true,则程序继续执行。
							如果为false,则程序抛出AssertionError,并终止执行。
							(2)assert[boolean 表达式 : 错误表达式 (日志)]
							如果[boolean表达式]为true,则程序继续执行。
							如果为false,则程序抛出java.lang.AssertionError,输出[错误信息]。*/					
		show(a); // 输出打印
	}	
}

算法2.1 选择排序

首先,找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素那么它就和自己交换)。再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。这种方法叫做选择排序,因为它在不断地选择剩余元素之中的最小者。

代码格式如下:

// Java
public class Selection
{
	public static void sort(Comparable[] a)
	{ // 将a[]按升序排列
		int N = a,length;   // 数组长度
		for(int i = 0; i < N; i++)
		{ // 将a[i]和 a[i+1,,,,N] 中最小的元素交换
			int min = i;    // min是最小元素的索引
			for(int j = i + 1; j < N; j++)
			{
				if(less(a[j], a[min]))
				{
					min = j;
				}
				exch(a, i, min)
			}
		}
	}
	// less()、exch()、isSorted()和main()方法见“排序算法类模板”	
}

对于长度为N的数组,选择排序需要大约N²/2次比较和N次交换。
特点
运行时间和输入无关,为了找出最小的元素而扫描一遍数组并不能为下一遍扫描提供什么信息。
数据移动是最少的。

算法2.2 插入排序

通常人们整理桥牌的方法是一张一张的来,将每一张牌插入到其他已经有序的牌中的适当位置。在计算机的实现中,为了给要插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移动一位。这种算法叫做插入排序

代码格式如下:

// Java
public class Insertion
{
	public static void sort(Comparable[] a)
	{ // 将a[]按升序排列
		int N = a,length;
		for(int i = 1; i < N; i++)
		{ // 将 a[i] 插入到 a[i-1]、a[i-2]、a[i-3]...之中
			for(int j = i; j > 0 && less(a[j], a[j-1]); j--)
			{
				exch(a, j, j-1)
			}
		}
	}
	// less()、exch()、isSorted()和main()方法见“排序算法类模板”
}

对于随机排列的长度为N 且主键不重复的数组,平均情况下插入排序需要~ N²/4 次比较以及~ N²/4 次交换。
要大幅提高插入排序的速度并不难,只需要在内循环中将较大的元素都向右移动而不总是交换两个元素(这样访问数组的次数就能减半)。下面给的代码是《算法4》配套网站上的代码链接: link.

代码格式如下:

// Java
public class InsertionX {

    // This class should not be instantiated.
    private InsertionX() { }

    /**
     * Rearranges the array in ascending order, using the natural order.
     * @param a the array to be sorted
     */
    public static void sort(Comparable[] a) {
        int n = a.length;

        // put smallest element in position to serve as sentinel
        int exchanges = 0;
        for (int i = n-1; i > 0; i--) {
            if (less(a[i], a[i-1])) {
                exch(a, i, i-1);
                exchanges++;
            }
        }
        // 先稍微对数组元素进行大体上的排序,使较大的元素向右移动。
        if (exchanges == 0) return;
		// 如果后面的数始终比前面的数大,那就说明不需要再进行交换了,直接返回。

        // insertion sort with half-exchanges
        for (int i = 2; i < n; i++) {
            Comparable v = a[i];
            int j = i;
            while (less(v, a[j-1])) {
                a[j] = a[j-1];
                j--;
            }
            a[j] = v;
        }

        assert isSorted(a);
    }

算法2.3 希尔排序

希尔排序为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。
希尔排序的思想是使数组中任意间隔为h 的元素都是有序的。这样的数组被称为h 有序数组。换句话说,一个h 有序数组就是h 个互相独立的有序数组编织在一起组成的一个数组。
实现希尔排序的一种方法是对于每个h,用插入排序将h 个子数组独立地排序。但因为子数组是相互独立的,一个更简单的方法是在h- 子数组中将每个元素交换到比它大的元素之前去(将比它大的元素向右移动一格)。只需要在插入排序的代码中将移动元素的距离由1 改为h 即可。这样,希尔排序的实现就转化为了一个类似于插入排序但使用不同增量的过程。
透彻理解希尔排序的性能至今仍然是一项挑战。(这是一个数学难题)

代码格式如下:

// Java
public class Shell
{
	public static void sort(Comparable[] a)
	{ // 将a[]按升序排列
		int N = a.length;
		int h = 1;
		while (h < N/3) 
		{
			h = 3*h + 1; // 1, 4, 13, 40, 121, 364, 1093, ...
		}
		while (h >= 1) // 这个外循环用来将h按照递增序列递减
		{ // 将数组变为h有序
			for (int i = h; i < N; i++)
			{ // 将a[i]插入到a[i-h], a[i-2*h], a[i-3*h]... 之中
				for (int j = i; j >= h && less(a[j], a[j-h]); j -= h)
				{
					exch(a, j, j-h);
				}
			}
			h = h/3;
		}
	}
	// less()、exch()、isSorted()和main()方法见“排序算法类模板”
}

已知在最坏的情况下,算法2.3的比较次数和N的3/2次方成正比。由插入排序到希尔排序,一个小小的改变就突破了平方级别的运行时间。很神奇~
(谢谢你看到最后~)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值