排序知识总结

排序的概念及引用

排序是使一串记录,按照某个关键字的大小,递增或递减排列起来的操作

稳定性:相同关键字排序前后相对顺序不变

内部排序:数据元素全部放在内存中排序

外部排序:数据太多不能同时放到内存中,根据排序的过程要求能在内外存之间移动数据的排序

常见的排序算法

插入排序

插入排序

基本思想:类比扑克牌接牌

时间复杂度0(N^2)数据越有序,直接插入排序越快

空间复杂度0(1)

稳定性:稳定(如果一个排序是稳定的,那么他也可以实现为不稳定的)

  public static void insertSort(int[] array){
        for (int i = 1; i <array.length ; i++) {
            int tmp=array[i];
            int j=i-1;
            for (;j>=0;j--){
                if(array[j]>tmp){
                    array[j+1]=array[j];
                }else{
                    array[j+1]=tmp;
                    break;
                }
            }
            array[j+1]=tmp;
        }
    }

希尔排序(缩小增量排序)

基本思想:先选定一个整数,把待排序的文件中所有记录分为多个组,所有距离为指定距离的分在同一个组,并对每组内的记录进行排序,然后重复上述分组和排序的工作,当达到1时,所有记录在同一组内排序

跳跃式分组:大的数据靠后,小的数据靠前

分组排序--->插入排序,组内进行插入排序

缩小增量--->数据少,数据无序;数据多,数据有序

当增量大于1时都是预排序

空间复杂度为0(1)时间复杂度为n^(1.3-1.5)来记忆

不稳定的

 public static void shellSort(int[] array){
        int gap= array.length;
        while(gap>1){
            gap/=2;
            shell(array,gap);
        }
    }

    private static void shell(int[] array, int gap) {
        for(int i=gap;i<array.length;i++){
            int tmp=array[i];
            int j=i-gap;
            for(;j>=0;j-=gap){
                if(array[j]>tmp){
                    array[j+gap]=array[j];
                }else{
                    array[j+gap]=tmp;
                    break;
                }
            }
            array[j+gap]=tmp;
        }
    }

选择排序

选择排序

基本思想:每次从数据中选最大/最小的排

时间复杂度:O(N^2)   空间复杂度:O(1)    稳定性:不稳定的排序

public static void selectSort1(int[]array){
        for (int i = 0; i < array.length; i++) {
            int minIndex=i;
            for ( int j=i+1; j <array.length ; j++) {
                if(array[j]<array[minIndex]){
                    minIndex=j;
                }
            }
            swap(array,minIndex,i);
        }
    }

   public static void swap(int[] array, int x, int y) {
        int tmp=array[x];
        array[x]=array[y];
        array[y]=tmp;
    }

思路二:左右同时找。left=0,right=arr.length-1 有一个min 有一个max初始为left

int i=left+1,找最大和最小,然后交换

 public static void selectSort(int[]arr){
       int left=0;
       int right=arr.length-1;
       while(left<right){
           int min=left;
           int max=left;
           for (int i = left+1; i <=right ; i++) {
               if(arr[i]>arr[max]){
                   max=i;
               }
               if(arr[i]<arr[min]){
                   min=i;
               }
           }
           swap(arr,min,left);
           if(max==left){
               max=min;
           }
           swap(arr,right,max);
           right--;
           left++;
       }
    }

堆排序

思路:创建大根堆  0下标和end交换

稳定性:不稳定

时间复杂度0(n*logN)  空间复杂度0(1)

public static void heapSort(int[]arr){
        createHeap(arr);
        int end=arr.length-1;
        while(end>0){
            swap(arr,0,end);
            siftDown(arr,0,end);
            end--;
        }
    }

    private static void createHeap(int[] arr) {

        for(int parent=(arr.length-1-1)/2;parent>=0;parent--){
            siftDown(arr,parent,arr.length);
        }
    }

    public static void siftDown(int[] arr, int parent, int end) {
        int child=parent*2+1;
        while(child<end){
            if(child+1<end&&arr[child]<arr[child+1]){
                child=child+1;
            }
            if(arr[child]>arr[parent]){
                swap(arr,parent,child);
                parent=child;
                child=parent*2+1;
            }else{
                break;
            }
        }
    }

交换排序

冒泡排序

基本思想:比较交换

时间复杂度0(N^2) 优化后可能会达到O(N)

空间复杂度O(1)

稳定的排序  

优化:1.int j=arr.length-1-i;2.设置一个flg如果未交换break

 public static void bubbleSort(int[]arr){
        for (int i = 0; i < arr.length; i++) {
            boolean flg=false;
            for (int j = 0; j < arr.length-1-i; j++) {
                if(arr[j]>arr[j+1]){
                    swap(arr,j,j+1);
                    flg=true;
                }
            }
            if(!flg){
                break;
            }
        }
    }

