数据结构第七章排序 排序(九种)(可以比较速度)

包括:

插入排序:直接插入排序,折半插入排序,希尔排序

交换排序:冒泡排序,快速排序

选择排序:简单选择排序

其它排序:归并排序,堆排序,桶排序

查询“排序算法动图”更好理解

import java.util.*;

public class Main
{
	public static void main(String[] args)
	{
		//获取数字
		Scanner s1=new Scanner(System.in);
		System.out.println("请输入随机数列的数的个数,程序自动构建随机数列(0-10000范围内):");	
		int n=s1.nextInt();
		int[] a=randomArray(n);
		
		//插入排序
		{
			System.out.println("插入排序");	
			//直接插入排序用时和结果
			//straightInsertionSort(a);
		
			//折半插入排序用时和结果
			//binarySort(a);
		 
			//希尔排序用时和结果
			shellSort(a);
		}
		
		//交换排序
		{
			System.out.println("交换排序");	
			//冒泡排序用时和结果
			//bubbleSort(a);
		 
			//快速排序用时和结果
			quickSort(a);
		}
		
		//选择排序
		{
			System.out.println("选择排序");	
			//简单选择排序
			//selectSort(a);
		}
		
		//其它排序方式
		{
			System.out.println("其它排序方式");	
			//归并排序
			mergeSort(a);
			
			//堆排序
			heapSort(a);
			
			//桶排序
			bucketSort(a);
		}
	}
	
	//构建给定位数随机数
	public static int[] randomArray(int n)
	{
		int[] a=new int[n];
		for(int i=0;i<a.length;i++)
		{
			a[i]=(int)(Math.random()*10000);
		}
		//System.out.println("随机数列"+Arrays.toString(a));
		return a;
	}
	
	
	
	//直接插入排序
	public static void straightInsertionSort(int[] a)
	{ 
		int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
		long t1,t2;//引入时间
		t1 = System.currentTimeMillis();//计时器
		
		//功能段
		{
			int i=0,j=0;
			int c=0;//临时存放元素
			for (i=1;i<b.length;i++)
			{
				if(b[i]<b[i-1])//小于才要动,大于是不用动的,
				{
					c=b[i];
					for(j=i-1;(j>=0)&&(c<b[j]);j--)//我的b[0]放了东西的,没有放哨兵,所以可能会有j<0导致数组下标越界,为放这种情况就j<0就跳出,而且这个判断要放前面来短路后面的c<b[j],否则j=-1时调用了判断语句也可能超限。我觉得我这样写其实不是太好。
					{
						b[j+1]=b[j];
					}
					b[j+1]=c;
				}
			}
			
		}
		
		t2=System.currentTimeMillis();
        System.out.println("直接插入排序用时"+(t2-t1)+"ms");//输出用时
        //System.out.println("排序结果"+Arrays.toString(b));
		return ;
	}
	
	//折半插入排序
	public static void binarySort(int[] a)
	{ 
		int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
		long t1,t2;//引入时间
		t1 = System.currentTimeMillis();//计时器
			
		//功能段
		{
			int i=0,j=0;
			int c=0;//临时存放元素
			int low,high,mid;
			for (i=1;i<b.length;i++)
			{
				c=b[i];
				low=0;
				high=i-1;
				
				//折半查找
				while(low<=high)
				{
					mid=(low+high)/2;
					if(b[mid]>c)
					{
						high=mid-1;
					}
					else low=mid+1;
				}
				//和直接插入一样后移元素
				//由于跳出情况是high<low,这个时候high+1就是该放的位置
				for(j=i-1;j>=high+1;j--)
					b[j+1]=b[j];
				b[high+1]=c;
			}		
		}
			
		t2=System.currentTimeMillis();
	    System.out.println("折半插入排序用时"+(t2-t1)+"ms");//输出用时
	    //System.out.println("排序结果"+Arrays.toString(b));
	    return ;
	}
	
	
	//希尔排序:难
	public static void shellSort(int[] a)
	{ 
		int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
		long t1,t2;//引入时间
		t1 = System.currentTimeMillis();//计时器
				
		//功能段
		{
			int i=0,j=0;
			int c=0;//临时存放元素
				
			//设置步长,每次循环内两个观察点的距离
			int bc=0;
				
			for (bc=b.length/2;bc>=1;bc=bc/2)//步长每个循环整除2
			{
				for(i=bc;i<b.length;i++)//你或许会奇怪为什么这里不是两个循环,一个就够了,这是因为在bc相同的情况下,每个元素的处理代码是一样的,也就是说只要扫到每一个数就行了(下面的处理代码是一样的),一个for循环即可
												//i从bc开始算是因为,根据插入排序是和从第二个数开始循环和第一个数比
				{
					//
					if(b[i]<b[i-bc])
					{
						c=b[i];					
						for(j=i-bc;(j>=0)&&(c<b[j]);j=j-bc)
						{
							b[j+bc]=b[j];
						}
					b[j+bc]=c;
					}
				}

			}
		}		
		t2=System.currentTimeMillis();
		System.out.println("希尔插入排序用时"+(t2-t1)+"ms");//输出用时
		//System.out.println("排序结果"+Arrays.toString(b));
		return;
	}
	
	
	//冒泡排序
	public static void bubbleSort(int[] a)
	{ 
		int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
		long t1,t2;//引入时间
		t1 = System.currentTimeMillis();//计时器
				
		//功能段
		{
			int i=0,j=0;
			int c;
			for(i=0;i<b.length;i++)
			{
				for(j=b.length-1;j>i;j--)
				{
					if(b[j]>b[j-1])
					{
						c=b[j-1];
						b[j-1]=b[j];
						b[j]=c;
					}
				}
			}
		}		
		t2=System.currentTimeMillis();
		System.out.println("冒泡排序用时"+(t2-t1)+"ms");//输出用时
		//System.out.println("排序结果"+Arrays.toString(b));
		return;
	}
	
