排序算法一(直接选择,堆排序,冒泡排序和快速排序)

对于一个排序算法来说,一般从如下3个方面衡量算法的优劣:

1.时间复杂度:它主要是分析关键字的比较次数和记录的移动次数

2.空间复杂度:分析排序算法中需要多少辅助内存

3.稳定性:若两个记录A和B的关键字相等,排序前后二者的先后次序没有发生变化就称之为稳定排序

现有的排序算法分为:内部排序和外部排序

内部排序:所有的操作都是在内存中完成的,而外部排序需要借助外部存储器(如磁盘等)

外部排序最常用的方法是多路归并排序,即将原先的文件分解为多个能够一次性装入内存的部分,

分别把每一部分调入内存完成排序,接下来再对多个有序的子文件进行归并排序。

接下来主要介绍内部排序,分为:

选择排序(直接选择和堆排序),交换排序(冒泡和快速),插入排序(直接插入,折半插入和Shell排序),归并排序,基数排序和桶式排序。

本文介绍将介绍选择排序和交换排序

数组元素类DataWrap:flag用来标记相同元素出现的次数,a 表示第一次出现,b表示第二次出现,以此类推,用来测试算法的稳定性

package sort_lyf;

public class DataWrap implements Comparable<DataWrap> {
	int data;
	String flag;
	public DataWrap(int data,String flag){
		this.data = data;
		this.flag = flag;
	}
	public String toString(){
		return data+flag;
	}
	public static void swap(DataWrap[] data, int i, int j) {
		DataWrap temp = data[i];
		data[i] = data[j];
		data[j] = temp;
	}
	@Override
	public int compareTo(DataWrap datawrap) {
		return this.data > datawrap.data ? 1:(this.data == datawrap.data? 0:-1);
	}
}

算法的接口类:ISort(采用策略模式)

package sort_lyf;

public interface ISort {
	public void sort(DataWrap[] data);
}

算法的实现类:

package sort_lyf;
/**
 * @author liyafang 直接选择排序:
 * 程序将记录定位在第一个数据上,拿第一个数据和后边的数据依次比较,
 * 如果第一个数据大于后边的数据,发生交换,这样第一趟下来,就选出
 * 最小的一个元素了,它被排在了第一位,然后在定位第二个数据。
 */
public class DirectSelectSort implements ISort {
	public  void sort(DataWrap[] data) {
		System.out.println("开始进入直接选择排序:");
		int arrayLength = data.length;
		for (int i = 0; i < arrayLength - 1; i++) {// 外层循环需要遍历到倒数第二个元素
			for (int j = i + 1; j < arrayLength; j++) {// 内层循环需要遍历到倒数第一元素
				if (data[i].compareTo(data[j]) > 0) {
					DataWrap.swap(data, i, j);
				}
			}
			System.out.println(java.util.Arrays.toString(data));
		}
		// 存在的问题:在每趟比较过程中,程序一旦发现某个数据比第一
		// 位的数据小,立即交换他们,这增加了交换次数,导致算法效率低下。
	}
}
排序之前:
[21a, 30a, 49, 30b, 16, 9a, 9b, 21b]
开始进入直接选择排序:
[9a, 30a, 49, 30b, 21a, 16, 9b, 21b]
[9a, 9b, 49, 30b, 30a, 21a, 16, 21b]
[9a, 9b, 16, 49, 30a, 30b, 21a, 21b]
[9a, 9b, 16, 21a, 49, 30b, 30a, 21b]
[9a, 9b, 16, 21a, 21b, 49, 30a, 30b]
[9a, 9b, 16, 21a, 21b, 30a, 49, 30b]
[9a, 9b, 16, 21a, 21b, 30a, 30b, 49]
排序之后:
[9a, 9b, 16, 21a, 21b, 30a, 30b, 49]


package sort_lyf;
/**
 * @author liyafang 优化后的直接选择排序算法:
 * 用一个变量记录本趟最小元素的索引,每趟选择
 * 只发生一次交换
 */
public class DirectSelectSortOptimized implements ISort {
	// 
	public void sort(DataWrap[] data) {
		System.out.println("开始进入优化版的直接选择排序:");
		int arrayLength = data.length;
		for (int i = 0; i < arrayLength - 1; i++) {
			int minIndex = i;// 永远保留本躺比较中最小值的索引
			for (int j = i + 1; j < arrayLength; j++) {
				if (data[minIndex].compareTo(data[j]) > 0) {
					minIndex = j;
				}
			}
			if (minIndex != i) {
				DataWrap.swap(data, i, minIndex);
			}
			System.out.println(java.util.Arrays.toString(data));
		}
	}
	// 小结:对于直接选择排序,假如有n项数据,数据交换的次数最多有n-1次
	// 比较次数较多,时间复杂度为O(n*n),空间效率很高为O(1);是不稳定排序。
}
排序之前:
[21a, 30a, 49, 30b, 16, 9a, 9b, 21b]
开始进入优化版的直接选择排序:
[9a, 30a, 49, 30b, 16, 21a, 9b, 21b]
[9a, 9b, 49, 30b, 16, 21a, 30a, 21b]
[9a, 9b, 16, 30b, 49, 21a, 30a, 21b]
[9a, 9b, 16, 21a, 49, 30b, 30a, 21b]
[9a, 9b, 16, 21a, 21b, 30b, 30a, 49]
[9a, 9b, 16, 21a, 21b, 30b, 30a, 49]
[9a, 9b, 16, 21a, 21b, 30b, 30a, 49]
排序之后:
[9a, 9b, 16, 21a, 21b, 30b, 30a, 49]


