数据结构——八大排序算法(面试/数据结构期末考试-简单且详细)

目录

1.直接插入排序

1.1 代码实现

1.2 复杂度

2.希尔排序

2.1 代码实现

2.2 复杂度 

3.选择排序

3.1 代码实现

 3.2 复杂度      

4.堆排序

4.1 代码实现

4.2 复杂度

5.冒泡排序

5.1 代码实现

5.2 复杂度1

5.3 优化后的代码实现

5.4 复杂度2

6.快速排序

6.1 代码实现

6.2 复杂度

7.归并排序

 7.1 代码实现

7.2 归并排序的递归实现

7.3 复杂度

7.4 归并排序的非递归实现

8.非比较排序

基数排序


1.直接插入排序

 

         对于一组数据,直接插入排序,认为第一位有序,将后面的数据与第一位比较,小于第一位数据则排在第一位数据的前面,大于则不动。

例如:有如下15个数据

1223402238985550784566493720

定义一个tmp作为中间变量。由于认为第一位有序,则定义i从第二位开始,并定义j为第一位的下标,用于比较。如下图

 比较i下标与j下标值的大小,发现有序,则i++

发现有序,则

i++

j = i -1

先 tmp = array[i]

如下图

发现array[j] > tmp,则

array[j+1] = array[j];

j--;

如下图

发现array[j] > tmp, 则

array[j+1] = array[j];

j--;

如下图

此时j < 0,则

array[j+1] = tmp;

i++;

j = i - 1;

 如下图

 这样就实现了j下标之前的数据完成了有序。

往复如此,就可以实现整个数组的有序。

