Introduction for sort algorithm

排序的分类

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)

  1. 基数排序是桶排序(bucket sort)的拓展,属于“分配式排序(distribution sort)”;它是通过键值得各个位的值,将要排序的元素分配至某些“桶”中,达到排序的目的。
  2. radix sort是属于稳定性的排序,是效率最高的稳定性排序
  3. 实现方法:将整数按位数切割成不同的数字,然后按每个位数分别比较

Basic Thought

  1. 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。依次进行依次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值