快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,从后边开始找到比基准小的停下来,从前面开始找到比基准大的停下来直至两个相遇,相遇的即为新基准,找到基准后再分两边找,类似二叉树

时间复杂度:当给定1,2,3,4......有序情况下0(n^2)

                     当每次都能均匀分开时0(logN)

空间复杂度:最坏单分支树0(N)      最好情况:0(logN)  完全二叉树

稳定性:不稳定的排序

挖坑法(做题优先考虑的思想):先把left(0)拿出,0坐标空出即为坑,从后边找比基准小的直接填坑,再从前边找比基准大的挖坑

前后指针法:两个指针prev和cur 

prev=legft,cur=left+1

一前一后走,找到比基准小的进行交换

优化思路:减少他的递归次数

1.三数取中法(left,right,mid找到这三个数据中)

2.直接插入法(针对一定范围) 二叉树越深,递归次数越多,可以在最后几层直接插入排序(因为数据少且趋于有序)

public static void quickSort(int[]arr){
       if(arr==null){
           return;
       }
       quick(arr,0,arr.length-1);
   }
   public static void quick(int[]arr,int begin,int end){
       if(begin>=end){
           return;
       }
       if(begin+end-1<=7){
           insertSortRange(arr,begin,end);
       }
       int midIndex=getMid(arr,begin,end);
       swap(arr,begin,midIndex);
       int pivot=partition(arr,begin,end);
       quick(arr,0,pivot-1);
       quick(arr,pivot+1,end);
   }
   public static int getMid(int[]arr,int begin,int end){
       int mid=(begin+end)/2;
       if(arr[begin]>arr[end]){
           if(arr[mid]>arr[begin]){
               return begin;
           }else if(arr[mid]>arr[end]){
               return mid;
           }else{
               return end;
           }
       }else{
           if(arr[mid]>arr[end]){
               return end;
           }else if(arr[mid]>arr[begin]){
               return mid;
           }else{
               return begin;
           }
       }
   }
   public static void insertSortRange(int[]arr,int begin,int end){
       for (int i = begin+1;i<=end;i++){
           int tmp=arr[i];
           int j=i-1;
           for(;j<=end;j--){
               if(arr[j]>tmp){
                   arr[j+1]=arr[j];
               }else{
                   arr[j+1]=tmp;
                   break;
               }
           }
           arr[j+1]=tmp;
       }
   }
    public static int partition2(int[]arr,int left,int right){
      int prev=left;
      int cur=prev+1;
      while(cur<=right){
          if(arr[cur]<arr[left]&&arr[++prev]!=arr[cur]){
              swap(arr,prev,cur);
          }
          cur++;
      }
      swap(arr,prev,left);
      return  prev;
    }
   public static int partition(int[]arr,int left,int right){
      int tmp=arr[left];
      while(left<right){
          while(left<right&&arr[right]>=tmp){
              right--;
          }
          arr[left]=arr[right];
          while(left<right&&arr[left]<=tmp){
              left++;
          }
          arr[right]=arr[left];
      }
      arr[left]=tmp;
      return left;
   }
   public static int partitionHoare(int[]arr,int left,int right){
       int tmp=arr[left];
       int tmpLeft=left;
       while(left<right){
           while(left<right&&arr[right]>=tmp){
               right--;
           }
           while(left<right&&arr[left]<=tmp){
               left++;
           }
           swap(arr,left,right);
       }
       swap(arr,left,tmpLeft);
       return left;
   }

   public static void swap(int[]arr,int i,int j){
       int tmp=arr[i];
       arr[i]=arr[j];
       arr[j]=tmp;
   }

快速排序的非递归实现:利用的是栈

找到基准如果基准左右有两个元素及以上再次找基准 只要栈不为空,先找左再找右

 public static void quickSort(int[]arr){
        if(arr==null){
            return;
        }
        quickNor(arr,0,arr.length-1);
    }
    public static void quickNor(int[]arr,int start,int end){
        Deque<Integer> stack=new ArrayDeque<>();
        int pivot=partition(arr,start,end);
        if(pivot>start+1){
            stack.push(start);
            stack.push(pivot-1);
        }
        if(pivot<end-1){
            stack.push(pivot+1);
            stack.push(end);
        }
        while(!stack.isEmpty()){
            end=stack.pop();
            start=stack.pop();
            pivot=partition(arr,start,end);
            if(pivot>start+1){
                stack.push(start);
                stack.push(pivot-1);
            }
            if(pivot<end-1){
                stack.push(pivot+1);
                stack.push(end);
            }
        }
  }

归并排序(mergeSort)

建立在归并操作的一种有效的排序算法,归并排序是最常用的外部排序

分解+合并

时间复杂度0(NlogN) 空间复杂度0(N)稳定性:稳定

如图为归并排序的拆分过程

