排序的概念:排序是将一组数据按照指定的顺序进行排列的过程。
排序可以分为内部排序和外部排序,内部排序是将处理的所有数据都加载到内部存储器中进行排序。
若数据量过大,无法全部加载到内存中,需要借助于外部存储进行排序。
1.1、内部排序
直接插入排序、希尔排序
简单选择排序、堆排序
冒泡排序、快速排序
1.2外部排序
概念:冒泡排序是一种交换排序,它的基本思想是:按照某种排序规则,两两比较相邻的元素的关键字,如果是反序则交换,直到没有反序的记录为止。
普通的冒泡算法
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
System.out.println(Arrays.toString(arr));
}
优化排序算法
存在这样一种情况,没有数据交换时,即说明此序列已经有序了,即不再需要判断后面的数据了,我们需要优化代码,即当序列已经有序时,不再对后面的数据进行判断。
boolean flag = true;
for (int i = 0; i < arr.length - 1&&flag; i++) {
flag=false;
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
flag = true;
}
}
System.out.println(Arrays.toString(arr));
}
概念:即通过n-i次关键字间的比较,从n-i+1(包括自己)个记录中选出关键字最小的记录,并和第i个记录交换。
for (int i = 0; i < arr.length - 1; i++) {
int minSize = i;
int min = arr[minSize];
for (int j = i + 1; j <= arr.length - 1; j++) {
if(min>arr[j]){
minSize=j;
min=arr[j];
}
}
if(i!=minSize){
arr[minSize]=arr[i];
arr[i]=min;
}
}
概念:把n个待排序的元素看成一个有序列表和一个无序表,开始时有序表只包含一个元素,无序表中包含n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
int insertScrpt=0;//要插入的位置
int insertValue=0;//插入的值
for(int i=1;i<=arr.length-1;i++){
insertScrpt=i-1;
insertValue=arr[i];
while (insertScrpt>=0&&insertValue<arr[insertScrpt]){
arr[insertScrpt+1]=arr[insertScrpt];//向后移
insertScrpt--;
}
if(insertScrpt+1!=i){
arr[insertScrpt+1]=insertValue;
}
}
概念:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量的逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件掐被分成一组,算法终止。
交换法(时间复杂度为17s左右)
int temp = 0;
for (int increment = arr.length / 2; increment > 0; increment = increment / 2) {
for (int i = increment; i < arr.length; i++) {
for (int j = i - increment; j >= 0; j -= increment) {
if (arr[j] > arr[j + increment]) {
temp = arr[j];
arr[j] = arr[j + increment];
arr[j + increment] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
移位法(时间复杂度为1s左右)
for (int increment = arr.length / 2; increment > 0; increment = increment / 2) {
for (int i = increment; i < arr.length; i++) {
int j = i;
int temp = arr[j];
if (arr[j] < arr[j - increment]) {
while (j - increment >= 0 && temp < arr[j - increment]) {
arr[j] = arr[j - increment];
j -= increment;
}
arr[j] = temp;
}
}
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 privot=arr[(right+left)/2];
while (l<r){
while (arr[l]<privot){
l++;
}
while (arr[r]>privot){
r--;
}
if(l>=r){
break;
}
temp=arr[l];
arr[l]=arr[r];
arr[r]=temp;
if(arr[l]==privot){
r--;
}
if(arr[r]==privot){
l++;
}
}
if(l==r){
l+=1;
r-=1;
}
//左递归
if (left<r){
quickSort(arr,left,r);
}
//向右递归
if(right>l){
quickSort(arr,l,right);
}
}
归并排序是利用归并的思想进行排序的方法,采用经典的分治策略(分治是将问题分解成一些小问题然后递归求解,然后就将各个阶段的答案修补在一起)
网上找的图解
这种结构很想一个完全二叉树,我们采用递归的方法去实现,也可以采用迭代方法去实现,分阶段可以理解为递归拆分子序列的过程。
序列的拆分
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; //临时变量的索引
//1、将拆开的左右数组中的元素分别比较大小,并放入临时数组,直至某一数组被放完
while (i <= mid && j <= right) {
if (arr[i] < arr[j]) {
temp[t] = arr[i];
i++;
t++;
} else {
temp[t] = arr[j];
j++;
t++;
}
}
//2、将剩下数组的元素全部移动到临时数组中
while (i <= mid) {
temp[t] = arr[i];
i++;
t++;
}
while (j <= right) {
temp[t] = arr[j];
j++;
t++;
}
//3 将临时数组的元素合更新到原数组
t = 0;
int tempLeft = left;
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
算法描述:基数排序就是分离出数字的每一位,根据每一位的大小放入指定的桶中,桶用数组来表示,共分成编号为0-9的桶。如249,分离个位时,249放入编号为9的桶中,分离十位时,246放入编号为4的桶中,分离千位时,249放入编号为0的桶中。存在负数的序列不能使用基数排序。
基本思想:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零,然后,从最低位开始,依次进行一次排序,这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
public static void radisSort(int[] arr){
//找出最大数并确定位数
int max=arr[0];
for(int i=1;i<arr.length;i++){
if(max<arr[i]){
max=arr[i];
}
}
int maxLength=(max+"").length();
//定义桶,为了防止元素溢出,需要将每个桶的容量都定义为:arr.length
int[][] bucket=new int[10][arr.length];
//定义一个数组bucketElementCount[],用来记录每个桶中放入的元素的数量
int[] bucketElementCount=new int[10];
/**
* 将数据元素放入桶中
*/
for(int l=0,n=1;l<maxLength;n=n*10,l++){
for(int i=0;i<arr.length;i++){
int endDigital=arr[i]/n%10;
bucket[endDigital][bucketElementCount[endDigital]]=arr[i];
bucketElementCount[endDigital]++;
}
/**
* 取出元素
*/
int index=0;
for(int i=0;i<bucket.length;i++){
if(bucketElementCount[i]!=0){
for(int j=0;j<bucketElementCount[i];j++){
arr[index]=bucket[i][j];
index++;
}
}
//将bucketElemnetCount清零
bucketElementCount[i]=0;
}
System.out.println(Arrays.toString(arr));
}
}