排序算法的Java实现

夏天的时候学习网上的课程实现的几个排序算法,放这儿方便查看

package com.zhangyan.algorithm;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * 
 * @author 哈哈哈
 * 8种排序算法:
 * 		插入排序:①直接插入排序②希尔排序
 * 		选择排序:①简单选择排序②堆排序
 * 		交换排序:①冒泡排序②快速排序
 * 		归并排序
 * 		基数排序
 */
public class MySort {
	/*
	 * 冒泡排序算法实现
	 * 比较相邻的两个元素,选出最值向上冒泡
	 */
	public static void bubbleSort(int[] data) {
		for (int i = 0; i < data.length - 1; i++) {// 排序执行的轮数
			for (int j = 0; j < data.length - i - 1; j++) {// 每轮的元素比较次数
				// 判断元素交换的条件,如果成立则交换位置
				if (data[j] > data[j + 1]) {
					// 元素互相交换
					int temp = data[j];
					data[j] = data[j + 1];
					data[j + 1] = temp;
				}
			}
		}
	}

	/*
	 * 快速排序算法实现
	 * 虽然能排序总觉得思路有些问题,再说吧
	 */
	public static void quickSort(int[] data) {
		quickSort_1(data,0,data.length-1);
	}
	//递归的快速排序实现
	private static void quickSort_1(int[] data,int left,int right) {
		if(left>=right) {
			return;
		}
		int l = left;//左下标
		int r = right;//右下标
		int pivot = data[(left+right)/2];
		//如果左下标小于右下标,继续,否则停止
		//因为选出的中轴值不一定就是真正的中轴值,所以在条件结束后需要重选边界
		while(l<r) {
			//左边一直找,直到找到大于等于中轴值的值停止
			while(data[l]<pivot) {
				l++;
			}
			//右边一直找,直到找到小于等于中轴值的值停止
			while(data[r]>pivot) {
				r--;
			}
			//如果l不小于r则退出,不再进行下面的交换操作
			//需要递归操作的两部分边界为(0-r)(l-完)
			if(l>r) {
				break;
			}else if(l==r) {
				l++;
				break;
			}
			//如果l还是大于r则交换两个索引对应的值
			int temp = data[l];
			data[l] = data[r];
			data[r] = temp;
			//以中轴值分开的两部分,一边大一边小,但两边都可能有等于中轴值的值
			if(data[l]==pivot) {
				l++;
			}
			if(data[r]==pivot) {
				r--;
			}
		}
		//重选边界,继续递归操作
		quickSort_1(data,left,r);
		quickSort_1(data,l,right);
	}
	/*
	 * 选择排序算法实现 
	 * 注意:每次选出最值,进行排序,只有选出最值才交换元素,其余只记录索引,不交换元素
	 */
	public static void checkSort(int[] data) {
		for (int i = 0; i < data.length - 1; i++) {// 排序执行的轮数
			int minElementIndex = i;// 定义一个变量,用于放置最大的元素的索引,暂定为第一个元素的索引
			for (int j = i + 1; j < data.length; j++) {// 每轮需要比较元素大小的次数
				if (data[minElementIndex] > data[j]) {
					// 如果元素值大于最大值,则把此索引赋值给最大索引变量
					minElementIndex = j;
				}
			}
			// 内层循环完毕,说明找出了最大值,此时交换最大值到没排序的元素的头部
			if (minElementIndex != i) {
				// 交换元素位置
				int temp = data[i];
				data[i] = data[minElementIndex];
				data[minElementIndex] = temp;
			}
		}
	}

