算法之排序算法浅谈

    在8大排序算法之间的时间复杂度,辅助空间以及稳定性情况如下表1:

    

表1

   一:快速排序。

   快速排序的核心在鱼partition函数部分,如何选择pivot分割点,以及如何围绕分割点进行分割;

   法一:如下代码

/**
*   快速排序法
*/
public class QuickSort {
	//运用分治思想,按照每次的中间值分开两部分后,每部分继续递归去分为两部分排序
	public void Quick_Sort(int A[],int start,int end){
		int position = QCCore01(A, start, end);
		if(position>start){
			Quick_Sort(A,start,position-1);
		}
		if(position<end){
			Quick_Sort(A,position+1,end);
		}
	}

	//快排核心部分,选取一个分界值,然后把数组分为两部分,运用前后两指针对向移动方法
	private int QCCore01(int A[],int start,int end){
		int key = A[start];//这里取的是第一个元素作为分界值
		while(start<end){
            //右指针先左移,直到遇到“小”值
            while(A[end]>=key&&start<end){
            	end--;
            }
            //把“小”值交换到左边,把分界值转换过来
            if(start<end){
            	int temp = A[end];
            	A[end] = key;
            	A[start] = temp;
            }

            //左指针右移,直到遇到“大”值
            while(A[start]<=key&&start<end){
            	start++;
            }
            //把“大”值交换到右边,把分界值转换过来
            if(start<end){
            	int temp = A[start];
            	A[start] = key;
                A[end] = temp; 
            }
             
		}
		return start;//返回中间值的标号
	}
} 
   该方法比较常用,即选择每次排序段数组第一个元素作为pivot元素,然后两遍依次向中间夹逼,每次都是与pivot元素交换;


   法二:代码如下

   

/**
*   快速排序法
*/
public class QuickSort {
	//运用分治思想,按照每次的中间值分开两部分后,每部分继续递归去分为两部分排序
	public void Quick_Sort(int A[],int start,int end){
		int position = QCCore02(A, start, end);
		if(position>start){
			Quick_Sort(A,start,position-1);
		}
		if(position<end){
			Quick_Sort(A,position+1,end);
		}
	}

    //快排核心部分,运用双指针同时从前向后,产生大小差值,并根据差值数替换。
    private int QCCore02(int A[],int start, int end){
        int small = start;//记录“小”值的数目,big遇到“大”值不增加,遇到“小”值才增加
        int big = start+1;//代表当前指针去遍历元素
        int key = A[start];//取第一个数作为分界值
        
        //双指针去遍历
        while(big<=end){
            if(A[big]<key){
                small++;
                if(big!=small){
                    int temp = A[big];
                    A[big] = A[small];
                    A[small] = temp;
                }
            }
            big++;
        }

        //最后需要交换分界值到指定位置
        int temp = A[small];
        A[small] = A[start];
        A[start] = temp;
        
        return small;//返回分界值的位置
    }
} 
   该方法pivot元素仍然是选择数组段第一个元素作为分割点,但是交换方法上改为比较巧妙的双指针同向运动,一个small指针表示当前最后一个小段元素,big指针表示当前最后一个大段元素,即保证两个指针间的元素都为大段元素,最后只需要把pivot元素放在small指针后面即可(该方法的交换不是鱼pivot元素直接交换);

   

   法三:代码如下

   

/**
 * 快速排序
 * */
public class QuickSort {
	/**
	 * 主调用方法
	 * */
	public void quickSort(int[] array,int n){
		if(n>2){
			this.recurSort(array, 0, n-1);
		}
	}
	/**
	 * 递归进行不断划分,首先进行三个元素间的分割点选择,接着进行划分,并递归
	 * */
	private void recurSort(int[] array,int start,int end){
		if(start<end){
			int pivot = (end-start)/2+start;
			this.threeSort(array, start, pivot, end);
			int curPivot = this.partition(array, pivot, start+1, end-1);
			this.recurSort(array, curPivot+1, end);
			this.recurSort(array, start, curPivot-1);
		}
	}
	/**
	 * 对数组中三个位置进行排序,针对于快排中的选分割点,选择左中右三个元素中的中间元素作为分割点,防止最坏情况发生;
	 * 如果不采用该选择策略,该方法可以不用;
	 * */
	private void threeSort(int[] array,int l,int m,int r){
		if(array[l]>array[m]){
			this.swap(array, l, m);
			if(array[r]<=array[l]){
				this.swap(array, r, l);
				this.swap(array, m, r);
			}else if(array[r]<=array[m])
				this.swap(array, m, r);
		}else{
			if(array[r]<=array[l]){
				this.swap(array, r, l);
				this.swap(array, m, r);
			}else if(array[r]<=array[m])
				this.swap(array, m, r);
		}
	}
	/**
	 * 交换两数组中两个元素位置
	 * @param i 位置1
	 * @param j 位置2
	 * */
	private void swap(int[] array,int i,int j){
		int temp = array[i];
		array[i] = array[j];
		array[j] = temp;
	}
	/**
	 * 一次快排的划分
	 * @param array 待排序数组
	 * @param pivot 划分点的序号
	 * @param start 数组中划分段的起始点序号
	 * @param end 数组中划分段的结束点序号
	 * */
	private int partition(int[] array,int pivot,int start, int end){
		int pivotIndex = pivot;
		if(start<end){
			/*记录结束点的序号*/
			int toail = end;
			/*记录划分点的值*/
			int value = array[pivot];
			/*把划分点与划分段最后一个数交换,即把划分点的值放到最后*/
			this.swap(array, pivot, end);
			end--;
			/*主循环*/
			while(start<end){
				if(array[start]<value)
					start++;
				else if(array[end]>=value)
					end--;
				else{
					this.swap(array, start, end);
				}
			}
			/*循环出来后,做扫尾工作,准备还原分割点*/
			/*当start的值大于分割点,则必须交换*/
			if(array[start]>=value){
				this.swap(array, start, toail);
				pivotIndex = start;
			}
			/*如果start的值小于分割点的值,则交换start右边一个元素与分割点;如果右边没有元素,则保持不动*/
			else if(array[start]<value && start+1<toail){
				this.swap(array, start+1, toail);
				pivotIndex = start+1;
			}else{
				pivotIndex = toail;
			}
		}
		return pivotIndex;
	}
}
   该方法是通过把pivot元素一开始就放置到数组末端,然后双指针向中间夹逼,同时找到一大一小才互相交换元素,而不是与pivot元素交换;在选择pivot的方法上,当然允许继续选择数组段的第一个元素作为pivot元素,但是这里提供了一个threeSort()函数,该函数是将数组段的最左,中间,最右三个元素排序后返回中间元素的序号,将该元素作为pivot即可防止快排最坏情况的发送。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值