美文网首页
排序算法的小结

排序算法的小结

作者: s1991721 | 来源:发表于2018-04-12 21:20 被阅读0次

    这里将列举知名度较高的十种算法,附上自己的理解和代码。

    • 冒泡排序
    • 选择排序
    • 插入排序
    • 希尔排序
    • 归并排序
    • 快速排序
    • 堆排序
    • 计数排序
    • 桶排序
    • 基数排序

    启发和图片来自 十大经典排序算法(动图演示)

    先来理解两个概念:时间复杂度与空间复杂度
    他俩是相互对立的,时间复杂度的减小必然伴随空间复杂度的增加,所以算法的选择也是时间与空间的取舍。


    冒泡排序

    理解:从左至右顺序判断当前气泡是否大于后一个气泡,将大气泡右移。
    内循环,将较大的气泡向后移,保证了内循环的当前值比前面的所有值都大。(冒泡的过程,一步一步的移动)
    外循环第一次,扫描出数组中的最大值并逐个的交换到数组最末端。
    外循环第二次,扫描出数组中的次最大值并逐个的交换到数组次末端。

        public String sort(int[] numbers) {
    
            long stamp = System.currentTimeMillis();
    
            for (int i = 0; i < numbers.length; i++) {
                for (int j = 0; j < numbers.length - 1 - i; j++) {
                    if (numbers[j] > numbers[j + 1]) {
                        Utils.swap(numbers, j, j + 1);
                    }
                }
            }
    
            return Utils.intsToString(numbers) + "   cost:" + (System.currentTimeMillis() - stamp);
    
        }
    

    时间空间复杂度:O(n2)、O(0) (取决于Utils.swap(numbers, j, j + 1)是哪种交换方式)

    选择排序


    理解:在未排序的部分,每次选择一个最小的交换到未排序的头项。

        public String sort(int[] numbers) {
    
            long stamp = System.currentTimeMillis();
    
            for (int i = 0; i < numbers.length - 1; i++) {
                int position=i;
                for (int j = i + 1; j < numbers.length; j++) {
                    if (numbers[j]<numbers[position]){
                        position=j;
                    }
                }
                Utils.swap(numbers,i,position);
            }
            return Utils.intsToString(numbers) + "   cost:" + (System.currentTimeMillis() - stamp);
    
        }
    

    时间空间复杂度:O(n2)、O(0)

    *蹩脚的选择排序

    没写这篇文章之前我一直用的是这种排序:

            for (int i = 0; i < numbers.length - 1; i++) {
                for (int j = i + 1; j < numbers.length; j++) {
                    if (numbers[j] < numbers[i]) {
                        Utils.swap(numbers, i, j);//正规的是记录最小位置
                    }
                }
                //在内循环完成后再交换,这样可以减少交换次数
            }
    

    很像,但是这种排序把选择的功能弱化了,增加了交换的次数。

    插入排序


    理解:选取一项,如果小于前一项,将前一项后移,操作往复,直到前一项小于它,将选取的项放在当前位置(当前位置项已被后移)

        public String sort(int[] numbers) {
    
            long stamp = System.currentTimeMillis();
    
            for (int i = 1; i < numbers.length; i++) {
    
                int prePosition = i - 1;
                int current = numbers[i];
                while (prePosition >= 0 && current < numbers[prePosition]) {
                    numbers[prePosition + 1] = numbers[prePosition];
                    prePosition--;
                }
                numbers[prePosition + 1] = current;
            }
    
            return Utils.intsToString(numbers) + "   cost:" + (System.currentTimeMillis() - stamp);
    
        }
    

    时间空间复杂度:O(n2)、O(1)

    希尔排序

    理解:
    设定一个间隔gap,对按gap组成的新数组进行排序;
    对gap设置一个递减规则,直到1;
    gap每次变化后执行第一步;

        public String sort(int[] data) {
    
            long stamp = System.currentTimeMillis();
    
            for (int gap = data.length / 2; gap > 0; gap /= 2) {
                for (int i = gap; i < data.length; i++) {
                    int temp = data[i];
                    int j;
                    for (j = i - gap; j >= 0; j -= gap) {
                        if (temp < data[j]) {
                            data[j + gap] = data[j];//交换的话会有很多次
                        } else {
                            break;//之前的已经排好序了,继续循环也没意义
                        }
                    }
                    data[j + gap] = temp;
                }
            }
    
            return Utils.intsToString(data) + "   cost:" + (System.currentTimeMillis() - stamp);
    
        }
    

    时间空间复杂度:O(n2)、O(1) 三层for并不是3次方,因为每层for不全为n

    归并排序


    理解:将数组中间切开直到不可切,然后按每次翻倍的规律(1-1、2-2、4-4、8-8),按大小合并。最后一次合并为排好序的数组。

        private int[] mergeSort(int[] numbers) {
            int len = numbers.length;
            if (len < 2) {
                return numbers;
            }
            int middle = len / 2;
            int[] left = new int[middle];
            int[] right = new int[len - middle];
    
            System.arraycopy(numbers, 0, left, 0, middle);
            System.arraycopy(numbers, middle, right, 0, numbers.length - middle);
    
            return merge(mergeSort(left), mergeSort(right));
        }
    
        private int[] merge(int[] left, int[] right) {
            int[] result = new int[left.length + right.length];
    
            int leftP = 0;
            int rightP = 0;
            int resultP = 0;
    
            while (leftP < left.length && rightP < right.length) {
                if (left[leftP] > right[rightP]) {
                    result[resultP++] = right[rightP++];
                } else if (left[leftP] < right[rightP]) {
                    result[resultP++] = left[leftP++];
                } else {
                    result[resultP++] = left[leftP++];
                    result[resultP++] = right[rightP++];
                }
            }
    
    
            while (leftP < left.length) {
                result[resultP++] = left[leftP++];
            }
    
            while (rightP < right.length) {
                result[resultP++] = right[rightP++];
            }
    
            return result;
        }
    

    时间空间复杂度:O(nlog2n)、O(n)

    快速排序

    理解:选取一个基准值,将该基准值所在数组中的排序位置找到。循环选取基准值,直到数组中所有值都充当过基准值,则数组排序完成。

        private void quickSort(int[] numbers, int left, int right) {
            int partitionIndex;
    
            if (left < right) {
                partitionIndex = partition(numbers, left, right);
                quickSort(numbers, left, partitionIndex - 1);
                quickSort(numbers, partitionIndex + 1, right);
            }
        }
    
        private int partition(int[] numbers, int left, int right) {
            int pivot = left;
            int index = pivot + 1;
            for (int i = index; i <= right; i++) {
                if (numbers[i] < numbers[pivot]) {
                    Utils.swap(numbers, i, index++);
                }
            }
    
            Utils.swap(numbers, pivot, index - 1);
            return index - 1;
        }
    

    时间空间复杂度:O(n2)、O(nlog2n)(nlog2n为交换时产生的临时变量,可通过加减交换减少空间复杂度)

    堆排序

    先前知识

    一颗完全二叉树,其非叶子节点必须大于(或小于)其叶子节点

    大顶堆

    非叶子节点大于其叶子节点的称为大顶堆

    小顶堆

    非叶子节点小于于其叶子节点的称为小顶堆

    理解:利用大顶堆的特性,上一层的节点必定大于下一层节点。
    取根节点(最大值)与最后一个叶子节点交换,将剩下的节点按大顶堆的性质重新排序。重复此操作,可得到由小到大的排好序的数组。

        public String sort(int[] numbers) {
    
            long stamp = System.currentTimeMillis();
    
            len=numbers.length;
            for (int i = numbers.length / 2; i >= 0; i--) {//生成大顶堆
                adjustHeap(numbers, i);
            }
    
            for (int i = numbers.length-1; i >=0 ; i--) {
                Utils.swap(numbers,0,i);
                len--;
                adjustHeap(numbers,0);
            }
    
    
            return Utils.intsToString(numbers) + "   cost:" + (System.currentTimeMillis() - stamp);
    
        }
    
        int len;
    
        private void adjustHeap(int[] numbers, int p) {
    
            int left = p * 2 + 1;
            int right = p * 2 + 2;
            int bigP = p;
    
            if (left < len && numbers[left] > numbers[bigP]) {
                bigP = left;
            }
    
            if (right < len && numbers[right] > numbers[bigP]) {
                bigP = right;
            }
    
            if (p != bigP) {
                Utils.swap(numbers, p, bigP);
                adjustHeap(numbers, bigP);
            }
    
        }
    

    时间空间复杂度:O(nlog2n)、O(0)由交换方法决定

    计数排序

    理解:计数排序适用于数据范围密集的集合。
    建立一个临时数组(数据范围)用于存放数据,临时数组的下标代表数据值,临时数组的值代表数据出现的次数。数据按规则全部放到临时数组后,按临时数组下标全部取出数据,即为排好序的数组。

        public String sort(int[] numbers) {
    
            long stamp = System.currentTimeMillis();
    
            int[] result = new int[numbers.length];
            int max = numbers[0], min = numbers[0];
            for (int number : numbers) {
                if (number > max) {
                    max = number;
                }
                if (number < min) {
                    min = number;
                }
            }
    
            int[] temp = new int[max - min + 1];
    
            for (int i = 0; i < numbers.length; i++) {
    
                temp[numbers[i] - min]++;
    
            }
    
            int p=0;
            for (int i = 0; i <temp.length ; i++) {
                while (temp[i]>0){
                    result[p++]=i+min;
                    temp[i]--;
                }
            }
    
    
            return Utils.intsToString(result) + "   cost:" + (System.currentTimeMillis() - stamp);
    
        }
    

    时间空间复杂度:O(n+k)、O(n+k) k表示范围数组的大小

    桶排序

    理解:计数排序是桶排序的一种特例,计数排序每个桶内放的数据都是相同的,排序时只需按桶的顺序倒出即可得到排好序的数组;而桶排序每个桶内的数据不相同,需要对桶内数据排序,然后按桶的顺序倒出才能得到排好序的数组;

        public String sort(int[] numbers) {
    
            long stamp = System.currentTimeMillis();
    
            int max = numbers[0];
            int min = numbers[0];
    
            for (int number : numbers) {
                if (number > max) {
                    max = number;
                }
    
                if (number < min) {
                    min = number;
                }
            }
    
            int bucketNum = (max - min) / numbers.length + 1;
            ArrayList<ArrayList<Integer>> buckets = new ArrayList<ArrayList<Integer>>();
    
            for (int i = 0; i < bucketNum; i++) {
                buckets.add(new ArrayList<Integer>());
            }
    
            for (int i = 0; i < numbers.length; i++) {
                int p = (numbers[i] - min) / numbers.length;
                buckets.get(p).add(numbers[i]);
            }
    
            for (int i = 0; i < buckets.size(); i++) {
                Collections.sort(buckets.get(i));
            }
    
            return buckets.toString() + "   cost:" + (System.currentTimeMillis() - stamp);
    
        }
    

    时间空间复杂度:O(n)、O(n)

    基数排序

    理解:所有数字都是由0-9组成,大小的比较也是位数与每位上的数字的比较。
    先按个位数字大小将数组排序、然后按十位数字大小将数组排序,位数一直增加,直到数组内最大项的位数。

        public String sort(int[] numbers) {
    
            long stamp = System.currentTimeMillis();
    
            ArrayList<ArrayList<Integer>> temp = new ArrayList<ArrayList<Integer>>();
            int max = numbers[0];
            int radix = 10;
    
            for (int i = 0; i < 10; i++) {
                temp.add(i, new ArrayList<Integer>());
            }
    
    
            do {
                for (int i = 0; i < numbers.length; i++) {
                    if (numbers[i] > max) {
                        max = numbers[i];
                    }
                    temp.get(numbers[i] % radix / (radix / 10)).add(numbers[i]);
                }
    
                int p = 0;
    
                for (int i = 0; i < temp.size(); i++) {
                    for (int j = 0; j < temp.get(i).size(); j++) {
                        numbers[p++] = temp.get(i).get(j);
                    }
                    temp.get(i).clear();
                }
                radix *= 10;
            } while (radix < max * 10);
    
            return Utils.intsToString(numbers) + "   cost:" + (System.currentTimeMillis() - stamp);
    
        }
    

    时间空间复杂度:O(nk)、O(n+k) 有些说法是空间复杂度为O(n)那是对n取了极限


    此外还有一家公司的上机题:为String中char出现的次数排序,相同次数的按字典排序。
    此题就不写了,此题和上述十种排序算法的代码在GitHub

    相关文章

      网友评论

          本文标题:排序算法的小结

          本文链接:https://www.haomeiwen.com/subject/gqnqhftx.html