	//快速排序:难
	public static void quickSort(int[] a)
	{ 
		int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
		long t1,t2;//引入时间
		t1 = System.currentTimeMillis();//计时器
				
		//功能段
		{
			QuickSort(b,0,b.length-1);
		}		
		t2=System.currentTimeMillis();
		System.out.println("快速排序用时"+(t2-t1)+"ms");//输出用时
		//System.out.println("排序结果"+Arrays.toString(b));
		return;
	}
	
	public static void QuickSort(int[] A,int low,int high)//递归调用自己
	{
		if(low<high)
		{
			int zhongXin=huaFen(A,low,high);
			QuickSort(A,low,zhongXin-1);
			QuickSort(A,zhongXin+1,high);
		}
	}
	
	public static int huaFen(int[] A,int low,int high)//每一次的处理过程
	{
		int jiZun=A[low];
		while(low<high)
		{
			while(low<high&&A[high]>=jiZun){high--;}
			A[low]=A[high];
			while(low<high&&A[low]<=jiZun){low++;}
			A[high]=A[low];						
		}
		A[low]=jiZun;
		return low;
	}

	//简单选择排序
	public static void selectSort(int[] a)
	{ 
		int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
		long t1,t2;//引入时间
		t1 = System.currentTimeMillis();//计时器
				
		//功能段
		{
			//其实我感觉比冒泡还简单,每扫一遍把最小的数拿出来放前面
			int i,j;
			int min;
			int c;//用于交换的
			for (i=0;i<b.length;i++)
			{
				min=i;
				for(j=i+1;j<b.length;j++)
				{
					if(b[j]<b[min]) {min=j;}
				}
				if(min!=i)
				{
					c=b[i];
					b[i]=b[min];
					b[min]=c;
				}
			}
		}		
		t2=System.currentTimeMillis();
		System.out.println("简单选择排序用时"+(t2-t1)+"ms");//输出用时
		//System.out.println("排序结果"+Arrays.toString(b));
		return;
	}
	
	//归并排序,也要迭代,但是最坏和最好情况一样,这一点比快排好
	public static void mergeSort(int[] a)
	{ 
		int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
		long t1,t2;//引入时间
		t1 = System.currentTimeMillis();//计时器
				
		//功能段
		{
			int[] c=new int[b.length];//构建辅助数组从头构建是因为,这样的话从头到尾只有一个辅助数组
			MergeSort(b,0,b.length-1,c);
		}		
		t2=System.currentTimeMillis();
		System.out.println("归并排序用时"+(t2-t1)+"ms");//输出用时
		//System.out.println("排序结果"+Arrays.toString(b));
		return;
	}
	
	public static void  MergeSort(int[] b,int low,int high,int[] c)//递归
	{
		if(low<high)//low=high的时候只有一个数就不用再向下归并了
		{
			int mid=(low+high)/2;
			MergeSort(b,low,mid,c);
			MergeSort(b,mid+1,high,c);
			Merge(b,low,mid,high,c);
		}
	}
	
	public static void Merge(int[] b,int low,int mid,int high,int[] c)//每一次的处理过程
	{

		//先把原数组的数复制到辅助数组上,因为等会要操作的是原数组
		for(int k=low;k<=high;k++)
		{
			c[k]=b[k];
		}
		
		//其实下面这段你在一元多项式加法乘法里面就试过,不过没有下面这么简洁
		int i,j;//分别控制第一个数列和第二个,由于我是二路归并也可以理解为前半后半
		int k;//控制原数组新情况个元素位置
		for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)//谁小谁先进原数组,注意i和k别设成起始值为0
		{
			if(c[i]<=c[j])//比较c中左右两段的元素
			{
				b[k]=c[i];
				i++;
			}
			else
			{
				b[k]=c[j];
				j++;
			}
		}
		//如果有没排进去的
			while(i<=mid)//若第一个表没检测完,复制
			{
				b[k]=c[i];
				k++;
				i++;
			}

