Java数据结构与算法(五)——排序

一、冒泡排序

package SortAlgorithm;


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

public class BubbleSort {

	public static void main(String[] args) {
		int[] arr = {-1, 20, -10, 10, 5};
		System.out.println("排序前的数组:");
		System.out.println(Arrays.toString(arr));
		Date date1 = new Date();
		SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
		String datestr1 = simpleDateFormat1.format(date1);
		System.out.println(datestr1);
		
		BubbleSort(arr);
		System.out.println("排序后的数组:");
		System.out.println(Arrays.toString(arr));
		
		Date date = new Date();
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
		String datestr = simpleDateFormat.format(date);
		System.out.println(datestr);
	}
	
	public static void BubbleSort(int[] arr) {
		
		int temp = 0; //临时变量,用于数组中相邻两个元素比较后交换位置
		boolean flag = false; //判断数组中是否有元素交换,用于冒泡排序优化,可提前结束循环
		
		for (int j = 0; j < arr.length - 1; j++) { //每次放置好一个数字后,就减少一轮比较
			
			for (int i = 0; i < arr.length - 1 - j; i++) { //每次放置好一个大的数,从最大的数开始
				if (arr[i] > arr[i+1]) {
					flag = true; //一旦有元素交换,说明还未完成排序
					temp = arr[i];
					arr[i] = arr[i+1];
					arr[i+1] = temp;
//					System.out.println("第一次排序" + Arrays.toString(arr));
				}
				
			}
			if (flag == false) { //一次交换也没进行
				break;
				
			}else {
				flag = false; //重置flag,进行下次判断
			}
//			System.out.println("第一次排序" + Arrays.toString(arr));
		}
		
	}

}

二、选择排序
速度比冒泡排序快。

package SortAlgorithm;

import java.util.Arrays;

public class SelectSort {

	public static void main(String[] args) {
		int[] arr = {101, 34, 119, 1};
		SelectSort(arr);

	}

	public static void SelectSort(int[] arr) {
		for (int j = 0; j < arr.length - 1; j++) {
			int minIndex = j; //用于指向找到的最小的元素的下标,方便后面元素交换,先假设当前的元素就是最小的
			int min = arr[j]; //存放最小的元素
			
			//从当前元素的后一个开始循环比较,直到找到最小的,然后置换
			for (int i = j+1; i < arr.length; i++) {
				
				if (min > arr[i]) {
					minIndex = i; //重置minIndex
					min = arr[i]; //重置min
				}
				
			}
			
			//将最小值和arr[j]置换
			if (minIndex != j) { //当最小元素不是当前元素时才互换
				arr[minIndex] = arr[j];
				arr[j] = min;	
			}
			System.out.println("第一轮后:" + Arrays.toString(arr));
		}
		
	}	
}
		

三、插入排序

package SortAlgorithm;

import java.util.Arrays;

public class InsertSort {

	public static void main(String[] args) {
		int[] arr = {101, 34, 119, 1};
		insertSort(arr);
	}
	
	public static void insertSort(int[] arr) {
		//定义待插入的数
		for (int i = 1; i < arr.length; i++) {
			
			int insertVal = arr[i];
			int insertIndex = i-1; //即arr[1]前面的一个数的下标
			
			//insertIndex >= 0保证不越界
			//insertVal < arr[insertIndex],当insertVal比前面的数小时,将前面的数后移一位
			while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
				arr[insertIndex+1] = arr[insertIndex];
				insertIndex--; //将insertIndex向前移动一位,再将insertVal与之比较,直到找到其位置
			}
			
			//当退出while循环时,说明insertVal位置找到,即insertIndex+1的位置
			arr[insertIndex+1] = insertVal;
			System.out.println(Arrays.toString(arr));
		}
		
	}

}

四、希尔排序

package SortAlgorithm;

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


public class ShellSort {
	public static void main(String[] args) {
		
		int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0}; //数组共有十个数,第一次分成五组进行插入排序,第二次分成2组,第三次分成一组
		
		int[] arr1 = new int[80000];
		for (int i = 0; i < arr1.length; i++) {
			arr1[i] = (int)(Math.random()*80000); //生成一个[0,80000)的数组
		}
		
		//测试速度
		//排序前
		Date date1 = new Date();
		SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
		String strdate1 = simpleDateFormat1.format(date1);
		System.out.println(strdate1);
		
		//交换法
		shellSort1(arr);
		//移动法
		shellSort2(arr);
		
