八大排序算法复习之交换排序

  先来谈谈排序的概念:

        所谓排序,就是根据排序码的递增或者递减顺序把数据元素依次排列起来,使一组任意排列的元素变为一组按其排序码线性有序的元素。

      排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。我们这里说说八大排序就是内部排序。

                                          

 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。

 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;

 1、交换排序

         交换排序的基本思想:根据序列中两个元素的比较结果来对换这两个记录在序列中的位置,也就是说,将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

(1)冒泡排序:

     冒泡排序的思想:根据序列中两个元素的比较结果交换两个元素的位置,将键值较大的元素向序列尾部移动,将键值较小的元素向序列前部移动。

冒泡排序示例:

                                    

算法实现:

package com.wit.stan.sort;
/**
 * 交换排序:
 * 交换排序的基本思想:根据序列中两个元素的比较结果来交换这两个记录在序列中的位置,
 * 也就是说,将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
 * 如:
 * 冒泡排序
 * 时间复杂度:最好情形O(n),平均情形O(n^2),最差情形O(n^2) 
 * 空间复杂度:O(1) 
 * 稳 定 性:稳定
 * 内部排序(在排序过程中数据元素完全在内存)
 * @author 关启培
 *
 */
public  class BubbleSort {
	
	public static void main(String[] args) {
		int[] a= {25,1,2,3,4,5,6,7,24,34};
		BubbleSort bubbleSort=new BubbleSort();
	/*	bubbleSort.bubbleSort(a, a.length);*/
		bubbleSort.bubbleSortPlus(a, a.length);
		System.out.println("排序为:");
		for(int i=0;i<a.length;i++){
			System.out.print(a[i]+"  ");
		}
		System.out.println();
		System.out.println("=====================");
		//以数组形式输出
		System.out.println(Arrays.toString(a));
	}
	
	//原始算法
	public void bubbleSort(int a[],int n) {
		int temp;
		int m=0;
		for(int i=0;i<n-1;i++) {
			for(int j=0;j<n-1-i;j++) {
				if(a[j]>a[j+1]){
				  temp=a[j];
				  a[j]=a[j+1];
				  a[j+1]=temp;
				  System.out.println(a[j+1]+"和"+a[j]+"进行了交换");
				}
				 m++;
			}
		}
		System.out.println("判断的次数:"+m);
	}
	
	//优化算法
	public void bubbleSortPlus(int a[],int n){
		int temp;
		int m=0;
		for(int i=0;i<n-1;i++) {
			//添加一个标记位
			int flag=0;
			for(int j=0;j<n-1-i;j++){
				if(a[j]>a[j+1]) {
					  temp=a[j];
					  a[j]=a[j+1];
					  a[j+1]=temp;
					  flag=1;
				}
		      m++;
			}
			if(flag==0) {
				break;
			}
		}
		System.out.println("优化判断的次数:"+m);
	}
 
}

(2)快速排序: 

快速排序算法是基于分治策略的另一个排序算法。

快速排序的思想:

1)选择一个基准元素,通常选择第一个元素或者最后一个元素,记为X。

2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。

3)此时基准元素在其排好序后的正确位置(即最终位置)

4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。 

快排目前有两类实现算法,第一种是标准算法,第二种是两头交换法。总的思想与上面三步一样,在细节处理上有一些差异。

 

标准算法思想及实现

标准算算法采用的思想是挖坑填坑的思想:

以一个数组作为示例,取区间第一个数为基准数。

0

1

2

3

4

5

6

7

8

9

72

6

57

88

60

42

83

73

48

85

初始时,i = 0;  j = 9;   X = a[i] = 72

由于已经将a[0]中的数保存到X中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。

从j开始向前找一个比X小或等于X的数。当j=8,符合条件,将a[8]挖出再填到上一个坑a[0]中。a[0]=a[8]; i++;  这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。这次从i开始向后找一个大于X的数,当i=3,符合条件,将a[3]挖出再填到上一个坑中a[8]=a[3]; j--;

 

数组变为:

0

1

2

3

4

5

6

7

8

9

48

6

57

48

60

42

83

73

88

85

 i = 3;   j = 7;   X=72

再重复上面的步骤,先从后向前找,再从前向后找

 

从j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++;

从i开始向后找,当i=5时,由于i==j退出。

此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将X填入a[5]。

 

数组变为:

0

1

2

3

4

5

6

7

8

9

48

6

57

42

60

72

83

73

88

85

可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间重复上述步骤就可以了。

 

对挖坑填数进行总结

1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。

2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。

3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。

4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。

代码实现:

package com.wit.stan.sort;

import java.util.Arrays;

/**
 * 快速排序算法
 * 基本思想:
 * 1.先从数列中取出一个数作为基准数,记为x。

   2.分区过程,将不小于x的数全放到它的右边,不大于x的数全放到它的左边。(这样key的位置左边的没有大于key的,右边的没有小于key的,只需对左右区间排序即可)

   3.再对左右区间重复第二步,直到各区间只有一个数
 * @author stan
 *
 */
public class Quick_Sort {

	public static void main(String[] args) {
		
		int[] a= {3,5,22,45,5,8,4,6};
		Quick_Sort qu=new Quick_Sort();
		qu.quick_sort(a, 0, a.length-1);
		System.out.println("快速排序后的数组为:");
		System.out.println(Arrays.toString(a));
		
	}
 public void quick_sort(int a[],int l,int r) {
	 int i=l,j=r;
	 int x=a[l];
	 while(i<j) {
		 while(i<j&&a[j]>x) {
			 j--;
		 }
		 if(i<j) {
			 a[i++]=a[j];		
			 }
		 while(i<j&&a[i]<x){
			 i++;
		 }
		 if(i<j) {
			a[j--]=a[i];
		 }
	 }
	 a[j]=x;
	 //此时i==j,下面s[i]或者s[j]都可以,j-1,j+1也ok
	 if(l<i) {
		quick_sort(a, l, i-1); 
	 }
	 if(r>i) {
		 quick_sort(a, i+1, r);
	 }
 }
}

 

两头交换法思想及实现

两头交换法与标准算法思想的差异是,先从左边开始找到大于基准值的那个数,再从右边找到小于基准值的那个数,将两个数交换(这样比基准值小的都在左边,比基准值大的都在右边)。直到数列分成大于基准值和小于基准值的两个区间,以这两个区间进行同样的排序操作。

代码实现如下(这里只有算法,没有实现过程):

//两头交换法实现快速排序
 public void quickSort(int a[],int begin,int end) {
	 if(begin>end)
		 return;
	 int i=begin,j=end,temp=0;
	 //这里选取了中间为基准值
	 int x=a[(i+j)>>1];
		 while(i<=j) {
			 while(a[i]<x) {
				 i++;
			 }
			 while(a[j]>x) {
				 j--;
			 }
			 if(i<=j) {
				 temp=a[i];
				 a[i]=a[j];
				 a[j]=temp;
				 i++;
				 j--;
			 }
		 }
	 quickSort(a, begin,j);
	 quickSort(a, i,end);
 }

上面的算法是两头交换法官方的版本,边界情况较少,比较健壮。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值