【快速排序】

【快排】

- 核心思想:本来的问题规模很大,不断缩小问题规模

- 算法

  假定待排序的数组为 arr ,start = 0,end = arr.length -1 

(1)在[ start , end ] 范围中 选择一个arr[ index ],以这个数为基准,遍历将数组分成两半

(2)将原问题 变成了 [ start , index-1] 和 [ index+1, end] 两个子问题 ,对两个子问题 运用步骤(1)

(3)直到问题规模变成 1 ,就不在再解子问题

【代码】

private void swap(int[] numbers, int a, int b) {
        int temp = numbers[a];
        numbers[a] = numbers[b];
        numbers[b] = temp;
    }
    
    // 随机选择一个基准,将大于基准和小于基准的数分别放两边
    private int partition(int[] numbers, int start, int end) {
        // if(start == end) return start;
        int index = new Random().nextInt(end-start)+start;
        
        swap(numbers, index, end);    // 将基准放末尾
        int smallCnt = start;        // 小于基准个数
        for(int i=start; i<end; ++i) {
            if(numbers[i] < numbers[end]) {
                if(i!=smallCnt) {
                    swap(numbers, i, smallCnt);
                }
                smallCnt++;
            }
        }
            
        swap(numbers, smallCnt, end); // 将基准放中间
        return smallCnt;
    }
    
    public void quickSort(int[] data, int start, int end) {
        if(end==start) return;
        
        int index = partition(data, start, end);
        if(index>start) {
            quickSort(data, start, index-1);
        }
        if(end>index) {
            quickSort(data, index+1, end);
        }
    }
Java实现

运用快排的思想可以解决很多类似的问题:

运用快排中的partition函数,可以减小问题规模

1、找第 k 大或者第k小的数

    private void swap(int[] numbers, int a, int b) {
        int temp = numbers[a];
        numbers[a] = numbers[b];
        numbers[b] = temp;
    }
    private int partition(int[] numbers, int start, int end) {
        if(start == end) return start;
        int index = new Random().nextInt(end-start)+start;
        
        swap(numbers, index, end);
        int smallCnt = start;
        for(int i=start; i<end; ++i) {
            if(numbers[i] < numbers[end]) {
                if(i!=smallCnt) {
                    swap(numbers, i, smallCnt);
                }
                smallCnt++;
            }
        }
        
        swap(numbers, smallCnt, end);
        return smallCnt;
    }
    
    
    public int finKMin(int[] numbers, int k) {
        int start = 0;
        int end = numbers.length-1;
        
        // 使用partition 每次缩小问题规模
        // 如果 index < k-1 那么,第k小在[index+1, end]中
        // 如果index > k-1 那么,第k小在[start, index-1]
        int index = partition(numbers, start, end);
        while(index != k-1) {
            if(index < k-1) {
                start = index + 1;
            }else {
                end = index-1;
            }
            index = partition(numbers, start, end);
        }
        
        return numbers[index];
    }
View Code

2、找数组中k个最小的数

  - 思路同问题1

private void swap(int[] numbers, int a, int b) {
        int temp = numbers[a];
        numbers[a] = numbers[b];
        numbers[b] = temp;
    }
    private int partition(int[] numbers, int start, int end) {
        if(start == end) return start;
        int index = new Random().nextInt(end-start)+start;
        
        swap(numbers, index, end);
        int smallCnt = start;
        for(int i=start; i<end; ++i) {
            if(numbers[i] < numbers[end]) {
                if(i!=smallCnt) {
                    swap(numbers, i, smallCnt);
                }
                smallCnt++;
            }
        }
        
        swap(numbers, smallCnt, end);
        return smallCnt;
    }
    
    // 找最小的k个数
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> ans = new ArrayList<>();
        
        // 特殊情况,处理
        if(input == null || k<=0) return ans;
        if(input.length <= k) {
            for(int i=0;i<input.length;++i) {
                ans.add(input[i]);
            }
            return ans;
        }
        
        // 核心代码
        int start = 0;
        int end = input.length-1;
        int index = partition(input, start, end);
        while(k!=index+1) {
            if(index+1>k) {
                end = index-1;
            }else {
                start = index+1;
            }
            index = partition(input, start, end);
        }
        
        // 保存结果
        for(int i=0;i<k;++i) {
            ans.add(input[i]);
        }
        return ans;
    }
View Code

3、找数组出现次数过半的数,

  - 就是找数组的中位数,不过要注意处理检查结果的正确性,因为中位数不一定再数组中出现次数过半

/**
 * 面试题39: 数组中出现次数超一半的数字
 * @author Saber
 * (1)最容易想到,先排序,再找中间数
 * (2)利用哈希表记录每个数字出现的次数
 * (3)快排变形
 * (4)以下方法
 */

public class P39 {
    
/*
 *
采用阵地攻守的思想:
第一个数字作为第一个士兵,守阵地;count = 1;
遇到相同元素,count++;
遇到不相同元素,即为敌人,同归于尽,count--;当遇到count为0的情况,又以新的i值作为守阵地的士兵,继续下去,到最后还留在阵地上的士兵,有可能是主元素。
再加一次循环,记录这个士兵的个数看是否大于数组一般即可。
 */
    public int MoreThanHalfNum_Solution(int [] array) {
        // 参数检验
        if(array == null || array.length<=0) return 0;
        
        int result = array[0];
        int times = 1;
        for(int i=1;i<array.length;++i){
            if(times == 0){
                result = array[i];
                times = 1;
            }else if(result == array[i]){
                times++;
            }else{
                times--;
            }
        }
        
        
        // 检查result 是否是答案
        int cnt = 0;
        for(int i=0;i<array.length;++i){
            if(array[i]==result){
                cnt++;
                if(cnt>array.length/2){
                    return array[i];
                }
            }
        }
        return 0;
    }
        
    private void swap(int[] numbers, int a, int b) {
        int temp = numbers[a];
        numbers[a] = numbers[b];
        numbers[b] = temp;
    }
    private int partition(int[] numbers, int start, int end){
        if(start == end) return start;
        int index = new Random().nextInt(end-start) + start;
        //先把选中的数 与 数组最后一个数 交换位置
        swap(numbers, index, end);
        
        //遍历数组,以选中的数字为界限,将数组中的元素分两半
        int smallCnt = start;
        for(int i=start;i<end;++i) {
            if(numbers[i]<numbers[end]) {
                if(i!=smallCnt) {
                    swap(numbers, i, smallCnt);
                }
                smallCnt++;
            }
        }
        
        swap(numbers, smallCnt, end);
        
        return smallCnt;
    }
        
    // 基于partition的解法
    public int fun(int [] array) {
        int start = 0;
        int end = array.length-1;
        int middle = array.length>>1;
        int index = partition(array, start, end);
        while(index != middle) {
            if(index > middle) {
                end = index-1;
            }else {
                start = index+1;    
            }
            index = partition(array, start, end);
        }
        
        int ans = array[middle];
        int cnt = 0;
        for(int i=0;i<array.length;++i){
            if(array[i]==ans){
                cnt++;
                if(cnt>middle){
                    return array[i];
                }
            }
        }
        return 0;
    }
    
    public static void main(String[] args) {
        int[] numbers = new int[] {3,1,1};
        System.out.println(new P39().fun(numbers));
        
    }

}
View Code

猜你喜欢

转载自www.cnblogs.com/chsobin/p/10569675.html