		//排序后
		Date date2 = new Date();
		SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
		String strdate2 = simpleDateFormat2.format(date2);
		System.out.println(strdate2);
	}
	
	
	
	//交换法希尔排序
	public static void shellSort1(int[] arr) {
		 
		int temp = 0;
		for (int gap = arr.length/2; gap > 0; gap = gap/2) { //gap是步长每次循环排序后,gap/2重新进行分组排序,直到gap=1
			for (int i = gap; i < arr.length; i++) { //i=5的时候是数组中第6个数3,分成5组的时候3要和8进行插入排序。
				//这个循环是让后面的5个数和前面的5个进行比较,从后往前,那么嵌套循环中的j就要从i=0开始从数组中的第一个数往后
				for (int j = i - gap; j >= 0; j -= gap) {
					//j>=0和j-=5的目的是因为是从后往前比较的,比如i=2的时候,第一次是arr[0]和arr[2]比较,当i增加到4,j=2,先是arr[2]先和arr[4]比较,较小的数放在arr[2]中
					//然后j-=2,j=0,arr[0]需要再次和arr[2]比较,因为arr[4]有可能换到了arr[2]中。j>=0是为了保证从后往前比较的时候不越界。
					while (arr[j] > arr[j+gap]) {
						temp = arr[j];
						arr[j] = arr[j+gap];
						arr[j+gap] = temp;		
					}
				}
			}
			System.out.println(Arrays.toString(arr));
		}
	
		/*
		 * 第一次分组示例
		int gap = arr.length/2; //第一次的步长
		int temp = 0;
		for (int i = 5; i < arr.length; i++) { //i=5的时候是数组中第6个数3,分成5组的时候3要和8进行插入排序。
			//这个循环是让后面的5个数和前面的5个进行比较,从后往前,那么嵌套循环中的j就要从i=0开始从数组中的第一个数往后
			for (int j = i - 5; j >= 0; j -= 5) {
				//j>=0和j-=5的目的是因为是从后往前比较的,比如i=2的时候,第一次是arr[0]和arr[2]比较,当i增加到4,j=2,先是arr[2]先和arr[4]比较,较小的数放在arr[2]中
				//然后j-=2,j=0,arr[0]需要再次和arr[2]比较,因为arr[4]有可能换到了arr[2]中。j>=0是为了保证从后往前比较的时候不越界。
				while (arr[j] > arr[j+5]) {
					temp = arr[j];
					arr[j] = arr[j+5];
					arr[j+5] = temp;		
				}
			}
		}
		System.out.println(Arrays.toString(arr));
		*/	
	}
	
	
	//移动法希尔排序
	public static void shellSort2(int[] arr){
		for (int gap = arr.length/2; gap > 0; gap = gap/2) { //gap是步长每次循环排序后,gap/2重新进行分组排序,直到gap=1
			for (int i = gap; i < arr.length; i++) {
				int insertIndex = i;
				int temp = arr[insertIndex];
					while (insertIndex-gap >= 0 && temp < arr[insertIndex - gap]) { //参考看插入排序
						arr[insertIndex] = arr[insertIndex - gap];
						insertIndex = insertIndex - gap;
					}
					arr[insertIndex] = temp;
			}
				
		}
		System.out.println(Arrays.toString(arr));
	}

五、快速排序

package SortAlgorithm;

import java.util.Arrays;

public class QuickSort {

	public static void main(String[] args) {

		int[] arr = {9,0,78,23,-567,70};
		quickSort(arr, 0, arr.length-1);
		System.out.println(Arrays.toString(arr));

	}
	//快速排序法需要传进去三个参数,数组arr,数组的最左边的元素下标left,数组最右边的元素下标right
	public static void quickSort(int[] arr, int left, int right) {
		
		int l = left; //左指针
		
		int r = right; //右指针
		
		int temp = 0; //临时变量,用于交换
		
		int pivot = arr[(left+right)/2];
		
		//首先让数组分别从两头开始查找,比中间值大的放在左边,比中间值小的放在右边,进行左右交换
		while (l < r) {
			while (arr[l] < pivot) { //在左边寻找比中间值大的值
				l++; 
			}
			while (arr[r] > pivot) { //在右边寻找比中间值小的值
				r--;
			}
			
			if (l == r) { //如果左右指针在中间值处相遇,说明左右交换完成,跳出循环,开始进入递归,对左右两边的值分别进行快排
				break;
			}
			
			temp = arr[l]; //交换左右两边的值
			arr[l] = arr[r];
			arr[r] = temp;

			//下面这两个条件的作用是发生在中间值被交换到其他位置,发生移位
			//为什么中间值要被移位?为了保证中间值左边的数一定比其小,右边的数一定比它大
			if (arr[l] == pivot) { //如果中间值被交换到左边,就让右边的指针向中间值靠拢,直到左右指针在中间值处相遇
				r--;
			}
			
			if (arr[r] == pivot) {
				l++;
			}
	
		}
		
		
	
		if (l == r) { //跳出循环时,r和l是相等的,这个时候需要将r和l分别向前移动,形成两个分组
			l++;
			r--;
		}
		//对这两个分组使用递归分别进行快速排序
		if (left < r) {
			quickSort(arr, left, r);	
		}
		
		if (right > l) {
			quickSort(arr, l, right);
		}
		
	}
	
}