package sort_lyf;
/**
 * @author liyafang 堆排序:
 * 对于一棵顺序结构的完全二叉树而言,对于索引为k的节点,它的两个子节点的索引分别为2k+1,2k+2,反过来,对于
 * 节点为k的,父节点为(k-1)/2.堆排序的步骤就是重复一下两个步骤:1.建堆   2.拿堆的根节点和最后一个节点交换。
 * 对于包含n个元素的数组,堆排序需要n-1次建堆,建堆从最后一个非叶节点开始,因为只要保证每个非叶子节点的值大
 * 于等于其左右子节点的值。每次建堆的作用就是选出最大值(注意大堆排出来的是升序),实际上也属于选择排序,只
 * 不过堆排序可以通过树形结构保存部分比较结果,可减少比较次数。
 */
public class HeapSort implements ISort {
	public void sort(DataWrap[] data) {
		System.out.println("开始进入堆排序");
		int arrayLength = data.length;
		for (int i = 0; i < arrayLength; i++) {
			buildMaxHeap(data, arrayLength - 1 - i);// 每次建堆完成之后,堆顶元素为第i+1大元素
			DataWrap.swap(data, 0, arrayLength - 1 - i);// 交换堆顶和arrayLength - 1
														// - i元素
			// 交换完成重新再次对其余元素建堆
			System.out.println(java.util.Arrays.toString(data));
		}
	}

	// 对data数组从0到lastIndex建最大堆,选出0到lastIndex索引范围内的最大元素(堆顶元素)
	private void buildMaxHeap(DataWrap[] data, int lastIndex) {
		for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
			int comparingIndex = i;// 当前正在判断的节点
			while ((2 * comparingIndex + 1) <= lastIndex) {// 如果当先节点的左子节点还存在
				int biggerIndex = 2 * comparingIndex + 1;// 左子节点的索引
				if (biggerIndex < lastIndex) {// 如果右子节点存在
					if (data[biggerIndex].compareTo(data[biggerIndex + 1]) < 0) {
						biggerIndex++;
					}
				}
				if (data[comparingIndex].compareTo(data[biggerIndex]) < 0) {
					DataWrap.swap(data, comparingIndex, biggerIndex);
					// 继续判断发生交换的子节点(biggerIndex)是否大于其左右子节点
					comparingIndex = biggerIndex;
				} else {
					break;
				}
			}
		}
	}
	// 小结:对于堆排序算法而言,需要进行n-1次建堆,每次建堆耗时logN,
	// 则时间效率为O(N*logN),空间效率很高为O(1),不稳定排序。
}
排序之前:
[21a, 30a, 49, 30b, 16, 9a, 9b, 21b]
开始进入堆排序
[21b, 30a, 21a, 30b, 16, 9a, 9b, 49]
[9b, 30b, 21a, 21b, 16, 9a, 30a, 49]
[9a, 21b, 21a, 9b, 16, 30b, 30a, 49]
[9a, 16, 21a, 9b, 21b, 30b, 30a, 49]
[9b, 16, 9a, 21a, 21b, 30b, 30a, 49]
[9a, 9b, 16, 21a, 21b, 30b, 30a, 49]
[9b, 9a, 16, 21a, 21b, 30b, 30a, 49]
[9b, 9a, 16, 21a, 21b, 30b, 30a, 49]
排序之后:
[9b, 9a, 16, 21a, 21b, 30b, 30a, 49]
package sort_lyf;

/**
 * @author liyafang 优化版的冒泡法: 
 * 依次比较0和1,1和2,2和3,n-2和n-1的值,如果前者大,就交换
 * 经过第一趟,最大的元素排到了最后,然后进行第二趟... 一旦一
 * 趟冒泡没有发生交换,即可提前结束排序。
 */
