数据结构之常见排序算法

二:常见排序算法

  • 排序另一种分法

    • 外排序:需要在内外存之间多次交换数据才能进行

    • 内排序:

      • 插入类排序

        • 直接插入排序

        • 希尔排序

      • 选择类排序

        • 简单选择排序

        • 堆排序

      • 交换类排序

        • 冒泡排序

        • 快速排序

      • 归并类排序

        • 归并排序

2.1选择排序

 选择排序也是一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

 //选择排序
 public class SelecetSort {
     public static void SelecetSort(int[] arr) {
         if (arr==null || arr.length<2) return;
         for(int i=0;i<arr.length-1;i++){//i~N-1
             int minIndex=i;
             for(int j=i+1;j<arr.length;j++){//i~N-1上找最小值的下表
                 minIndex=arr[j]>arr[minIndex] ? j:minIndex;
 ​
             }
             swap(arr,i,minIndex);
         }
     }
     public static void swap(int arr[] ,int i, int j){
         int temp=arr[i];
         arr[i]=arr[j];
         arr[j]=temp;
     }
 }
 ​

2.2冒泡排序

冒泡排序算法的运作如下:

  1. 比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。

  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

  3. 针对所有的元素重复以上的步骤,除了最后一个。

  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

 //冒泡排序
 public class BubbleSort {
     public static void BubbleSort(int[] arr) {
         if (arr==null || arr.length<2) return;
         for(int i=arr.length-1;i>0;i--){
             for(int j=0;j<i;j++){//比较前一个树是否比后一个数大 冒泡 最后每一轮都会将当前论最大值冒到最后面
                 if(arr[j]>arr[j+1]){
                     swap(arr,j,j+1);
                 }
             }
 ​
         }
     }
     public static  void swap(int[] arr,int i,int j){
         arr[i]=arr[i]^arr[j];
         arr[j]=arr[i]^arr[j];
         arr[j]=arr[i]^arr[j];
 ​
     }

2.3插入排序

 public class InsertSort {
     public static void InsertSort(int [] arr ){
         if(arr==null || arr.length<2) return;
         for (int i=0;i<arr.length;i++){
             for (int j=i-1;j>=0 &&      arr[j]>arr[j+1];j--){
                 swap(arr,j,j+1);
             }
         }       
     }
 ​
 public static void swap(int arr[] ,int i, int j{
         int temp=arr[i];
         arr[i]=arr[j];
         arr[j]=temp;
     }
 }
 ​

2.4归并排序

 归并排序的实现分为递归实现非递归(迭代)实现。递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

  归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

  4. 重复步骤3直到某一指针到达序列尾

  5. 将另一序列剩下的所有元素直接复制到合并序列尾

 public class MergeSort {
     public static void MergeSort(int[] arr) {
         if (arr==null || arr.length<2) return;
         process(arr,0,arr.length-1);
 ​
     }
     public static  void process(int [] arr,int L,int R){
         if (L==R) return;
         int mid=L+((R-L)>>1); //此处的>>是移位操作 移动一1位相当于除以2 等同于Mid=L+(R-L/2)
         process(arr,L,mid);
         process(arr,mid+1,R);
         merge(arr,L,mid,R);
 ​
     }
     public  static void merge(int[] arr,int L, int M,int R){
         int [] help=new  int[R-L+1];//申请一个空间helP 进行存放合并后的数组
         int i=0;
         int p1=L;
         int p2=M+1;
         while (p1<=M && p2<=R){
             help[i++] =arr[p1]<=arr[p2] ? arr[p1++]:arr[p2++];
 ​
         }
         while (p1<=M){
             help[i++] =arr[p1++]];
 ​
         }
         while (p1<=M && p2<=R){
             help[i++] =arr[p2++];
 ​
         }
         for (i=0;i<help.length;i++){
             arr[L+i]=help[i];
         }
     }
 }
 ​

2.5快速排序

 快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列。步骤为:

  1. 从序列中挑出一个元素,作为"基准"(pivot).

  2. 把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。

  3. 对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。

空间复杂度最差是O(N),好情况类似于二叉树(选的基准很好)则是O(logN)

 public class quickSort {
     public static void quickSort(int[] arr) {
         if (arr ==null || arr.length<2) return;
         quickSort(arr,0,arr.length-1);
 ​
     }
     public static void quickSort(int[] arr,int L,int R) {
         if (L<R){
             swap(arr,L+(int)(Math.random()*(R-L+1)),R);//等概率的随机选一个数字把他和R交换到最后一各位置
             int[] p =parttition(arr,L,R);//这个数组一个为2,分别为等于区域的左边界和右边界
             quickSort(arr,L,p[0]-1);//<这个数的区域
             quickSort(arr,p[1]+1,R);//<这个数的区域
 ​
         }
     }
 ​
 ​
     //这里默认处理的arr[L-R]之间的函数
     //返回值为一个长度为2的数组
     public static  int [] parttition(int[] arr,int L ,int R){
             int less=L-1;
             int more=R;
             while (L<more)
             {
                 if (arr[L]<arr[R]){ //当前值小于划分值
                     swap(arr,++less,L++);
                 }
                 else if (arr[L]>arr[R]){//当前值 大于划分值
                     swap(arr,--more,L);
                 }
             }
             return new int []{less+1,more};
     }
 ​
 ​
     public static void swap(int arr[] ,int i, int j){
         int temp=arr[i];
         arr[i]=arr[j];
         arr[j]=temp;
     }
     }
 ​

备注:  

Java系统提供的Arrays.sort函数。对于基础类型,底层使用快速排序。对于非基础类型,底层使用归并排序。请问是为什么?

答:这是考虑到排序算法的稳定性。对于基础类型,相同值是无差别的,排序前后相同值的相对位置并不重要,所以选择更为高效的快速排序,尽管它是不稳定的排序算法;而对于非基础类型,排序前后相等实例的相对位置不宜改变,所以选择稳定的归并排序。

2.6堆排序

堆排序其实就是结合了堆的两个操作

 import java.util.Scanner;
 ​
 public class Code04_Heapsort {
     public static void heapSort(int[] arr) {
         if (arr==null || arr.length<2) return;
 ​
         for (int i =0;i<arr.length;i++){//将数组的整体范围整成个大根堆
             heapInsert(arr,i);
         }
 ​
         int heapsize=arr.length;
         swap(arr,0,--heapsize);//0位置的数与堆上最后一个数交换 然后堆大小--
         while (heapsize>0){
             heapify(arr,0,heapsize);
             swap(arr,0,--heapsize);
         }
     }
 ​
     //某一数的下标index位置上一直与其父亲进行比较,如果比父亲大,则与之交换位置并更新下标
     public  static  void heapInsert(int[] arr,int index){
         while(arr[index]>arr[(index-1)/2]){
             swap(arr,index,(index-1)/2);
             index=(index-1)/2;
         }
 ​
     }
 ​
 ​
     //某个数的idnex位置,能否向下移动
     public static void heapify(int [] arr,int index ,int heapsize){
         //heapsize是数组中维持堆的最大长度,用来判断左右孩子下标是否越界的
         int left=index*2+1;//左孩子的节点
         while (left<heapsize){//此时进入循环,证明有左孩子也可以代表证明右孩子,因为一旦左孩子都没,右孩子更没有了,
 ​
             //两个孩子中谁的孩子值大 将下标给largest
             int largest=left+1 <heapsize && arr[left+1]>arr[left] ? left+1:left;
 ​
             //将父节点与较大的孩子节点进行比较,谁的值更大, 将下标给谁
             largest=arr[largest]>arr[index] ? largest:index;
 ​
             if (largest==index) {
                 //如果最大的下标就是这个数的下标,及连同这个index下标下的所有孩子节点来说 它就是最大值。跳出循环完成调整
                 break;
             }
             swap(arr,largest,index);//二者交换数据
             index=largest;//交换下标,证明Index还未调整成功,继续向下调整
             left=index*2+1;
         }
 ​
     }
 ​
     public static void swap(int arr[] ,int i, int j){
         int temp=arr[i];
         arr[i]=arr[j];
         arr[j]=temp;
     }
 ​
     public static void printArray(int [] arr){
         if (arr==null) return;
         for(int i=0;i<arr.length;i++){
             System.out.println(arr[i]+"");
 ​
         }
         System.out.println();
     }
     public static void main(String[] args) {
         Scanner scanner=null;
              scanner=new Scanner(System.in);
             System.out.print( "请输入个数: " );
             int inputNum = scanner.nextInt();
             if( inputNum <= 0 ) {
                 System.out.println("输入错误");
             }
         System.out.println( "请输入数字: " );
         int arr[] = new int[inputNum];
         int num = 0;
         int count = 0;
         while( count < inputNum ) {
             num = scanner.nextInt();
             arr[count] = num;
             count++;
         }
         printArray(arr);
         heapSort(arr);
         printArray(arr);
 ​
     }
 }
 ​
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值