挖坑法,Hoare,非递归法实现快速排序

时间:O(N*lgn)->最坏n^2(有序,逆序) 

空间:logN N*2

Hoare 

Hoare法与其他快速排序算法的不同之处在于它使用两个指针(分别指向数组的起始位置和结束位置),并通过交换元素的方式来确定基准值的最终位置。

具体步骤如下:

  1. 选择一个基准值,通常是待排序数组的第一个元素。
  2. 设定两个指针,一个指向数组的起始位置,称为 left,另一个指向数组的结束位置,称为 right
  3. 从 right 开始,向左移动直到找到一个小于等于基准值的元素。从 left 开始,向右移动直到找到一个大于等于基准值的元素。
  4. 如果 left 指针小于等于 right 指针,则交换这两个元素,然后分别将 left 和 right 指针向后、向前移动一位。
  5. 重复,直到 left 指针大于 right 指针。
  6. 将基准值与 right 指针所指向的元素交换,此时基准值左边的元素都小于等于它,右边的元素都大于等于它。
  7. 对基准值左边和右边的子数组分别递归执行上述步骤,直到每个子数组只有一个元素或为空。

    public static int partition2(int[] nums,int left,int right){//Hoare
        int temp=nums[left];
        int i=left;
        while(left=temp){
                right--;
            } while(left

挖坑 

具体步骤如下:

  1. 选择一个基准值,通常是待排序数组的第一个元素。
  2. 从数组的右端开始,设定一个指针 right 指向最右边的元素,然后从右往左扫描数组,找到第一个小于基准值的元素,将该元素填入基准值所在的位置,即将基准值挖空形成一个坑。
  3. 接着从数组的左端开始,设定一个指针 left 指向最左边的元素,然后从左往右扫描数组,找到第一个大于基准值的元素,将该元素填入上一步挖空的坑中,并将该元素所在的位置再次挖空。
  4. 重复步骤2和步骤3,直到 left 和 right 指针相遇。
  5. 将基准值填入最后一个挖空的坑中,此时基准值左边的元素都比它小,右边的元素都比它大。
  6. 对基准值左边和右边的子数组分别递归执行上述步骤,直到每个子数组只有一个元素或为空。

挖坑法快速排序的核心思想是通过挖空和填坑的方式,将待排序数组划分为左右两个部分,左边部分的元素都小于基准值,右边部分的元素都大于基准值,然后对左右子数组进行递归排序,最终完成整个数组的排序。

import java.util.Arrays;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 86180
 * Date: 2023-10-28
 * Time: 1:38
 */

public class sort {
    public static void quickSort(int[] nums){
        quick(nums,0,nums.length-1);


    }
    public static void quick(int[] nums,int start,int end){
        if(start>=end){//1 2 3 4 s=0,e=-1
            return;
        }
        int pivot=partition(nums,start,end);
        quick(nums,start,pivot-1);
        quick(nums,pivot+1,end);


    }
    public static int partition(int[] nums,int left,int right){
        int temp=nums[left];
        while(left=temp){//>=是为了6,1,2,6的情况,不加=会导致6和6一直在交换。
                right--;
            }
            nums[left]=nums[right];
            while(left

每次进行快排的结果

[5, 1, 2, 4, 3, 6, 9, 7, 10, 8]
[3, 1, 2, 4, 5, 6, 9, 7, 10, 8]
[2, 1, 3, 4, 5, 6, 9, 7, 10, 8]
[1, 2, 3, 4, 5, 6, 9, 7, 10, 8]
[1, 2, 3, 4, 5, 6, 8, 7, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

问题

为啥左边作为基准,就要从右边先开始?

就比如我们把Hoare中的代码,

while (left < right && nums[left] <= temp) { left++; }放在

while (left < right && nums[right] >= temp) { right--; }

前面就会出问题得到不是我们想要的代码

6,1,2,7,9,3,4,5,10,8

如果我们先移动 `left` 指针,那么 `left` 指针会一直向右移动,直到找到一个比基准值大的元素或者 `left` 和 `right` 指针相遇。这样会导致 `left` 指针停在一个比基准值大的位置上,而 `right` 指针可能停在一个比基准值小的位置上,违反了我们的划分目标。

第一次快排,最后r指针最后会停在一个比6大地方,l++,会导致l和r相遇,此时nums[l]=nums[r]>6,交换6和9就导致了左边出现比6大的数字

挖坑法,Hoare,非递归法实现快速排序_第1张图片

为啥还要对快排进行优化

对快速排序进行优化的主要目的是提高其在各种情况下的性能和效率。尽管快速排序通常是一种高效的排序算法,但在某些情况下,它可能会出现性能下降的情况。下面是一些导致性能下降的情况:

  1. 已排序或近乎有序的数组:如果输入数组已经是有序的或近乎有序的,传统的快速排序算法可能会导致分割不均匀,使得递归调用的层数变多,从而降低了性能。

  2. 大量重复元素的数组:当待排序的数组包含大量重复元素时,传统的快速排序算法可能会导致分割不均匀。这是因为传统算法只考虑基准元素的大小,而没有考虑相等元素的情况,从而导致划分不均匀,影响性能。

  3. 小规模子数组:当待排序的子数组变得很小(通常小于一定阈值)时,快速排序的递归调用开销可能会超过插入排序的开销。在这种情况下,使用插入排序或其他简单排序算法可能更加高效。

  4. 递归调用栈溢出:传统的快速排序算法使用递归来处理子数组,当待排序的数组规模非常大时,递归调用栈可能会溢出,导致程序崩溃。这种情况可以通过尾递归优化或迭代方式实现来解决。

以上情况中的任何一种都可能导致快速排序的性能下降。为了克服这些问题,可以采取相应的优化措施,如随机选择基准元素、三数取中法。

三数取中

    private static void sort1(int[] array,int start,int end){
        //防止只有左子树或者右子树的情况
        if(start >= end){
            return;
        }
        //在找基准之前,解决划分不均匀的问题,将关键值改变为中间大小的值后,能解决单分支的情况
        int index = findMidValOfIndex(array,start,end);
        swap(array,start,index);
 
        int povit = partion(array,start,end);
        sort(array,start,povit-1);
        sort(array,povit+1,end);
    }
    /*
    找到中位数
     */
    private static int findMidValOfIndex(int[] array,int start,int end){
        int midIndex = (start+end)/2;
 
        if(array[start] < array[end]){
            if(array[midIndex] < array[start]) {
                return start;
            } else if (array[end] < array[midIndex]) {
                return end;
            }
            else {
                return midIndex;
            }
        }
        else {
            if (array[midIndex] > array[start]){
                return start;
            } else if (array[midIndex] < array[end]) {
                return end;
            }
            else {
                return midIndex;
            }
        }
    }

 随机选基准

private static void sort2(int[] array,int start,int end){
        //防止只有左子树或者右子树的情况
        if(start >= end){
           return;
        }
        if(( end-start+1) <= 15){
        //插入排序减少后几层 的递归
            insertSort1(array,start,end);
        }
        //在找基准之前,解决划分不均匀的问题,将关键值改变为中间大小的值后,能解决单分支的情况
        int index = findMidValOfIndex(array,start,end);
        swap(array,start,index);
 
        int povit = partion(array,start,end);
        sort(array,start,povit-1);
        sort(array,povit+1,end);
    }
    public static void insertSort1(int[] array,int left,int right){
        for (int i = left+1; i <= right ; i++) {
            int j = i-1;
            int tmp = array[i];
            for (; j >= left ; j--) {
                if(array[j] > array[i]){
                    array[j+1] = array[j];
                }
                else{
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }

非递归实现快排

import java.util.Stack;

public class QuickSortNonRecursive {
    public static void quickSort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        
        Stack stack = new Stack<>();
        stack.push(0);
        stack.push(arr.length - 1);
        
        while (!stack.isEmpty()) {
            int end = stack.pop();
            int start = stack.pop();
            
            int pivotIndex = partition(arr, start, end);
            
            if (pivotIndex - 1 > start) {
                stack.push(start);
                stack.push(pivotIndex - 1);
            }
            
            if (pivotIndex + 1 < end) {
                stack.push(pivotIndex + 1);
                stack.push(end);
            }
        }
    }
    
    private static int partition(int[] arr, int start, int end) {
        int pivot = arr[end];
        int i = start - 1;
        
        for (int j = start; j < end; j++) {
            if (arr[j] < pivot) {
                i++;
                swap(arr, i, j);
            }
        }
        
        swap(arr, i + 1, end);
        return i + 1;
    }
    
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    
    public static void main(String[] args) {
        int[] arr = {5, 2, 9, 1, 7, 6, 8};
        quickSort(arr);
        
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }
}

随机

class Solution {
    public int[] sortArray(int[] nums) {
        qsort(nums,0,nums.length-1);
        return nums;

    }
    public void qsort(int[] nums,int l,int r){
        if(l>=r) return;
        int key=nums[new Random().nextInt(r-l+1)+l];
        int left=l-1,right=r+1,i=l;
        while(i

第k大的元素

class Solution {
    public int findKthLargest(int[] nums, int k) {
        return qsort(nums,0,nums.length-1,k);
    }
    public int qsort(int[] nums,int l,int r,int k){
        if(l==r) return nums[l];
      int left=l-1,right=r+1,i=l;
      int key=nums[new Random().nextInt(r-l+1)+l];
      while(i=k) return qsort(nums,right,r,k);
      else if(b+c>=k) return key;
      else return qsort(nums,l,left,k-b-c);
    }
    public void swap(int[] nums,int i,int j){
        int t=nums[i];
        nums[i]=nums[j];
        nums[j]=t;
    }
}

你可能感兴趣的:(Java,Java数据结构与算法,算法,数据结构,排序算法)