public static void mergeSort(int[]arr){
        mergeSortMap(arr,0,arr.length-1);
   }
    public static void mergeSortMap(int[]arr,int left,int right){
        if(left>=right){
            return;
        }
        int mid=(left+right)/2;
        mergeSortMap(arr,left,mid);
        mergeSortMap(arr,mid+1,right);
        merge(arr,left,mid,right);
    }
    public static void merge(int[]arr,int left,int mid,int right){
       int[]tmp=new int[right-left+1];
       int k=0;
       int s1=left;
       int s2=mid+1;
        while (s1 <= mid && s2 <= right) {
            if(arr[s1] <= arr[s2]) {
                tmp[k++] = arr[s1++];
            }else {
                tmp[k++] = arr[s2++];
            }
        }
        while (s1 <= mid) {
            tmp[k++] = arr[s1++];
        }
        while (s2 <= right) {
            tmp[k++] = arr[s2++];
        }
        //可以保证tmp数组 是有序的
        for (int i = 0; i < k; i++) {
            arr[i+left] = tmp[i];
        }
    }
}

非递归的归并排序

gap=1一个一个有效

gap=2两个两个有效....gap>=len有序

public static void merge(int[]arr,int left,int mid,int right){
       int[]tmp=new int[right-left+1];
       int s1=left;
       int s2=mid+1;
       int k=0;
       while(s1<=mid&&s2<=right){
           if(arr[s1]<=arr[s2]){
               tmp[k++]=arr[s1++];
           }else{
               tmp[k++]=arr[s2++];
           }
       }
       while(s1<=mid){
           tmp[k++]=arr[s1++];
       }
       while(s2<=right){
           tmp[k++]=arr[s2++];
       }
        for (int i = 0; i <k ; i++) {
            arr[i+left]=tmp[i];
        }
    }
    public static void mergeSortNor(int[]arr){
       int gap=1;
       while(gap<arr.length){
           for (int i = 0; i < arr.length; i = i + gap * 2) {
               int left = i;
               int mid = left + gap - 1;
               if(mid >= arr.length) {
                   mid = arr.length-1;
               }
               int right = mid + gap;
               if(right >= arr.length) {
                   right = arr.length-1;
               }
               merge(arr,left,mid,right);
           }
           gap *= 2;
       }
    }

其他非基于比较的排序

计数排序(鸽巢原理)

是对哈希直接地址法的变形应用

步骤:1.统计相同元素的次数

           2.根据统计的结果将序列回收到原来的序列中

public static void countSort(int[] array) {
        int maxVal = array[0];
        int minVal = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i] < minVal) {
                minVal = array[i];
            }
            if(array[i] > maxVal) {
                maxVal = array[i];
            }
        }
        int len = maxVal - minVal + 1;
        int[] count = new int[len];
        
        for (int i = 0; i < array.length; i++) {
            int index = array[i];
            count[index-minVal]++;
        }
        int index = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] != 0) {
                array[index] = i+minVal;
                index++;
                count[i]--;
            }
        }
    }

桶排序

划分多个范围相同的区间,每个子区间自排序,最后合并。

是计数排序的扩展版本,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素映射到各个对应的桶中,对每个桶中的元素进行排序,最后将非空桶中的元素逐个放入原序列中。

桶排序需要尽量保证元素分散均匀,否则当所有数据集中在同一个桶中时,桶排序失效。

public static void bucketSort(int[] arr){
    
    // 计算最大值与最小值
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    for(int i = 0; i < arr.length; i++){
        max = Math.max(max, arr[i]);
        min = Math.min(min, arr[i]);
    }
    
    // 计算桶的数量
    int bucketNum = (max - min) / arr.length + 1;
    ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
    for(int i = 0; i < bucketNum; i++){
        bucketArr.add(new ArrayList<Integer>());
    }
    
    // 将每个元素放入桶
    for(int i = 0; i < arr.length; i++){
        int num = (arr[i] - min) / (arr.length);
        bucketArr.get(num).add(arr[i]);
    }
    
    // 对每个桶进行排序
    for(int i = 0; i < bucketArr.size(); i++){
        Collections.sort(bucketArr.get(i));
    }
    
    // 将桶中的元素赋值到原序列
	int index = 0;
	for(int i = 0; i < bucketArr.size(); i++){
		for(int j = 0; j < bucketArr.get(i).size(); j++){
			arr[index++] = bucketArr.get(i).get(j);
		}
	}  
}

基数排序

基数排序(Radix Sort)是一种非比较型的排序算法,它通过逐位比较元素的每一位(从最低位到最高位)来实现排序。基数排序的核心思想是将整数按位数切割成不同的数字,然后按每个位数分别进行排序。基数排序的时间复杂度为 O(n * k),其中 n 是列表长度,k 是最大数字的位数。

算法步骤:

  1. 确定最大位数:找到列表中最大数字的位数,确定需要排序的轮数。

  2. 按位排序:从最低位开始,依次对每一位进行排序(通常使用计数排序或桶排序作为子排序算法)。

  3. 合并结果:每一轮排序后,更新列表的顺序,直到所有位数排序完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值