1.1 代码实现

    public static void insertSort(int[] array){
        for (int i = 1; i < array.length; i++) {
            int j = i - 1;
            int tmp = array[i];
            for (; j >= 0; j--) {
                if(array[j] > tmp){
                    array[j+1] = array[j];

                }else{
                    //array[j+1] = tmp;
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }

1.2 复杂度

时间复杂度:O(N^2)   

空间复杂度:O(1)

稳定性:稳定

2.希尔排序

        希尔排序又称为缩小增量排序

        希尔排序是对直接插入排序的优化,利用分组的思想,每组进行直接插入排序,有效降低时间复杂度。

例如:有如下15个数据

1223402238985550784566493720

按正常人思维,应该是按顺序分组

第一次分组:分5组

                   组别1:12      23       4                   排序后:4      12      23

                   组别2:0        22       38                 排序后:0      22      38

                   组别3:98      55       50                 排序后:50    55      98

                   组别4:78      45       66                 排序后:45    66      78

                   组别5:49      37       20                 排序后:20     37     49

分组并排序后,此时数据如下

4122302238505598456678203749

第二次分组:分3组:

        组别1:4     12     23     0     22      排序后:0      4     12     22     23

        组别2:38   50     55    98    45      排序后:38    45    50     55    98

        组别3:66   78     20    37    49      排序后:20    37    49     66    78

第三次分组:分一组,并进行直接插入排序。

                      最后一定是分成一组,之前的分组都是预分组。

注意,为什么是5 ,3 ,1分组?而不是5  4  1等其他的分组?这涉及希尔排序的缩小增量的方法。

        希尔排序的分析是一个复杂的问题,因为它的时间是所取“增量”的函数,这涉及一些数学上的尚未解决的难题。因此,到目前为止尚未有人求的一种最好的增量序列,但大量的研究得到了一些局部的结论。增量序列有多种取法,但需注意:应使增量序列中的值没有除1之外的公因子(素数),且最后一个增量值必须为1。

 一般认为希尔排序的分组如下所示,每个颜色对应一组,共5组,并对每一组进行直接插入排序

由于是分5组,我们令gap = 5,i与j始终相差5,从第一组开始进行排序,排序后令i++。

        这种分组方法相比于前面说的按顺序分组的方法,大概率将小的数据放在前面,大的数据放在后面,使得这组数据更加有序。

2.1 代码实现

        经上面分析,希尔排序最重要的一点就是如何分组。

        写出shell()函数进行排序,shell()函数本质上就是直接插入排序。

        shellSort()函数进行分组。令gap等于array数组的长度,总而进行排序,每次分组都是gap /= 2。以上都是预排序,注意最后一定要令gap = 1进行排序。

代码如下:

    public static void shell(int[] array,int gap){
        for(int i = gap;i < array.length;i++){
            int tmp = array[i];
            int j = i - gap;
            for(;j >= 0;j -= gap){
                if(array[j] > tmp){
                    array[j + gap] = array[j];
                }else {
                    break;
                }
            }
            array[j + gap] = tmp;
        }
    }
    public static void shellSort(int[] array){
        int gap = array.length;
        while(gap > 1){
            shell(array,gap);
            gap /= 2;
        }
        shell(array,1);
    }

代码“gap /= 2”保证了每次增量都是缩小的,目前来说无法保证每次都是素数。

2.2 复杂度 

时间复杂度:和它的增量有关 。为O(n^1.3--n^1.5)   

空间复杂度:O(1)

稳定性:不稳定

由于对于直接插入排序来说,数据越有序越快,所以它经常用于排序的优化上

3.选择排序

         对于一组数据,i指向第一个元素,j指向i下一个元素,判断i下标的元素与j下标元素大小,若i下标元素大于j下标元素,则交换它们。j++走完后得出i下标后面的最小元素并完成了交换,此时i++,直至i走完整个数组。

3.1 代码实现

    public static void selectSort(int[] array){
        for (int i = 0; i < array.length; i++) {
            for (int j = i + 1; j < array.length; j++) {
                if(array[i] > array[j]){
                    int tmp = array[i];
                    array[i] = array[j];
                    array[j] = tmp;
                }
            }
        }
    }

 3.2 复杂度      

时间复杂度:O(n^2)

空间复杂度:O(1)

稳定性:不稳定

4.堆排序

        基本原理是选择排序,排升序建立大堆,排降序建立小堆

        首先我们创建大根堆,令child = array.length - 1,则child的父节点是parent = (rray.length - 1 - 1)/2,

4.1 代码实现

public static void swap(int[] array,int i,int j){
    int tmp = array[i];
    array[i] = array[j];
    array[j] = tmp;
}
public static void heapSort(int[] array){
        createHeap(array);
        int end = array.length - 1;
        while(end > 0){
            swap(array,0,end);
            shiftDown(0,end,array);
            end--;
        }
    }
    public static void createHeap(int[] array){
        for(int parent = (array.length - 1 - 1) / 2;parent >= 0;parent--){
            shiftDown(parent,array.length,array);
        }
    }
    public static void shiftDown(int parent,int len,int[] array){
        int child = 2*parent+1;
        while(child < len){
            if(child + 1 < len &&array[child] < array[child+1]){
                child++;
            }

            if(array[child] > array[parent]){
                swap((array,child,parent);
                parent = child;
                child = 2*parent+1;
            }else {
                break;
            }
        }
    }

4.2 复杂度

时间复杂度:O(N*log(2)(N))

空间复杂度:O(1)

稳定性:不稳定

5.冒泡排序

例如:有如下15个数据

1223402238985550784566493720

        定义一个j,从0下标开始,从左往右依次比较排序,直至本次j走完数组,然后在此基础上再次从左往右依次比较排序,直到结束。

5.1 代码实现

    public static void bubbleSort(int[] array){
        for (int i = 0; i < array.length-1; i++) {
            for (int j = 0; j < array.length-1; j++) {
                if(array[j] > array[j+1]){
                    int t = array[j];
                    array[j] = array[j+1];
                    array[j+1] = t;
                }
            }

        }
    }

5.2 复杂度1

时间复杂度:O(N^2)

空间复杂度:O(1)

稳定性:稳定

注意:冒泡排序可以优化从而减小时间复杂度;例如每次j走完后,后面的元素就会有序,不用再进行排序,又例如,当某一次j走完后数据已经完成了有序,就不用再进行排序。

5.3 优化后的代码实现

    public static void bubbleSort2(int[] array){
        for (int i = 0; i < array.length-1; i++) {
            boolean flag = false;
            for (int j = 0; j < array.length-1-i; j++) {
                if(array[j] > array[j+1]){
                    int t = array[j];
                    array[j] = array[j+1];
                    array[j+1] = t;
                    flag = true;
                }
            }
            if(flag == false){
                break;
            }

        }
    }

5.4 复杂度2

时间复杂度:最坏情况O(N^2)。最好情况O(N)

空间复杂度:O(1)

稳定性:稳定

6.快速排序

从待排序的数据中选择一个数据作为基准(pivot),遍历待排序的数据,小于基准值的放在左边,大于基准值的放在右边,对左右两边用同样的方法继续处理,直至最小区间长度为1。

例如:有如下15个数据

1223402238985550784566493720

对于基准(pivot)的寻找,可以用以下方法:

        定义start为0下标,定义end为最后一个的下标,并定义tmp作为中间变量。

        令tmp = array[start] 

        从后面开始,判断end下标数据是否大于tmp中的值,若array[end] > tmp,则end--;若array[end] < tmp,则array[start] = array[end],然后start++,判断start下标数据是否小于tmp中的值,若array[start] < tmp,则start++,若array[start] > tmp,则array[end] = array[start],然后end--,再进行end下标的判断,直至end = start,此时定义一个pivot = tmp = array[start] = array[end]。

        可以利用递归的方法,如此反复,直至序列有序。

6.1 代码实现

    //排序
    public static void quickSort(int[] array,int left,int right){
        if(left >= right) return;
        int pivot = partition(array,left,right);//基准
        quickSort(array,left,pivot-1);
        quickSort(array,pivot+1,right);
    }
    //寻找基准
    private static int partition(int[] array,int start,int end){
        int tmp = array[start];
        while(start < end) {
            while(start < end && array[end] >= tmp) end--;
            array[start] = array[end];
            while(start < end && array[start] <= tmp) start++;
            array[end] = array[start];
        }
        int pivot = array[end] = tmp;
        return start;
    }

6.2 复杂度

时间复杂度:最好情况O(N*log(2)(N)),最坏情况:O(N^2)

空间复杂度:最好情况O(log(2)(N)),最坏情况:O(N)

稳定性:不稳定

7.归并排序

所谓归并排序是指将两个或两个以上有序的数列(或有序表),合并成一个仍然有序的数列(或有序表)

如以下两个序列

排序后

 对于归并排序,可以给array1定义一个start1,给array2定义start2,都从序列的首位开始,遍历序列,每次遇到更大的数就给新的array,直至两个待归并的排序列遍历结束。

 7.1 代码实现

    public static int[] mergeArray(int[] array1,int[] array2){
        int start1 = 0;
        int start2 = 0;
        int x = 0;
        int[] array = new int[array1.length + array2.length];
        while(start1 <= array1.length - 1 && start2 <= array2.length - 1){
            if(array1[start1] < array2[start2]){
                array[x] = array1[start1];
                x++;
                start1++;
            }else {
                array[x] = array2[start2];
                x++;
                start2++;
            }
        }
        while(start1 <= array1.length - 1 ){
            array[x] = array2[start1];
            x++;
            start1++;
        }
        while(start2 <= array2.length - 1){
            array[x] = array2[start2];
            x++;
            start2++;
        }
        return array;
    }

7.2 归并排序的递归实现

public static void mergeSort(int[] array){
        mergeSortInternal(array,0,array.length-1);

    }
    private static void mergeSortInternal(int[] array,int low,int high){

        if(low >= high){
            return;
        }

        //int mid = (low+high) >>> 1;//也可以写作
        int mid = low + ((high - low)>>> 1);

        mergeSortInternal(array,low,mid);//左
        mergeSortInternal(array,mid+1,high);//右

        merge(array,low,mid,high);

    }

    private static void merge(int[] array,int low,int mid,int high){
        int[] tmp = new int[high-low+1];
        int x = 0;

        int start1 = low;
        int end1 = mid;
        int start2 = mid + 1;
        int end2 = high;

        while(start1 <= end1 && start2 <= end2){
            if(array[start1] < array[start2]){
                tmp[x] = array[start1];
                x++;
                start1++;
            }else {
                tmp[x] = array[start2];
                x++;
                start2++;
            }
        }
        while(start1 <= end1){
            tmp[x] = array[start1];
            x++;
            start1++;
        }
        while(start2 <= end2){
            tmp[x] = array[start2];
            x++;
            start2++;
        }
        for (int i = 0; i < x; i++) {
            array[low+i] = tmp[i];

        }

    }

7.3 复杂度

时间复杂度:O(N*log(2)(N))

空间复杂度:O(N)

稳定性:不稳定

7.4 归并排序的非递归实现

 private static void merge(int[] array,int low,int mid,int high){
        int[] tmp = new int[high-low+1];
        int x = 0;

        int start1 = low;
        int end1 = mid;
        int start2 = mid + 1;
        int end2 = high;

        while(start1 <= end1 && start2 <= end2){
            if(array[start1] < array[start2]){
                tmp[x] = array[start1];
                x++;
                start1++;
            }else {
                tmp[x] = array[start2];
                x++;
                start2++;
            }
        }
        while(start1 <= end1){
            tmp[x] = array[start1];
            x++;
            start1++;
        }
        while(start2 <= end2){
            tmp[x] = array[start2];
            x++;
            start2++;
        }
        for (int i = 0; i < x; i++) {
            array[low+i] = tmp[i];

        }

    }


    public static void mergeSort(int[] array){
        int nums = 1;

        while(nums < array.length){
            for (int i = 0; i < array.length; i += 2*nums) {
                int left = i;
                int mid = left + nums - 1;
                if(mid >= array.length){
                    mid = array.length - 1;
                }
                int right = mid + nums;
                if(right >= array.length){
                    right = array.length - 1;
                }
                merge(array,left,mid,right);
            }
            nums *= 2;

        }
    }

8.非比较排序

基数排序

例如:有以下数据:12,245,78,119,656,38,500

假设有十个桶(可想象成队列),从0开始依次排序。(由于是十进制,所以是十个桶)

如下图:

根据每个数的个位数,遍历数组12,245,78,119,656,38,500,依次进入个位数对应的“桶”

如下图

再从0桶开始依次出桶(根据队列的方式出桶) 如下图

根据每个数的十位数,遍历数组500,12,245,656,78,38,119,依次进入十位数对应的“桶”

如下图

再从0桶开始依次出桶(根据队列的方式出桶)如下图

 根据每个数的百位数,遍历数组500,12,119,38,245,656,78依次进入百位数对应的“桶”

如下图

再从0桶开始依次出桶(根据队列的方式出桶)如下图

 最后得到12,38,78,119,245,500,656这个有序的序列。

基数排序仅作了解,面试基本不会考到。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
目 录 摘 要 1 前 言 2 正 文 3 1. 采用类C语言定义相关的数据类型 4 2. 各模块的伪码算法 5 3. 函数的调用关系图 11 4. 调试分析 11 5. 测试结果 13 6. 源程序(带注释) 16 总 结 28 参考文献 29 致 谢 30 附件Ⅰ 部分源程序代码 31 摘要 排序是计算机程序设计中的一种重要操作。各种部排序算法的时间复杂度分析结果只给出了算法执行时间的阶,或大概执行时间。 关键字:排序,性能分析。 前 言 排序是计算机程序设计中的一种重要操作。它的功能是将一个数据元素的任意序列,重新排列成一个按关键字有序的序列。内部排序的方法很多,但是就其全面性能而言,很难 提出一种被认为是最好的方法,每一种方法都有各自的优缺点,适合在不同的环境下使用。如果按排序过程中依据的不同原则对内部排序方法进行分类,则大致可分为插入排序,交换排序,选择排序,归并排序和记数排序等五类。 这几种排序算法是在顺序存储结构上实现的,因此在排序过程中需要进行大量记录的移动。当记录很大时,时间耗费很大,此时可采用静态链表作存储结构。但是有的排序方法,无法实现表排序。在这种情况下可以进行地址排序,即另设一个地址向量指示相应记录。 正文 1. 采用类c语言定义相关的数据类型 Int整型, char字符型, 2. 各模块的伪码算法 (1) 插入排序伪码算法: Void InsertSort(Splist&L){ For(i=2;i<=L.length;++i) If(LT(L.r[i].key,L.r[i-1].key))  //“《”,须将L.r[i]插入有序子表 { L.r[0]= L.r[i];         //复制为哨兵 L.r[i]= L.r[i-1]; For(j)i-2;LT(L.r[0].key,L.r[j].key);--j) L.r[j+1]= L.r[j];         //记录后移 L.r[j+1]= L.r[0]; //插入到正确位置 } }//InsertSort (2) 希尔排序 Void shllInsert(Splist & L,int dk){ For(i=dk+1;i<=L.length;++i) If(LT(L.r[i].key,L.r[i-dk].key)) { L.r[0]= L.r[i];           //暂存 For(j=i-dk;j>0&<(L.r0].key,L.r[j].key);j-=dk) L.r[j+dk]=L.r[j];       //记录后移 L.r[j+dk]=L.r[0];} //插入 }//shellsort Void shllsort (Splist & L,int data[],int t){ For(k=0;k<t;++k) shllInsert(L,data[k]); }//shellsort

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

噜噜噜噜鲁先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值