六、归并排序

package SortAlgorithm;

import java.util.Arrays;

public class MergeSort {

	public static void main(String[] args) {
		
		int[] arr = {8,4,5,7,1,3,6,2};
		int[] temp = new int[arr.length];
		
		
		mergeSort(arr, 0, arr.length-1, temp);

	}
	
	
	
	//分解数组和合并数组的总的方法
	public static void mergeSort(int[] arr, int left, int right, int[] temp) {
		if (left < right) {
			int mid = (left+right)/2;
			//向左递归进行分解
			mergeSort(arr, left, mid, temp);
			//向右递归进行分解
			mergeSort(arr, mid+1, right, temp);
			
			System.out.println("归并排序后分解的数组" + Arrays.toString(arr));
			//合并
			merge(arr, left, right, mid, temp);
			System.out.println("归并排序后合并的数组" + Arrays.toString(arr));
			System.out.println();
		}	
	}
	
	
	
	
	//合并的方法
	/**
	 * 
	 * @param arr 需要排序的数组
	 * @param left 数组最左边的下标
	 * @param right 数组最左边的下标
	 * @param mid 数组分成两份后,左边那份最右边的值的下标
	 * @param temp 临时数组,用于存放合并后的数
	 * 
	 */
	public static void merge(int[] arr, int left, int right, int mid, int[] temp) {
		int i = left; //左指针,用于指向左边那份数值的下标
		int j = mid + 1; //右指针,用于指向右边那份数值的下标
		int t = 0; //临时数组的指针,指向临时数组当前值的下标
		
		//1、按照规则把两份分好的数排好顺序放入临时数组中
		while (i <= mid && j <= right) { //当i <= mid && j <= right时,说明还没有循环一遍
			
			if (arr[i] <= arr[j]) { //如果左边的数比右边的数小,就把左边的数放入临时数组中
				temp[t] = arr[i];
				i += 1; //将指针右移继续对比下一个数
				t += 1;
			}else {
				temp[t] = arr[j];
				j += 1; //将指针右移继续对比下一个数
				t += 1;
			}
			
		}
		
	
		
		//2、当一边的数全部放进数组后,另一边的数就直接按顺序依次放入临时数组中,因为他已经排好顺序
		while (i <= mid) { //当i<=mid时,说明左边的数还有剩余
				temp[t] = arr[i];
				i += 1; //将指针右移继续对比下一个数
				t += 1;
		}
		
		while (j <= right) { //当j <= right时,说明右边的数还有剩余
				temp[t] = arr[j];
				j += 1; //将指针右移继续对比下一个数
				t += 1;
		}
		

		
		//3、把排好顺序的临时数组temp中的数重新放回原数组arr中
		//由于前面分组之后也要排序,存放到临时数组中,然后排好后放进原数组,
		t = 0;
		int tempLeft = left;
		System.out.println("tempLeft和right" + "," + tempLeft + "," + right);
		while (tempLeft <= right) {
			arr[tempLeft] = temp[t];
			t++;
			tempLeft++;
		}
			
	}
	
}

七、基数排序

package SortAlgorithm;

import java.util.Arrays;

public class RadixSort {

	public static void main(String[] args) {

		int[] arr = { 53, 3, 542, 748, 14, 214 };
		radixSort(arr);

	}

	public static void radixSort(int[] arr) {
		// 为了计算出数组中的个十百位数,需要对其进行除法和取模运算,因此就需要找到数组中的最大数
		int max = arr[0]; // 先假定数组中第一个数最大,然后让其与数组中其它数比较,直到找到最大数
		for (int i = 0; i < arr.length; i++) {
			if (max < arr[i]) {
				max = arr[i];
			}
		}

		// 求最大数是几位数
		int maxLength = (max + "").length(); // 先将max转换成字符串,再计算其长度

		// 先定义一个用于排序的桶,即一个二维数组,里面有10个一维数组,分别存放各个位是0-9的数
		// 为了保证存入每个桶中的数不溢出,就用空间换时间,多给每个一维数组一些空间
		int[][] bucket = new int[10][arr.length];

		// 然后定义一个计数的一维数组,用来保存每个桶中放了几个数据。
		// 因为有十个桶,所以一维数组需要统计这十个桶中每个分别存放了多少数据,比如count[0] =
		// 5,说明第一个桶bucket[0][]中存放了5个数据
		int[] count = new int[10];

		for (int n = 0, l = 1; n < maxLength; n++, l *= 10) {

			// 下面将数组中的每个数据按照个位数值放入对应的桶中,并计数,统计每个桶中放了多少
			for (int i = 0; i < arr.length; i++) {
				// 计算每个数值的个位数
				int digitOfElement = arr[i] / l % 10;
				// 按照个位数,将其放入到对应的桶中
				// 放到digitOfElement桶中的第count[digitOfElement]个位置,第一个位置是0,第二个是1
				bucket[digitOfElement][count[digitOfElement]] = arr[i];
				// count计数加一,计数以及改变元素放入上面桶中的位置
				count[digitOfElement] += 1;
			}

			// 从桶中依次取出数据放入原数组
			// j表示桶,从第一个桶到最后一个桶,共十个
			int index = 0;
			for (int j = 0; j < count.length; j++) {
				// 先判断桶中是否有数据
				if (count[j] != 0) { // 说明桶中有count[j]个数
					// 循环该桶(一维数组),取出数据放入原数组
					for (int k = 0; k < count[j]; k++) {
						arr[index] = bucket[j][k];
						index++;
					}
				}

				// 第一轮结束后将count[j]中的数取出后需要将其置零,否则会影响第二轮的统计
				count[j] = 0;
			}

			System.out.println(Arrays.toString(arr));

		}

	}

}

