基础排序算法总结及实现

最近复习了一下基础排序算法,这里简单总结一下算法实现原理及实现代码。由于代码是根据原理写的基础算法,并没有过多的考虑性能优化。我们这里讲的所有算法都是升序排序。

冒泡排序

原理简介:数组左边无序,右边有序,每次比较两个相邻的元素,然后选出最大的放在有序队列队首。
算法:
      1、比较相邻的元素。如果第一个比第二个大,就交换他们两个,这样最后的元素就是最大的数。
      2、重复步骤一,每次选出一个最大的元素,无序数组少一个元素,有序数组多一个元素。
      3、每次选择一个相对最大的数放在右边,进行length-1次比较即可。
代码实现
//冒泡排序
	public static void bubbleSort(int[] a){
		for(int i = 0; i<a.length-1;i++)
			for(int j=0;j<a.length-i-1;j++){
				if(a[j]>a[j+1]){
					int temp = a[j];
					a[j] = a[j+1];
					a[j+1] = temp;
				}
			}
	}
改进:我们知道,每一轮两两比较,选出最大数则交换,最后最右边的数必定为本轮选出的最大数,那么如果在某轮比较中,如果没有发生交换,即左边的数都比右边的数小,则说明当前数组已经是有序的。
改进代码实现
//冒泡排序改进算法
	public static void bubbleSortOpt(int[] a){
		boolean exchange;
		for(int i = 0; i<a.length-1;i++)
		{
			exchange = false;
			for(int j=0;j<a.length-i-1;j++){
				if(a[j]>a[j+1]){
					int temp = a[j];
					a[j] = a[j+1];
					a[j+1] = temp;
					exchange = true;
				}
			}
			if(!exchange){
				return;
			}
		}
	   }

选择排序

原理简介:选择排序,左边是有序区,右边是无序区,每一轮选择出最小数放在左边有序区,直到无序区长度为1.
算法:
        1、 记录无序区最左边位置为i,初始化无序区中最小值索引为index
        2、将位置i与其右边无序区每一个元素依次对比,比它小则更新索引index。
        3、一轮比较完成后,如果i不为index,则交换两个元素位置。
        4、循环进行下一轮比较,每一轮选择一个最小值,进行length-1轮即可。
代码实现
//选择排序
	public static void selectSort(int[] a){
		int index = 0;
		for(int i = 0; i<a.length-1; i++){
			index = i;
			for(int j =i+1; j<a.length;j++ ){
				if(a[index]>a[j]){
					index = j;
				}
			}
			if(index !=i){
			int temp = a[index];
			a[index] = a[i];
			a[i] = temp;
			}
		}
	}

插入排序

原理简介:左边是有序区,右边是无序区,将无序区最左边的值依次与有序区数值比较,插入到适当位置。直到无序区长度为0。
算法:
      1、取出无序区最左边的数,将其与有序区的数值比较,如果小于则交换,如果大于则此次插入结束。
      2、无序区从下标为1开始取值。
      3、无序区下标超出数组长度,则排序完成。
代码实现
//插入排序
	public static void insertSort(int[] a){
		
		for(int i=1;i<a.length;i++){
			for(int j =i;j>0;j--){
				if(a[j]<a[j-1]){
					int temp = a[j-1];
					a[j-1] =a[j];
					a[j] = temp;
				}else{
					break;
				}
			}
		}
	}

二分排序

原理简介:二分排序是对插入排序改进的一种排序算法。区别在于,插入排序在有序区中插入一个值的时候,是从后往前一个个比较。而二分排序,会记录有序区的待插入范围,初始化范围为有序区的最左low和最右high,然后计算位置mid,如果比mid小,则说明插入范围在左边,更新high值为mid-1,反之更新low值为mid+1,然后进行下一次mid更新查找,直到找到合适的插入位置。
算法:
      1、取出无序区最左边位置为i的数,暂存在temp变量中。初始化low和high分别为0和i-1
      2、计算mid=(low+high)/2,如果mid位置的变量比temp小,则更新low=mid+1,反之更新high=mid-1
      3、一直循环比较逐渐缩小low和high的距离(也就是缩小待插入位置的范围),直到low不小于high,那么这个时候,low的值即为待插入位置。
      4、将有序区中位于low右边的值往右移,更新low位置的变量为temp。此次插入完成。
      5、循环插入所有数,进行length-1次插入即可。
代码实现
//二分排序
	public static void binarySort(int[] a){
		int i,j;
		int low,high,mid;
		int temp;
		for(i=1;i<a.length;i++){
			temp = a[i];
			low = 0;
			high = i-1;
			while(low<=high){
				mid = (low+high)/2;
				if(a[mid]>temp){
					high = mid-1;
				}else{
					low = mid+1;
				}
			}
			for(j = i;j>low;j--){
				a[j]=a[j-1];
			}
			a[low] = temp;

		}
	}

