当前位置: 首页 > news >正文

随机快速排序

随机快速排序
在这里插入图片描述

经典随机快速排序

// 随机快速排序,acm练习风格
// 测试链接 : https://www.luogu.com.cn/problem/P1177
// 务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class Code01_QuickSort {

	public static int MAXN = 100001;

	public static int[] arr = new int[MAXN];

	public static int n;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		in.nextToken();
		n = (int) in.nval;
		for (int i = 0; i < n; i++) {
			in.nextToken();
			arr[i] = (int) in.nval;
		}
		quickSort2(0, n - 1);
		for (int i = 0; i < n - 1; i++) {
			out.print(arr[i] + " ");
		}
		out.println(arr[n - 1]);
		out.flush();
		out.close();
		br.close();
	}

	// 随机快速排序经典版(不推荐)
	// 甚至在洛谷上测试因为递归开太多层会爆栈导致出错
	public static void quickSort1(int l, int r) {
		// l == r,只有一个数
		// l > r,范围不存在,不用管
		if (l >= r) {
			return;
		}
		// 随机这一下,常数时间比较大
		// 但只有这一下随机,才能在概率上把快速排序的时间复杂度收敛到O(n * logn)
		// l......r 随机选一个位置,x这个值,做划分
		int x = arr[l + (int) (Math.random() * (r - l + 1))];
		int mid = partition1(l, r, x);
		quickSort1(l, mid - 1);
		quickSort1(mid + 1, r);
	}

	// 已知arr[l....r]范围上一定有x这个值
	// 划分数组 <=x放左边,>x放右边
	// 并且确保划分完成后<=x区域的最后一个数字是x
	public static int partition1(int l, int r, int x) {
		// a : arr[l....a-1]范围是<=x的区域
		// xi : 记录在<=x的区域上任何一个x的位置,哪一个都可以
		int a = l, xi = 0;
		for (int i = l; i <= r; i++) {
			if (arr[i] <= x) {
				swap(a, i);
				if (arr[a] == x) {
					xi = a;
				}
				a++;
			}
		}
		swap(xi, a - 1);
		return a - 1;
	}

	public static void swap(int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

	// 随机快速排序改进版(推荐)
	// 可以通过所有测试用例
	public static void quickSort2(int l, int r) {
		if (l >= r) {
			return;
		}
		// 随机这一下,常数时间比较大
		// 但只有这一下随机,才能在概率上把快速排序的时间复杂度收敛到O(n * logn)
		int x = arr[l + (int) (Math.random() * (r - l + 1))];
		partition2(l, r, x);
		// 为了防止底层的递归过程覆盖全局变量
		// 这里用临时变量记录first、last
		int left = first;
		int right = last;
		quickSort2(l, left - 1);
		quickSort2(right + 1, r);
	}

	// 荷兰国旗问题
	public static int first, last;

	// 已知arr[l....r]范围上一定有x这个值
	// 划分数组 <x放左边,==x放中间,>x放右边
	// 把全局变量first, last,更新成==x区域的左右边界
	public static void partition2(int l, int r, int x) {
		first = l;
		last = r;
		int i = l;
		while (i <= last) {
			if (arr[i] == x) {
				i++;
			} else if (arr[i] < x) {
				swap(first++, i++);
			} else {
				swap(i, last--);
			}
		}
	}

}

荷兰国旗问题优化

import java.io.*;

