zoukankan      html  css  js  c++  java
  • 十大排序算法_java实现

    排序算法

    • 复杂度

      • 时间复杂度:执行时间与数据规模的关系
      • 空间复杂度:存储空间与数据规模的关系
    • 稳定性

      • 稳定:一组数据中,a在b的前面并且a==b,排序后a依旧在b的前面
      • 不稳定:一组数据中,a在b的前面并且a==b,排序后a可能在b的前面
    • 原地算法

      • 原地算法不依赖额外的资源,或者依赖少数的额外资源,仅依靠输出来覆盖输入
      • 空间复杂度为O(1)的都可以认为是原地算法
    • 一般面试考察点:

      • 分析某种具体的排序算法 时间 |空间| 稳定性
      • 对比某几种排序算法
      • 代码实现某几种排序算法 O(nlogn) 重点: 快速排序,归并排序,堆排序

    一:冒泡排序[Bubble sort]

    • 比较相邻元素
    • 大的元素后置
    • 嵌套循环,每次查看相邻的元素,如果逆序,则交换
    • O(n^2)
    /**
         * 冒泡排序
         */
        
        private static void bubbleSort(int[] nums) {
            if (nums.length == 0){return;}
            for (int i = 0; i < nums.length; i++) {
                for (int j = 0; j < nums.length-i-1; j++) {
                    if(nums[j] > nums[j+1]){
                        //相邻元素比较,判断是不要交换
                        int temp = nums[j];
                        nums[j] = nums[j+1];
                        nums[j+1] = temp;
                    }
                }
            }
        }
    
    • 优化:如果序列已经完全有序,可以提前终止冒泡排序

      private static void bubbleSort(int[] nums) {
          if (nums.length == 0){return;}
          boolean sorted = true;  //1:假定有序
          for (int i = 0; i < nums.length; i++) {
              for (int j = 0; j < nums.length-i-1; j++) {
                  if(nums[j] > nums[j+1]){
                      //相邻元素比较,判断是不要交换
                      int temp = nums[j];
                      nums[j] = nums[j+1];
                      nums[j+1] = temp;
                      sorted = false; //2进来了说明无序
                  }
              }
              //3 有序就直接break--看情况走不走这一步
              if (sorted) {break;}
          }
      }
      

    二:选择排序[SelectionSort]

    • 找到最小的元素
    • 放到已经排序序列的末尾
    • O(n^2)
    private static void selectionSort(int[] nums) {
            //nums[i...n排序]
            for (int i = 0; i < nums.length; i++) {
                int minIndex = i;
                for (int j = i; j < nums.length; j++) {  //从i出发
                    if(nums[j] < nums[minIndex]){
                        //找到nums[i...n]中的最小值的索引
                        minIndex = j;
                    }
                }
                //找到最小的索引之后交换==》 nums[i...n]中的最小值要放到nums[i]的位置上
                int temp = nums[i];
                nums[i] = nums[minIndex];
                nums[minIndex]  = temp;
            }
        }
    

    三:插入排序(InsertionSrot)

    • 遍历数组
    • 插入到已经排序的序列中的正确位置 (需要比较:比前面的数小,就交换)
    • 从前到到逐步构建有序序列;对于未排序数组,在已排序序列中从后向前扫描,找到相应的位置,并插入
    • O(n^2)
    private static void insertionSort(int[] nums) {
            if(nums.length==0){return;}
        
            for (int i = 0; i < nums.length; i++) {
                //将nums[i]插入到合适的位置
                for (int j = i; j-1 >=0 ; j--) {   //j从i开始,循环比较它前面的一个元素
                     if(nums[j] < nums[j-1]){
                         //交换
                         int temp = nums[j-1];
                         nums[j-1] = nums[j];
                         nums[j] = temp;
                     }else{
                         break;
                     }
                }
            }
        }
    
    注意边界:int j = i; j-1 >=0 ; j--
    

    四:快速排序(QuickSort)

    • 挑一个基准元素(pivot)

    • 小于pivot放前面,pivot,大于pivot放后面 [<pivot] pivot [>pivot]

    • 然后依次对左边和右边的子数组进行快排,最后达到有序

    • O(NlogN)

    public class QuickSort {
    
    
        private static void quickSort(int[] nums,int l,int r) {
            if (l < r){
                int p = partition(nums,l,r);
                quickSort(nums,l,p-1); //递归
                quickSort(nums,p+1,r);  //递归
            }
        }
    
        /**
         *
         * @param nums  区间数组
         * @param l    区间数组最左边的下标
         * @param r  区间数组最右边的下标
         * @return
         */
        private static  int partition(int[] nums,int l ,int r){
            //nums[l+1...j] < v; v; nums[j+1...i]>=v
            int j = l;
            for (int i = l+1; i <=r ; i++) {
                if(nums[i] < nums[l]){
                    j++;
                    //交换
                    swap(nums,i,j);
                } //大于和等于的话,i直接++
            }
            swap(nums,l,j);  //最后一步交换nums[l]和nums[j]的值,将pivot的值放到中间
            return j;  //返回j的下标
        }
    
        //交换元素
        private static void swap(int[] nums,int a,int b){
            int temp = nums[a];
            nums[a] = nums[b];
            nums[b] = temp;
        }
    
    
    
    
        public static void main(String[] args) {
            int [] arrs = {5,3,1,7,2,6};
            quickSort(arrs,0,arrs.length-1);
            for (int arr:arrs){
                System.out.println(arr);
            }
        }
    
    
    }
    
    public class QuickSort2 {
    
        public static void quickSort(int[] array, int begin, int end) {
            if (end <= begin) return;
            int pivot = partition(array, begin, end);
            quickSort(array, begin, pivot - 1);
            quickSort(array, pivot + 1, end);
        }
        
        static int partition(int[] a, int begin, int end) {
            // pivot: 标杆位置取最右边,counter: 小于下标pivot的元素的个数,最开始是0个,然后1个,2个
            int pivot = end, counter = begin;
    
            for (int i = begin; i < end; i++) {
                if (a[i] < a[pivot]) {
                    int temp = a[counter];
                        a[counter] = a[i];
                        a[i] = temp;
                    counter++;
    //  counter的下标如果是3,表示它前面有3个元素小于pivot下标的值,它自己的值一定是大于pivot的值的,它也就是最后的大于pivot下标值的位置
                }
            }
            //交换pivot和counter的值,使其达到 [<pivot] pivot  [>pivot] 
            int temp = a[pivot];
                a[pivot] = a[counter];
                a[counter] = temp;
            return counter;
        }
    
    
        public static void main(String[] args) {
            int [] arrs = {5,3,1,7,2,6,4};
            quickSort(arrs,0,arrs.length-1);
            for (int arr:arrs){
                System.out.println(arr);
            }
        }
    }
    

    五:归并排序[MergeSort]

    • 将一组数据分成2半排序
    • 合并两半
    public class MergeSort {
    
        public static  void mergeSort(int[] nums ,int l ,int r){
            if(l>=r){return;}
            int mid =  l + (( r - l) >>1);
            mergeSort(nums,l,mid);
            mergeSort(nums,mid+1,r);
            merge(nums,l,mid,r);
        }
    
        //合并两个有序的区间 arr[l,mid]和arr[mid+1,r]
        private static  void merge(int[] nums,int l,int mid,int r){
            //复制数组
            int[] temp = Arrays.copyOfRange(nums,l,r+1); //最右边的是不包含的,所以要+1
            int i =l,j=mid+1;
            //每轮循环为arr[k]赋值,从之前复制的数组中拷贝到nums中
            for (int k = l; k <= r  ; k++) {
                //处理越界
                if(i > mid){ //左边区间不再有元素了,就把右边的值直接拷贝到arr中
                    nums[k] = temp[j-l];j++;
                }else if( j > r){ //右边区间不再有元素了,直接拷贝左边的值到arr
                    nums[k] = temp[i-l];i++;
                }
                
                //比较arr[i]和arr[j]
                else if(temp[i-l] <= temp[j-l] ){
                    nums[k] = temp[i-l];i++;
                }else{
                    nums[k]=temp[j-l];j++;
                }
            }
        }
    
    
        public static void main(String[] args) {
            int [] arrs = {5,3,1,7,2,6,9,8};
            mergeSort(arrs,0,arrs.length-1);
            for (int arr:arrs){
                System.out.println(arr);
            }
        }
    }
    
    public class MergeSort2 {
    
    
        public static void mergeSort(int[] array,int left,int right){
            if(right <= left){return;}
            int mid = left + (right- left)/2;
            mergeSort(array,left,mid);
            mergeSort(array,mid+1,right);
            merge(array,left,mid,right);
    
        }
    
        private static void merge(int[] array, int left, int mid, int right) {
            //1:开辟一个辅助数组
            int[] temp = new int[right-left+1];
            
            
            //2:合并有序数组
            int i = left, j = mid + 1, k = 0; 
            while (i <= mid && j <= right){
                temp[k++] = array[i] <= array[j] ?  array[i++]:array[j++];
            }
            while (i <= mid ){ temp[k++]=array[i++]; }
            while (j <=right ){ temp[k++]=array[j++];}
    
            //3:拷贝数组
            for (int p = 0; p < temp.length ; p++) {
                array[left+p] = temp[p];
            }
    
        }
    
    
        public static void main(String[] args) {
            int [] arrs = {5,3,1,7,2,6,9,8};
            mergeSort(arrs,0,arrs.length-1);
            for (int arr:arrs){
                System.out.println(arr);
            }
        }
    
    
    }
    

    归并和快排:

    • 快排:先调配出左右子数组,然后对于左右子数组进行排序
    • 归并:先排序左右子数组,然后合并有序子数组

    六:堆排序(HeapSort)

    • 使用堆结构
    • 构造一个大顶堆
    • 取堆顶元素,交换到数组末尾,再将剩下的数字构建一个大顶堆
    • 重复以上操作,直到取完堆中的数字,最终得到一个从小到大排列的序列
    • 时间复杂度:建堆的时间复杂度为O(N),heapify的时间复杂度为O(logN),
    • 堆排序对N个数进行heapify,所以时间复杂度为 O(NlogN)
    /**
     * 堆排序
     * 1:构造一个大顶堆
     * 2: 取堆顶元素,交换到数组末尾,再将剩下的数字构建一个大顶堆
     * 3: 重复以上操作,直到取完堆中的数字,最终得到一个从小到大排列的序列
     */
    public class HeapSort {
    
        /**
         * 维护堆的性质
         * @param array  存储堆的数组
         * @param length  数组长度
         * @param i     待维护结点的下标
         */
        static void heapify(int[] array, int length, int i) {
    
            /**
             * 找到父节点和左右孩子中最大的值的下标
             */
            int left = 2 * i + 1, right = 2 * i + 2;
            int largest = i;
            if (left < length && array[largest] < array[left]) {
                largest = left;
            }
            if (right < length && array[largest] < array[right]) {
                largest = right;
            }
    
            /**
             * 判断是不是左右孩子中 有结点的值 比 原父节点大
             *  交换
             */
            if (largest != i) {
                int temp = array[i];
                    array[i] = array[largest];
                    array[largest] = temp;
    
                 //交换完之后,还要递归维护  交换后,孩子的堆的性质
                heapify(array, length, largest);
            }
        }
        public static void heapSort(int[] array) {
            if (array.length == 0) {
                return;
            }
    
            int length = array.length;
            /**
             * 建立堆:自下而上的下滤操作
             * length / 2 - 1 ===》最后一个非叶子节点--》第一个叶子节点的size-1
             */
            for (int i = length / 2 - 1; i >= 0; i--) {
                heapify(array, length, i);
            }
    
            /**
             * 排序: 取堆顶的数字(array[0]) 和 堆末尾的数字(array[length-1])  交换,最大值堆顶元素就到数组末尾了
             * 交换之后维护(剩余元素的)的堆的性质
             */
            for (int i = length - 1; i >= 0; i--) {
                int temp   = array[0];
                  array[0] = array[i];
                  array[i] = temp;
                  heapify(array, i, 0);
            }
        }
    
        public static void main(String[] args) {
            int [] arrs = {5,3,1,7,2,6};
            heapSort(arrs);
            for (int arr:arrs){
                System.out.println(arr);
            }
        }
    }
    

    七; 希尔排序(shell sort)

    • 一种高效的插入排序:让数组越来越有序
    • 希尔增量 gap =length/2 length/4 length/8 .... 1
    • 每组进行插入排序
    public class ShellSort {
        /**
         * 希尔排序
         * @param nums
         */
        private static void shellSort(int[] nums) {
            int h = nums.length >> 1;
            while( h >= 1){
                for (int start = 0; start < h; start++) {   //h循环  
                    //对nums[start,start+h,start+2h....],进行插入排序--下面两个for循环就是插入排序
                    for (int i = start + h; i < nums.length; i+=h) {
                        int t = nums[i];  // 当前要插入的元素
                        int j;
                        for (j = i; j-h >=0 && t <= nums[j-h]  ; j-=h) {
                            nums[j] = nums[j-h]; //将元素右移
                        }
                        nums[j] = t;
                    }
                }
                h = h >> 1; //gap
            }
        }
    
    
    
        public static void main(String[] args) {
            int [] arrs = {5,3,1,7,2,6};
            shellSort(arrs);
            for (int arr:arrs){
                System.out.println(arr);
            }
        }
    
    }
    

    八:计数排序(Counting Sort)

    • 计数排序要求输入的数据必须是有确定范围的整数。将输入的数据值转化为键存
      储在额外开辟的数组空间中;然后依次把计数大于 1 的填充回原数组

    • 步骤:

      • 1:找最大值
      • 2:遍历,下标就是这个数,数组[下标]的值就是它的个数
      • 3:按照顺序遍历赋值
      public class CountingSort {
          /**
           * 计数排序
           * @param nums
           */
          private static void countingSort(int[] nums) {
              if (nums.length == 0){return;}
              //1:找最大值
              int max = nums[0];
              for (int i = 1; i < nums.length; i++) {
                  if (nums[i] > max){
                      max = nums[i];
                  }
              }
              //2:遍历数组统计 , 创建一个辅助数组
              int[] counts = new int[max+1];
              for (int i = 0; i < nums.length; i++) {
                  counts[nums[i]]++;
              }
              //3:按照顺序赋值
              int index = 0;
              for (int i = 0; i < counts.length; i++) {
                  while (counts[i]--  > 0){
                      nums[index++] = i;
                  }
              }
          }
      
      
          public static void main(String[] args) {
              int [] arrs = {5,5,5,3,1,7,2,6};
              countingSort(arrs);
              for (int arr:arrs){
                  System.out.println(arr);
              }
          }
          
      }
      

    九:桶排序(Bucket Sort)

    • 待排序的序列分到若干个桶中,每个桶内的元素再进行个别排序。---不同数类型数组,桶排序的实现方式可能不同
    • 时间复杂度最好可能是线性O(n),桶排序不是基于比较的排序

    代码略

    10:基数排序(Radix Sort)

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

    • 非负数整数

    • 对数的位置进行排序,先排序个位数,再排序十位数,百位数

    public class RadixSort {
    
        /**
         * 基数排序---LSD实现 + 桶数组
         * @param array
         */
        private static void radixSort(int[] array) {
            // 找出最大值, 方便知道它的位数
            int max = array[0];
            for (int i = 1; i < array.length; i++) {
                if (array[i] > max) {
                    max = array[i];
                }
            }
    
            //桶数组
            int[][] buckets = new int[10][array.length];
            //每个桶的元素数量
            int[] bucketSizes = new int[buckets.length];
    
            for (int divider = 1; divider <=max  ; divider *= 10) {
                
                //将第i次的数放到桶里面去
                for (int i = 0; i < array.length; i++) {
                    //base为n位数的数值,比如25取10位数就是(25/10%20)==2
                    int base = (array[i] / divider) % 10;
                    buckets[base][bucketSizes[base]++] = array[i];
                }
    
                //取出桶中的元素放到 原数组中去
                int index = 0;
                for (int i = 0; i < buckets.length; i++) {
                    for (int j = 0; j < bucketSizes[i]; j++) {
                        array[index++]=buckets[i][j];
                    }
                    bucketSizes[i] = 0;
                }
    
    
            }
    
        }
    
    
        public static void main(String[] args) {
            int [] arrs = {500,312,110,70,25,36,245};
            radixSort(arrs);
            for (int arr:arrs){
                System.out.println(arr);
            }
        }
    
    
    }
    
  • 相关阅读:
    ecshop在PHP 5.4以上版本各种错误问题处理
    PHP:字符串转数组,数组转字符串;字符串截取、替换、查找
    js 数据存入数组
    javascript获取焦点对象ID
    ajax 提交序列化表单
    muiwe-选项卡bivew和div模式
    mui的header调整位置
    MUI页面传值取值
    百度地图js自定义控件
    解决百度地图js在移动端点击无效
  • 原文地址:https://www.cnblogs.com/zhoujun007/p/13904364.html
Copyright © 2011-2022 走看看