八、堆排序

package SortAlgorithm;

import java.util.Arrays;

public class HeapSort {

	public static void main(String[] args) {
		//要求对数组进行升序排列
		int[] arr = { 4, 6, 8, 5, 9 };
		int[] arr1 = { 100, 22, 15, 46, -12, 15, 14, 4, 6, 8, 5, 9 };
		
		heapSort(arr1);
	}
	

	//编写一个堆排序的方法
	public static void heapSort(int[] arr) {
		int temp = 0;
		
//		adjustHeap(arr, 1, arr.length);
//		System.out.println(Arrays.toString(arr)); //4 9 8 5 6
//		adjustHeap(arr, 0, arr.length);
//		System.out.println(Arrays.toString(arr)); //9 6 8 5 4
		
//		//这一步可省略,因为直接对i输入0,可以一次性将大顶堆构建完成
//		//1.完成将数组转换成大顶堆
//		//i从第一个非子叶节点开始,直到最后一个
//		for (int i = arr.length/2 - 1; i >= 0; i--) {
//			adjustHeap(arr, i, arr.length);
//		}
//		System.out.println(Arrays.toString(arr)); //9 6 8 5 4
		
		
		//2.将对顶元素与末尾元素进行交换,此时,末尾元素就是最大元素
		//3.重新调整结构,用剩下的元素重构大顶堆,继续第2步操作
		//j就是末尾元素
		for (int j = arr.length - 1; j > 0; j--) {
			//交换
			temp = arr[j]; //队尾元素
			arr[j] = arr[0];
			arr[0] = temp;
			
			//必须从第0个元素开始构建大顶堆,这样可以一次性将大顶堆构建完成
			//如果从第一个非叶子节点开始,还要传入的i的值要一直改变,需要多次传入才能最终完成,而从0开始,一次性就可以完成
			adjustHeap(arr, 0, j);
		}
		
		System.out.println(Arrays.toString(arr));
		
	}
	

	
	
	//将一个数组(二叉树),调整成一个大顶堆的方法
	/**
	 * 功能:完成将以i为非叶子节点的树调整成大顶堆
	 * 举例:{ 4, 6, 8, 5, 9 },第一次i=1,调整后变成{ 4, 9, 8, 5, 6 }
	 * 第二次i=0,调整成{ 9, 6, 8, 5, 4 }
	 * @param arr 待调整的数组
	 * @param i 表示非叶子结点在数组中的索引
	 * @param length 表示对多少个元素进行调整,因为调整一次后,就会少一个元素
	 * 
	 * 
	 */
	public static void adjustHeap(int[] arr,int i, int length) {
		//先用一个临时变量保存传进来的非叶子节点i中的值,为了方便后面的交换
		int temp = arr[i];
		//然后比较非叶子节点i中的值和其左右子节点中的值得大小
		//先比较左右子节点中的值的大小,其中k表示左子节点下标k+1表示右子节点下标,要保证左右子节点的下标不越界
		for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
			//k = 2 * k + 1是比较完当前的三个节点后,进入到和传进来的节点交换的那个节点的子节点进行同样的比较
			if (k + 1 < length && arr[k] < arr[k + 1]) {
				k++;
			} //结束这句后,k就指向左右节点中较大的一个
			//然后比较非子叶节点中的值和k指向的值得大小
			if (temp < arr[k]) { //第二次比较时,temp中的数相当于交换后的k中的数,但是实际上还没有交换,因为还要进行下一轮比较
				arr[i] = arr[k];
				i = k; //为了结束循环后将原来i中的值放入到k指向的位置,如果有多轮交换,为了第二轮交换时将k中的值和其左右节点交换
			}else {
				break;
			}
		}
		arr[i] = temp; //将i中的值放入k中,此时的i指向k的位置
		
	}
	


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值