• 常见的排序算法之Java代码解释


    一 简要介绍

    一般排序均值的是将一个已经无序的序列数据重新排列成有序的
    常见的排序分为:
    1 插入类排序
    主要就是对于一个已经有序的序列中,插入一个新的记录。它包括:直接插入排序,折半插入排序和希尔排序
    2 交换类排序
    这类排序的核心就是每次比较都要“交换”,在每一趟排序都会两两发生一系列的“交换”排序,但是每一趟排序都会让一个记录排序到它的最终位置上。它包括:起泡排序,快速排序
    3 选择类排序
    每一趟排序都从一系列数据中选择一个最大或最小的记录,将它放置到第一个或最后一个为位置交换,只有在选择后才交换,比起交换类排序,减少了交换记录的时间。属于它的排序:简单选择排序,堆排序
    4 归并类排序
    将两个或两个以上的有序序列合并成一个新的序列
    5 基数排序
    主要基于多个关键字排序的。
    下面针对上面所述的算法,讲解一些常用的java代码写的算法
    二 插入类排序之直接插入排序
    直接插入排序,一般对于已经有序的队列排序效果好。
    基本思想:每趟将一个待排序的关键字按照大小插入到已经排序好的位置上。

    算法思路,从后往前先找到要插入的位置,如果小于则就交换,将元素向后移动,将要插入数据插入该位置即可。时间复杂度为O(n2),空间复杂度为O(1)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package sort.algorithm;
    public class DirectInsertSort {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int data[] = { 261039801162720 };
            int temp, j;
            for (int i = 1; i < data.length; i++) {
                temp = data[i];
                j = i - 1;
                // 每次比较都是对于已经有序的
                while (j >= 0 && data[j] > temp) {
                    data[j + 1] = data[j];
                    j--;
                }
                data[j + 1] = temp;
            }
            // 输出排序好的数据
            for (int k = 0; k < data.length; k++) {
                System.out.print(data[k] + "  ");
            }
        }
    }
    三 插入类排序之折半插入排序(二分法排序)
    条件:在一个已经有序的队列中,插入一个新的元素
    折半插入排序记录的比较次数与初始序列无关
    思想:折半插入就是首先将队列中取最小位置low和最大位置high,然后算出中间位置mid
    将中间位置mid与待插入的数据data进行比较,
    如果mid大于data,则就表示插入的数据在mid的左边,high=mid-1;

    如果mid小于data,则就表示插入的数据在mid的右边,low=mid+1

    最后整体进行右移操作。

    时间复杂度O(n2),空间复杂度O(1)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package sort.algorithm;
    //折半插入排序
    public class HalfInsertSort {
        public static void main(String[] args) {
            int data[] = { 261039801162720 };
            // 存放临时要插入的元素数据
            int temp;
            int low, mid, high;
            for (int i = 1; i < data.length; i++) {
                temp = data[i];
                // 在待插入排序的序号之前进行折半插入
                low = 0;
                high = i - 1;
                while (low <= high) {
                    mid = (low + high) / 2;
                    if (temp < data[mid])
                        high = mid - 1;
                    else
                        // low=high的时候也就是找到了要插入的位置,
                        // 此时进入循环中,将low加1,则就是要插入的位置了
                        low = mid + 1;
                }
                // 找到了要插入的位置,从该位置一直到插入数据的位置之间数据向后移动
                for (int j = i; j >= low + 1; j--)
                    data[j] = data[j - 1];
                // low已经代表了要插入的位置了
                data[low] = temp;
            }
            for (int k = 0; k < data.length; k++) {
                System.out.print(data[k] + "  ");
            }
        }
    }
    四 插入类排序之希尔排序
    希尔排序,也叫缩小增量排序,目的就是尽可能的减少交换次数,每一个组内最后都是有序的。
    将待续按照某一种规则分为几个子序列,不断缩小规则,最后用一个直接插入排序合成
    空间复杂度为O(1),时间复杂度为O(nlog2n)

    算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package sort.algorithm;
    public class ShellSort {
        public static void main(String[] args) {
            int a[] = { 154637834124556100 };
            double d1 = a.length;
            int temp = 0;
            while (true)
            {
                //利用这个在将组内倍数减小
                //这里依次为5,3,2,1
                d1 = Math.ceil(d1 / 2);
                //d为增量每个分组之间索引的增量
                int d = (int) d1;
                //每个分组内部排序
                for (int x = 0; x < d; x++)
                {
                    //组内利用直接插入排序
                    for (int i = x + d; i < a.length; i += d) {
                        int j = i - d;
                        temp = a[i];
                        for (; j >= 0 && temp < a[j]; j -= d) {
                            a[j + d] = a[j];
                        }
                        a[j + d] = temp;
                    }
                }
                                                                                                                                   
                if (d == 1)
                    break;
            }
            for (int i = 0; i < a.length; i++)
                System.out.print(a[i]+"  ");
        }
    }

    五 交换类排序之冒泡排序

    交换类排序核心就是每次比较都要进行交换
    冒泡排序:是一种交换排序
    每一趟比较相邻的元素,较若大小不同则就会发生交换,每一趟排序都能将一个元素放到它最终的位置!每一趟就进行比较。

    时间复杂度O(n2),空间复杂度O(1)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package sort.algorithm;
    //冒泡排序:是一种交换排序
    public class BubbleSort {
        // 按照递增顺序排序
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int data[] = { 261039801162720131003716 };
            int temp = 0;
            // 排序的比较趟数,每一趟都会将剩余最大数放在最后面
            for (int i = 0; i < data.length - 1; i++) {
                // 每一趟从开始进行比较,将该元素与其余的元素进行比较
                for (int j = 0; j < data.length - 1; j++) {
                    if (data[j] > data[j + 1]) {
                        temp = data[j];
                        data[j] = data[j + 1];
                        data[j + 1] = temp;
                    }
                }
            }
            for (int i = 0; i < data.length; i++)
                System.out.print(data[i] + " ");
        }
    }
    六 交换类排序之快速排序
    快速排序算法,初始的时候选择一个轴线,一般来说选择第一个元素,每一趟排序交换后,最后出现的就是该轴左边比它小,右边比他大!交替扫描,先从右边开始扫描,如果遇到比它小的就停止,将该元素与轴线交换,马上换成从左开始扫描,如果遇到比它大的就停止,将该元素与轴线数据交换,重复这样的!一般就是递归做的
    时间复杂度O(nlog2n),平均时间是最好的

    空间复杂度O(long2n),快速排序需要递归用到了栈

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    package sort.algorithm;
    //快速排序算法:是一种交换排序
    public class QuikSort {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int data[] = { 261039801162720105344419 };
            QuikSort sort = new QuikSort();
            sort.sortArray(data, 0, data.length - 1);
            for (int i = 0; i < data.length; i++)
                System.out.print(data[i] + " ");
        }
        // 这里不用返回值,直接对传入的数组进行操作
        public void sortArray(int data[], int first, int end) {
            int temp;
            int i = first, j = end;
            if (first < end) {
                temp = data[i];
                // 当i=j的时候,则说明扫描完成了
                while (i < j) {
                    // 从右边向左边扫描找到一个小于temp的元素
                    while (j > i && data[j] > temp)
                        j--;
                    if (i < j) {
                        // 将该元素赋值给temp
                        data[i] = data[j];
                        // 赋值后就应该将i+1指向下一个序号
                        i++;
                    }
                    // 然后从左边向右边开始扫描,找到一个大于temp的元素
                    while (i < j && temp > data[i])
                        i++;
                    if (i < j) {
                        // 将该元素赋值给temp
                        data[j] = data[i];
                        // 赋值后就应该将j-1指向前一个序号
                        j--;
                    }
                }
                // 将轴数据放在i位置中
                data[i] = temp;
                sortArray(data, first, i - 1);
                sortArray(data, i + 1, end);
            }
        }
    }
    七 选择类排序之简单选择排序

    简单选择排序,每一趟从数据中选择一个最小值放到最前面,但是不需要交换位置,只记录该交换的位置,只有找到后才做一次交换!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package sort.algorithm;
    //简单选择排序:是一种选择排序
    public class SelectSort {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int data[] = { 261039801162720113110089 };
            int temp, k;
            // 开始时间
            long start = System.nanoTime();
            // 选择的每一趟数,每一趟都会将一个最小的放在最前面。
            for (int i = 0; i < data.length - 1; i++) {
                // 使用k来记录要交换的位置,且k在比较的过程不断变化
                k = i;
                // 由于每一趟都会将最小的放在最前面,所以索引+1
                for (int j = i; j < data.length; j++)
                    // 这里始终要与k比较
                    if (data[j] < data[k])
                        k = j;
                // k已经存放了交换的位置了
                temp = data[i];
                data[i] = data[k];
                data[k] = temp;
            }
            System.out.println(System.nanoTime() - start);
            // 输出排序好的数据
            for (int m = 0; m < data.length; m++) {
                System.out.print(data[m] + "  ");
            }
        }
    }
     

    八 选择类排序之堆排序

    堆排序就是建立大顶堆或者小顶堆,若建立大顶堆,每次对于建好的大顶堆将根元素与最后一个元素交换,无序的数目减少,有序的数目增加。

    对于求N个数据中的前n个最小的数据,首先就是建立一个n个的大顶堆,然后让其余的元素来进行与这堆顶元素比较,如果小于则与堆顶互换元素。

    这里采用数组存储节点,并且下标统一从0,length-1,所以对于这样处理的左孩子节点下标为

    2 * i+1,右孩子的节点下标为2 * i+2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    package sort.algorithm;
    public class HeapSort {
        public static int heap_size;
        // 左孩子编号
        public static int leftChild(int i) {
            return 2 * i+1;
        }
        // 右孩子编号
        public static int rightChild(int i) {
            return 2 * i + 2;
        }
        /**
         * 保持最大堆的性质
         * 堆中的数组元素
         * 对以该元素为根元素的堆进行调整,假设前提:左右子树都是最大堆
         * 由于左右孩子都是最大堆,首先比较根元素与左右孩子,找出最大值,假如大于根元素,则调整两个元素的值;
         * 由于左孩子(右孩子)的值与根元素交换,有可能打破左子树(右子树)的最大堆性质,因此继续调用,直至叶子元素。
         */
        public static void max_heapify(int[] a, int i) {
            int left = leftChild(i);
            int right = rightChild(i);
            int largest = 0;
                                                                                                                                                                                                                                                                                
            if (left < heap_size && a[i] < a[left]) {
                largest = left;
            else {
                largest = i;
            }
                                                                                                                                                                                                                                                                                
            if (right < heap_size && a[right] > a[largest]) {
                largest = right;
            }
            if (largest == i) {
                return;
            else {
                int temp = a[i];
                a[i] = a[largest];
                a[largest] = temp;
                max_heapify(a, largest);
            }
        }
        /**
         * 建立最大堆。在数据中,下标a.length/2+1一直到最后的元素a.length-1都是叶子元素
         * 因此从其前一个元素开始,一直到
         * 第一个元素,重复调用max_heapify函数,使其保持最大堆的性质
         */
        public static void build_max_heap(int[] a) {
            //从0~a.length/2中建立最大堆
            for (int i = a.length / 2; i >= 0; i--)
            {
                max_heapify(a, i);
            }
        }
        /**
         * 堆排序:首先建立最大堆,然后将堆顶元素(最大值)与最后一个值交换,同时使得 堆的长度减小1
         * 调用保持最大堆性质的算法调整,使得堆顶元素成为最大值,此时最后一个元素已被排除在外、
         */
        public static void heapSort(int[] a) {
            //构建最大堆
            build_max_heap(a);
            for (int i = a.length - 1; i >= 0; i--)
            {
                //将第一个元素和最后一个元素进行互换
                int temp = a[0];
                a[0] = a[i];
                a[i] = temp;
                                                                                                                                                                                                                                                                                    
                heap_size--;
                //调整堆为最大堆
                max_heapify(a, 0);
            }
        }
        public static void main(String[] args) {
            int a[] = {54132169101487};
            heap_size = a.length;//最大数
            heapSort(a);
            //输出结果
            for (int i = 0; i < a.length; i++) {
                System.out.print(a[i] + "  ");
            }
        }
    }

    九 二路归并排序

    归并排序主要分为分割和归并,每次分割后,对于每一个部分进行排序,然后进行归并,建立一个临时表存储归并后的结果,在将两路进行归并的时候,每一路都已经是有序的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    package sort.algorithm;
    import java.util.Arrays;
    //二路归并排序主要分为
    //分割和合并
    public class MergeSort {
        public static void main(String[] args) {
            int data[] = { 261039801162720 };
            mergeSort(data,0,data.length-1);
            //直接打印
            System.out.println(Arrays.toString(data));
        }
        //二路归并的分割处理
        public static void mergeSort(int[] array,int start,int end)
        {
            if(start<end)
            {
                //划分为两部分,每次两部分进行归并
                int mid=(start+end)/2;
                //两路归并
                //先递归处理每一个部分
                mergeSort(array,start,mid);
                mergeSort(array,mid+1,end);
                //然后将已经排序好的,两两归并排序再进行合并处理
                merge(array,start,mid,mid+1,end);
            }
        }
        //二路归并两个部分的时候进行排序
        public static void merge(int[] array,int start1,int end1,int start2,int end2)
        {
            int i=start1;//左路起始索引
            int j=start2;//右路起始索引
            int k=0;
            //归并的时候,会将两个数组数据按照大小输入到一个临时数组中
            //建立临时长度为两个子列表长度的数组
            int[] temp=new int[end2-start1+1];
            //循环遍历,按顺序找出两个表中的最小数据依次放入临时表中
            //注意此时左路和右路已经是有序的了。
            //当一路有一个小的,则会索引加1,继续喝另外一路的上次索引进行比较
            while(i<=end1&&j<=end2)
            {
                //这里确定归并的次序大小
                if(array[i]>array[j])
                    temp[k++]=array[j++];
                else
                    temp[k++]=array[i++];
            }
            //把剩下的元素放入临时数组中,只有一路的
            while(i<=end1)
                temp[k++]=array[i++];
            while(j<=end2)
                temp[k++]=array[j++];
            k=start1;
            for(int item:temp)
                array[k++]=item;
        }
    }

     

    十 各种排序总结:

    时间复杂度:巧记“快些以nlog2n归队”,快代表快速排序,些代表希尔排序,归代表归并排序,队代表堆排序
    算法稳定性:巧记“心情不稳定,快些选人吧”,快代表快速排序,些代表希尔排序,选代表选择排序,队代表堆排序
    从一大堆中选择最大的几个或者最小的几个数,直接用堆排序
    原始序列有序号,直接用插入排序
    经过一趟排序能使一个元素达到它最终位置的是交换类排序(冒泡,快速)和选择类排序(简单选择,堆)。
    排序方法元素比较次数与原始序列无关---简单选择排序,折半插入排序
    排序方法的排序趟数和原始队列无有关--交换类排序
     

    本文出自 “在云端的追梦” 博客,请务必保留此出处http://computerdragon.blog.51cto.com/6235984/1161859

  • 相关阅读:
    定位及CSS常见属性
    浮动及清浮动的方法
    C语言II博客作业04
    C语言II—作业03
    C语言II—作业02
    C语言II博客作业01
    期末总结
    C语言I博客作业09
    C语言I博客作业08
    C语言I博客作业07
  • 原文地址:https://www.cnblogs.com/reynold-lei/p/3468571.html
Copyright © 2020-2023  润新知