			while(j<=high)//若第二个表没检测完,复制
			{
				b[k]=c[j];
				k++;
				j++;
			}
	}
	
	//堆排序
	public static void heapSort(int[] a)
	{ 
		int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
		long t1,t2;//引入时间
		t1 = System.currentTimeMillis();//计时器
				
		//功能段
		{
			//需要先构建一个为堆的类
			MaxHeap test1 = new MaxHeap(b);
			int bLength = b.length;
			for (int i=0; i < bLength; i++) {
				b[i]=test1.deleteMax();
			}
			
			
		}		
		t2=System.currentTimeMillis();
		System.out.println("堆排序用时"+(t2-t1)+"ms");//输出用时
		//System.out.println("排序结果"+Arrays.toString(b));
		return;
	}
	
	//桶排序:4位数,这里只是一个简单的桶排序,实际情况里桶里面还会有排序
	public static void bucketSort(int[] a)
	{ 
		int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
		long t1,t2;//引入时间
		t1 = System.currentTimeMillis();//计时器
				
		//功能段
		{
			int[] c=new int[10000];//数组c相当于创建了10000个桶
			int i;//数组的第i位的i
			int j;//数组c的第j位的j
			int k;//数组c的第j位的大小
			int m;
			//进桶
			for(i=0;i<b.length;i++)
			{
				m=b[i];
				c[m]++;
			}
			//导回数组b
			i=0;
			for(j=0;j<c.length;j++)
			{
				if(c[j]>0)
				{
					for(k=0;k<c[j];k++)
					{
						b[i]=j;
						i++;
					}
				}
			}
		}		
		t2=System.currentTimeMillis();
		System.out.println("桶排序用时"+(t2-t1)+"ms");//输出用时
		//System.out.println("排序结果"+Arrays.toString(b));
		return;
	}
}

class MaxHeap {
	int maxSize = 10000000;//堆的最大大小
	int[] array = new int[maxSize+1];
	int nowSize = 0;//目前堆使用的部分的大小
	
	public MaxHeap() {
		array[0] = Integer.MAX_VALUE;
	}
	public MaxHeap(int[] inputArray) {
		int inputArrayLength = inputArray.length;
		nowSize =  inputArrayLength;
		array[0] = Integer.MAX_VALUE;
		for (int i = 1; i <= inputArrayLength; i++) {
			array[i] = inputArray[i-1];
		}
		buildMaxHeap();
	}
	
	public boolean insert (int data) {
		
		if (nowSize == maxSize) {
			System.out.println("最大堆已满");
			return false;
		}
		nowSize++;
		int i = nowSize;
		for (;array[i/2] < data; i = i/2) {// 完全二叉树 n/2是父节点
			array[i] = array[i/2];// 和插入排序的移动方式类似
		}
		array[i] = data;
		return true;
	}
	
	public int deleteMax() {
		if (nowSize == 0) {
			System.out.print("最大堆已为空");
			return -100000;
		} else {
			int maxData = array[1];// 取出根节点最大值
			array[1] = array[nowSize];
			nowSize--;
			buildFromTreeRoot(1);
			return maxData;//这里可以返回maxData
		}
	}   
	
	
	public void buildMaxHeap() {
		//先理子树,子树理完再理根,这样时间复杂度只用O(n)
		for(int i = nowSize / 2; i > 0; i--) {
			buildFromTreeRoot(i);
		}	
	}
	
	//将根节点与两边比较和移动的函数
	public void buildFromTreeRoot(int p) {
		int temp = array[p];
		int Parent;
		int Child;
		for (Parent = p ; Parent * 2 <= nowSize ; Parent = Child) {
			Child = Parent * 2;
			/* 开始循环说了Parent * 2 <= nowSize,且Child = Parent * 2且Child != nowSize时,那么可以肯定Child+1<=nowSize。
			 * 那么就可以比较Child和Child++的大小了,反之如果且Child == nowSize,那么Child==nowSize那么自然后面的不用考虑。
			 */
			if ((Child != nowSize) && array[Child] < array[Child + 1]) { 
				Child++;//Child指向左右子节点中的最大者
			}
					
			if ( temp >= array[Child] ) {
				break;
			} else { 
				array[Parent] = array[Child];
			}
		}
		//跳出时没有子结点,或者比子结点大可以跳出了
		array[Parent] = temp;
	}
	
	/**
	 * 层序遍历
	 */
	public void printMaxHeap() {//打印数组相当于层序遍历
		System.out.print("{");
		int i;
		for(i = 1; i <= nowSize; i++) {
			if (i != 1) {
				System.out.print(",");
			}
			System.out.print(array[i]);
		}
		System.out.print("}");
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章