public class BubbleSortOptimized implements ISort {
	public void sort(DataWrap[] data) {
		System.out.println("开始进入改进版冒泡排序:");
		int arrayLength = data.length;
		int count = 0;
		boolean IsSwap = false;
		for (int i = 0; i < arrayLength; i++) {
			for (int j = 0; j < arrayLength - 1 - i; j++) {
				if (data[j].compareTo(data[j + 1]) > 0) {
					DataWrap.swap(data, j, j + 1);
					IsSwap = true;
					count++;
				}
			}
			if (!IsSwap) {// 如果某趟没有发生交换,则表明已处于有序状态
				break;
			}
			System.out.println(java.util.Arrays.toString(data));
		}
		System.out.println("总的交换次数:" + count);
	}
	/**
	 * 冒泡排序而言的时间效率是不确定的:最好的情况下,初始数据序列已经处于有序的状态
	 * 执行一趟冒泡即可,做n-1次比较,无需进行任何交换;但在最坏情况下,初始数据序列
	 * 处于完全逆序,算法执行n-1躺冒泡,第i趟做了n-i次比较,执行n-i-1次对象交换,
	 * 此时交换总次数为n(n-1)/2;算法空间效率很高,为O(1); 属于稳定排序;
	 */
}
排序之前:
[21a, 30a, 49, 30b, 16, 9a, 9b, 21b]
开始进入改进版冒泡排序:
[21a, 30a, 30b, 16, 9a, 9b, 21b, 49]
[21a, 30a, 16, 9a, 9b, 21b, 30b, 49]
[21a, 16, 9a, 9b, 21b, 30a, 30b, 49]
[16, 9a, 9b, 21a, 21b, 30a, 30b, 49]
[9a, 9b, 16, 21a, 21b, 30a, 30b, 49]
[9a, 9b, 16, 21a, 21b, 30a, 30b, 49]
[9a, 9b, 16, 21a, 21b, 30a, 30b, 49]
[9a, 9b, 16, 21a, 21b, 30a, 30b, 49]
总的交换次数:18
排序之后:
[9a, 9b, 16, 21a, 21b, 30a, 30b, 49]


package sort_lyf;
/**
 * @author liyafang
 */
public class QuickSort implements ISort{
	public  void sort(DataWrap[] data) {
		System.out.println("开始进入快速排序:");
		subSort(data, 0, data.length - 1);
		System.out.println(java.util.Arrays.toString(data));
	}

	public  void subSort(DataWrap[] data, int start, int end) {
		if (start < end) {
			DataWrap base = data[start];
			// 定一个变量leftIndex,从左边第一个索引开始
			int leftIndex = start;
			// 定一个变量rightIndex,从右边第一个索引开始
			int rightIndex = end + 1;
			// 此处三个while循环用的非常秒,注意体会!
			while (true) {
				// 找出大于分界值的元素的索引,并用leftIndex记录它
				while (leftIndex < end
						&& (data[++leftIndex].compareTo(base) <= 0))
					;
				// 找出小于分界值的元素的索引,并用rightIndex记录它
				while (rightIndex > start
						&& (data[--rightIndex].compareTo(base) >= 0))
					;
				// 如果i<j,交换i、j两处索引的元素
				if (leftIndex < rightIndex) {
					DataWrap.swap(data, leftIndex, rightIndex);
				} else {
					break;// 重复执行以上步骤,直到leftIndex>=rightIndex,
				}
			}
			// 最后将分界值和rightIndex索引处的元素交换即可
			DataWrap.swap(data, start, rightIndex);
			// 递归左子序列
			subSort(data, start, rightIndex - 1);
			// 递归右子序列
			subSort(data, rightIndex + 1, end);
		}
	}
	/**
	 * 快速排序的时间效率很好,因为它每趟能确定的元素呈指数增长。 
	 * 快速排序需要使用递归,而递归使用栈,因此它的空间效率为O(logN)。
	 * 快速排序包含跳跃式交换,因此是不稳定的排序。
	 */
}
排序之前:
[21a, 30a, 49, 30b, 16, 9a, 9b, 21b]
开始进入快速排序:
[9a, 9b, 16, 21a, 21b, 30b, 30a, 49]
排序之后:
[9a, 9b, 16, 21a, 21b, 30b, 30a, 49]


Main函数:

package sort_lyf;

public class Test {
	public static void main(String args[]) {
		//a 代表该元素第一次出现  代表该元素第二次出现,主要是为了测试排序是否稳定
		DataWrap[] data = { new DataWrap(21, "a"), new DataWrap(30, "a"),
				new DataWrap(49, ""), new DataWrap(30, "b"),
				new DataWrap(16, ""), new DataWrap(9, "a"),
				new DataWrap(9, "b"), new DataWrap(21, "b"), };
		/*
		 * DataWrap[] data = { new DataWrap(10, "a"), new DataWrap(9, "a"), new
		 * DataWrap(8, ""), new DataWrap(7, "b"), new DataWrap(6, ""), new
		 * DataWrap(5, "a"), new DataWrap(4, "b"), new DataWrap(3, "b"), new
		 * DataWrap(2, "b"), new DataWrap(1, "b")};
		 */
		/*
		 * DataWrap[] data = { new DataWrap(1, "a"), new DataWrap(2, "a"), new
		 * DataWrap(3, ""), new DataWrap(4, "b"), new DataWrap(5, ""), new
		 * DataWrap(6, "a"), new DataWrap(7, "b"), new DataWrap(8, "b"), new
		 * DataWrap(9, "b"), new DataWrap(10, "b")};
		 */
		System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
		/*ISort lyf = new DirectSelectSort();
		lyf.sort(data);
		ISort lyf = new DirectSelectSortOptimized();
		lyf.sort(data);
		ISort lyf = new HeapSort();
		lyf.sort(data);
		ISort lyf = new BubbleSortOptimized();
		lyf.sort(data);*/
		ISort lyf = new QuickSort();
		lyf.sort(data);
		System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
	}
}




 























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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值