目录
排序分为内部排序和外部排序。
所谓内部排序就是将程序先加载进内存再进行排序。
而外部排序就是若数据量比较大的时候,如果无法将程序全部加载进内存,则用外部排序,外部排序是内部和外部相结合的方式去进行排序。
常见的内部排序有插入排序,选择排序,交换排序,归并排序,基数排序
其中 插入排序有可以分为:直接插入排序,希尔排序。
选择排序又可以分为:简单选择排序和堆排序。
交换排序可以分为:冒泡排序和快速排序。
交换排序:
冒泡排序原理和实现_今天你学习了ma的博客-CSDN博客_冒泡排序实现原理冒泡排序算法的原理如下:比较相邻的元素。如果第一个比第二个大,就交换他们两个。对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。针对所有的元素重复以上的步骤,除了最后一个。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。比如说我现在有个数组 {8,6,5,11}第一大次从第一个数开始,8如果碰到比自己小的数就会把它和换个位置,开始时,第一次8和6比较,6比...https://blog.csdn.net/weixin_52875557/article/details/122006096?spm=1001.2014.3001.5501
插入排序:
插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。
首先将整个数组看成一个有序的和一个无序的数组
第一次先将第一个元素17看成一个有序的数组
第二次将3和17看成一个有序的数组,根据大小关系讲1和17进行大小排序
第三次将1,17,25看成一个有序的数组,根据大小排序
第四次和第5次以此类推,直到数组所有的元素都变成有序的数组
其它的都很好理解,插入排序最重要的就是怎么把从无序数组进来到有序数组的数找到合适的位置,也就是每进来一个数,给有序数组排序.
思路:
排序时,假设现在时第三次插入.此时要插入的数时14,先将14保存在insertValue中,将前面一个数的索引保存在insertIndex中,(从小到大排序)若刚刚要插入的数比insertIndex对应的数要大,则直接插入到后面,不需要比较找到合适的位置.若要插入的数据比insertIndex对应的数要小则就需要找到合适的位置去插入. 插入条件:
while(insertIndex >= 0 && insertVal < arr[insertIndex] ) {
arr[insertIndex + 1] = arr[insertIndex];// arr[insertIndex]
insertIndex--;
}
完成之后 arr[insertIndex + 1] = insertVal;
例如:第三次插入
将3 17 25 14 将14插入有序数组并且找到合适位置
insertIndex>=0&&14小于25满足条件进循环,则将14的位置用25覆盖,也就是数组变成了
3 17 25 25,insertIndex-- 此时insertIndex变成1,对应17的值,此时也满足while条件.
则将数组变成 3 17 17 25 ,后将insertIndex-1=0,对应3.此时不满足while条件,则将 arr[insertIndex + 1] = insertVal; 后有序数组变成 3 14 17 25
package sort;
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args) {
int[] arr={17,3,25,14,20,9};
int insertValue;
int insertIndex;
for (int i = 1; i < arr.length; i++) {
insertIndex=i-1;
insertValue=arr[i];
while(insertIndex>=0&&insertValue<arr[insertIndex]){
arr[insertIndex+1]=arr[insertIndex];
insertIndex--;
}
arr[insertIndex+1]=insertValue;
}
System.out.println(Arrays.toString(arr));
}
}
用随机数函数模拟了8万个数,不到1秒就排完了,选择和冒泡可排了足足9秒!
希尔排序
在前面所提到的插入排序中,如果数组 arr = {2,3,4,5,6,1}
这时需要插入的数 1(最小), 这样的过程是:
{2,3,4,5,6,6}
{2,3,4,5,5,6}
{2,3,4,4,5,6}
{2,3,3,4,5,6}
{2,2,3,4,5,6}
{1,2,3,4,5,6}
后移的次数太多,对排序的效率影响太大了
希尔排序算法思想:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含 的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止
快速排序
快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两 部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排 序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
整个排序依赖递归去完成,重点注意一下条件!
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class QuickSort {
public static void main(String[] args) {
// 创建要给 80000 个的随机的数组
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
}
System.out.println("排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
quickSort(arr, 0, arr.length - 1);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
// System.out.println("arr=" + Arrays.toString(arr));
}
public static void quickSort(int[] arr, int left, int right) {
int l = left; // 左下标
int r = right; // 右下标
// pivot 中轴值
int pivot = arr[(left + right) / 2];
int temp = 0; // 临时变量,作为交换时使用
// while 循环的目的是让比 pivot 值小放到左边
// 比 pivot 值大放到右边
while (l < r) {
// 在 pivot 的左边一直找,找到大于等于 pivot 值,才退出
while (arr[l] < pivot) {
l += 1;
}
// 在 pivot 的右边一直找,找到小于等于 pivot 值,才退出
while (arr[r] > pivot) {
r -= 1;
}
// 如果 l >= r 说明 pivot 的左右两的值,已经按照左边全部是
// 小于等于 pivot 值,右边全部是大于等于 pivot 值
if (l >= r) {
break;
}
// 交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
// 如果交换完后,发现这个 arr[l] == pivot 值 相等 r--, 前移
if (arr[l] == pivot) {
r -= 1;
}
// 如果交换完后,发现这个 arr[r] == pivot 值 相等 l++, 后移
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);
}
}
}
归并排序
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer) 策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修 补"在一起,即分而治之)
分的过程其实没有实质性的作用,其实是把数据拆分
分到每组元素都是一个元素的时候开始治
治的过程利用了归并排序
归并排序思想:归并排序这里的思想和合并俩个有序链表差不多.主要思想就是利用 双指针,一个指针指向数组一0号元素,一个指针指向数组二0号元素.通过比较,俩个数大小,小的数取到临时的数组temp,随后将指针后移,再比较俩个元素,一次比较,随后将数组有序化.
package sort;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MergetSort {
public static void main(String[] args) {
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
arr[i] = (int) (Math.random() * 8000000);
}
System.out.println("排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
int temp[] = new int[arr.length]; //归并排序需要一个额外空间
mergeSort(arr, 0, arr.length - 1, temp);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
}
//分+合方法
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);
//合并
merge(arr, left, mid, right, temp);
}
}
//合并的方法
/**
* @param arr 排序的原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 做中转的数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left; // 初始化 i, 左边有序序列的初始索引
int j = mid + 1; //初始化 j, 右边有序序列的初始索引
int t = 0; // 指向 temp 数组的当前索引
//(一)
//先把左右两边(有序)的数据按照规则填充到 temp 数组
//直到左右两边的有序序列,有一边处理完毕为止
while (i <= mid && j <= right) {//继续
//如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
//即将左边的当前元素,填充到 temp 数组
//然后 t++, i++
if(arr[i] <= arr[j]) {
temp[t] = arr[i];
t += 1;
i += 1;
} else { //反之,将右边有序序列的当前元素,填充到 temp 数组
temp[t] = arr[j];
t += 1;
j += 1;
}
}
//(二)
//把有剩余数据的一边的数据依次全部填充到 temp
while( i <= mid) { //左边的有序序列还有剩余的元素,就全部填充到 temp
temp[t] = arr[i];
t += 1;
i += 1;
}
while( j <= right) { //右边的有序序列还有剩余的元素,就全部填充到 temp
temp[t] = arr[j];
t += 1;
j += 1;
}
//(三)
//将 temp 数组的元素拷贝到 arr
//注意,并不是每次都拷贝所有
t = 0;
int tempLeft = left; //
//第一次合并 tempLeft = 0 , right = 1 // tempLeft = 2 right = 3 // tL=0 ri=3
//最后一次 tempLeft = 0 right = 7
while(tempLeft <= right) {
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
}
基数排序
1) 基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或 bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
2) 基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法
3) 基数排序(Radix Sort)是桶排序的扩展
例如将arr={53, 3, 542, 748, 14, 214}使用基数排序
先准备10个桶,序号分别是0-9,用来存储元素
第一轮排序规则:将arr各个元素的个位取出看对应哪个桶,后将元素顺序取出
第一轮排序的结果 arr={542,53,3,14,214,748}
第二轮排序规则:将arr各个元素的是十位取出看对应哪个桶,后将元素顺序取出
第二轮排序的结果 arr={3,14,214,542,748,53}
第三轮排序规则: 将arr各个元素的是百位取出看对应哪个桶,后将元素顺序取出
第二轮排序的结果 arr={3,14,53,214,542,748}
数组arr排序完成!
轮次由数组中最大元素决定.