排序--快速排序
1、什么是快速排序?
快速排序和冒泡排序都是采用交换法,与冒泡不同的是,快速排序还采用分治法:通过选定一个“基点(可随机选择)”,将小于基点的元素放在基点左侧,大于基点的放在右侧,然后用递归对基点两侧的元素再排序
2、快速排序的代码思路:
双边循环法:
-
- 假定基点pivot是数组最左侧元素,设置两个指针left,right分别指向待排元素最左侧和最右侧
- left开始向右移动,如果left指向的数大于基点,停止移动
- right开始移动,如果right指向的数小于基点,停止移动
- arr[left]和arr[right]交换位置
- 如果left==right,第一轮分治结束,将基点pivot与arr[left]交换位置,接下来进行左边和右边的分治
3、双边循环法代码实现:
//目标:排序数组内元素 int[] arr={-9,78,0,23};
public static void quickSort(int[] arr,int startIndex,int endIndex) { if (startIndex>=endIndex){ //一般都是=的时候结束 return; } int pivotIndex=singlePartition(arr,startIndex,endIndex); quickSort(arr,startIndex,pivotIndex-1); quickSort(arr,startIndex+1, endIndex); }
public static int partition(int[] arr,int startIndex,int endIndex){ int pivot=arr[startIndex]; //假定的基点 int left=startIndex; //左边移动的指针 int right=endIndex; //右边移动的指针 while (left!=right){ //左边不等于右边,说明还没有移动完 while (left<right && arr[right]>=pivot){ //left<right是个限定,arr[right]>=pivot 是右边指针left指向的元素比基点大,左移,因为基点右边的本来就该比基点大,我们找的是右边开始小于基点的 right--; } while (left<right && arr[left]<=pivot){ //寻找左边开始大于基点的,找不到就后移,直到找到跳出 left++; } if (left<right){ //跳出后执行交换,这就把左边大的转移到了右边,右边小的转移到了左边 int p=arr[left]; arr[left]=arr[right]; arr[right]=p; } } //当left==right时,交换left指向的元素和基点的位置 arr[startIndex]=arr[left]; arr[left]=pivot; return left; //这里返回的left主要时为了确定左右递归时的结束位置和开始位置 }
4、单边循环法
4.1 代码思路:
-
- 选择基准元素pivot,设置mark指针指向数列起始位置
- 遍历的元素大于基准元素,继续向后遍历
- 遍历元素小于基准元素,指针mark右移,将遍历元素和mark指向的元素交换位置
- 遍历完数列后,基准元素和mark指向的元素交换位置,这就分成了两队(左边小于基准元素,右边大于基准元素)
- 递归排序左边和右边的待排数列
4.2 代码实现
public static int singlePartition(int[] arr,int startIndex,int endIndex){ int pivot=arr[startIndex]; //设置基准元素 int mark=startIndex; //mark指针 for (int i=startIndex+1;i<endIndex;i++){ //遍历基准元素后面的数列 if (arr[i]<mark){ //如果当前元素大于基准元素,mark后移,并让mark指向的元素和当前元素交换位置 mark++; int p=arr[mark]; arr[mark]=arr[i]; arr[i]=p; } } //遍历完成,mark和基准元素交换位置 arr[startIndex]=arr[mark]; arr[mark]= pivot; return mark; }
5、快速排序时间复杂度
O(nlogn)