1.题目
给定你一个长度为n的整数数列。
请你使用快速排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入格式
输入共两行,第一行包含整数 n。
第二行包含 n 个整数(所有整数均在1~109范围内),表示整个数列。
输出格式
输出共一行,包含 n 个整数,表示排好序的数列。
数据范围
1≤n≤100000
输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5
2.解题
基于分治的思想
(1)确定分界点
(2)调整区间
(3)递归
快速排序,首先令数组第一个元素为key,然后让数组左边的数都比key小,右边的数都比key大,即定义两个指针,分别从左和从右进行遍历,将右边比key小的数和左边比key大的数进行交换,直到i==j时结束,然后将key的值和i所在位置元素进行交换,同理,再分别对左边半个数组和右边半个数组进行排序,最终得到有序数组。
java:
import java.util.Scanner;
import java.util.Arrays;
public class Main{
public static void quickSort(int[] arr, int low, int high){
if(low > high) return;
int key = arr[low];
int i = low, j = high;
while(i < j){
while(i < j && key <= arr[j]){
j--;
}
while(i < j && key >= arr[i]){
i++;
}
if(i < j){
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
arr[low] = arr[i];
arr[i] = key;
quickSort(arr, low, j - 1);
quickSort(arr, j + 1, high);
}
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = Integer.parseInt(scan.nextLine());
int[] arr = new int[n];
String[] str = scan.nextLine().split(" ");
for(int i = 0; i < n; i++){
arr[i] = Integer.parseInt(str[i]);
}
quickSort(arr, 0, n - 1);
for(int j = 0; j < n; j++){
System.out.print(arr[j]);
}
}
}
c++:
#include <iostream>
using namespace std;
const int N = 100010;
int q[N];
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i ++ ; while (q[i] < x);
do j -- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j);
quick_sort(q, j + 1, r);
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);
quick_sort(q, 0, n - 1);
for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);
system("pause");
return 0;
}
时间复杂度:
当数组每一次取到的元素正好平分整个数组为最优情况,时间复杂度为$O(nlogn)$
当数组每一次取到的元素是整个数组的最大或最小值时,为最差情况,时间复杂度为$ O(n^2) $
综上所述,快速排序的平均时间复杂度为$O(nlogn)$
3.相关知识
3.1 java控制台接收输入
3.1.1 in
System.in.read()
System是一个类,属于java.lang包它表示系统类
定义如下:
public final class System extends Object//此类为最终类,不能被继承。
它有以下两个重要的成员变量:
(1)in:它是一种输入流对像,它的源是键盘,可以读取用户从键盘输入的数据,在读取数据时会引起堵塞,直到用户按下Enter键,读取方法:
public int read(char c[],int off,int len)和public int read(char c[])。
(2)out:它是一种输出流对象,它的目的地是命令行窗口,写入方法:
public void write(char c[],int off,int len)和public void write(char c[])
in和out为System类的属性:
(1)public static final InputStream in//静态方法,用类名称直接调用,返回InputStream类,也就是说可以为InputStream实例化,System.in 是 InputStream类型,字节流,程序使用它可读取键盘输入的数据。“标准”输入流。此流已打开并准备提供输入数据。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。
(2)public static final PrintStream out//静态方法,用类名称直接调用,返回PrintStream类,也就是说可以为OutputStream实例化,System.out 是 PrintStream类型(是OutputStream的子类),字节流,程序使用它可将数据输出到显示屏上。“标准”输出流。此流已打开并准备接受输出数据。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。
实际上在JAVA中也对IO给予了一定的支持,System.in, System.out, System.err这3个流同样是常见的数据来源和数据流目的地。使用最多的可能是在控制台程序里利用System.out将输出打印到控制台上。JVM启动的时候通过Java运行时初始化这3个流,所以你不需要初始化它们(尽管你可以在运行时替换掉它们)。
3.1.2 替换系统流
尽管 System.in , System.out 这2个流是java.lang.System类中的静态成员(这2个变量均为final static常量),并且已经预先在JVM启动的时候初始化完成,你依然可以更改它们。
只需要把一个新的InputStream设置给System.in或者一个新的OutputStream设置给System.out,之后的数据都将会在新的流中进行读取、写入。
可以使用System.setIn(), System.setOut()方法设置新的系统流(这二个方法均为静态方法,内部调用了本地native方法重新设置系统流)。请记住,务必在JVM关闭之前冲刷System.out(调用flush()),确保System.out把数据输出到了文件中。
// InputStreamReader和BufferedReader方法
// 优点: 可以获取键盘输入的字符串
// 缺点: 如何要获取的是int,float等类型的仍然需要转换
public static void ReadTest(){
System.out.println("ReadTest, Please Enter Data:");
InputStreamReader is = new InputStreamReader(System.in); //new构造InputStreamReader对象
BufferedReader br = new BufferedReader(is); //拿构造的方法传到BufferedReader中,此时获取到的就是整个缓存流
try{ //该方法中有个IOExcepiton需要捕获
String name = br.readLine();
System.out.println("ReadTest Output:" + name);
}
catch(IOException e){
e.printStackTrace();
}
}
InputStreamReader: 是字节流与字符流之间的桥梁,能将字节流输出为字符流并且能为字节流指定字符集如果不指定的话将采用底层操作系统的默认编码方式,可输出一个个的字符,它封裝了InputStream在里头,以较高级的方式一次读取一个一个字符。
BufferedReader: 它则是比InputStreamReader更高级,它封裝了StreamReader类,一次读取取一行的字符提供通用的缓冲方式文本读取,readLine读取一个文本行, 从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。
java.io下面有两个抽象类:InputStream和Reader也就是分别表示字节流和字符流, InputStream是表示字节输入流的所有类的超类,提供的是字节流的读取,而非文本读取,这是和Reader类的根本区别。
Reader是用于读取字符流的抽象类,通常Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求,如果没有缓冲则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。即用Reader读取出来的是char数组或者String ,使用InputStream读取出来的是byte数组。
那为什么需要InputStreamReader和BufferedReader互相配合,因为InputStreamReader是字节输出(汉字会被分为两个字节),而BufferedReader是它的“包装”(整行读取),效率更高,所以配合使用更好。可以通过BufferedReader 流的形式进行流缓存,之后通过readLine方法获取到缓存的内容。
3.1.3 Scanner
//Scanner类中的方法
//优点一: 可以获取键盘输入的字符串
//优点二: 有现成的获取int,float等类型数据,非常强大,也非常方便;
public static void ScannerTest(){
Scanner sc = new Scanner(System.in);
System.out.println("ScannerTest, Please Enter Name:");
String name = sc.nextLine(); //读取字符串型输入
System.out.println("ScannerTest, Please Enter Age:");
int age = sc.nextInt(); //读取整型输入
System.out.println("ScannerTest, Please Enter Salary:");
float salary = sc.nextFloat(); //读取float型输入
System.out.println("Your Information is as below:");
System.out.println("Name:" + name +"\n" + "Age:"+age + "\n"+"Salary:"+salary);
}
Scanner是Java5的新特征,主要功能是简化文本扫描。Scanner的中文意思就是扫描仪,也就是将一份数据从一个地方扫描并显示到另外一个地方。一个可以使用正则表达式来解析基本类型和字符串的简单文本扫描器。
Scanner使用分隔符模式将其输入分解为标记,默认情况下该分隔符模式与空白匹配。然后可以使用不同的 next 方法将得到的标记转换为不同类型的值。当通过new Scanner(System.in)创建一个Scanner,控制台会一直等待输入,直到敲回车键结束,把所输入的内容传给Scanner,作为扫描对象。而且Scanner类有一个假设,在输入结束时会抛出IOException,而Scanner类会把这个异常吞掉。
经常有人说使用Scanner的原因是因为它使用简便,不如说Scanner的构造器支持多种方式,构建Scanner的对象很方便。可以从字符串(Readable)、输入流、文件等等来直接构建Scanner对象,有了Scanner了,就可以逐段(根据正则分隔式)来扫描整个文本,并对扫描后的结果做想要的处理。
scanner好比一个带游标或者指针的扫描仪,调用其hasNextLine()好比将游标向前探索直到遇到一个换行符,如果这个过程中遇到字符串对象则返回true并且游标复位。调用其nextLine()方法其实就是游标向前探索直到遇到一个换行符,此时并不复位,而是游标直接定位到目标数据的下一行,并返回探索过程中检测到的数据包括空格。
java里常使用的方法next() 与 nextLine() 区别
next():
1、一定要读取到有效字符后才可以结束输入。
2、对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
3、只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
next() 不能得到带有空格的字符串。
nextLine():
1、以Enter为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符。
2、可以获得空白。import java.io.BufferedReader;
3.2 java类型转换
string 和int之间的转换
string转换成int :Integer.valueOf(“12”)
int转换成string : String.valueOf(12)
char和int之间的转换
首先将char转换成string
String str=String.valueOf(‘2’)
Integer.valueof(str) 或者Integer.PaseInt(str)
Integer.valueof返回的是Integer对象,Integer.paseInt返回的是int