排序算法
1、内部排序:指将需要处理的的所有数据都需要加载到内部存储中进行排序
2、外部排序:数据量过大,无法将全部加载到内存中,需要借助外部存储进行排序
冒泡排序
介绍
通过从前往后遍历序列,依次比较相邻元素的值,将值较大的逐渐移动到最后
- 一共进行数组大小-1循环
- 每一趟排序的次数都在减少
- 优化,在某一次排序中没有发生一次交换,就可以提前结束循环
图解
代码实现
/**
* 冒泡排序
*/
public class BobbleSort {
public static void main(String[] args) {
int arr[] = {3,9,-1,10,20};
int temp = 0;//用于交换数据的辅助变量
for(int i = 0; i < arr.length-1; i++){
for(int j = 0; j < arr.length-1-i;j++){//每一趟排序最大都会排在最后一位
//arr.length-1原因arr[j+1]已经指向最后一位元素,如果是arr.length则出界
if(arr[j]>arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
发现当进行到第三趟时,数据已经不进行交换,其实排序已经完成,所以为了优化程序,减少程序执行时间,可以定义一个boolean
变量用来标记当一趟数据不发生交换时,直接跳出循环,输出排序后的结果,代码优化如下:
public class BobbleSort {
public static void main(String[] args) {
// int arr[] = {4,1,3,2};
int [] arr = new int[80000];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random()*800000);
}
Date data1 = new Date();
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format1 = simpleDateFormat1.format(data1);
System.out.println("排序前的时间:"+format1);
boolean flag = false;//标记是否发生交换
int temp = 0;//用于交换数据的辅助变量
for(int i = 0; i < arr.length-1; i++){
for(int j = 0; j < arr.length-1-i;j++){//每一趟排序最大都会排在最后一位
//arr.length-1原因arr[j+1]已经指向最后一位元素,如果是arr.length则出界
if(arr[j]>arr[j+1]){
flag = true;
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
if(!flag){//一趟排序没有发生数据交换,已经排序完成,不用继续排序
break;
}else {
flag = false;//重置
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
Date data2 = new Date();
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format2 = simpleDateFormat2.format(data2);
System.out.println("排序后的时间:"+format2);
}
}
选择排序
基本介绍
第一次从数组中选择出一个最小的数,然后与数组第一个位置交换数据;第二次从数组的第二个位置选出一个最小的数,与数组第二个位置交换数据,直到排序完成
代码实现
public class SelectSort {
public static void main(String[] args) {
int[] arr = {4,2,6,3,7,5};
selectSort(arr);
}
public static void selectSort(int[] arr){
for(int i= 0;i<arr.length-1;i++){
int minIndex = i;//用来交换数据的辅助变量
int min = arr[i];//每一趟遍历的最小值
for (int j = i+1; j < arr.length; j++) {
if(min>arr[j]){
min = arr[j];
minIndex = j;
}
}
if(minIndex!=i){//当arr[minIndex]刚好是剩下的数中最小的,不需要做交换
arr[minIndex] = arr[i];
arr[i] = min;
}
}
System.out.println(Arrays.toString(arr));
}
}
插入排序
基本介绍
把n个待排序的元素看成一个有序表和一个无序表,开始有序表只有一个元素,无序表中有n-1个元素,排序过程中每次从无序表中取出第一个元素,把他的排序码一次与有序表元素的排序码进行比较,将他插入有序表中适当位置,使之成为新的有序表
图解
代码实现
public class InsertSort {
public static void main(String[] args) {
int[] arr = {56,23,11,45,87};
insertSort(arr);
}
public static void insertSort(int[] arr){
for (int i = 1; i < arr.length; i++) {
int insertValue = arr[i];
int insertIndex = i-1;
while (insertIndex>=0 && insertValue<arr[insertIndex]){
arr[insertIndex+1] = arr[insertIndex];//往后移位置,直到找到要插入的位置之后都要移位置
insertIndex--;//在已经排好顺序的前边,寻找
}
//退出while循环时,已经找到insertValue要插入的位置,insertIndex+1
//判断是否赋值,要插入的位置刚好是排序完部分的最后一个元素的位置后一个,说明不用赋值
if(insertIndex+1!=i){
arr[insertIndex+1] = insertValue;
}
}
System.out.println(Arrays.toString(arr));
}
}
希尔排序
基本介绍
希尔排序是希尔在1959年提出的一种排序算法,也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序
图解
交换法
public class ShellSort {
public static void main(String[] args) {
int[] arr = {8,3,5,9,4,6,7,2};
int n = arr.length;
shellSort(arr);
}
public static void shellSort(int[] arr){
int temp = 0;
for(int gap = arr.length/2; gap > 0; gap /=2){
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
System.out.println(Arrays.toString(arr));
}
}
效率不高,发现一个就去交换,速度不是很快
移位法
public static void shellSort(int[] arr){
int temp = 0;
for(int gap = arr.length/2; gap > 0; gap /=2){
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
System.out.println(Arrays.toString(arr));
}
快速排序
基本介绍
快速排序是对冒泡排序的一种改进,基本思想是通过一趟排序将要排序的数据分隔成独立的两部分,其中一部分的所有数据都比另外一部分数据都要小,然后再按此方法对这两部分进行快速排序,整个过程可以递归 进行,一次达到整个数据变成有序序列
图解
代码实现
public class QuickSort {
public static void main(String[] args) {
int [] arr = {2,1,0,-6,5,7};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
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 += 1;
}
//找到比pivot大的数据下标
while (arr[r]>pivot){
r -= 1;
}
//找到比pivot小的数据下标
if(l>=r){
break;
}
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//发现交换完的数据和pivot相等,右指针左移
if(arr[l]==pivot){
r -= 1;
}
if(arr[r]==pivot){
l += 1;
}
}
//如果l==r,必须l++,r--,否则会溢出
if(l==r){
l += 1;
r -= 1;
}
//向左递归
if(left<r){
quickSort(arr,left,r);
}
//向右递归
if(right>l){
quickSort(arr,l,right);
}
}
}
归并排序
基本介绍
归并排序是利用归并的 思想实现的排序方法,该算法采用的分治策略,将问题分成一些小问题然后递归求解,而治的夹断就将分的阶段得到的个答案合并在一起
图解
代码实现
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);
System.out.println(Arrays.toString(arr));
}
/**
* 分和合方法
* @param arr
* @param left
* @param right
* @param 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);
//合并
mergeSort(arr,left,mid,right,temp);
}
}
/**
* 合并方法
* @param arr 排序的数据
* @param left 左边有序列的初始序列
* @param mid 中间索引
* @param right 右边索引
* @param temp 做中转的数组
*/
public static void mergeSort(int[] arr, int left, int mid, int right, int[] temp){
int i = left;
int j = mid + 1;//
int t = 0;//指向temp数组的当前索引
while (i<=mid && j<=right){
if(arr[i] <= arr[j]){
temp[t] = arr[i];
t += 1;
i += 1;
}else {
temp[t] = arr[j];
t += 1;
j += 1;
}
}
//将一边序列的剩余数据全部填充到数组中
while (i<=mid){//左边序列剩余数据
temp[t] = arr[i];
t += 1;
i += 1;
}
while (j<=right){//右边序列会剩余数据
temp[t] = arr[j];
t += 1;
j += 1;
}
//将所有数据拷贝到arr中,但是并不是每一次都要全部拷贝
t = 0;
int tempLeft = left;
System.out.printf("tempLeft=%d right=%d\n",tempLeft,right);
while(tempLeft<=right){//第一次合并tempLeft=0 right=1 第二次合并tempLeft=1 right=2 第三次合并tempLeft=0 right=3...
// 直到最后一次才是tempLeft=0 right=7
arr[tempLeft] = temp[t];
t += 1;
tempLeft +=1;
}
}
}
基数排序
基本介绍
基数排序属于“分配式排序”,又称“桶子法”,它是通过键值的各个位的值,将要排序的元素分配在某些桶中,达到排序的作用
思路分析
将每个元素的个位数取出,然后看这个数在哪个对应的桶中
代码实现
package com.bai.sort;
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int arr[] = {53,3,542,748,14,214};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void radixSort(int[] arr){
//定义一个二维数组,表示十个桶,每一个桶就是一个数组
//为了防止溢出,将每一个数组的大小都设为arr.length
//所以基数排序是以空间换时间的经典算法
int[][] bucket = new int[10][arr.length];
//记录每个桶中存入数据的个数,bucketElementsCounts[0]表示bucket[0]的数据个数
int[] bucketElementsCounts = new int[10];
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if(arr[i]>max){
max = arr[i];
}
}
int maxLength = (max+"").length();
for (int s = 0, n = 1;s<maxLength;s++, n *= 10){
for (int i = 0; i < arr.length; i++) {
int digitOfElement = arr[i]/n%10;
bucket[digitOfElement][bucketElementsCounts[digitOfElement]] = arr[i];
bucketElementsCounts[digitOfElement]++;
}
int index = 0;
//遍历每一个桶
for (int j = 0; j < bucket.length; j++){
if(bucketElementsCounts[j] != 0){
for (int k = 0;k<bucketElementsCounts[j];k++){
arr[index++] = bucket[j][k];
}
}
//每趟排序完都得至0,下一个存入
bucketElementsCounts[j] = 0;
}
System.out.println(Arrays.toString(arr));
}
}
}