Android开发

十大经典排序算法(java实现)

2020-06-16  本文已影响0人  梦星夜雨

前言

本文我们将以java代码实现十大经典排序算法,包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序、基数排序。

排序算法相关的时间复杂度和空间复杂度

1、冒泡排序(Bubble Sort)

算法描述

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  3. 针对所有的元素重复以上的步骤,除了最后一个;
  4. 重复步骤1~3,直到排序完成。

代码实现

public void sort(int[] nums) {
        for(int i = 0;i<nums.length -1;i++){
            for (int j = 0; j < nums.length - 1-i; j++) {
                int c;
                if (nums[j] > nums[j + 1]) {
                    c = nums[j];
                    nums[j] = nums[j + 1];
                    nums[j + 1] = c;
                }
            }
        }
    }

这里有个问题,冒泡排序的时间复杂度最好是n,但上述代码不管如何时间复杂度都是n²,所以有了以下改进算法。

public void bubbleSort(int arr[]) {
        boolean didSwap;
        for(int i = 0, len = arr.length; i < len - 1; i++) {
            didSwap = false;
            for(int j = 0; j < len - i - 1; j++) {
                if(arr[j + 1] < arr[j]) {
                    int temp = arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                    didSwap = true;
                }
            }
            if(didSwap == false)
                return;
        }
    }

2、选择排序(Selection Sort)

算法描述
选择排序一种简单直观的排序算法。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
代码实现

public int[] sort(int[] nums) {
        int len = nums.length;
        int temp;
        for(int i = 0;i<len;i++){
            for(int j =i;j<len;j++){
                if(nums[i]>nums[j]){
                    temp = nums[i];
                    nums[i] = nums[j];
                    nums[j] = temp;
                }
            }
        }
        return nums;
    }

3、插入排序(Insertion Sort)

算法描述

  1. 从第一个元素开始,该元素可以认为已经被排序;
  2. 取下一元素,在已经排序的元素序列中从后向前扫描;
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  5. 将新元素插入到该位置后;
  6. 重复步骤2~5。

代码实现

public void sort(int[] nums) {
        int len = nums.length;
        int preIndex,current;
        for(int i = 0;i<len;i++){
            preIndex = i - 1;
            current = nums[i];
            while(preIndex >= 0 && nums[preIndex] > current){
                nums[preIndex + 1] = nums[preIndex];
                preIndex--;
            }
            nums[preIndex + 1] = current;
        }
    }

4、希尔排序(Shell Sort)
算法排序
希尔排序(Shell's Sort)是插入排序)的一种又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。

  1. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  2. 按增量序列个数k,对序列进行k 趟排序;
  3. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
    代码实现
public void sort(int[] nums) {
        int len = nums.length;
        for (int gap = (int) Math.floor(len / 2); gap > 0; gap = (int) Math.floor(gap / 2)) {
            // 注意:这里和动图演示的不一样,动图是分组执行,实际操作是多个分组交替执行
            for (int i = gap; i < len; i++) {
                int j = i; 
                int current = nums[i];
                while (j - gap >= 0 && current < nums[j - gap]) {
                    nums[j] = nums[j - gap];
                     j = j - gap;
                }
                nums[j] = current;
            }
        }
        
    }

5、归并排序(Merge Sort)

算法描述
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并排序是一种稳定的排序方法。实际上就是用的递归的思想。

  1. 把长度为n的输入序列分成两个长度为n/2的子序列;
  2. 对这两个子序列分别采用归并排序;
  3. 将两个排序好的子序列合并成一个最终的排序序列。

代码实现

public int[] sort(int[] nums) {
        int len = nums.length;
        if(len<2){
            return nums;
        }
        int middle = (int) Math.floor(len/2);
        int[] left = new int[middle],right = new int[len-middle];
        System.arraycopy(nums, 0, left, 0, middle);
        System.arraycopy(nums, middle, right, 0, len-middle);
        return merge(sort(left),sort(right));
    }
    
    private int[] merge(int[] left, int[] right) {
        // TODO Auto-generated method stub
          int[] result = new int[left.length+right.length];
          int i = 0, j = 0, k = 0;
            while ((left.length - j) > 0 && (right.length - k) > 0) {
                if (left[j] <= right[k]) {
                    result[i] = left[j];
                    j++;
                } else {
                    result[i] = right[k];
                    k++;
                }
                i++;
            }
            while (j < left.length){
                System.arraycopy(left, j, result, i, left.length-j);
                j++;
                i++;
            }
                
            while (k < right.length){
                System.arraycopy(right, k, result, i, right.length-k);
                k++;
                i++;
            }
            return result;
    }

6、快速排序(Quick Sort)

算法描述
快速排序(Quicksort)是对冒泡排序的一种改进。

  1. 首先选择一个分界值,通过该分界值将数组分成左右两部分。
  2. 重新对数组进行排序,比分界值小的集中到数组左边,比分界值大的位于数组右边。
  3. 然后左右两边数组再次进行快速排序。
  4. 最后重复上述步骤,实现递归。

代码实现

public void sort(int[] arr,int low, int high) {
        int i, j ,temp,snap;
        if(low > high){
            return;
        }
        i = low;
        j = high;
        // 基准位置
        temp = arr[low];
        while (i<j) {
            while (temp<=arr[j] && i<j) {
                j--;
            }
            while (temp>=arr[i] && i<j) {
                i++;
            }
            if (i<j) {
                // 交换满足条件的 i j位置的值
                snap = arr[i];
                arr[i] = arr[j];
                arr[j] = snap;
            }
        }
        // 交换基准位置的值
        arr[low] = arr[i];
        arr[i] = temp;
        
        // 递归调用, 左右
        sort(arr,low,j-1);
        sort(arr,j+1,high);
    }

7、堆排序(Heap Sort)

算法描述
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

  1. 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆
  2. 将堆顶元素R[1]与最后一个元素R[n]交换。
  3. 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

代码实现

public static void sort(int[] array) {
        if (array == null || array.length <= 1) {
            return;
        }
        buildMaxHeap(array);

        int temp;
        for(int arg:array){
            System.out.print(arg+" ");
        }
        for (int i = array.length - 1; i >= 1; i--) {

            temp = array[0];
            array[0] = array[i];
            array[i] = temp;
            
            maxHeap(array, i, 0);
            System.out.println(" ");
            System.out.println("-------"+i+"-------");
            for(int arg:array){
                System.out.print(arg+" ");
            }
        }
    }

    private static void buildMaxHeap(int[] array) {
        if (array == null || array.length <= 1) {
            return;
        }

        int half = array.length / 2;
        for (int i = half; i >= 0; i--) {
            maxHeap(array, array.length, i);
        }
    }

    private static void maxHeap(int[] array, int heapSize, int index) {
        int left = index * 2 + 1;
        int right = index * 2 + 2;
        int largest = index;
        if (left < heapSize && array[left] > array[index]) {
            largest = left;
        }

        if (right < heapSize && array[right] > array[largest]) {
            largest = right;
        }

        if (index != largest) {

            int temp;
            temp = array[index];
            array[index] = array[largest];
            array[largest] = temp;
            
            maxHeap(array, heapSize, largest);
        }
    }

8、计数排序(Counting Sort)

算法描述
计数排序是一个非基于比较的排序算法,牺牲空间换取时间。

  1. 得到数组的最大和最小元素。
  2. 统计出每个数据为i的值出现的次数,将次数存入数组C中。
  3. 以数组C为模板重新填充目标数组。

代码实现

public void sort(int[] nums) {
        int min = nums[0],max = nums[0];
        for(int num: nums){
            max = Math.max(max, num);
            min = Math.min(min, num);
        }
        int[] counts = new int[max - min+1];
        for(int num: nums){
            counts[num - min]++;
        }
        int i = 0,j = 0;
        for(int count:counts){
            while (count > 0) {
                nums[i] = j + min;
                count --;
                i ++;
            }
            j++;
        }
    }

9、桶排序(Bucket Sort)

算法描述
将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法)。

  1. 设置一个定量的链表当作空桶。
  2. 遍历输入数据,并且把数据一个一个放到对应的桶里去。
  3. 对每个不是空的桶进行排序。
  4. 从不是空的桶里把排好序的数据拼接起来。

