实验5《快速排序》
1.需求分析
本程序完成生成一系列的随机数并且存入文件,存入之后再导出,通过不同的排序方法实现对于这组数据的有序化。
同时不需要输入值。
输出可以的到排序之后的数字以及排序程序运行的时间。
下面是对于八种不同的排序方法以及一种写入数据的程序的算法以及概要分析
Writein程序
2.概要设计
本程序使用文件输入输出流,实现对于数据的生成,以及random类里面的nextint方法实现对于随机数点的生成。本程序主要是main函数里面创建fos对象实现对于文件的输入。
以及random的nextint生成20000次可以实现生成两万次随机数,同时记录在文件里面的时候需要加上分隔符,也就是\n回车符。
3.详细设计
public class writein {
public static void main(String[] args) throws IOException {
File file= new File(".\\data.txt");//创建文件路径
FileOutputStream fos= new FileOutputStream(file);
Random random=new Random();
int ran;
for(int i=0;i<=20000;i++)
{
//创建两万次数据
ran= random.nextInt();
byte []ra= new byte[1024];
ra=(String.valueOf(ran)+"\n").getBytes();
fos.write(ra);//写入随机数
}
}
}
**********************************************************************************************************************************************************
冒泡排序
2.概要设计
本程序通过冒泡的思想实现对于得到的数据排序。该程序需要的空间是存储所有数据的数组。主程序可以直接实现对于数据的排序
说明本程序中用到的所有抽象数据类型的定义、主程序的流程以及各程序模块之间的层次(调用)关系。
3.详细设计
具体的算法:1.每次从最底下开始向上寻找最小的数字,每遇到比自己小的数据就进行交换
- 找到最小的数字,将它排在第一个位置
- 重复第1,2步骤直到一次交换都没有进行
1.从文件里面读取数据
int data[]= new int [20002];
int e;
String fileName = ".\\data.txt";
// 读取文件内容到Stream流中,按行读取
Stream<String> lines = Files.lines(Paths.get(fileName));
final int[] t = {0};
// 随机行顺序进行数据处理
lines.forEach(ele -> {
t[0]++;
data[t[0]]= Integer.parseInt(ele);
});
int flag=0;
//以上是读取一个数据的环节
Long start=System.currentTimeMillis();
//后面就是bbuble排序阶段
2.冒泡排序找到的最小的值
for(int i=0;i<20001;i++)
{
3.通过标志来观察是否发生过交换数据
flag=0;
//找出一万次最小的
for(int j=20000;j>1;j--)
{
if(data[j]>data[j-1])
{
}
else{
e=data[j];
data[j]=data[j-1];
data[j-1]=e;//交换数据
flag=1;
}
}
if(flag==0)break;
}
long end= System.currentTimeMillis();
for(int i=1;i<20002;i++)
{
System.out.println(data[i]);
}
System.out.println(end-start);
4.调试分析
该种算法时间效率并不高,是O(n2)(一般情况),但是是一种经典的排序算法,写该程序的时候可以通过flag的精妙实现方法来观察交换次数从而提高了时间效率。
5.用户使用说明
改程序可以实现对于文件内数据的排序,但是时间效率并不高
6.测试结果
冒泡三种情况的不同输出结果(由于操作系统的原因倒置最坏情况的时间小于一般情况):
其中:依次为一般情况,最坏情况,最好情况
**********************************************************************************************************************************************************
归并排序
2.概要设计
获取数据环节与冒泡排序一致,归并排序主函数调用归并排序的函数,函数内将数组分为两块分别调用归并排序,最后将两块再次组合在一起,调用merge函数。
说明本程序中用到的所有抽象数据类型的定义、主程序的流程以及各程序模块之间的层次(调用)关系。
3.详细设计
函数调用关系:
对于归并排序,主要是先拆分为一个一个的数据,在进行组合,这个算法的时间效率是n*logn,但是在拆分为一个一个的数据的时候要调用栈,对于时间的要求比较高,所以实践起来时间效率并不高。
归并排序
public static void Msort(final int arr1[],int arr2[],int start,int end)
{
int arr[]=new int[end+1];
//归并排序
if(start==end){arr2[start]=arr1[end];}
else {
//没到最底下
int m=(end+start)/2;
Msort(arr1,arr2,start,m);
Msort(arr1,arr2,m+1,end);//改变arr,注意,这样写最终的结果不会改变arr
arr= Merge(arr2,start,m,end);//这样子改变arr2是传不出的
}
}
public static void merge_sort1(int data[])
{int data1[]=data;
Msort(data1,data,1,20001);
}
两数列归并算法
public static int [] Merge(int arr1[],int start,int in,int end)
{
//归并两个数组,归并第一个数组的start到in,和第二个数组的in到end,返回arr
int arr[]=new int[end+1];
int count=start;
int i,j;
for( i=start,j=in+1;i<=in&&j<=end;)
{
//合并两个数组到唯一一个数组
if(arr1[i]>arr1[j])
{
arr[count] = arr1[j];
j++;
}
else{
arr[count]=arr1[i];
i++;
}
count++;
}
if(i<=in)for(;i<=in;i++,count++)
{
arr[count]=arr1[i];
}
if(j<=end)for(;j<=end;j++,count++)
{
arr[count]=arr1[j];
}
for(int m=start;m<=end;m++)
{
arr1[m]=arr[m];
}
return arr;
}
主函数算法
public static void main(String[] args) throws IOException {
int data[]= new int [20002];
int e;
String fileName = ".\\inorder.txt";
// 读取文件内容到Stream流中,按行读取
Stream<String> lines = Files.lines(Paths.get(fileName));
final int[] t = {0};
// 随机行顺序进行数据处理
int[] finalData = data;
lines.forEach(ele -> {
t[0]++;
finalData[t[0]]= Integer.parseInt(ele);
});
//以上是读取一个数据的环节
Long start=System.currentTimeMillis();
//实现归并排序
merge_sort1(data);
Long end= System.currentTimeMillis();
// int a[]={1,2,8,65};
// Merge(a,1,2,3);
for(int i=1;i<20002;i++)
{
System.out.println(data[i]);
}
System.out.println(end-start);
}
4.调试分析
1.要注意:在某个函数里面是否可以传出来
* 2.数组如果要传出来,必须是值要修改,否则只能传二维数组,可以改变
在调试过程中使用少量的数据可以实现通过少量的数据达到观察这个程序是否能实现对于数组的排序,在传入参数的时候一定要注意是否能够传出来,例如通过传入的是一个数组的时候如果想要传出这个数组的值不能改变这个数组的指向堆里面的数据来实现对于数组的值得改变,所以在传出的时候一定要直接改变数组里面的数据的值。
这个算法,在分开每个数据之后的算法效率上是极高的,但是在分开数据的时候需要的时间比较多,所以,在排序的时间上可能会不如一些只有n2的算法来的快。
5.用户使用说明
可以的到文件里面排好序的数组,以及算法程序运行的时间
- 测试结果
下面分别是一般情况和两种最坏情况
**********************************************************************************************************************************************************
快速排序
2.概要设计
快速排序主要是通过一次快排得到第一个数据所处位置,以及实现多次分开两次快排,就可以实现对于数据的排序,其中需要用到调用栈。主函数调用快排函数之后快排函数直接调用一次快排,再自身递归调用实现排序。
3.详细设计
快速排序算法分析:1.从最后一个开始,后面的元素与第一个元素(哨兵)进行比较,比较之后如果比第一个元素小,就把这个元素和前面的元素调换位置,如果比他大,那就继续向前比较
- 比较之后最后得到的是一些比他大的元素在他的后面,比他小的元素在他的前面,得到多次这样的交换之后就可以快速的排序。(通过分为左右两边进行排序)
函数调用关系:
快速排序算法
public static void fast_sort(int []arr,int start,int end)
{
//一趟快排
if(start<end)
{
int pivot= fast(arr,start,end);
fast_sort(arr,start,pivot-1);
fast_sort(arr,pivot+1,end);
}
}
一趟快排算法
public static int fast(int []arr,int start,int end)
{
int pivot,pivotkey;
arr[0]=arr[start];//首字母作为支点
pivotkey=arr[0];
int temp;
while(start<end)
{
while(start<end&&arr[end]>=pivotkey)//结束的那个比关键词大
end--;
temp=arr[start];
arr[start]=arr[end];
arr[end]=temp;
while(start<end&&arr[start]<pivotkey)
start++;
temp=arr[end];
arr[end]=arr[start];
arr[start]=temp;
}
arr[start]=arr[0];
return start;
}
主函数算法
public static void main(String[] args) throws IOException {
int data[]= new int [20002];
int e;
String fileName = ".\\inorder.txt";
// 读取文件内容到Stream流中,按行读取
Stream<String> lines = Files.lines(Paths.get(fileName));
final int[] t = {0};
// 随机行顺序进行数据处理
lines.forEach(ele -> {
t[0]++;
data[t[0]]= Integer.parseInt(ele);
});
int flag=0;
flag=0;
//以上是读取一个数据的环节
Long start=System.currentTimeMillis();
// for(int i=0;i<data.length;i++) System.out.println(data[i]);
fast_sort(data,1,20000);
for(int i=1;i<20002;i++)
{
System.out.println(data[i]);
}
Long end=System.currentTimeMillis();
System.out.println(end-start);
}
}
4.调试分析
* 数组大小跟文件的行数有关,所以文件最后一行需要删除,否则就会多出一个零,所以在读取文件的时候会先去掉最后一行。
实现快速排序时间复杂度为n*logn,实现快速排序的时候由于要使用到调用栈,所以在排序的时候如果数据量过大,容易引起栈溢出的结果,所以要尽量控制数据量。
5.用户使用说明
可以得到文件里面排好序的数组,以及算法程序运行的时间
- 测试结果
快速排序实验结果:
快速排序的一般情况和两种最坏情况,一种是正序一种是反序列(都会造成栈溢出),但是一般情况就能较好的排序。
**********************************************************************************************************************************************************
堆排序
2.概要设计
堆排序是改良锦标赛排序,堆排序主要调用创建大根堆,以及调整大根堆来实现堆排序,他不需要递归调用。主程序也是先读取数据,在调用排序函数,最后输出数据的方式进行
3.详细设计
堆排序算法:
1.一组待排序的n个数据元素序列;
2.按某种方法将其生成一个大根堆,则该大根堆的根,就是键值最大的元素;
3.将根与序列尾部元素互换,即最大值放到序列的末尾;
4.将剩余的n-1个元素再次调整成大根堆,可得到次大值,将次大值放到序列的倒数第二的位置;
5.如此反复,直到全部关键字排好序为止
函数调用关系图:
调整算法:
public static void HeadAdjust(int array[], int r, int n) {
int tmp = array[r];//实现调整r到n的二叉树
for (int i = 2*r; i <= n ; i*=2)
{
if (i<n&&array[i]<array[i+1])
{
i++;
}
if (tmp >= array[i])break;
else
{
array[r] = array[i];
r = i;
}
}
array[r] = tmp;
}
建立大根堆算法:
public static void BuildMaxHeap(int array[], int n) {
for (int i = n/2; i > 0 ; i--)
{
HeadAdjust(array, i, n);
}
}
堆排序算法:
public static void PileSort(int array[], int n) {
BuildMaxHeap(array, n);
int temp=array[n];
array[n]= array[1];
array[1]=temp;
for (int i = n; i > 0; i--)
{
HeadAdjust(array, 1, i );
temp=array[i];
array[i]= array[1];
array[1]=temp;
}
}
主函数算法:
public static void main(String[] args) throws IOException {
Long start=System.currentTimeMillis();
int data[]= new int [20002];
int e;
String fileName = ".\\disorder.txt";
// 读取文件内容到Stream流中,按行读取
Stream<String> lines = Files.lines(Paths.get(fileName));
final int[] t = {0};
// 随机行顺序进行数据处理
lines.forEach(ele -> {
t[0]++;
data[t[0]]= Integer.parseInt(ele);
});
//以上是读取一个数据的环节
//实现堆排序
// for(int i=1;i<20002;i++)
// {
// System.out.println(data[i]);
// }
Long end= System.currentTimeMillis();
PileSort(data,20000);
for(int j:data)
{
System.out.println(j);
}
System.out.println(end-start);
}
4.调试分析
二叉树一开始不能传零进去,进行调整,否则只能得到零乘以零,就不能找到他的叶子结点。该算法的时间效率很高为n*logn,需要的内存空间也不是很多,所以可以比较快的实现排序
5.用户使用说明
可以得到文件里面排好序的数组,以及算法程序运行的时间
6.测试结果
堆排序的一般,最好和最坏情况,从实验结果中可以看出,这是一个比较好的排序算法(时间效率比较高)
**********************************************************************************************************************************************************
基数排序
2.概要设计
基数排序主要是通过多个关键词排序的方法进行改进,得到单个关键词排序的优良算法那,其中需要使用到队列,实现先进先出的效果,进而实现对于排序的改良。主要流程就是通过不同的关键词进行排序,其中,最小的关键词要优先,最重要的关键词要往后放。
3.详细设计
基数排序首先找到关键词的多少种情况,要开radix个队列,进行排序。这里的数字最大是十位,所以有十个关键词,但是又有正号和负号,所以需要有十一个关键词。
1.基数排序算法
public static void Rsort(Queue <Integer>L) throws InterruptedException {
//实现基数排序
LinkedBlockingQueue<Integer>[]key1=new LinkedBlockingQueue[10];//创建十个队列来实现关键词的修改
LinkedBlockingQueue<Integer>[]key2=new LinkedBlockingQueue[3];
for(int i=0;i<10;i++)
{
key1[i]=new LinkedBlockingQueue<Integer>();//创建对象
}
for(int i=0;i<3;i++)
{
key2[i]=new LinkedBlockingQueue<Integer>();
}
int m,re;
int a=L.size();
for(int i=9;i>=0;i--)
{
//对于不同的关键字进行排序,10的0到9次方
//分配
for(int j=0;j<a;j++){//对于每个元素进行分配
re=L.remove();//出一个元素
int d=(int)(re/Math.pow(10,9-i))%10;//得到这个位置的数字
key1[Math.abs(d)].add(re);
}//将队列某个位置上的元素全部添加进key1
//扫描队列
for(int t=0;t<10;t++)
{int size2=key1[t].size();
for(int q=0;q<size2;q++)
{
L.add(key1[t].remove());//转移元素
}
}
}
//对于不同的关键字进行排序,10的0到9次方
//分配
//第十一次判断
for(int j=0;j<a;j++){//对于每个元素进行分配
re=L.remove();//出一个元素
if(re<0)key2[0].add(re);
else if(re==0)key2[1].add(re);
else if(re>0)key2[2].add(re);
}//将队列某个位置上的元素全部添加进key1
//扫描队列
for(int t=0;t<3;t++)
{
int size1=key2[t].size();
if(t==0) {
Stack<Integer> key = new Stack<>();
for (int q = size1 - 1; q >= 0; q--) {
key.add(key2[t].remove());
}
for (int q = size1 - 1; q >= 0; q--) {
L.add(key.pop());
}
}
else
for(int q=0;q<size1;q++)
{
L.add(key2[t].remove());//转移元素
}
}
}
主函数算法
LinkedBlockingQueue<Integer> list=new LinkedBlockingQueue<>();
int e;
String fileName = ".\\data.txt";
// 读取文件内容到Stream流中,按行读取
Stream<String> lines = Files.lines(Paths.get(fileName));
// 随机行顺序进行数据处理
lines.forEach(ele -> {
//将数据存入队列中
list.add(Integer.parseInt(ele));
});
int flag=0;
flag=0;
//以上是读取一个数据的环节
Long start=System.currentTimeMillis();
// for(int i=0;i<data.length;i++) System.out.println(data[i]);
Rsort(list);
LinkedBlockingQueue<Integer> list1=new LinkedBlockingQueue<>();
// int size=list.size();
// for(int i=0;i<size;i++)
// {
// int b=list.remove();
// System.out.println(b);
// }
//输出排序之后的队列
Long end=System.currentTimeMillis();
int size=list.size();
for(int i=0;i<size;i++)
{
int b=list.remove();
System.out.println(b);
}
System.out.println(end-start);
4.调试分析
* 1.根据多重排序的思想,首先确定有几个要比较的要点
* 2.从比较小的要点开始,进行比较(这里的数据最大是十位数)
* 3.比较大的要点是1.是否有正号或者负号2.数字的位数3.以及十个数字的大小的比较
* 4.基数排序的正负的判断的关键词
注意如果有一个队列需要遍历的时候remove他的某个值,那就必须要首先存入他的长度,否则遍历的时候他的长度会跟着遍历的进行而改变,同样的在遍历线性表的时候也要注意这个问题。同时对于这个算法的时间复杂度是o(d*(dadix+n))也是一个时间效率非常高的算法。
5.用户使用说明
可以的到文件里面排好序的数组,以及算法程序运行的时间
- 测试结果
以下是三种基数排序的实验结果图片(由于对于每个数据来说都是由是一个关键词组成的,所以都是一般情况):
**********************************************************************************************************************************************************
插入排序
2.概要设计
插入排序主要是通过一个数据与他前面的数据进行比较从而通过n次轮回比较可以得到一个顺序的序列。主函数是通过读取数据,插入排序,输出数据进行的。
3.详细设计
本程序只有一个主函数实现插入排序。
主函数算法
String fileName = ".\\inorder.txt";
// 读取文件内容到Stream流中,按行读取
Stream<String> lines = Files.lines(Paths.get(fileName));
final int[] t = {0};
// 随机行顺序进行数据处理
lines.forEach(ele -> {
t[0]++;
data[t[0]]= Integer.parseInt(ele);
});
//以上是读取一个数据的环节
Long start=System.currentTimeMillis();
//实现插入排序
data[0]=data[1];
for(int j=1;j<data.length;j++)
{
data[0]=data[j-1];
int i=j;
while(data[0]>data[i]&&i!=1)
{
int temp= data[i];
data[i]=data[i-1];
data[i-1]=temp;
//实现与前一个数据的交换
i--;
data[0]=data[i-1];
}
}
// for(int i=1;i<20002;i++)
//{
// System.out.println(data[i]);
//}
Long end= System.currentTimeMillis();
for(int i=1;i<20002;i++)
{
System.out.println(data[i]);
}
System.out.println(end-start);
4.调试分析
该种插入排序是相邻比较的,所以时间效率为O(n2)所以时间效率并不高,可以通过希尔排序进行改进。
5.用户使用说明
可以的到文件里面排好序的数组,以及算法程序运行的时间
6.测试结果
插入排序的一般情况,最坏情况,最好情况三种情况:
**********************************************************************************************************************************************************
希尔排序
2.概要设计
希尔排序通过改良插入排序实现程序的优化,通过跳跃式的插入排序,来优化。同样也只有主程序
3.详细设计
希尔排序通过每15,13,11,9,7,5,3,1进行希尔排序,通过多次排序,对最后一次排序结果进行优化
具体算法如下:
主函数算法
int data[]= new int [20002];
int e;
String fileName = ".\\inorder.txt";
// 读取文件内容到Stream流中,按行读取
Stream<String> lines = null;
try {
lines = Files.lines(Paths.get(fileName));
} catch (IOException ex) {
ex.printStackTrace();
}
final int[] t = {0};
// 随机行顺序进行数据处理
lines.forEach(ele -> {
t[0]++;
data[t[0]]= Integer.parseInt(ele);
});
//以上是读取一个数据的环节
Long start=System.currentTimeMillis();
//实现插入排序
data[0]=data[1];
for(int m=15;m>=1;m=m-2)
for(int j=1;j<data.length;j=j+m)
{
data[0]=data[j-1];
int i=j;
while(data[0]>data[i]&&i!=1)
{
int temp= data[i];
data[i]=data[i-1];
data[i-1]=temp;
//实现与前一个数据的交换
i--;
data[0]=data[i-1];
}
}
Long end= System.currentTimeMillis();
for(int i=1;i<20002;i++)
{
System.out.println(data[i]);
}
System.out.println(end-start);
4.调试分析
希尔排序时间效率任然为O(n2)但是通过希尔排序可以用前面的排序实现对于一个一个插入排序的优化(首先就让他们变得有序化)
5.用户使用说明
可以的到文件里面排好序的数组,以及算法程序运行的时间。
- 测试结果
希尔排序的一般情况,最好情况,最坏情况实验结果如下所示:
**********************************************************************************************************************************************************
简单选择排序
2.概要设计
简单选择排序通过每次简单的选择最小的数字,并且让他移出线性表来实现排序,主函数调用选择排序函数。
3.详细设计
简单选择算法分析:
- 选择出线性表里面最小的元素
- 将最小的元素放进另一个线性表
- 重复步骤12,直到原来的线性表为空
该算法需要用到线性表,
找一个最小的元素:
public static int selectmin(ArrayList<Integer> arr)
{
int minin,min;
min=arr.get(0);//获取数组的第一个
minin=0;
for(int i=0;i<arr.size();i++)
{
if(min>arr.get(i))
{
min=arr.get(i);
minin=i;
}
}
arr.remove(minin);
return min;
}
主函数算法:
ArrayList<Integer>data=new ArrayList<>();//创建一个线性表
int e;
String fileName = ".\\data.txt";
// 读取文件内容到Stream流中,按行读取
Stream<String> lines = Files.lines(Paths.get(fileName));
final int[] t = {0};
// 随机行顺序进行数据处理
lines.forEach(ele -> {
t[0]++;
data.add(Integer.parseInt(ele)) ;
});
//以上是读取一个数据的环节
Long start=System.currentTimeMillis();
//实现简单排序
ArrayList<Integer>data1=new ArrayList<>();
int size=data.size();
for(int i=0;!data.isEmpty();i++)
{
//遍历线性表,依次找出最小的
data1.add(selectmin(data));
}
for(int ele:data1)
{
System.out.println(ele);
}
Long end= System.currentTimeMillis();
System.out.println(end-start);
4.调试分析
该算法时间效率为o(n2),空间效率为o(n)。同样需要注意,在遍历线性表的时候会改变线性表原有的长度所以要先存好size然后在进行遍历,这样才能达到全部遍历的目的。
5.用户使用说明
可以的到文件里面排好序的数组,以及算法程序运行的时间
- 测试结果
简单插入的最坏最好以及一般情况
**********************************************************************************************************************************************************