zoukankan      html  css  js  c++  java
  • 常见的排序算法之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

  • 相关阅读:
    cf1100 F. Ivan and Burgers
    cf 1033 D. Divisors
    LeetCode 17. 电话号码的字母组合
    LeetCode 491. 递增的子序列
    LeetCode 459.重复的子字符串
    LeetCode 504. 七进制数
    LeetCode 3.无重复字符的最长子串
    LeetCode 16.06. 最小差
    LeetCode 77. 组合
    LeetCode 611. 有效三角形个数
  • 原文地址:https://www.cnblogs.com/reynold-lei/p/3468571.html
Copyright © 2011-2022 走看看