本文主要内容:
1.非递归的快速排序
2.归并排序
3.非递归的归并排序
4.简单的排序总结
5.简单的排序实验
- 快速排序
Arrays.sort(基本数据类型的数组),基本使用快排
优化点:(1)基准值的选择
(2)做partition过程中是否考虑基准值相等的问题
(3)快排更适合对数据量比较大的场景使用,往往数据量比较小时,使用插入排序
- 如何用非递归实现快速排序?
自己使用栈记录待排序区间的左右边界(栈)
首先定义一个栈,将数组末尾元素及数组首元素一次放入栈中,当栈不为空时循环继续,找到基准值,再依次往栈中放入元素。
- 归并排序[左闭右开]
合并两个有序数组的过程:首先将数组分为两个区间然后分别排序,将两个有序数组合并的过程。
(0)平均切分待排序区间,如果带排序区间的左右两个小区间已经有序,则按照合并有序数组的方式,使最终区间有序
(1)先找到中间位置,划分左右两个小区间
(2)分治的思想,先排序左右两个小区间
(3)合并有序数组
时间复杂度:O(n*log(n))
空间复杂度:O(n)[申请一个额外的数组]
稳定性:稳定
优化点:(1)减少搬移的次数
(2)判断左边的最大的数与右边最小的数
应用:Arrays.sort(引用数据类型的排序) 归并排序
归并排序是一种最常见的外部排序:不是所有的操作都需要借助内存的排序,如果数据量大到内存放不下了,用归并排序来处理
内存有256M,现有10G的数据需要排序,怎么处理?
(1)排序好256M的数据,10G数据可以分成40G的有序小文件
(2)多路归并(40路归并)
归并排序的非递归
- 排序总结
时间复杂度的角度:
(1)O(n^2) 插排/选择/冒泡
(2)O(n*log(n))堆排/快排/归并
(3)O(n^1.2) 希尔
空间复杂度的角度:
(1)O(1):插排/选择/冒泡/希尔/堆排
(2)O(log(n)):快排
(3)O(n):归并
稳定:插排/冒泡/归并
数据不敏感:选择/堆排/归并
分治:快排/归并 减治:插排/选择/冒泡/堆排
简单的排序实验
代码演示
public class Solution1 {
//随机创建一个长度为10的数组
public static int[] createArray(){
Random random = new Random(20190829);
int[] a = new int[10];
for(int i = 0;i<a.length;i++){
a[i] = random.nextInt(100);
}
return a;
}
//打印数组元素
public static void showArray(int[] array){
for(int i = 0;i<array.length;i++){
System.out.print(array[i]+" ");
}
}
//将数组中的两个下标所代表的元素交换
public static void swap(int[] array,int a,int b){
int t = array[a];
array[a] = array[b];
array[b] = t;
}
public static int partition(int[] array,int left,int right){
//首先定义基准值为数组最后一个元素
int indexPartition = array[right];
//定义low指向数组首元素,定义high指向数组尾元素,当low小于high时循环继续
int low = left;
int high = right;
while (low<high){
while (low<high&&array[low]<=indexPartition){
low++;
}
while (low<high&&array[high]>=indexPartition){
high--;
}
//当两边都停止时,互相交换
swap(array,low,high);
}
//low或high就为基准值
swap(array,right,low);
return low;
}
//快速排序非递归
public static void quickSortNDG(int[] array){
Stack<Integer> stack = new Stack<>();
stack.push(array.length-1);
stack.push(0);
while (!stack.isEmpty()){
int left = stack.pop();
int right = stack.pop();
if(left>right){
continue;
}
int indexPartition = partition(array,left,right);
//区间为:[left,indexPartition-1][indexPartition+1,right]
stack.push(indexPartition-1);
stack.push(left);
stack.push(right);
stack.push(indexPartition+1);
}
}
//归并排序
public static void mergeSort(int[] array){
//左闭右开
mergeSortInternal(array,0,array.length);
}
public static void mergeSortInternal(int[] array,int left,int right){
//数组中只剩一个元素,或者没有元素
if(left+1>=right){
return;
}
//[left,mid)[mid,right)
int mid = (left+right)/2;
mergeSortInternal(array,left,mid);
mergeSortInternal(array,mid,right);
//合并有序数组
merge(array,left,mid,right);
}
public static void merge(int[] array,int left,int mid,int right){
int length = right-left;
int[] extra = new int[length];
int pLeft = left;
int pRight = mid;
int pExtra = 0;
while (pLeft<mid&&pRight<right) {
if (array[pLeft] <= array[pRight]){
extra[pExtra++] = array[pLeft++];
} else {
extra[pExtra++] = array[pRight++];
}
}
while (pLeft < mid) {
extra[pExtra++] = array[pLeft++];
}
while (pRight < right) {
extra[pExtra++] = array[pRight++];
}
for (int i = 0; i < length; i++) {
array[left+i] = extra[i];
}
}
//归并排序非递归
public static void mergeFDG(int[] array){
for(int i = 1;i<array.length;i = i*2){
for(int j = 0;j<array.length;j = j+2*i){
int low = j;
int mid = j+i;
int high = mid+i;
if(mid>array.length){
continue;
}
if(high>array.length){
high = array.length;
}
merge(array,low,mid,high);
}
}
}
public static void main(String[] args) {
int[] array = createArray();
showArray(array);
System.out.println();
quickSortNDG(array);
showArray(array);
System.out.println();
System.out.println("================================");
mergeSort(array);
showArray(array);
System.out.println();
mergeFDG(array);
showArray(array);
System.out.println();
}
}
运行结果:
15 92 56 30 7 80 63 72 14 42
7 14 15 30 42 56 63 72 80 92
================================
7 14 15 30 42 56 63 72 80 92
7 14 15 30 42 56 63 72 80 92
//简单的排序实验
//需要写一个接口、实现接口的类、主类
//注意在idea中填入参数
public interface Sorts {
String name();
void sort(int[] array);
}
public class insertSort implements Sorts {
@Override
public String name() {
return "直接插入排序";
}
@Override
public void sort(int[] array) {
for(int i = 0;i<array.length-1;i++){
//有序:[0,i] 无序:[i+1,array.length-1]
int key = array[i+1];
int j = 0;
for(j = i;j>=0;j--){
if(key>=array[j]){
break;
}
array[j+1] = array[j];
}
array[j+1] = key;
}
}
}
public class selectSort implements Sorts {
@Override
public String name() {
return "选择排序";
}
@Override
public void sort(int[] array) {
for(int i = 0;i<array.length;i++){
//无序:[0,array.length-i) 有序:[array.length-i,array.length)
int max = 0;
for(int j = 1;j<array.length-i;j++){
if(array[max]<array[j]){
max = j;
}
swap(array,max,array.length-i-1);
}
}
}
public static void swap(int[] array,int a,int b){
int temp = array[a];
array[a] = array[b];
array[b] = temp;
}
}
public class Runner {
private static final Sorts[] sorts = {new insertSort()};
public static void main(String[] args) {
if(args.length<2){
System.out.println("需要指定序列的顺序以及数据量的大小");
return;
}
String order = args[0];
int size = Integer.parseInt(args[1]);
int[] array;
if(order.equals("顺序")){
array = buildOrderArray(size);
}else if(order.equals("逆序")){
array = buildReverseArray(size);
}else {
array = buildRandomArray(size);
}
testSort(array);
}
public static int[] buildOrderArray(int size){
int[] array = new int[size];
for(int i = 0;i<size;i++){
array[i] = i;
}
return array;
}
public static int[] buildReverseArray(int size){
int[] array = new int[size];
for(int i = size;i>0;i--){
array[i] = i;
}
return array;
}
public static int[] buildRandomArray(int size){
int[] array = new int[size];
Random random = new Random(20190903);
for(int i = 0;i<size;i++){
array[i] = random.nextInt(100);
}
return array;
}
public static void testSort(int[] origin){
for(Sorts sort:sorts){
int[] array = Arrays.copyOf(origin,origin.length);
long begin = System.nanoTime();
sort.sort(array);
long end = System.nanoTime();
int[] second = Arrays.copyOf(origin,origin.length);
Arrays.sort(second);
if(!Arrays.equals(array,second)){
System.out.println("排序错误");
}
System.out.printf("%s: 排序消耗的时间是: %.4f 毫秒%n",
sort.name(), (end - begin) * 1.0 / 1000 / 1000);
}
}
}
//直接插入排序的运行结果为:
直接插入排序: 排序消耗的时间是: 0.0085 毫秒
//选择排序的运行结果为:
选择排序: 排序消耗的时间是: 0.0165 毫秒