zoukankan      html  css  js  c++  java
  • 排序算法总结

    排序算法比较

    https://www.cnblogs.com/bjwu/articles/10006419.html

    冒泡排序

     1     /**冒泡排序
     2      * 第一次比较0~N-1位 将最大值沉底
     3      * 第二次比较0~N-2位 将最大值沉底
     4      * ......
     5      * 第N-1次比较0~1位 将最大值沉底(在0~1中沉底)
     6      * 时间复杂度:O(n^2)
     7      */
     8     public static void sort(int[] nums){
     9         if(nums == null || nums.length < 2){
    10             return;
    11         }
    12         for(int end = nums.length-1; end > 0; end--){
    13             for(int j = 0; j < end; j++){
    14                 if(nums[j] > nums[j+1]){
    15                     swap(nums,j,j+1);
    16                 }
    17             }
    18         }
    19 
    20     }
    21     public static void swap(int[] nums, int i, int j){
    22         int tmp = nums[i];
    23         nums[i] = nums[j];
    24         nums[j] = tmp;
    25     }

    插入排序

     1     /** 插入排序
     2      *  0~end-1 位置的数都有序
     3      *  每次考察end位置的数应该插在哪里
     4      *  如果前面的数比end位置数大则交换
     5      *
     6      *  插入选择时间复杂度和数据样本无关
     7      *  插入排序 有序 O(n) 逆序O(n^2)
     8      *  时间复杂度按最差的算
     9      *  所以插入排序的时间复杂度是O(n^2)
    10      */
    11     public static void sort(int[] nums){
    12         for(int end = 1; end < nums.length; end++){
    13             for(int i = end-1; i >= 0; i--){//与前面有序部分比较是否能够交换
    14                 if(nums[i] > nums[i+1]){
    15                     swap(nums,i,i+1);
    16                 }
    17             }
    18         }
    19     }
    20 
    21     public static void swap(int[] nums, int i, int j){
    22         int tmp = nums[i];
    23         nums[i] = nums[j];
    24         nums[j] = tmp;
    25     }

    选择排序

        /**选择排序
         * 第一次比较 0~N-1 找到最小值的位置和0交换
         * 第二次比较 1~N-2 找到最小值的位置和1交换
         * ......
         * 第N-1次比较 N-2~N 找到最小值的位置和N-2交换
         * 时间复杂度:O(N^2)
         */
        public static void sort(int[] nums){
            if(nums == null || nums.length < 2){
                return;
            }
            for(int start = 0; start < nums.length; start++){
                int minIndex = start;
                for(int i = start; i < nums.length; i++){
                    minIndex = nums[i] < nums[minIndex]? i:minIndex;
                }
                swap(nums, minIndex, start);
            }
    
        }
    
        public static void swap(int[] nums, int i, int j){
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }

    快排

     1     public static void quickSort(int [] nums, int left, int right){
     2         if (nums == null || nums.length < 2) {
     3             return;
     4         }
     5         if(left < right){
     6             //随机选一个位置和最后一个数字交换
     7             swap(nums, left+(int)Math.random()*(right-left+1), right);
     8             int[] p = partition(nums, left, right);//等于区域的左边界和右边界
     9             quickSort(nums, left, p[0]-1); // 小于区域递归
    10             quickSort(nums, p[1]+1, right); // 大于区域递归
    11         }
    12     }
    13 
    14     public static int[] partition(int[] nums, int left, int right){
    15         int smaller = left - 1; // 小于区域的右边界
    16         int bigger = right; // 大于区域的左边界
    17         while(left < bigger){
    18             int target = nums[right];
    19             if(nums[left] > target){
    20                 swap(nums,left,bigger-1);
    21                 bigger--;
    22             }else if(nums[left] < target){
    23                 swap(nums,left,smaller+1);
    24                 smaller++;
    25                 left++;
    26             }else {
    27                 left++;
    28             }
    29         }
    30         swap(nums, bigger, right);
    31         return new int[]{smaller+1,bigger};
    32     }
    33     public static void swap(int[] nums, int i, int j){
    34         int tmp = nums[i];
    35         nums[i] = nums[j];
    36         nums[j] = tmp;
    37     }

    归并排序

     1     /**归并排序
     2      * 左边排序 右边排序
     3      * 外排整体排序 辅助数组
     4      * 左边有序数组和右边有序数组谁小谁加入辅助数组
     5      * T(N) = 2T(N/2) + O(N)
     6      * log2(2) = 1 = d -->
     7      * 时间复杂度为 O(N*logN)
     8      */
     9     public static void mergeSort(int[] nums){
    10         if(nums ==null || nums.length < 2){
    11             return;
    12         }
    13         sortProcess(nums, 0, nums.length-1);
    14     }
    15     public static void sortProcess(int[] nums, int left, int right){
    16         if(left == right){//结束递归条件 这个范围只有一个数
    17             return;
    18         }
    19         int mid = left + (right - left)/2;
    20         sortProcess(nums,left,mid);//左边有序 T(N/2)
    21         sortProcess(nums,mid+1,right);//右边有序 T(N/2)
    22         merge(nums, left, mid, right);//外排序 O(N)
    23     }
    24 
    25     public static void merge(int[] nums, int left, int mid, int right){
    26         int[] help = new int[right-left+1];//left到right有多少个数
    27         int i = 0;//指向help
    28         int p1 = left;//指向左侧数组第一个数
    29         int p2 = mid+1;//指向右侧数组第一个数
    30 
    31         while(p1 <= mid && p2 <= right){//有一个数组读完了就退出循环
    32             //哪边小哪边的p指向的元素加入help 加入之后移动p指针
    33             help[i++] = nums[p1] < nums[p2] ? nums[p1++] : nums[p2++];
    34         }
    35         //只有一个越界 把剩余的数组一次性添加进help
    36         while(p2 <= right){
    37             help[i++] = nums[p2++];
    38         }
    39         while(p1 <= mid){
    40             help[i++] = nums[p1++];
    41         }
    42         //将help中的依次覆盖nums
    43         for(int j = 0; j < help.length; j++){
    44             nums[left+j] = help[j];
    45         }
    46     }

    逆序和  & 最小和都可以用归并排序的思路来解

    逆序对

     1 /**
     2  * 逆序对和最小和求解思路的区别
     3  *
     4  * 【最小和】是优先找小值
     5  * [1,1,2] [2,3,4]
     6  * 优先在左侧数组找到小值后  右边剩下的就都比该值要大了 从左向右遍历
     7  * 例如:对于左边第一个元素1 右边第一个元素2及2之后的所有元素都对1有一个小和
     8  * smallsum += 1 * (right-p2+1)
     9  * 即 如果左小于右 累加
    10  *
    11  * 【逆序对】要求是左侧元素大于右侧
    12  * 优先在左侧找大值 对于升序排序 大值在最右边 应该从右向左遍历
    13  * [1,1,5] [1,3,4]
    14  * 例如:对于左边最后一个元素5 大于右边最后一个元素4 则5可以和4及4之前的所有元素形成一个逆序对
    15  * [5,1][5,3][5,4]
    16  * count += right-(mid+1)+1  5 - 3 + 1 = 3 个
    17  * 即 如果左大于右 累加
    18  */
    19 public class ReversePair {
    20     private static int count;
    21     private static List<int[]> list = new ArrayList<>();
    22     public static void mergeSort(int[] nums){
    23         if(nums ==null || nums.length < 2){
    24             return;
    25         }
    26         sortProcess(nums, 0, nums.length-1);
    27     }
    28     public static void sortProcess(int[] nums, int left, int right){
    29         if(left == right){//结束递归条件 这个范围只有一个数
    30             return;
    31         }
    32         int mid = left + (right - left)/2;
    33         sortProcess(nums,left,mid);//左边有序 T(N/2)
    34         sortProcess(nums,mid+1,right);//右边有序 T(N/2)
    35         merge(nums, left, mid, right);//外排序 O(N)
    36     }
    37 
    38     public static void merge(int[] nums, int left, int mid, int right){
    39         int[] helper = new int[right -left + 1];
    40         int p = right - left;
    41         int rightBegin = mid + 1;
    42         while(left <= mid && rightBegin <= right){
    43             if(nums[mid] > nums[right]){
    44                 count += (right - rightBegin)+1;
    45                 for(int k = right; k >= rightBegin; k--){
    46                     list.add(new int[] {nums[mid],nums[k]});
    47                 }
    48                 helper[p--] = nums[mid--];
    49             }else{
    50                 helper[p--] = nums[right--];
    51             }
    52         }
    53         while(left <= mid){
    54             helper[p--] = nums[mid--];
    55         }
    56         while(right >= rightBegin){
    57             helper[p--] = nums[right--];
    58         }
    59         for(int help:helper){
    60             nums[left++] = help;
    61         }
    62     }
    63 
    64     public static void main(String[] args) {
    65         int[] nums = {3,5,4,2,1};
    66         mergeSort(nums);
    67         System.out.println(Arrays.toString(nums));
    68         System.out.println(count);
    69         for(int[] pair: list){
    70             System.out.println(Arrays.toString(pair));
    71         }
    72     }
    73 }

    最小和

     1 public class SmallSum {
     2     public static int smallSum(int[] arr){
     3         if(arr == null|| arr.length < 2){
     4             return 0;
     5         }
     6         return mergeSort(arr,0,arr.length-1);
     7     }
     8 
     9     /**
    10      * 计算left ~ right范围内有多少小和
    11      */
    12     public static int mergeSort(int[] arr, int left, int right){
    13         if(left == right){
    14             return 0;
    15         }
    16         int mid = left + ((right - left) >> 1); //右移1位 等于 除以2
    17         //mid = left + (right - left)/2; 防止溢出
    18         return mergeSort(arr, left, mid)+//左边排序产生的小和
    19                 mergeSort(arr, mid+1, right)+//右边排序产生的小和
    20                 merge(arr, left, mid, right);//最后合并产生的小和
    21     }
    22 
    23     public static int merge(int[] arr, int left, int mid, int right){
    24         int[] help = new int[right-left+1];//准备一个临时数组
    25         // 长度和传进来的arr一样 是原arr的一部分 用left和right划分出来的一部分
    26         int i = 0;
    27         int p1= left;
    28         int p2 = mid+1;
    29         int res = 0;//计算小和
    30         while(p1 <= mid && p2 <= right){
    31             //关键在这里:
    32             // 每一次比较可以知道比当前值更大的值有几个,这个较小的当前值就需要累加多少次。
    33             //如果左边小于右边,那就有(r - p2 + 1)个arr[p1]元素的和是最小和,进行累加。
    34             //p2后面的个数 * p1指向的数
    35             res += arr[p1] < arr[p2] ? (right - p2 + 1) * arr[p1] : 0;
    36             help[i++] = arr[p1] > arr[p2] ? arr[p2++] : arr[p1++];
    37         }
    38         while(p1 <= mid){
    39             help[i++] = arr[p1++];
    40         }
    41         while (p2 <= right){
    42             help[i++] = arr[p2++];
    43         }
    44         for(i = 0; i < help.length; i++){
    45             arr[left+i] = help[i];
    46         }
    47         return res;
    48     }
    49 
    50     public static void main(String[] args) {
    51         int[] nums = {1,3,4,2,5};
    52         int i = smallSum(nums);
    53         System.out.println(i);
    54     }
    55 }

    堆排序

    /**
     * HeapSort展示了heapinsert和heapify的两个过程
     * 时间复杂度是O(NlogN)
     * 实际上可以只用heapify
     * 用heapify代替heapinsert从后向前heapify
     * 原来的heapinsert是O(NlogN) heapify时间复杂度是O(N) 可以稍微快一点
     * 但最后的时间复杂度还是O(NlogN) 因为后面的操作还是O(NlogN)
     */
    public class HeapSort {
        //堆化【调整堆】 某个数在index值 能否往下移动
        public static void heapify(int[] nums, int index, int heapSize){
            //index是初始的父节点
            int left = index * 2 + 1; //左孩子
            while(left < heapSize){//下方还有孩子时 没有越界
                int largest = left;
                if(left + 1 < heapSize) {//右孩子存在 没有越界
                    //左右两孩子PK
                    largest = nums[left]>nums[left+1]?left:left+1;
                }
                //较大孩子和父亲PK
                largest = nums[index]>nums[largest]?index:largest;
                //父亲最大不交换
                if(largest == index){
                    break;
                }
                //父亲和较大孩子交换
                swap(nums, largest, index);
                //下一个比较的数 新父亲
                index = largest;
                //交换后的新的左孩子
                left = index * 2 + 1;
            }
        }
    
        /**
         * 无序数组 相当于依次插入大根堆 heapinsert
         * 得到大根堆后
         * 每次让数组第一个元素(最大值)和最后一个元素交换
         * 交换后heapsize--,调整堆heapify为大根堆
         * 最后heapsize == 0 数组有序
         */
        public static void heapSort(int[] nums){
            if(nums == null || nums.length < 2){
                return;
            }
            for (int i = nums.length-1; i >= 0; i--) {
                heapify(nums,i,nums.length);
            }
            int heapSize = nums.length;
            swap(nums,0,--heapSize);//先交换
            while(heapSize > 0){
                heapify(nums,0,heapSize);//再堆化
                swap(nums,0,--heapSize);//继续交换
            }
        }
    
        public static void swap(int[] nums, int i, int j){
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }
    
        public static void main(String[] args) {
            int[] nums = {2,3,1,4,4,3,3,4,5,1};
            heapSort(nums);
            System.out.println(Arrays.toString(nums));
        }
    }

    适用于求基本有序的数列

     * 已知一个几乎有序的数组,几乎有序是指,
     * 如果把数组排好顺序的话,每个元素移动的距离可以不超过K
     * 可以用堆排序来解决这个问题
     *
     * 思路:
     * 1.建立有K个元素的小顶堆,然后取出顶上元素
     * 2.堆顶用没有建堆的下一元素替代,重新建堆
     * 3.反复调用,完成排序,此算法因为每个元素移动都在k以内,所以时间复杂度为O(NlogK)
     */
    public class KHeapSort {
    
        public static void kHeapSort(int[] nums, int k) {
            PriorityQueue<Integer> heap = new PriorityQueue<>();
            int index= 0;
            //k个值加入堆
            for(; index <= Math.min(k, nums.length); index++){
                heap.add(nums[index]);
            }
            int i = 0;
            //从index的下一个位置开始遍历 也就是k+1
            for(; index < nums.length; i++,index++){
                heap.add(nums[index]);//加一个 heapsize+1
                nums[i] = heap.poll();//弹一个 heapsize-1
            }
            while (!heap.isEmpty()){
                nums[i++] = heap.poll();
            }
    
        }
    
        public static void main(String[] args) {
            int [] nums ={3,2,7,1,8,6,4,5,12,10,15,9,13,14,11,20,16,17,18,19};
            kHeapSort(nums,7);
            System.out.println(Arrays.toString(nums));
        }
    }

    基数排序

    public class RadixSort {
        /**
         * 统计最大值有多少位
         */
        public static int maxbits(int[] nums){
            int max = nums[0];
            for(int i = 0; i < nums.length; i++){
                max = Math.max(nums[i], max);
            }
            int count = 0;
            while(max > 0){
                max /= 10;
                count++;
            }
            return count;
        }
        /**
         * 获取倒数第i位的数字 i=1 取出个位
         */
        public static int getDigit(int num,int i){
            while(i > 1){
                num /= 10;
                i--;
            }
            return num%10;
        }
    
        /**
         * 基数排序
         */
        public static void radixSort(int[] arr){
            int digit = maxbits(arr);
            //基数 10
            final int radix = 10;
            //给每个数准备一个桶
            int[] buckets = new int[arr.length];
            //有多少位 就要出桶进桶几次
            for(int d = 1; d <= digit; d++){
                //计数器 等于count的有几个
                int[] count = new int[radix];
                for(int i = 0; i < arr.length; i++){
                    int j = getDigit(arr[i],d);
                    count[j]++;
                }
                //将count变为前缀和 小于等于count的有几个
                for(int i = 1; i < radix; i++){
                    count[i] += count[i-1];
                }
                System.out.println(Arrays.toString(count));
    
                //数组从右向左遍历 入桶
                for(int i = arr.length-1; i >=0; i--){
                    int j = getDigit(arr[i],d);
                    buckets[count[j]-1] = arr[i];
                    count[j]--;
                }
    
                //当前位数出桶
                for(int i = 0; i < arr.length; i++) {
                    arr[i] = buckets[i];
                }
            }
        }
    
        public static void main(String[] args) {
            int[] nums = {123,423,19,32,321,1200};
            radixSort(nums);
            System.out.println(Arrays.toString(nums));
        }
    }
  • 相关阅读:
    CSS绿色导航代码
    一款简单另类的CSS导航菜单代码
    来自腾讯QQ网站首页的选项卡菜单代码
    JavaScript学习笔记之应用技巧二
    JavaScript学习笔记之创建字符串比较
    Delegate学习笔记
    ADO.NET Entity FrameWork学习笔记
    Reflector学习之特性理解
    字符串范围截取(转载)
    Delegate学习笔记之事件订阅
  • 原文地址:https://www.cnblogs.com/xdcat/p/13048132.html
Copyright © 2011-2022 走看看