	/*
	 * 堆排序,是利用堆这种数据结构设计的排序算法,是一种不稳定排序
	 * 一般升序排序的话使用大顶堆,降序使用小顶堆
	 * 每个节点的值都大于或等于其左右两个孩子节点的值的完全二叉树称为大顶堆,小于等于的完全二叉树为小顶堆
	 * 完全二叉树定义:若设二叉树的深度为h,除了h层外其他各层的节点都达到最大个数,且第h层所有的节点都集中在最左边,这就是完全二叉树
	 * 堆排序的基本步骤:
	 * ①将待排序序列构成一个大顶堆
	 * ②将堆顶最大元素与末尾元素交换位置,此时末尾元素为最大值
	 * ③将除末尾元素之外的n-1个元素从新构造一个大顶堆,这样就得到了堆顶的为整个序列的第二大值,重复以上操作,将得到一个有序序列
	 * 注意:下面我的实现步骤是第一次构建堆先循环执行所有的非叶子节点,从最底部开始向顶部结束
	 * 然后第二次及以后构建堆,则从顶部开始,因为此完全二叉树除了顶部元素外其他已经符合堆的要求了
	 * 所以只要把顶部的元素沉入适当的位置即可
	 * 每次构建完堆后,把顶部元素与末尾元素交换位置,如此往复
	 * 至始至终都没有构建树结构,只是把数组对应的完全二叉树调整为堆
	 */
	public static void heapSort(int[] data) {
		int temp = 0;//定义一个临时变量用于交换
		//第一次调整,从下向上调整完全二叉树为堆,需执行所有的非叶子节点
		for(int i=data.length/2-1;i>=0;i--) {
			adjustHeap(data, i, data.length);
		}
		//把顶部元素与末尾元素交换,并继续调整堆,此时除最顶部元素外其余已经符合堆,只需把顶部元素沉入适当位置即可
		for(int j=data.length-1;j>0;j--) {
			//交换
			temp = data[j];
			data[j] = data[0];
			data[0] = temp;
			//继续构建堆
			adjustHeap(data, 0, j);
		}
	}
	//调整完全二叉树为堆的方法
	/**
	 * 
	 * @param data 待调整数组
	 * @param i 非叶子节点在数组中的索引
	 * @param length 需要调整的元素个数,因为每次调整完毕都会摘出一个最值,所以length在逐渐减少
	 */
	private static void adjustHeap(int[] data,int i,int length) {
		int temp = data[i];//要调整的树的最顶部节点保存出来
		for(int k=i*2+1;k<length;k=k*2+1) {
			//切记要判断k+1<length,否则刚拍好序的又给弄乱了
			if(k+1<length&&data[k]<data[k+1]) {
				k++;//让k指向当前节点中较大的那个子节点
			}
			if(data[k]>temp) {
				//如果较大的子节点大于当前顶部节点,则交换
				data[i] = data[k];
				i = k;//注意改变当前顶部节点为以上和原顶部节点交换的子节点
			}else {
				//因为我们是从下向上构建整个堆,上面构建完成,下面肯定也好了,所以可以退出
				break;
			}
		}
		//循环完毕,把最顶部保存出来的元素赋值为当前节点
		data[i] = temp;
	}
	/*
	 * 插入排序算法实现 (交换法)有执行流程冗余的问题,可用下一个例子中的移位法来优化
	 * 把数组看成是两部分,一部分有序,一部分无序,每循环一轮就把一个无序的元素放入有序的部分内
	 */
	public static void insertSort_1(int[] data) {
		for (int i = 1; i < data.length; i++) {// 需要执行的轮数
			for (int j = i; j > 0; j--) {// 向前插入,每轮执行的次数
				// 判断当前元素和前一个元素的大小
				// 如果前一个元素大于当前元素,则向前插入,否则跳出循环
				if (data[j - 1] > data[j]) {
					// 向前插入元素
					int temp = data[j - 1];
					data[j - 1] = data[j];
					data[j] = temp;
				} else {
					//跳出循环
					break;
				}
			}
		}
	}

	/*
	 * 插入排序算法实现(移位法)
	 */
	public static void insertSort_2(int[] data) {
		for(int i=1;i<data.length;i++) {
			int j = i;//需要向前插入的变量的索引存放在变量j中
			int temp = data[j];//需要向前插入的变量也保存在临时变量temp中
			//判断向后移位的结束条件
			while(j>0&&data[j-1]>temp) {
				//移位
				data[j] = data[j-1];
				j--;
			}
			data[j] = temp;//把保存在临时变量中的元素赋值给当前j索引在的位置
		}
	}
	/*
	 * 希尔排序算法实现(交换法)有冗余执行流程,移位法对其进行了优化
	 * 希尔排序就是增强版的插入排序,如果数据在最坏的情况下最小的数在最后面,则需要向前插入次数将非常多,太耗时间
	 * 希尔排序改善了这个缺点,以改变步长的方式,加以优化
	 * 希尔排序在对最坏数组情况速度较快
	 */
	public static void shellSort_1(int[] data) {
		for(int step=data.length/2;step>0;step/=2) {//步长依次折半,直到小于0停止
			
			//再以各个步长进行普通的插入排序
			for(int j=step;j<data.length;j++) {//每次插入排序执行的轮数
				for(int k=j;k>=step;k-=step) {//每轮插入的次数
					//判断
					if(data[k-step]>data[k]) {
						//交换元素位置
						int temp = data[k];
						data[k] = data[k-step];
						data[k-step] = temp;
					}else {
						//如果当前元素对应的上一个元素不大于当前元素,退出循环
						break;
					}
				}
			}
		}
	}
	/*
	 * 希尔排序(移位法)
	 */
	public static void shellSort_2(int[] data) {
		for(int step=data.length/2;step>0;step/=2) {//步长
			for(int j=step;j<data.length;j++) {//执行的轮数
				int k=j;
				int temp = data[k];
				while(k>=step&&data[k-step]>temp) {
					data[k] = data[k-step];
					k-=step;
				}
				//循环结束,把临时变量中元素赋值给当前索引
				data[k] = temp;
			}
		}
	}
	