代码实现

public void sort(int[] nums) {
        int min = nums[0],max = nums[0];
        for(int num: nums){
            max = Math.max(max, num);
            min = Math.min(min, num);
        }
        int bucketBottom = (int) Math.floor((float)min/10);
        int bucketTop = (int) Math.ceil((float)max/10);
        ArrayList<ArrayList<Integer>> bucketsList = new ArrayList<ArrayList<Integer>>();
        for(int i = 0;i<(bucketTop-bucketBottom);i++){
            bucketsList.add(new ArrayList<Integer>());
        }
        for(int num: nums){
            int index = getBucketIndex(num);
            insertBucket(bucketsList.get(index),num);
        }
        int index = 0;
        for (ArrayList<Integer> list : bucketsList) {
            for(int data: list){
                nums[index++] = data;
            }
        }
    }
    
    // 将数据放入具体的桶内
    private void insertBucket(ArrayList<Integer> arrayList, int num) {
        boolean insertFlag = true;
        ListIterator<Integer> it = arrayList.listIterator();
        while (it.hasNext()) {
            if (num <= it.next()) {
                it.previous(); // 把迭代器的位置偏移回上一个位置
                it.add(num); // 把数据插入到迭代器的当前位置
                insertFlag = false;
                break;
            }
        }
        // 否则把数据插入到链表末端
        if (insertFlag) {
            arrayList.add(num); 
        }
    }
    // 判断放入哪个桶内的方法
    private int getBucketIndex(int data){
        return (int) Math.floor(data/10);
    }

10、基数排序(Radix Sort)

算法描述
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。

  1. 取得数组中的最大数,并取得位数。
  2. 使用链表对按照当前位数大小存储数据,然后重新写入数组。
  3. 根据位数的不同重复步骤2。

代码实现

public void sort(int[] nums) {
         int max = 0;
         int exp = 1;
         for (int num:nums){
             max = Math.max(max,num);
         }
         while(max/Math.pow(10,exp) > 1){
             exp++;
         }
        //存储待排元素
         ArrayList<ArrayList<Integer>> radixList = new ArrayList<ArrayList<Integer>>();
         for(int j = 0;j<10;j++){
             ArrayList<Integer> list = new ArrayList<Integer>();
             radixList.add(list);
         }
         
         for(int i = 0;i<=exp;i++){
             for(int num:nums){
                 int positionValue = getPositionValue(num, i);
                 radixList.get(positionValue).add(num);
             }
             int index = 0;
             for(ArrayList<Integer> list:radixList){
                 for(int value: list){
                     nums[index] = value;
                     index++;
                 }
                 list.clear();
             }
         }
    }
    // 获取当前位数对应的值
    private int getPositionValue(int num,int exp){
        return (int) (num % Math.pow(10, exp)/Math.pow(10, exp-1));
    }

源码地址

上一篇下一篇

猜你喜欢

热点阅读