7种排序算法(java实现)

1. 插入排序(直接插入排序、希尔排序)

2. 交换排序(冒泡排序、快速排序)

3. 选择排序(直接选择排序、堆排序)

4. 归并排序

5. 各种排序比较


1. 插入排序

有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序

插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外,而第二部分就只包含这一个元素。

1.1 直接插入排序

有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法。将新加入的元素与之前已排好序的数组元素一 一进行比较,通过移位将新元素插到数组的正确位置中。一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
⒈ 从第一个元素开始,该元素可以认为已经被排序
⒉ 取出下一个元素,在已经排序的元素序列中从后向前扫描
⒊ 如果该元素(已排序)大于新元素,将该元素移到下一位置
⒋ 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

⒌ 将新元素插入到下一位置中

⒍ 重复步骤2

/**
	 * 插入排序1:基本插入排序算法
	 * */
	public void insertSort(int unsorted[]) {
		int increamentey, i;

		// 数组的第一个数据作为默认已排好序的,然后坐标j从1开始
		for (int j = 1; j < unsorted.length; j++) {
			// 取出新元素,在已经排序的元素序列中从后向前扫描
			increamentey = unsorted[j];
			i = j - 1;
			// 将新元素与之前的已排好序的元素进行大小比较,并进行相应的移位
			while (i >= 0 && unsorted[i] > increamentey) {
				unsorted[i + 1] = unsorted[i];
				i = i - 1;

			}
			// 将新元素插入到正确的位置
			unsorted[i + 1] = increamentey;
		}

	}


1.2 希尔排序shell sort

希尔排序(Shell Sort)是插入排序的一种。是针对直接插入排序算法的改进。
基本思想:
  先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

 该方法实质上是一种分组插入方法。

抓狂注意:希尔排序算法在分组那里理解容易错误,希尔算法中,每个分组应含有所有距离为dl的倍数的记录,而不仅仅是只有2个记录(若是理解为只有2个记录,排序会不准确),然后对组内的元素进行直接插入排序。


微笑flash演示网址:
http://student.zjzk.cn/course_ware/data_structure/web/flashhtml/shell.htm


	/**
	 * 插入排序2:希尔排序算法
	 * */

	public void shellSort(int unsorted[]) {
		int temp;
		//初始增量=数组的长度/2,排序后,减少增量,直至增量=1
		for (int increament = unsorted.length / 2; increament > 0; increament /= 2) {
			/* 对所有小组进行排序 */
			for (int i = 0; i < unsorted.length - increament; i++) {
				/* 对单个组内部进行排序 */
				for (int j = i + increament; j < unsorted.length; j += increament) {
					if (unsorted[j - increament] > unsorted[j]) {
						temp = unsorted[j - increament];
						unsorted[j - increament] = unsorted[j];
						unsorted[j] = temp;
					}
				}
			}
		}
	
	}




2.交换排序  

交换排序的基本思想是:两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。

 应用交换排序基本思想的主要排序方法有:冒泡排序和快速排序。


2.1冒泡排序:

非常容易理解和实现,,以从小到大排序举例:

设数组长度为N:
a) 比较相邻的前后二个数据,如果前面数据大于后面的数据,就将二个数据交换。
b) 这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。
c) N=N-1,如果N不为0就重复前面二步,否则排序完成。


2.2快速排序

   快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。快速排序的核心是划分

1)首先对无序的记录序列进行“一次划分",之后分别对分割所得两个子序列“递归”进行快速排序

目标:找一个记录,以它的关键字作为枢轴,凡其关键字小于枢轴的记录均移动至该记录之前,反之,凡关键字大于枢轴的记录均移动至该记录之后

致使一趟排序之后,记录的无序序列R[s..t]分割成两部分R[s..i-1]R[i+1..t]且 

       R[j].key≤ R[i].key≤ R[j].key

      (s≤j≤i-1)   枢轴    (i+1≤j≤t)。 

                             如以下例图:

 


	/**
	 * 交换排序2:快速排序
	 * */
	public void quickSort(int d[], int left, int right) {
		if (left < right) {
			int pivotloc = partition(d, left, right);
			quickSort(d, left, pivotloc - 1);
			quickSort(d, pivotloc + 1, right);
		}
	}

	// 快速排序-划分
	public int partition(int d[], int low, int high) {
		int key = d[low];// 设置枢轴key 的值为d[low];
		while (low < high) {
			while (low < high && d[high] > key)
				high--; // 从右向左搜索
			d[low] = d[high];
			while (low < high && d[low] < key)
				low++;// 从左向右搜索
			d[high] = d[low];
		}
		d[low] = key;
		return low;
	}





3. 选择排序

每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。

3.1直接选择排序

直接选择排序(Straight Select Sorting) 也是一种简单的排序方法,它的基本思想是:第一次从R[0]~R[n-1]中选取最小值,与R[0]交换,第二次从R[1]~R[n-1]中选取最小值,与R[1]交换,....,
/**
	 * 选择排序1:直接选择排序
	 * */
	public void selectSort(int unsorted[]) {
		int min, temp;
		for (int i = 0; i < unsorted.length; i++) {
			min = i;

			for (int j = i + 1; j < unsorted.length; j++) {
				if (unsorted[min] > unsorted[j]) {
					min = j;

				}
			}
			temp = unsorted[i];
			unsorted[i] = unsorted[min];
			unsorted[min] = temp;
		}

	}



3.2堆排序-重难点

堆,是一棵完全二叉树 (若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。) ,根的值大于左右子树中所有结点的值,左右子树也是堆,除此之外,对其它元素之间的大小关系(如左右子树之间元素大小关系)没有要求。