public class Main{
    public static int MAXN = 100001;
    public static int arr[] = new int[MAXN];
    public static int n ;
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer in = new StreamTokenizer(br);
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        in.nextToken();
        n  = (int)in.nval;
        //输入
        for(int i = 0;i < n ;i++){
            in.nextToken();
            arr[i] = (int)in.nval;
        }
        quickSort(0,n-1);
        //输出
        for(int i = 0;i<n-1;i++){
            out.print(arr[i] + " ");
        }
        out.print(arr[n-1]);
        out.flush();
        out.close();
        
    }
    public static void quickSort(int l,int r){
        if(l >= r){
            return;
        }
        //求随机值x
        int x = arr[l + (int)(Math.random()*(r -l +1))];
        partition(l,r,x);

        int left = a;
        int right = b;
        
        quickSort(l,left - 1);
        quickSort(right + 1,r);
    }

    public static int a;
    public static int b;
    public static void partition(int l,int r,int x){
        a = l;
        b = r;
        int i = l;
        while(i<=b){
            if(arr[i] == x){
                i++;
            }else if(arr[i]< x){
                //交换arr[i]和arr[a]; a++;i++
                swap(i,a);
                a++;
                i++;
            }else{
                //交换arr[i]和arr[b],i不变;b--;
                swap(i,b);
                b--;
            }
        }
    }

    public static void swap(int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

实现细节1:
在输入和输出数组的时候,for循环的范围不同,主要是为了格式化输出,避免在最后一个数字后面多输出一个空格:

  1. 输入时:i < n
in.nextToken();
n = (int) in.nval;
for (int i = 0; i < n; i++) {
	in.nextToken();
	arr[i] = (int) in.nval;
}
  • 这里for循环中i < n 是正常的,因为要读取所有n个元素,所以i从 0 到 n - 1 都需要复制给arr[i]。

  • 这是标准的遍历数组方式,确保所有元素都被正确读取。

  • 输出时:i < n -1

for (int i = 0; i < n - 1; i++) {
			out.print(arr[i] + " ");
		}
		out.println(arr[n - 1]);
  • 这里for循环中i < n -1 ,意味着for循环只输出前n-1个元素,并在他们之间加上 空格" "
  • 最后单独输出arr[n - 1]。这样 不会在最后一个元素后面多加一个空格。

小结:
✔ 输入:for (int i = 0; i < n; i++)确保 所有元素都被读取。
✔ 输出: for (int i = 0; i < n - 1; i++) 避免最后一个元素后面多空格,然后 println(arr[n - 1]) 处理最后一个元素。

这样可以保证输出格式正确,特别是在 OJ 判题系统 中不会因为末尾额外的空格导致格式错误!

实现细节2:

代码作用
out.flush();强制将缓冲区数据输出,防止数据丢失
out.close();关闭 PrintWriter,释放输出资源
br.close();关闭 BufferedReader,释放输入资源

建议:
如果 out 是 System.out,close() 可以省略,因为 JVM 退出时会自动关闭。
但如果 out 关联文件,必须 close(),否则可能导致 文件写入不完整 或 文件句柄 泄漏。

两种快排:

  • 快速排序(x固定)
    最差情况 :(x不靠近中点,在左右两侧)
  • 时间复杂度 O(n^2)
  • 空间复杂度 O(n)
    最好情况:(x靠近中点)
  • 时间复杂度O(N*logN)
  • 空间复杂度O(logN)
  1. 随机快速排序 (x不固定)
    随机行为根据期望估计 时间复杂度O(N*logN) 空间复杂度O(logN)

至于为什么选择随机快速排序而不选择快速排序,因为测试数据可以轻易地构造,让每次选中间的数排序起来最难受!

相关文章:

  • 纯前端全文检索的两种实现方案:ElasticLunr.js 和 libsearch
  • 使用 kubectl cp 命令可以在 Kubernetes Pod 和本地主机之间拷贝文件或文件夹
  • 破局者登场:中国首款AI原生IDE Trae深度解析--开启人机协同编程新纪元
  • G-Star 公益行 | 温暖相约 3.30 上海「开源×AI 赋能公益」Meetup
  • Python和Docker实现AWS ECR/ECS上全自动容器化部署网站前端
  • Manus(一种AI代理或自动化工具)与DeepSeek(一种强大的语言模型或AI能力)结合使用任务自动化和智能决策
  • 【蓝桥杯单片机】第十一届省赛
  • 【算法day7】 Z 字形变换 (O2算法思路整理)
  • C语言实现斐波那契数列
  • 在知识的旅途中,奔赴导游职业资格考试的星辰大海
  • 嵌入式软件测试的东方智慧:WinAMS工具的技术哲学与实践启示——一名汽车电子工程师的七年工具演进观察
  • VSCode集成C语言开发环境
  • 力扣1251年
  • SAIL-RK3576核心板应用方案——无人机视觉定位与地面无人设备通信控制方案
  • 密闭空间可燃气体监测终端:守护城市命脉,智驭燃气安全!
  • Agisoft Metashape 创建分块建模
  • Servlet知识点之Parameter和Attribute
  • 设计模式-观察者模式、状态模式
  • qt5中使用中文报错error: C2001: 常量中有换行符
  • 股票-K线
  • 多条跨境铁路加速推进,谁是下一个“超级枢纽”?
  • 日本前卫艺术先驱群展上海:当具体派相遇古树古宅
  • 甘肃发布外卖食品安全违法行为典型案例:一商家用鸭肉冒充牛肉被罚
  • 一海南救护车在西藏无任务拉警笛开道,墨脱警方:已处罚教育
  • 中国恒大:清盘人向香港高等法院申请撤回股份转让
  • 香港暂停进口美国北达科他州一地区禽肉及禽类产品