快速排序
原理:
- 从待排序区间选择一个数,作为基准值(pivot);
- Partition:
遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的 (可以包含相等的)放到基准值的右边。 - 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,或者小区 间的长度 == 0,代表没有数据。
实现:
下面几种方法都要用到的代码
public static void swap(int[] array,int left,int right) {
int tmp = array[left];
array[left] = array[right];
array[right] = tmp;
}
1、horen:
public static int Partion(int[] array,int left,int right) {
int begin = left;
int end = right - 1;
int key = array[end];
while(begin < end) {
//1、left从前往后找比基准值大的元素
while (begin < end && array[begin] <= key) {
begin++;
}
//2、right从后往前找比基准值
while (begin < end && array[end] >= key) {
end--;
}
if (begin < end) {
swap(array, begin, end);
}
}
if(begin != right-1) {
swap(array, begin, right - 1);
}
return begin;
}
b、挖坑法
public static int Partion2(int[] array,int left,int right) {
int begin = left;
int end = right - 1;
int key = array[end];
while(begin < end) {
while(begin < end && array[begin] < key) {
begin++;
}
swap(array,begin,end);
while (begin < end && array[end] > key) {
end--;
}
swap(array,begin,end);
}
return 0;
}
c、前后索引法
public static int Partion3(int[] array,int left,int right) {
int cur = left;
int prev = cur - 1;
int key = array[right - 1];
while (cur < right) {
if(array[cur] < key && ++prev != cur) {
swap(array,cur,prev);
}
++cur;
if(++prev != right - 1) {
swap(array,prev,right - 1);
}
}
return prev;
}
性能分析:
稳定性:不稳定
存在最优情况:如果每次获取到的基准值都能够将区间划分成左右两半部分如:二叉树平衡树时间复杂度为0(NlogN)。
存在最差情况:每次划分之后,数据都在基准值的一-侧(每次拿到的基准值刚好是区间中的极值)如:单支树时间发杂度为0(N^2)。
优化方案:
尽量避免最差情况出现,采用三数取中法对降低获取到极值的概率。
三数取中法: left mid right—>返回返回着三个数据中最中间一个平均时间复杂度: 0(NlogN)
没有让递归到区间只剩一个数据时退出,因为递归到一定程度,区间中的数据实际慢慢的变少
非递归方法:
数据量如果比较大,递归层数比较深—>递归深度到一定程度,可能会导致栈溢出,这时采用非递归方法这里需要借助栈来实现。
代码实现:
public static void quickSortNor(int[] array) {
Stack<Integer> stack = new Stack<>();
stack.push(array.length);
stack.push(0);
while(!stack.empty()) {
int left = stack.pop();
int right = stack.pop();
if(right-left > 1) {
int div = Partion(array,left,right);
stack.push(right);
stack.push(div+1);
stack.push(div);
stack.push(left);
}
}