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

    插入排序

    • 思想: 利用遍历数组,将第 i 位的值与前面一个数比较,如果 arr[i] 小于前面一个数则替换 arr[i]和arr[j] 的位置并重复再往前面比较直到大于或者等于前面的数

      class InsertSort{
          private static void insertSort(int[] arr){
              // 遍历数组除0位以外所有元素
              for (int i = 1; i < arr.length; i++){
                  // 对于第 i 个元素将其与前面的元素比较,如果小于则替换位置直到不小于
                  for (int j = i; j > 0; j--){
                  if (arr[j] < arr[j - 1]){
                          // 如果小于则交换位置,并且继续对比
                          swap(arr, j);
                      } else {
                          break;
                      }
                  }
              }
          }
          
          private static void swap(int[] arr, int j){
              int tmp = arr[j];
              arr[j] = arr[j - 1];
              arr[j - 1] = tmp;
          }
      }
      
      • 时间复杂度 O(n2), 空间复杂度O(1)

    希尔排序

    • 思想: 由于插入排序直接对源数据进行操作,对于数据本身非常乱的话,位置靠后的较小数据需要进行很多次对比才能移动到前面,希尔排序的核心是利用较大的数据跨度比较,将较小的数据经过较少的交换次数移动到前面

      class shellSort{
          public static void main(String[] args) {
              int[] arr = new int[]{11, 15, 1, 12, 2, 13, 3, 14, 4};
              for (int gapNum = 4; gapNum > 0; gapNum >>= 1){
                  shellSort(arr, gapNum);
              }
          }
      
          private static void shellSort(int[] arr, int gapNum){
              // 此处就是插入排序的做法
              for (int i = gapNum; i < arr.length; i += gapNum){
                  for (int j = i; j > 0; j -= gapNum){
                      if (arr[j] < arr[j - gapNum]){
                          swap(arr, j, gapNum);
                      } else {
                          break;
                      }
                  }
              }
              System.out.println(Arrays.toString(arr));
          }
      
          private static void swap(int[] arr, int j, int gapNum){
              int tmp = arr[j];
              arr[j] = arr[j - gapNum];
              arr[j - gapNum] = tmp;
          }
      }
      
      • 优化点: 上面例子是 4 2 1 跨度的,最有序列应该是 knuth 序列 3n+1
      • 时间复杂度O(n1.3),空间复杂度O(1)

    归并排序

    单次归并排序

    class Main{
        public static void main(String[] args){
            // 新建一个无序的 但是左右两部分分别有序的数组
            int[] arr = new int[]{1, 3, 5, 7, 9, 2, 4, 6, 8};
            // 调用merge方法。第一个参数是需要排序的数组,第二个参数是需要排序的起始位置,第三个参数是右边有序部分的第一个数的位置,第四个参数是需要排序部分的结束位置
            merge(arr, 0, 5, 8);
        }
        
        static void merge(int[] arr, int left, int arrow, int right){
            // 新建一个临时数组,接收需要排序的部分排序好的顺序
            int[] tmpArray = new int[right - left + 1];
            // 创建双指针,一个指向左边有序部分的第一个位置(left), 另一个指向右边有序部分的第一个位置(arrow)
            int i = left;
            int j = arrow;
            // 创建一个变量用于指向临时数据未填充部分的第一个位置
            int k = 0;
            // 对比arr[i]与arr[j]的值,较小的依次放在临时数组k的位置
            while (i < arrow && j <= right){
                // 因为i只需要遍历右边有序前面的所有数,所以 < arrow
                // j需要遍历arrow到right里面所有的数据,所以 <= right
                if (arr[i] <= arr[j]) {
                    tmpArray[k++] = arr[i++];
                } else {
                    tmpArray[k++] = arr[j++];
                }
            }
            // 一顿操作猛如虎,最终会以i到达arrow值结束或j到达right+1直接,且只有一个
            // 所以需要判断一下哪一个没到达
            while (i < arrow) tmpArray[k++] = arr[i++];
            while (j <= right) tmpArray[k++] = arr[j++];
            
            // 最终将tmp位置的值全部放入arr[left ... right]中
            for (int m = 0; m < tmpArray.length; m++){
                arr[left + m] = tmpArray[m];
            }
        }
    }
    

    迭代排序

    • 二分法,取一半的一半…进行比较,直接取到1最终实现归并排序
    class Main2 {
        public static void main(String[] args) {
            // 新建一个无序的 但是左右两部分分别有序的数组
            int[] arr = new int[]{1, 3, 5, 7, 9, 2, 4, 6, 8};
            // 调用merge方法。第一个参数是需要排序的数组,第二个参数是需要排序的起始位置,第三个参数是右边有序部分的第一个数的位置,第四个参数是需要排序部分的结束位置
            sort(arr, 0, arr.length - 1);
            System.out.println(Arrays.toString(arr));
        }
    
        // 需要的是一个数组及排序的区间
        static void sort(int[] arr, int left, int right) {
            // 如果取到1个元素的数组,那么就不进行排序
            if (left == right) return;
            // 求出此区间的中值(防止溢出所以不是用(left + right)/2)
            int mid = left + (right - left) / 2;
            // 对左边进行排序(主要排序的对象还是2个元素的数组)
            sort(arr, left, mid);
            // 对右边进行排序
            sort(arr, mid + 1, right);
            //合并两边
            merge(arr, left, mid + 1, right);
        }
    
        static void merge(int[] arr, int left, int arrow, int right) {
            // 新建一个临时数组,接收需要排序的部分排序好的顺序
            int[] tmpArray = new int[right - left + 1];
            // 创建双指针,一个指向左边有序部分的第一个位置(left), 另一个指向右边有序部分的第一个位置(arrow)
            int i = left;
            int j = arrow;
            // 创建一个变量用于指向临时数据未填充部分的第一个位置
            int k = 0;
            // 对比arr[i]与arr[j]的值,较小的依次放在临时数组k的位置
            while (i < arrow && j <= right) {
                // 因为i只需要遍历右边有序前面的所有数,所以 < arrow
                // j需要遍历arrow到right里面所有的数据,所以 <= right
                if (arr[i] <= arr[j]) {
                    tmpArray[k++] = arr[i++];
                } else {
                    tmpArray[k++] = arr[j++];
                }
            }
            // 一顿操作猛如虎,最终会以i到达arrow值结束或j到达right+1直接,且只有一个
            // 所以需要判断一下哪一个没到达
            while (i < arrow) tmpArray[k++] = arr[i++];
            while (j <= right) tmpArray[k++] = arr[j++];
    
            // 最终将tmp位置的值全部放入arr[left ... right]中
            for (int m = 0; m < tmpArray.length; m++) {
                arr[left + m] = tmpArray[m];
            }
        }
    }
    
    • 时间复杂度O(nlogn),空间复杂度O(n)

    快速排序

    快排核心

    public class QuickSort2 {
    
        public static void main(String[] args) {
            int[] arr = {1, 3, 5, 7, 9, 2, 4, 6, 8};
            sort(arr);
            System.out.println(Arrays.toString(arr));
        }
    
        static void sort(int[] arr){
            // 确定一个基准,这个基准决定了前面部分的元素都小于等于这个基准,后面部分的元素都大于这个基准
            int pivot = arr[arr.length - 1];
            // 左指针指向数组的第一个位置
            int left = 0;
            // 右指针指向数组基准前面一个位置
            int right = arr.length - 2;
    
            // 循环开始,左指针左移到第一个大于基准的元素,右指针右移到第一个小于等于基准的元素
            while (left <= right){
                // 这里是 <= 会导致移动过程中 左指针超过右指针
                while (left <= right && arr[left] <= pivot) left++;
                while (left <= right && arr[right] > pivot) right--;
    
                if (left < right) swap(arr, left, right);
            }
    
            // 最后把轴与left值交换
            // 因为此时必然有left指向大数区第一个数组,right指向小数区最后一个数据
            swap(arr, left, arr.length - 1);
        }
    
        static void swap(int[] arr, int i, int j){
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
    }
    

    迭代算法

    public class QuickSort2 {
    
        public static void main(String[] args) {
            int[] arr = {1, 3, 5, 7, 9, 2, 4, 6, 8};
            midSort(arr, 0, arr.length - 1);
            System.out.println(Arrays.toString(arr));
        }
    
        static void midSort(int[] arr, int left, int right){
            // 首先判断是否只有一个元素,如果是,那就不用判断了
            if (left >= right) return;
            // 获取一次排序的基准轴
            int mid = sort(arr, left, right);
            // 对左半部分再次排序
            midSort(arr, left, mid - 1);
            // 对右半部分再次排序
            midSort(arr, mid + 1, right);
        }
    
        static int sort(int[] arr, int leftBonud, int rightBound){
            // 确定一个基准,这个基准决定了前面部分的元素都小于等于这个基准,后面部分的元素都大于这个基准
            int pivot = arr[rightBound];
            // 左指针指向需要排序的第一个位置
            int left = leftBonud;
            // 右指针指向基准前面一个位置
            int right = rightBound - 1;
    
            // 循环开始,左指针左移到第一个大于基准的元素,右指针右移到第一个小于等于基准的元素
            while (left <= right){
                // 这里是 <= 会导致移动过程中 左指针超过右指针
                while (left <= right && arr[left] <= pivot) left++;
                while (left <= right && arr[right] > pivot) right--;
    
                if (left < right) swap(arr, left, right);
            }
    
            // 最后把轴与left值交换
            // 因为此时必然有left指向大数区第一个数组,right指向小数区最后一个数据
            swap(arr, left, rightBound);
    
            return left;
        }
    
        static void swap(int[] arr, int i, int j){
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
    }
    
    
    • 时间复杂度 O(nlogn) 空间复杂度O(n)

    小结: 快排和归并,两者区别在于。在迭代方式上,归并需要前一步处理好的数据,而快排是处理好数据给下一步

    归并的核心是将排序好的数据按顺序放在一起,而快排只要将轴两边的数据放在前后就好了

  • 相关阅读:
    LINQ 详解
    oracle下查询的sql已经超出IIS响应时间
    IOC应用之 Ninject
    JSONP ---------跨域
    国内各大互联网公司相关技术站点2.0版 (集合腾讯、阿里、百度、搜狐、新浪、360等共49个)
    IO多路复用,以socket为例
    socket机制下实现的多用户与服务器交互
    在一个进程中定义多个线程
    基于tcp的socketserver,即tcp的多线程
    基于upd的socketserver,即udp的多线程
  • 原文地址:https://www.cnblogs.com/rainful/p/14854068.html
Copyright © 2011-2022 走看看