快排

原理简介:快排思想是,任意取一个数(这里我们选取待快排的第一个数),比它小的放它左边,比它大的放它右边,此时原来的数组被分割为两个数组,且被选取的数在其对应的正确位置上,然后再对两个数组分别使用快排,又分割成4个数组,此时有3个数在其对应的正确位置上。就这样一直使用递归调用,直到所有的数都在其正确位置上位置。
算法:
      1、初始化待快排位置low为0 ,high为length-1;
      2、如果low比high小,则进行快排分割,快排分割将待快排数组范围的第一个数据放在正确位置,并返回其放置的位置key
      3、得到别分割的两个数组,范围分别为low到key-1,key+1到high。递归再次对其进行快排。
代码实现
//快排  本函数为调用函数
	public static void quickSort(int[] a){
		int low = 0;
		int high = a.length-1;
		quickSortHelp(a, low, high);
	}
	public static void quickSortHelp(int[] a, int low, int high){
		if(low>=high)
			return;
		int key = partition(a, low, high);
		quickSortHelp(a, low, key-1);
		quickSortHelp(a, key+1, high);
	}
	public static int partition(int[] a, int low, int high){
		int i = low;
		int j = high;
		int temp;
		while(i<j){
			for(;j>i;j--){
				if(a[j]<a[i]){
				    temp = a[j];
					a[j] = a[i];
					a[i] = temp;
					break;
				}
			}
			for(;i<j;i++){
				if(a[j]<a[i]){
					temp = a[j];
					a[j] = a[i];
					a[i] = temp;
					break;
				}
			}
		}
		return j;
	}
说明:这里我的分割方法partition利用了嵌套循环,网上看到另外一个更优雅的实现,测试了下可行,不过我暂时没明白原理,代码这里给大家参考一下。
public static int partition(int[] a, int low, int high){
		int last = a[high];  
	    int i = low -1;  
	    for (int j = low; j <= high-1; j++) {  
	        if(a[j] <= last){  
	            i++;  
	            if(i != j){  
	                a[i] = a[i]^a[j];  
	                a[j] = a[i]^a[j];  
	                a[i] = a[i]^a[j];  
	            }  
	        }  
	    }  
	    if((i+1) != high){  
	        a[i+1] = a[i+1]^a[high];  
	        a[high] = a[i+1]^a[high];  
	        a[i+1] = a[i+1]^a[high];  
	    }  
	    return i+1;
	}

归并排序

原理简介:归并算法是在两个有序子序列的基础上,将其合并为一个子序列。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1}
第二次归并后:{6,100,202,301},{1,8,38}
第三次归并后:{1,6,8,38,100,202,301}
算法:
     1、初始化归并增量为1
     2、计算数组长度size,计算如果该次增量为len归并成功,会形成的多少个有序集合并赋给mid。计算此次归并中,是否存在单数集合并赋给c。
     3、如果执行归并后,有序集合数等于0,则说明已经是有序序列,退出。
     4、以增量len将数组划分开来,调用merge函数(具体在步骤7)。执行mid次merge即可。
     5、如果c变量为1,即该次归并经过mid次之后,还存在没有排序的数组。那么将为排序的数组与它左边len长度的数组进行归并。
     6、递归调用下一次归并函数。
     7、merge函数第一个参数为数组,第二个参数为需要归并的第一个序列的开始位置,第三个参数为第二个序列的开始位置,第四个参数为第二个序列的结束位置,两个序列物理位置是相邻的。首先 申请空间,使其大小为两个已经排序 序列之和,该空 间用来存放合并后的序列,因为待归并的两个序列都是有序的,因此只需要分别记录两个序列的左边位置,数据小的复制,然后右移索引,直到某个序列复制完成。然后将复制另一序列的剩余数据。最终将空间的数据回写到参数数组中。
代码实现
//归并排序  本函数为调用函数
	public static void mergeSort(int[] a){
		mergeSort(a ,1);
		
	}
	public static void mergeSort(int[] a, int len){
		int size = a.length;
		int mid = size/(len<<1);
		int c = size %(len << 1);
		
		if(mid ==0){
			return;
		}
		int s = 0;
		for(int i = 0; i<mid; ++i){
			merge(a, s, s+len,s+(len<<1)-1);
			s = s + (len<<1);
		}
		 if (c != 0){
	        merge(a, size - c - 2 * len, size - c, size - 1);
		 }
	     mergeSort(a,  2 * len);
		
	}
	
	public static void merge(int[] a, int s, int m, int t){
		int p = s;
		int q = m;
		int i=0;
		int[] c = new int[t-s+1];
		while(p<m && q<=t){
			if(a[p]<=a[q]){
				c[i] = a[p];
				p++;
				i++;
			}else{
				c[i] = a[q];
				q++;
				i++;
			}
		}
		if(p<m){
			for(int j =i;j<=t-s;j++){
				c[j] = a[p];
				p++;
			}
		}
		if(q<=t){
			for(int j =i;j<=t-s;j++){
				c[j] = a[q];
				q++;
			}
		}
		System.arraycopy(c, 0, a, s, c.length);
	}