堆排序是指利用堆的特性对记录序列进行排序的一种排序方法。


基本过程:
1。根据原始记录序列创建堆-即初始堆;
2。将根与堆中的最后一个结点交换;
3。将堆中的 最后结点 从堆中 删去
4。将剩余的结点重新调整成堆。


注意:用数组模拟二叉树(当然也包括堆)的话,如果根节点的下标为0的话,则对于每个结点i,其左孩子下标为2*i+1;其右孩子下标为2*i+2。

堆有两个基本操作:插入元素和删除元素。

插入: 将待插入元素添加到数组末尾,然后依次向上比较,如果该结点大于父亲节点,就与父节点交换,这样就构成局部大根堆,一直到该结点小于父节点位置,便完成了结点的插入操作。
删除: 要删除某元素,可以将数组的大小缩小一,然后用数组的末尾元素代替待删除元素,然后将缩小后的数组重新调整为堆,便完成了结点的删除操作

更详细的堆算法排序图解
/**
	 * 选择排序2:堆排序,重难点
	 * */

	public void heapSort(int[] h) { // 堆采用数组存储表示
		// 对数组h进行堆排序
		for (int i = h.length / 2 - 1; i >= 0; i--)
			// 把h[0..h.length-1]建成大根堆
			heapAdjust(h, i, h.length - 1);
		for (int j = h.length - 1; j >= 0; j--) {
			int temp = 0; // 辅助空间
			temp = h[0];
			h[0] = h[j]; // 将堆顶元素和当前未经排序子序列h[0..j]中的最后一个记录交换
			h[j] = temp;

			heapAdjust(h, 0, j - 1); // 将h[0..j-1]调整为大根堆
		}
	}

	public void heapAdjust(int[] h, int s, int m) { // h[s..m]为需要调整的元素范围
		// h[s-1..m]满足堆的定义,现在需要调整h[s]使h[s..m]成为一个大根堆
		int temp = 0;
		temp = h[s];
		for (int i = 2 * s + 1; i <= m; i = 2 * i + 1) {
			if (i < m && h[i] < h[i + 1]) // i应该为值较大的记录的下标
				i++;
			if (temp >= h[i]) // temp应该插入在位置s上
				break;
			h[s] = h[i];
			s = i;
		}
		h[s] = temp; // 插入在此位置
	}



4. 归并排序

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

假设我们有一个没有排好序的序列,那么首先我们使用分割的办法将这个序列分割成一个一个已经排好序的子序列,然后再利用归并的方法将一个个的子序列合并成排序好的序列。分割和归并的过程可以看下面的图例。




从上图可以看出,我们首先把一个未排序的序列从中间分割成2部分,再把2部分分成4部分,依次分割下去,直到分割成一个一个的数据,再把这些数据两两归并到一起,使之有序,不停的归并,最后成为一个排好序的序列。


	/**
	 * 归并排序
	 * */
	public void mergeSort(int d[], int start, int end) {
		int i;
		if (start < end) {
			i = (start + end) / 2;
			// 对前半段进行排序
			mergeSort(d, start, i);
			// 对后半段部分进行排序
			mergeSort(d, i + 1, end);
			// 合并前后两部分
			merge2(d, start, i, end);
		}
	}

	// 归并排序中的合并算法,采用“哨兵”
	public void merge1(int d[], int left, int middle, int right) {
		int n1 = middle - left + 1;
		int n2 = right - middle;
		int[] temp1 = new int[n1 + 1];
		int[] temp2 = new int[n2 + 1];
		// 拷贝前半部分数组
		for (int i = 0; i < n1; i++)
			temp1[i] = d[left + i];
		// 拷贝后半部分数组
		for (int j = 0; j < n2; j++)
			temp2[j] = d[middle + j + 1];
		/**
		 * 合并过程中采用“哨兵”,在Left数组与Right数组的最后一个元素存放的是一个很大的数,从而在比较之前检查子数组是否已空。
		 */
		temp1[n1] = temp2[n2] = Integer.MAX_VALUE;
		// 逐个扫描两部分数组然后放到相应的位置去
		for (int k = left, i = 0, j = 0; k <= right; k++) {
			if (temp1[i] <= temp2[j]) {
				d[k] = temp1[i];
				i++;
			} else {
				d[k] = temp2[j];
				j++;
			}
		}

	}

	// 合并算法2,不采用“哨兵”的版本
	public void merge2(int d[], int left, int middle, int right) {
		int i, j, k;
		int n1 = middle - left + 1;
		int n2 = right - middle;
		int[] temp1 = new int[n1 + 1];
		int[] temp2 = new int[n2 + 1];
		// 拷贝前半部分数组
		for (i = 0; i < n1; i++)
			temp1[i] = d[left + i];
		// 拷贝后半部分数组
		for (j = 0; j < n2; j++)
			temp2[j] = d[middle + j + 1];
		i = j = 0;

		for (k = left; k <= right; k++) {
			if (i == n1)// temp1中已经无元素,将temp2剩余元素复制到d中
			{
				d[k] = temp2[j];
				j++;

			} else if (j == n2)// temp2中已经无元素,将temp1剩余元素复制到a中
			{
				d[k] = temp1[i];
				i++;
			} else // 逐个扫描比较temp1和temp2中的数据,并按照从小到大的顺序复制到d中。
			{
				if (temp1[i] < temp2[j]) {

					d[k] = temp1[i];
					i++;
				} else {
					d[k] = temp2[j];
					j++;
				}

			}
		}
	}



5. 内部排序算法的比较




更多详细排序资料(PPT+代码)




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值