	/*
	 * 归并排序算法实现
	 * 归并排序采用经典的分治策略,把大的问题分成小的问题然后递归求解
	 */
	public static void mergeSort(int[] data) {
		//定义一个临时数组
		int[] temp = new int[data.length];
		mergeSort_split(data,0,data.length-1,temp);
	}
	//归并排序的分开合并的操作
	private static void mergeSort_split(int[] data,int left,int right,int[] temp) {
		if(left<right) {
			int mid = (left+right)/2;//中间轴
			mergeSort_split(data,left,mid,temp);//左分割
			mergeSort_split(data,mid+1,right,temp);//有分割
			//合并
			mergeSort_merge(data,left,mid,right,temp);
		}
	}
	//归并排序的合并操作
	private static void mergeSort_merge(int[] data,int left,int mid,int right,int[] temp) {
		int a = left;//左边部分的头索引
		int b = mid+1;//右边部分的头索引
		int t = 0;//指向临时数组的要添加元素位置的索引
		//循环向临时数组中合并元素,直到左右有一部分到最后停止
		while(a<=mid&&b<=right) {
			if(data[a]>=data[b]) {
				//移动b元素
				temp[t] = data[b];
				t++;
				b++;
			}else {
				//移动a元素
				temp[t] = data[a];
				t++;
				a++;
			}
		}
		//退出循环后,或许左右两部分只有一部分到底,需要把另一部分整个移动到临时数组
		while(a<=mid) {
			temp[t] = data[a];
			t++;
			a++;
		}
		while(b<=right) {
			temp[t] = data[b];
			t++;
			b++;
		}
		//合并完毕,需要把临时数组中的元素整个复制到原数组中
		t = 0;//指针t指向临时数组的头部,从开始移动元素
		int templeft = left;//定义一个临时变量指向原数组的要复制的位置索引
		//向原数组中复制临时数组中的所有元素
		while(templeft<=right) {
			data[templeft] = temp[t];
			templeft++;
			t++;
		}
	}
	
	/*
	 * 基数排序算法实现
	 * 基数排序是1887年赫尔曼发明,这也太早点了吧
	 * 基数排序也是桶排序,按照要排序的元素的位数,把元素分配到不同的桶中,达到排序的效果
	 * 基数排序是效率高的稳定排序算法,是典型的以空间换时间的排序算法
	 * 先按个位数把数组中的数值放入相应的桶中然后按顺序取出,在十位数,百位数以此类推,最大数值有几位,执行几次,位数不满的补0
	 */
	public static void radixSort(int[] data) {
		//先找出数组中元素位数最大的为几位
		int maxValue = data[0];//暂定第一个元素为最大值
		for(int i=1;i<data.length;i++) {
			if(data[i]>maxValue) {
				maxValue = data[i];
			}
		}
		int maxValueLength = (maxValue+"").length();
		//定义一个(10,data.length)的二维数组,当临时放置元素的桶
		int[][] bucket = new int[10][data.length];
		//定义一个一维数组,存放各个桶中有效元素的索引值
		int[] bucketIndex = new int[10];
		//按照最大位数开启for循环
		//定义一个局部变量n,用来计算元素的各个位上的值
		for(int k=0,n=1;k<maxValueLength;k++,n*=10) {
		//依次取出元素放入相应的桶中,从个位开始依次向上
		for(int i=0;i<data.length;i++) {
			int element = data[i]/n%10;//依次计算出各个位上的数值
			//放入相应的桶中,并把桶中的有效元素索引加一
			bucket[element][bucketIndex[element]] = data[i];
			bucketIndex[element]++;
		}
		//定义一个指向原数组的指针
		int index = 0;
		//依次把放入桶中的元素取出放回原数组
		for(int j=0;j<10;j++) {
			if(bucketIndex[j]!=0) {
				for(int l=0;l<bucketIndex[j];l++) {
					data[index++] = bucket[j][l];
				}
			}
			//元素取完后索引置零
			bucketIndex[j] = 0;
		}
		//循环完毕就拍好序了
		}
	}
	//----------------------------------------------------------------------
	public static void main(String[] args) {
		// 定义一个数组用于测试排序算法正确性
		int[] data = {21,11, 3,21, 6, 2,21, 33, 9, 1, 63,21, 45};
		
		// 定义一个数组,存入大的数据量,测试一下各个排序算法的性能
		int[] bigData = new int[10000000];
		for (int i = 0; i < bigData.length; i++) {
			bigData[i] = 1+(int) (Math.random() * 8000000);
		}
		System.out.println("------------------------------------------------");
		long start = System.currentTimeMillis();
		// 冒泡排序
//		bubbleSort(bigData);
		//快速排序
//		quickSort(bigData);
		// 选择排序
//		checkSort(bigData);
		//堆排序
		heapSort(bigData);
		//插入排序(交换法实现)
//		insertSort_1(bigData);
		//插入排序(移位法实现)
//		insertSort_2(bigData);
		//希尔排序(交换法实现)
//		shellSort_1(bigData);
		//希尔排序(移位法实现)
//		shellSort_2(bigData);
		//归并排序
//		mergeSort(bigData);
		//基数排序
//		radixSort(bigData);
		long end = System.currentTimeMillis();
		Date date = new Date(end - start);
		DateFormat df = new SimpleDateFormat("mm:ss:SSS");
		String time = df.format(date);
		System.out.println("总共耗时:" + time);
		System.out.println("------------------------------------------------");
		//判断排序是否正确
		boolean flag = true;
		for(int i=1;i<bigData.length;i++) {
			if(bigData[i-1]>bigData[i]) {
				System.out.println("排序是错误的");
				flag = false;
				break;
			}
		}
		if(flag) {
		System.out.println("排序正确");
		}
	}
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页