希尔排序

原理简介:希尔排序也是对插入排序的一种 改进算法。它是按增量来分割数组,对数组进行排序,增量初始化为length/2,然后递减增量知道增量为1,则排序完成。
如:a = 3 1 3 5 9 7 4 4 
d = length/2 = 4
那么a[0]和a[4],a[1]和a[5],a[2]和a[6],a[3]和a[7]一组,分别对四组进行快排
第一次排序为:3 1 3 4 9 7 4 5
类似的,第一次d=2,排序为3 1 3 4 4 5 9 7 
第三次d=1,排序为1 3 3 4 4 5 7 9
算法:
       1、初始化增量为d=length/2
       2、进行shell排序,d此时的隐含含义就是该增量下,数组被分割的小序列数目。因此进行需要进行d次快排。
       3、剩下的就是在每一次快排中调用之前的快排算法了,这部分可以参考前面的算法。
代码实现
//希尔排序 本函数为调用函数
	public static void shellSort(int[] a){
		int d = a.length/2;
		while(d>=1){
			shellSortHelp(a, d);
			d=d/2;
		}
	}
	
	public static void shellSortHelp(int[] a, int d){
		for(int x=0;x<d;x++)
			for(int i=x+d; i<a.length; i=i+d){
				for(int j =i;j-d>=0;j=j-d){
					if(a[j]<a[j-d]){
						int temp = a[j-d];
						a[j-d] =a[j];
						a[j] = temp;
					}else{
						break;
					}
				}
			}
	  
	}

堆排序

原理简介:大顶堆是指父节点必定大于子节点,因此堆顶元素必定是最大值。堆排序是指将我们的数组初始化为一个大顶堆,然后将堆顶元素取出来,调整剩下的数据,构造一个新的大顶堆,进行length-1次取堆顶元素即可。
这里需要了解的基础是,堆的特性,如果堆顶以标号0开始的话,其父节点为i,那么左右子节点分别为2i+1,2i+2。
更新堆顶元素的调整过程:一个大顶堆,更新堆顶元素之后,需要与其左右子节点比较,如果小于子节点,则需要交换,然后继续与子节点比较,直到大于等于子节点或者子节点序号大于数组长度为止。
参考文章,堆排序的更详细的原理介绍: http://www.cnblogs.com/mengdd/archive/2012/11/30/2796845.html
算法:
        1、首先编写堆适应函数heapAjust,第一个参数是数组,第二个参数是parent(即更新过后的不稳定节点),第三个参数是length,length为堆大小。
              数组左边为大顶堆,右边为有序序列。每次更新堆中节点parent,我们首先将a[parent]赋值给temp,计算左子节点索引为child=2*parent+1,判断左子节点是否存在,如果存在,则继续判断其右子节点是否存在且大于左子节点,如果是,则更新child++,即获取右子节点索引。然后比较temp值于子节点中较大值,如果temp大,则堆不变,如果temp小,则交换,此时更新的值为child节点,继续循环比较child的子节点操作。
        2、得到待排序数组后,首先我们将其初始化为大顶堆。从我们刚才编写的堆适应函数来看,它是从父节点来更新堆,因此为了包括所有的元素,我们可以从len/2这个索引开始初始化堆。然后依次递减,直到初始化所有元素。
        3、此时数组中是以大顶堆顺序排列的,有序序列在数组右边,左边为堆,我们取出第一个元素,与数组的最后一个元素交换,此时堆中元素少一个,有序序列中元素多一个,我们执行len-1次操作,即可将堆中元素全部取出得到有序序列。
代码实现
//堆排序 本函数为调用函数
	public static void heapSort(int[] a){
		for(int i=a.length/2-1; i>=0;i--){
			heapAjust(a, i, a.length);
		}
		for(int j=a.length-1;j>0;j--){
			int temp = a[j];
			a[j] = a[0];
			a[0] = temp;
			heapAjust(a,0,j);
		}
	}
	
	public static void heapAjust(int[] a, int parent, int length){
		int temp = a[parent];
		int child = parent*2+1;
		while(child<length){
			if(child+1<length&&a[child]<a[child+1]){
				child++;
			}
			if(temp>a[child]){
				
				break;
			}
			a[parent] = a[child];
			parent = child;
			child = 2*child+1;
		}

		a[parent] = temp;
		
	}

基础排序算法的学习就到此为止了。

猜你喜欢

转载自blog.csdn.net/u012195899/article/details/68923468
今日推荐