summary
排序的分类
1.内部排序
指将需要处理的所有数据都加载到内部存储器中进行排序
一共八种
1.插入排序(直接插入排序,希尔排序)
2.选择排序(简单选择排序,推排序)
3.交换排序(冒泡排序,快速排序)
4.归并排序
5.基数排序
2.外部排序
数据量过大,无法全部加载到内存中,需要借助外部存储进行排序
算法的时间复杂度
1.事后统计
执行程序,得到执行时间,对其进行分析。
2.事前估算
分析某个算法的时间复杂度
时间频度
一个算法执行的时间与算法中语句的执行次数成正比。
算法中语句执行的次数成为 时间频度 记为T(n)
eg. T(n) = n^2+5n+20
1.忽略常数
2.忽略底次项
3.忽略系数
时间复杂度
T(n)为时间频度
f(n)为某一函数
当n->∞时,T(n)/f(n)=C,C为任意常数,则称f(n)是T(n)的同数量级函数,记作T(n)=0(f(n)),称O(f(n))为该算法的渐进时间复杂度,简称时间复杂度
eg. T(n)=n^2 +7n+6 则有, T(n)/f(n)= n^2 +7n+6 / n^2=1 n->∞ ,则f(n)=n^2 有时;间复杂度 O(f(n))=n^2
常见的时间复杂度
1.对数阶 O(1)
2.对数阶O(log2 n) 2是底数
3.线性阶O(n)
4.线性对数阶O(nlog2n) 2为底数
5.平方阶
6.立方阶
7.k次方阶O(n^k)
8.指数阶O(2^n)
以上的时间复杂度从小排到大;1<2<3<4<5<6<7<8
平均时间复杂度和最坏时间复杂度
1.平均时间复杂度:所有可能的输入实例均以均等概率的情况出现。
2.最坏时间复杂度: 最坏情况下的时间复杂度
空间复杂度(Space Complexity)
一个算法的空间复杂度定义为该算法所耗费的存储空间,它也是问题规模n的函数。
冒泡排序(Bubble Sort)
通过对待排序序列从前向后(从index较小的元素开似乎),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐往后排。
因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序的过程中设置一个flag,判断元素是否交换过,从而减少不必要的交换。
eg.
array = 3,9,-1,10,20
第一躺排序:
(1). 3,9,-1,10,20 //如果相邻的元素逆序就交换
(2). 3,-1,9,10,20
(3).3,-1,9,10,20
(4).3,-1,9,10,20
第一趟排序下来,最大的那个数已经确定了。
第二趟排序
(1) -1 ,3 , 9, 10, 20
(2) -1 ,3 , 9, 10, 20
(3) -1 ,3 , 9, 10, 20
第二趟排序下来,最大的两个数已经确定了
第三躺排序
(1) -1 ,3 , 9, 10, 20
(2) -1 ,3 , 9, 10, 20
第二趟排序下来,最大的三个数已经确定了
第四躺排序
(1) -1 ,3 , 9, 10, 20
n-1个数已经确定了,不需要再比较了。
小结:
1.一共进行数组的大小-1次大的循环。
2.每一趟的排序逐渐的减少
3.相邻的元素比较
/**
*
小结:
1.一共进行数组的大小-1次大的循环。即 array.length -1 次大循环(外循环)
2.每一趟的排序逐渐的减少
3.相邻的元素比较
*/
public class BubbelSorting {
public static void main(String[] args) {
int arr[] = {
3,9,1,10,-2};
//为了方便理解,会展示演变过程,注意观察后面的 -0 -1 -2 -3
//1.第一躺排序,会将最大的数排在最后
int temp =0;// 临时变量
for (int i = 0; i < arr.length-1-0; i++) {
// 如果后面的数比前面的数大,则交换
if(arr[i]>arr[i+1]){
temp = arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
System.out.println("-----第一躺排序后的数组,找到最大的数----");
System.out.println(Arrays.toString(arr));
//第二趟排序,把第二大的数,排在倒数第二位
for (int i = 0; i < arr.length-1 -1 ; i++) {
// 如果后面的数比前面的数大,则交换
if(arr[i]>arr[i+1]){
temp = arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
System.out.println("-----第二躺排序后的数组,找到第二大的数----");
System.out.println(Arrays.toString(arr));
//第三趟排序,把第三大的数,排在倒数第三位
for (int i = 0; i < arr.length-1 -2 ; i++) {
// 如果后面的数比前面的数大,则交换
if(arr[i]>arr[i+1]){
temp = arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
System.out.println("-----第三躺排序后的数组,找到第三大的数----");
System.out.println(Arrays.toString(arr));
//第四趟排序,把第四大的数,排在倒数第四位
for (int i = 0; i < arr.length-1 -3; i++) {
// 如果后面的数比前面的数大,则交换
if(arr[i]>arr[i+1]){
temp = arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
System.out.println("-----第四躺排序后的数组,找到第四大的数----");
System.out.println(Arrays.toString(arr));
System.out.printf("\n\n\n\n\n");
System.out.println("----------根据观察,改进代码-----------");
int arr1[] = {
3,9,1,10,-2};
int tem =0;// 临时变量
for(int j =0;j<arr.length-1;j++){
//时间复杂度 O(n^2)
for (int i = 0; i < arr.length-1-j; i++) {
// 如果后面的数比前面的数大,则交换
if(arr1[i]>arr1[i+1]){
tem = arr1[i];
arr1[i]=arr1[i+1];
arr1[i+1]=tem;
}
}
System.out.println("这是第"+j+"躺排序");
System.out.println(Arrays.toString(arr1));
}
}
}
//将前面的冒泡排序改进,并封装成一个方法
public static void bubbleSorting(int arr[]){
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++){
if(arr[j]>arr[j+1]){
flag=true;
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
if(!flag){
break;
}
flag=false;
}
}
选择排序(Select Sort)
选择排序属于内部排序,从要排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的
basic thought
第一次从arr[0]~arr[n-1]中选取最小值,与arr[0]交换;
第二次从arr[1] ~ arr[n-1]中选取最小值,与arr[1]交换;
…
…
直到第n-1次从arr[n-2] ~ arr[n-1] 中选择最小值,与arr[n-2]交换。
通过n-1次,得到一个从小到大排列的有序序列。
原始数组:101,34,119,1
第一轮排序: 1,34,119,101
第二轮排序:1,34,119,101
第三轮排序:1,34,101,119
小结:
1.一共有array.length-1轮排序
2.每一轮排序,又是一个循环。
2.1.先假定当前这个数是最小的
2.2.和后面的每个数依次进行比较;如果发现比当前数更小的数,就重新确定最小数,并得到index
2.3 当遍历到数组的最后时,就得到本轮最小数和index
2.4 交换
public class SelectSorting {
public static void main(String[] args) {
int arr[]={
101,34,119,1};
// selctSorting(arr);
selectSortingImprove(arr);
}
//select sorting
public static void selctSorting(int[] arr){
//逐步推导
//第一轮
//原始的数组: 101,34,119,1
//第一轮排序: 1,34,119,101
//算法, 先做简单----》再做复杂;即把一个复杂的算法,差分成简单的问题-》逐步解决
//第1轮
int minIndex=0;
int min=arr[minIndex];
for(int j =0+1;j<arr.length;j++){
if(min >arr[j]){
//说明假定的最小值,并不是最小的
min=arr[j];//重置min
minIndex=j;//重置minIndex
}
}
//将最小值方在arr[0],交换
if(minIndex!=0){
arr[minIndex]=arr[0];
arr[0]=min;
}
System.out.println("第一轮后:"+Arrays.toString(arr));
//第二轮
minIndex=1;
min=arr[minIndex];
for(int j =0+2;j<arr.length;j++){
if(min >arr[j]){
//说明假定的最小值,并不是最小的
min=arr[j];//重置min
minIndex=j;//重置minIndex
}
}
//将最小值方在arr[1],交换
if(minIndex!=1){
arr[minIndex]=arr[1];
arr[1]=min;
}
System.out.println("第二轮后:"+Arrays.toString(arr));
//第三轮
minIndex=2;
min=arr[minIndex];
for(int j =0+3;j<arr.length;j++){
if(min >arr[j]){
//说明假定的最小值,并不是最小的
min=arr[j];//重置min
minIndex=j;//重置minIndex
}
}
//将最小值方在arr[1],交换
if(minIndex!=2){
arr[minIndex]=arr[2];
arr[2]=min;
}
System.out.println("第三轮后:"+Arrays.toString(arr));
}
public static void selectSortingImprove(int[]arr){
//选择排序的时间复杂度 O(n^2)
for(int i = 0; i<arr.length;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[i];
arr[i]=min;
}
System.out.println("第"+i+"轮排序:"+Arrays.toString(arr));
}
}
}
插入排序(Insrtion Sort)
插入式排序属于内部排序法,是对想要排序的元素以插入的方式寻找该元素的适当位置,以达到排序的目的。
Basic Thought
把n个待排序的元素看成:一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素。排序过程中每次从无序表中取出第一个元素,把它的排序码与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
public class InsertionSort {
public static void main(String[] args) {
int [] arr ={
101,34,119,1};
// insrtionSort(arr);
insertionSortImprove(arr);
}
public static void insrtionSort(int[] arr) {
//推导
//第一轮(101,34,119,1) => (34,101,119,1)
//定义待插入的数
int insertionVal = arr[1];
int insertionIndex = 1 - 1; //即arr[1]得前面这个数的下标
//给insertionVal 找到插入的位置
//说明
//1.insertionIndex >= 0 保证在给insrtionVal 找到插入位置,不越界
//2.insertionVal <arr[insertionIndex] 待插入的数,还没有找到插入位置
//3.就需要将 arr[insertionIndex]后移
while (insertionIndex >= 0 && insertionVal < arr[insertionIndex]) {
arr[insertionIndex + 1] = arr[insertionIndex];
insertionIndex--;
}
//当退出while循环时,说明插入的位置找到,insertionIndex + 1
//举例:
arr[insertionIndex + 1] = insertionVal;
System.out.println("第一轮插入:"+ Arrays.toString(arr));
insertionVal=arr[2];
insertionIndex=2-1;
while(insertionIndex>=0 && insertionVal <arr[insertionIndex]){
arr[insertionIndex +1]=arr[insertionIndex];
insertionIndex--;
}
arr[insertionIndex+1]=insertionVal;
System.out.println("第二轮插入:"+ Arrays.toString(arr));
insertionVal=arr[3];
insertionIndex=3-1;
while(insertionIndex>=0 && insertionVal <arr[insertionIndex]){
arr[insertionIndex +1]=arr[insertionIndex];
insertionIndex--;
}
arr[insertionIndex+1]=insertionVal;
System.out.println("第三轮插入:"+ Arrays.toString(arr));
}
public static void insertionSortImprove(int [] arr){
int insertionVal=0;
int insertionIndex=0;
//插入排序的时间复杂度 O()
for(int i = 1;i <arr.length;i++){
insertionVal = arr[i];
insertionIndex = i-1;
while(insertionIndex>=0 && insertionVal<arr[insertionIndex]){
arr[insertionIndex+1]=arr[insertionIndex];
insertionIndex--;
}
//判断是否需要赋值
if(insertionIndex+1 == i){
arr[insertionIndex+1]=insertionVal;
}
System.out.println("第"+i+"次插入"+Arrays.toString(arr));
}
}
}
希尔排序(Shell‘s Sort)
希尔排序也是一种插入排序,它是简单插入排序经过改进后的一个更高效的版本,也称为缩小增量排序
Basic Thought
把记录按index的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件被分为一组
public class ShellSort {
public static void main(String[] args) {
int[] arr = {
8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
// shellSort(arr);
sheelSrotImprove(arr);
shellSortImproveAgain(arr);
}
//使用逐步推导的方式,编写希尔排序
public static void shellSort(int arr[]) {
//希尔排序第一轮排序
//因为第一轮排序,是将10个数据分成5组
for (int i = 5; i < arr.length; i++) {
//遍历各组中所有的元素(共有5组,每组有2个元素),步长5
for (int j = i - 5; j >= 0; j -= 5) {
//如果当前元素大于加上步长后那个元素,说明需要交换
if (arr[j] > arr[j + 5]) {
int temp = arr[j];
arr[j] = arr[j + 5];
arr[j + 5] = temp;
}
}
}
System.out.println("希尔排序第一轮后:" + Arrays.toString(arr));
//希尔第二轮排序
//因为是第二轮排序,将10个数据分成了5/2 = 2组
for (int i = 2; i < arr.length; i++) {
for (int j = i - 2; j >= 0; j -= 2) {
if (arr[j] > arr[j + 2]) {
int temp = arr[j];
arr[j] = arr[j + 2];
arr[j + 2] = temp;
}
}
}
System.out.println("希尔排序第二轮后:" + Arrays.toString(arr));
//希尔第三轮排序
//因为是第二轮排序,将10个数据分成了2/2 = 1组
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0; j--) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println("希尔排序第三轮后:" + Arrays.toString(arr));
}
//交换法
public static void sheelSrotImprove(int arr[]) {
//根据前面的逐步分析
//得到一个公式,使用循环处理
//插入法
int temp = 0;
int count = 0;
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
//遍历各组中所有的元素(共gap组,每组有2个元素) 步长gap
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("希尔排序第" + (++count) + "轮:" + Arrays.toString(arr));
}
}
//对交换式的希尔排序进行优化--》移位法
public static void shellSortImproveAgain(int arr[]) {
//增量gap,并逐步缩小增量
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
//从gap个元素,逐个对其所在的组进行直接插入排序
for(int i=gap;i< arr.length;i++){
int insertionIndex = i;
int insertionVal =arr[insertionIndex];
if(arr[insertionIndex] <arr[insertionIndex-gap]){
while(insertionIndex -gap >=0 && insertionVal < arr[insertionIndex-gap]){
//移动
arr[insertionIndex]=arr[insertionIndex-gap];
insertionIndex -=gap;
}
//当推出while循环后,就给insertionVal找到插入位置
arr[insertionIndex]=insertionVal;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
快速排序(Quick Sort)
快速排序是对冒泡排序的一种改进
Basic Thought
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据比另外一部分的所有数据都要小。然后再按此方法对这两部分的数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
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));
}
public static void quickSort(int[] arr, int left, int right) {
int l = left;//左下标
int r = right;//右下标
//pivot 中轴值
int pivot = arr[(l + r) / 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 --;
}
//如果交换完后,发现这个arr[r] == pivot , l++ 后移
if(arr[r] == pivot){
l++;
}
}
// 如果 l == r 就必须 l++, r--, 否则会出现栈溢出
if(l == r ){
l ++;
r--;
}
//向左递归
if(left <r){
quickSort(arr,l,r);
}
//向右递归
if(right >l){
quickSort(arr,l,r);
}
}
}
归并排序(Merge Sort)
归并排序(Merge Sort)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)将divide时得到的各答案“修补”在一起,即分而治之)
Basic Thought
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));
}
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);
}
}
//Method for merge
/**
*
* @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){
if(arr[i] <= arr[j]){
//如果左边有序序列的当前元素,小于等于右边有序序列的当前元素
//把左边的当前元素拷贝到temp数组
temp[t]=arr[i];
t++;
i++;
}
if(arr[i] > arr[j]){
//反之,将右边有序序列的当前元素,拷贝到temp数组
temp[t] = arr[j];
t++;
j++;
}
}
//(二)
//把有剩余数据的一边的数据依次全部填充到temp
while( i<= mid){
// 说明左边的有序序列还有剩余的元素
temp[t]=arr[i];
i++;
t++;
}
while(j<=right){
// 说明右边的有序序列还有剩余的元素
temp[t] =arr[j];
j++;
t++;
}
//(三)
//将tmep数组的元素 copy 至 arr
//注意:并不是每次都拷贝所有元素
t = 0;
int tempLeft = left;
while(tempLeft <= right){
arr[tempLeft]=temp[t];
t ++;
tempLeft++;
}
}
}
https://www.cnblogs.com/chengxiao/p/6194356.html
基数排序 (Radix sort)
- 基数排序是桶排序(bucket sort)的拓展,属于“分配式排序(distribution sort)”;它是通过键值得各个位的值,将要排序的元素分配至某些“桶”中,达到排序的目的。
- radix sort是属于稳定性的排序,是效率最高的稳定性排序
- 实现方法:将整数按位数切割成不同的数字,然后按每个位数分别比较
Basic Thought
- 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。依次进行依次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。