1.排序的分类
1) 内部排序:指将需要处理的所有数据都加载到内部存储器中进行排序
2) 外部排序:无法全部加载到内存中,需要借助外部存储进行
3)图示:
2.算法的时间复杂度
1) 度量一个程序(算法)执行时间的两种方法
A.事后统计的方法 问题:a.需要实际运行该程序。b.时间也依赖于计算机的硬件,软件等环境因素。所以要在同一台计算机的相同状态下运行,才能比较哪个算法速度更快 B.事前估算的方法 通过分分析某个算法的时间复杂度来判断哪个算法更优
2) 时间频度
A. 基本介绍
一个算法花费的时间与算法中语句的执行次数成正比,哪个算法中语句执行次数多,它花费的时间越长,一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)
B. 忽略常数项,忽略低次项,忽略系数
C .图示:
3)常见的时间复杂度
A.
B.
C.
D.
E.
4)平均时间复杂度和最坏时间复杂度
A.平均时间复杂度:所有可能的输入实例均以等概率出现的情况下,该算法的运行时间
B.最坏时间复杂度:最坏情况下的时间复杂度,一般讨论的时间算法复杂度均是最坏时间复杂度
C.
3.算法的空间复杂度
1)基本介绍
该算法该耗费的存储空间,也是空间规模n的函数。随着n的增大而增大,当n较大时,将占用较多的存储单元,例如快速排序和归并排序算法
4.排序
1)冒泡排序
A.思想:
通过对待排序序列从前往后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部。
B.图示:
C.代码:
package com.offcn.sort; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; //冒泡排序 public class BubbleSort { public static void main(String[] args){ int[] arr = {3,9,-1,10,-2}; //冒泡优化 int[] arr1 = {1,2,3,4,5,6}; bubbleSort(arr); //冒泡排序的速度测试 int[] arrTime = new int[80000]; for(int i = 0; i < arrTime.length;i++){ arrTime[i] = (int)(Math.random()*8000000); } Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = sdf.format(date); System.out.println("排序前的时间为"+format); bubbleSort(arrTime); Date date1 = new Date(); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format1 = sdf1.format(date1); System.out.println("排序后的时间为"+format1); } public static void bubbleSort(int[] arr){ //临时遍历 int temp = 0; //定义一个变量 boolean flag = false; //总共循环arr.length-1次,每次拿到最大的值 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]){ //如果需要换位,则flag = true flag = true; temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } System.out.println("第"+(i+1)+"次排序结果"); System.out.println(Arrays.toString(arr)); //冒泡排序优化 if(!flag){ //如果本次排序未换位,说明已经排好,退出 break; }else { flag = false; } } } }
D 输出:
优化:
速度:
2)选择排序
A.思想:
第一次从arr[0]~arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]~arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]~arr[n-1]中选取最小值,与arr[2]交换…得到
一个按排序码从小到大排列的有序序列。
B.图示:
C.代码
package com.offcn.sort; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; //选择排序 public class SelectSort { public static void main(String[] args){ int[] arr={101,-1,119,1}; System.out.println("排序前~"+Arrays.toString(arr)); selectSort(arr); //选择排序的速度测试 int[] arrTime = new int[80000]; for(int i = 0; i < arrTime.length;i++){ arrTime[i] = (int)(Math.random()*8000000); } Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = sdf.format(date); System.out.println("排序前的时间为"+format); selectSort(arrTime); Date date1 = new Date(); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format1 = sdf1.format(date1); System.out.println("排序后的时间为"+format1); } public static void selectSort(int[] arr){ //循环arr.length-1次,每次把最小值往前移动 for(int i = 0;i < arr.length-1;i++){ //默认最小值的索引是i int minIndex = i; //默认最小值是arr[i] int min = arr[i]; //每次最小值都是跟后面的数比较 for(int j =(i+1);j<arr.length;j++){ //如果后面的值比默认最小值还小,就互换位置 if(min > arr[j]){ minIndex = j; min = arr[j]; } } //优化,如果索引为i,说明循环中没有换位 if(minIndex != i){ //把默认最小值换到后面去 arr[minIndex] = arr[i]; //把最小值放前 arr[i] = min; } System.out.println("第"+(i+1)+"次排序"+ Arrays.toString(arr)); } } }
D.输出:
速度:
3)插入排序
A.思想:
把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素
的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
B.图示:
C.代码
package com.offcn.sort; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; //插入排序 public class InsertSort { public static void main(String[] args){ int[] arr={101,-1,119,1}; System.out.println("排序前~"+Arrays.toString(arr)); insertSort(arr); //插入排序的速度测试 int[] arrTime = new int[80000]; for(int i = 0; i < arrTime.length;i++){ arrTime[i] = (int)(Math.random()*8000000); } Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = sdf.format(date); System.out.println("排序前的时间为"+format); insertSort(arrTime); Date date1 = new Date(); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format1 = sdf1.format(date1); System.out.println("排序后的时间为"+format1); } public static void insertSort(int[] arr){ for(int i = 1;i < arr.length;i++){ //arr[i]表示无序表的第一个数字,先存起来,找合适的位置插入进去 int insertVal = arr[i]; //insertIndex表示有序表的最后一个数字的下标 int insertIndex = i-1; //insertValue依次跟有序表中的数据比较,而且有序表的数要比insertVal大的情况下 while (insertIndex >=0 && arr[insertIndex] > insertVal){ //把大的数复制一份放入无序表的第一个数 arr[insertIndex+1] = arr[insertIndex]; //有序表索引前移 insertIndex--; } //把有序表的第一个值插入进去,因为while循环最后都是--,所以需要insertIndex+1 //优化,==i时,int insertVal = arr[i]; if(insertIndex+1 != i){ arr[insertIndex+1] = insertVal; } System.out.println("第"+(i)+"次排序"+ Arrays.toString(arr)); } } }
D.输出:
速度:
4)希尔排序
A.介绍:
希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。
B.思想:
把记录按下标的一定增量分组,对每组使用直接插入排序算法排序。随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件被分为一组时,算法便终止。
C.图示:
D.交换法代码
package com.offcn.sort; 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}; shellSort(arr); //希尔排序交换法的速度测试 int[] arrTime = new int[80000]; for(int i = 0; i < arrTime.length;i++){ arrTime[i] = (int)(Math.random()*8000000); } Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = sdf.format(date); System.out.println("排序前的时间为"+format); shellSort(arrTime); Date date1 = new Date(); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format1 = sdf1.format(date1); System.out.println("排序后的时间为"+format1); } //交换法 public static void shellSort(int[] arr){ int count = 0; //外层循环arr.length/2/2/2/2/2至=1时 for(int grap = (arr.length)/2;grap > 0;grap/=2){ //i表示一组中位置在后的数字 for(int i = grap;i < arr.length;i++){ for(int j = i-grap;j >=0;j-=grap){ //如果后面的数字小于前面的,则换位 if(arr[j+grap] < arr[j]){ int temp = arr[j]; arr[j] = arr[j+grap]; arr[j+grap] = temp; } } } System.out.println("第"+(++count)+"次排序:"+Arrays.toString(arr)); } } }
输出:
速度:
E.移位法代码
//希尔排序 public class ShellSort { public static void main(String[] args){ int[] arr = {8,9,1,7,2,3,5,4,6,0}; shellSort2(arr); //希尔排序移位法的速度测试 int[] arrTime = new int[80000]; for(int i = 0; i < arrTime.length;i++){ arrTime[i] = (int)(Math.random()*8000000); } Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = sdf.format(date); System.out.println("排序前的时间为"+format); shellSort2(arrTime); Date date1 = new Date(); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format1 = sdf1.format(date1); System.out.println("排序后的时间为"+format1); } public static void shellSort2(int[] arr){ //外层循环arr.length/2/2/2/2/2至=1时 int count =0; for(int grap = arr.length/2;grap > 0;grap/=2){ for(int i = grap;i<arr.length;i++){ //arr[j] 表示一组中位置在后的数字,arr[j-grap]表示位置在前 int j = i; int temp = arr[j]; //如果前面的数字比较打,移位 if(arr[j] < arr[j-grap]){ while (j-grap>=0 && temp<arr[j-grap]){ arr[j] = arr[j-grap]; j-=grap; } //当退出while循环,则找到位置 arr[j] = temp; } } System.out.println("第"+(++count)+"次排序:"+Arrays.toString(arr)); } }
输出:
速度:
5)快速排序
A.介绍:
是对冒泡排序的一种改进。
B.思想:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,
以此达到整个数据变成有序序列。
C.图示:
D.代码
package com.offcn.sort; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; //快速排序 public class QuickSort { public static void main(String[] args){ int[] arr = {-9,78,0,23,-567,70}; quickSort(arr,0,arr.length-1); System.out.println(Arrays.toString(arr)); //快速排序的速度测试 int[] arrTime = new int[80000]; for(int i = 0; i < arrTime.length;i++){ arrTime[i] = (int)(Math.random()*8000000); } Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = sdf.format(date); System.out.println("排序前的时间为"+format); quickSort(arrTime,0,arrTime.length-1); Date date1 = new Date(); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format1 = sdf1.format(date1); System.out.println("排序后的时间为"+format1); } //left表示数组最小下标,right表示数组最大下标 public static void quickSort(int[] arr,int left,int right){ int l = left;//左下标 int r = right;//右下标 //中轴值 int pivot = arr[(left+right)/2]; int temp = 0; while (l < r){ //找出左边比中轴值大的数字 while (arr[l] < pivot){ l+=1; } //找出右边比中轴值小的数字 while (arr[r] > pivot){ r-=1; } //如果l++,r--后,l的坐标比右边的大了或者相等了,说明已经遍历左右已经遍历完毕 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[] == 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); } } }
E.输出:
速度:
6)归并排序
A.介绍:
是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案修补在一起)
B.思想:
C.代码
package com.offcn.sort; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; //归并排序 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)); //归并排序的速度测试 int[] arrTime = new int[80000]; int[] temp2 = new int[arrTime.length]; for(int i = 0; i < arrTime.length;i++){ arrTime[i] = (int)(Math.random()*8000000); } Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = sdf.format(date); System.out.println("排序前的时间为"+format); mergeSort(arrTime,0,arrTime.length-1,temp2); Date date1 = new Date(); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format1 = sdf1.format(date1); System.out.println("排序后的时间为"+format1); } //分合 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){ //i作为左边有序序列的最小下标 int i = left; //j作为右边有序序列的最小下标 int j = mid+1; //t是中转数组的下标 int t = 0; //(一)把左右两边的有序序列填充到中转数组中 while (i <= mid&& j <= right){ //如果左边的数比右边的小,小的放入temp[t]中 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; } //(三)把temp数组中的元素拷贝到arr,temp下标从0开始,但是arr下标要从left开始,最后才是一个数组 int q = 0; //如left = 2,right = 3 int arrLeft = left; while (arrLeft <= right){ arr[arrLeft] = temp[q]; q+=1; arrLeft+=1; } } }
D.输出:
速度:
7)基数排序
A.介绍:
基数排序属于分配式排序,又称“桶子法”,它是通过键值的各个位的值,将要排序的元素分配至某些桶中,达到排序的作用。基数排序法是属于稳定性的排序,也是桶排序的扩展
B.思想:
将所有待比较数值统一为同样的数位长度,数位较短的数前面补零,然后从最低位开始,依次进行一次排序,这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
C.图示:
D.代码
package com.offcn.sort; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; //基数排序,有负数时尽量不使用基数排序 public class RedixSort { public static void main(String[] args){ int[] arr = {53,3,542,748,14,214}; redixSort(arr); //基数排序的速度测试 int[] arrTime = new int[80000]; for(int i = 0; i < arrTime.length;i++){ arrTime[i] = (int)(Math.random()*8000000); } Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = sdf.format(date); System.out.println("排序前的时间为"+format); redixSort(arrTime); Date date1 = new Date(); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format1 = sdf1.format(date1); System.out.println("排序后的时间为"+format1); } public static void redixSort(int[] arr){ //先得到数组中最大数是几位数 //拿到最大数 int max = arr[0]; for(int i = 1;i < arr.length;i++){ if(arr[i] > max){ max = arr[i]; } } //是几位数 int maxLength = (max+"").length(); //定义一个二维数组,总共十个桶,每个桶内有多少数未知,所以定为arr.length int[][] bucket = new int[10][arr.length]; //记录每个桶具体有多少数 int[] bucketCount = new int[10]; //总共循环maxLength次 for(int i = 0,n = 1; i < maxLength;i++,n*=10){ //对数组中的数字都进行判断,拿到相应的值 for(int j = 0;j < arr.length;j++){ //对元素中的个十百位进行处理 int num = arr[j] /n % 10; //num表示是第一个桶,bucketCount[num]表示该桶下第几个元素,第一个默认为0下标 bucket[num][bucketCount[num]] = arr[j]; //放入一个,下标加1 bucketCount[num]++; } //根据桶的顺序,把元素再存入原数组中 int index = 0; //有几个桶循环几遍 for(int k = 0;k < bucket.length;k++){ //如果i桶里有元素 if(bucketCount[k] != 0){ //循环bucketCount[k]次 for(int m = 0;m < bucketCount[k];m++){ //数据存入原数组中 arr[index] = bucket[k][m]; index++; } } //把元素放入原数组后,把当前桶的元素个数置为0; bucketCount[k] = 0; } System.out.println("第"+(i+1)+"次排序:"+ Arrays.toString(arr)); } } }
E.输出:
速度: