牛客高频面试算法--排序(冒泡、快排、归并、堆、队列)

本文详细介绍了冒泡排序、快速排序、归并排序、堆排序和优先级队列在数组排序中的应用,通过实例演示和时间复杂度分析,帮助读者理解各种排序算法的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

排序

题目描述:给定一个长度为 n 的数组,请你编写一个函数,返回该数组按升序排序后的结果。

输入: [5,2,3,1,4]
返回值: [1,2,3,4,5]

输入: [5,1,6,2,5]
返回值: [1,2,5,5,6]

调用库函数Arrays.sort

public int[] MySort (int[] arr) {
        // 直接调用函数
        Arrays.sort(arr);
        return arr;
    }

冒泡排序BubbleSort

以升序排列为例:将第一个元素和第二个元素比较,若前者大于后者,则交换两者的位置,再将第二个元素与第三个元素比较,若前者大于后者则交换两者位置,以此类推直到倒数第二个元素与最后一个元素比较,若前者大于后者,则交换两者位置。这样一轮比较下来将会把序列中最大的元素移至序列末尾,这样就安排好了最大数的位置,接下来只需对剩下的(n-1)个元素,重复上述操作即可。
时间复杂度:
若原数组本身就是有序的(这是最好情况),仅需n-1次比较就可完成,时间复杂度依然为O(n);
若是倒序,比较次数为 n-1+n-2+…+1=n(n-1)/2,交换次数和比较次数等值。所以,其时间复杂度依然为O(n2)
空间复杂度:
使用常数个辅助单元:O(1)
在这里插入图片描述
在这里插入图片描述

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 将给定数组排序
     * @param arr int整型一维数组 待排序的数组
     * @return int整型一维数组
     */
    public int[] MySort (int[] arr) {
        if(arr.length <2){
            return arr;
        }
        for(int i=0; i < arr.length-1;i++){
            for(int j=0;j<arr.length-i-1;j++){
                if(arr[j]> arr[j+1])
                    swap(arr, j, j+1);
            }
        }
        return arr;
    }
    
    private void swap(int[] arr, int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

快速排序QuickSort

快速排序(Quick Sort):是对冒泡排序的一种改进方法,在冒泡排序中,进行元素的比较和交换是在相邻元素之间进行的,元素每次交换只能移动一个位置,所以比较次数和移动次数较多,效率相对较低。而在快速排序中,元素的比较和交换是从两端向中间进行的,较大的元素一轮就能够交换到后面的位置,而较小的元素一轮就能交换到前面的位置,元素每次移动的距离较远,所以比较次数和移动次数较少,y速度较快,故称为“快速排序”。

快速排序的基本思想是:

在待排序的元素任取一个元素作为基准(通常选第一个元素,但最的选择方法是从待排序元素中随机选取一个作为基准),称为基准元素;
将待排序的元素进行分区,比基准元素大的元素放在它的右边,比其小的放在它的左边; 对左右两个分区重复以上步骤直到所有元素都是有序的

在这里插入图片描述

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 将给定数组排序
     * @param arr int整型一维数组 待排序的数组
     * @return int整型一维数组
     */
    public int[] MySort (int[] arr) {
        quickSort(arr, 0, arr.length-1);
        return arr;
    }
    
    private void quickSort(int[] arr, int left, int right){
        if(left < right){
            // 分割数组,找到分割点
            int point = partition(arr, left, right);
            // 进行递归调用,对左边数组进行快速排序
            quickSort(arr, left, point-1);
            //  进行递归调用,对右边数组进行快速排序
            quickSort(arr,point+1,right);
        }
    }
    
    private int partition(int[] arr, int left, int right){
        //将第一个元素作为基准数
        int first = arr[left];
        while(left < right){
            while(left < right && arr[right] > first){
                right--;
            }
            swap(arr,left,right);
            while(left< right && arr[left] <= first){
                left++;
            }
            swap(arr, left, right);
        }
        // 返回分割点所在的位置
        return left;
    }
    
    private void swap(int[] arr, int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }     
}

归并排序MergeSort

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差。java中Arrays.sort()采用了一种名为TimSort的排序算法,就是归并排序的优化版本。从上文的图中可看出,每次合并操作的平均时间复杂度为O(n),而完全二叉树的深度为|log2n|。总的平均时间复杂度为O(nlogn)。而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

import java.util.*;
public class Solution {
  /**
   * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
   * 将给定数组排序
   * @param arr int整型一维数组 待排序的数组
   * @return int整型一维数组
   */
  public int[] MySort (int[] arr) {
      mergeSort(arr,0,arr.length-1);
      return arr;
  }
 
  public void mergeSort(int[] arr,int l,int r){
      if(l==r){
          return;
      }
      int mid = l+((r-l)>>1); //中点位置,即(l+r)/2
      mergeSort(arr,l,mid);
      mergeSort(arr,mid+1,r);
      merge(arr,l,mid,r);
  }
 
  public void merge(int[] arr,int l,int mid,int r){
      int [] help= new int[r-l+1];    //辅助数组
      int i=0;
      int p1=l; //左半数组的下标
      int p2=mid+1; //右半数组的下标
 
      //判断是否越界
      while(p1<=mid && p2<=r){
          help[i++]=arr[p1]<arr[p2] ? arr[p1++] : arr[p2++];
      }
      //p1没有越界,说明p2越界了,将左边剩余元素拷贝到辅助数组
      while(p1<=mid){
          help[i++]=arr[p1++];
      }
      //p2没有越界,说明p1越界了
      while(p2<=r){
          help[i++]=arr[p2++];
      }
      //将辅助数组元素拷贝会原数组
      for(i=0;i<help.length;i++){
          arr[l+i]=help[i];
      }
  }
}

堆排序HeapSort

堆结构就是将一颗完全二叉树映射到数组中的一种存储方式
在这里插入图片描述
heapInsert和heapify
大根堆最重要的两个操作就是heapInsert和heapify,前者是当一个元素加入到大根堆时应该自底向上与其父结点比较,若大于父结点则交换;后者是当堆中某个结点的数值发生变化时,应不断向下与其孩子结点中的最大值比较,若小于则交换。下面是对应的代码:

//判断该结点与父结点的大小,大结点一直往,建立大根堆
   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;
       }
   }    
   //一个值变小往下沉的过程
   public static void heapify(int[] arr,int index,int size){
       int left=index*2+1;
       while(left<size){
           int largest = left + 1 < size && arr[left+1] > arr[left] ? left+1 : left;
           largest = arr[largest] > arr[index] ? largest :index;
           if(largest==index){
               break;
           }
           swap(arr,largest,index);
           index=largest;
           left=index*2 +1;

       }
   }

在这里插入图片描述

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 将给定数组排序
     * @param arr int整型一维数组 待排序的数组
     * @return int整型一维数组
     */
    public int[] MySort (int[] arr) {
        heapSort(arr);
        return arr;
    }
 
    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 size = arr.length;
        swap(arr,0,--size);
        while(size>0){
            heapify(arr,0,size);// 最后一个数与根交换
            swap(arr,0,--size);
        }

    }
    //判断该结点与父结点的大小,大结点一直往,建立大根堆
    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;
        }
    }
 
    //一个值变小往下沉的过程
    public static void heapify(int[] arr,int index,int size){
        int left=index*2+1;
        while(left<size){
            int largest = left + 1 < size && arr[left+1] > arr[left] ? left+1 : left;
            largest = arr[largest] > arr[index] ? largest :index;
            if(largest==index){
                break;
            }
            swap(arr,largest,index);
            index=largest;
            left=index*2 +1;

        }
    }
 
    //交换函数
    public static void swap(int[] arr, int i, int j){
        int tmp;
        tmp=arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }
    
}

优先级队列PriorityQueue

优先队列不再遵循先入先出的原则,而是分为两种情况:

最大优先队列,无论入队顺序,当前最大的元素优先出队;

最小优先队列,无论入队顺序,当前最小的元素优先出队;
比如有一个最大优先队列,它的最大元素是8,那么虽然元素8并不是队首元素,但出队的时候仍然让元素8首先出队:
在这里插入图片描述
优先级队列,也叫二叉堆、堆(不要和内存中的堆区搞混了,一个是内存区域,一个是数据结构)。
​堆的本质上是一种完全二叉树,分为:
大根堆:树中每个非叶子结点都不小于其左右孩子结点的值,也就是根节点最大的堆,图a
小根堆:树中每个非叶子结点都不大于其左右孩子结点的值,也就是根节点最小的堆,图b
在这里插入图片描述

public int[] MySort (int[] arr) {
        PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>(){
            public int compare(Integer a, Integer b){
                return a.compareTo(b);
            }
        });
        for(int i=0; i< arr.length;i++)
            queue.add(arr[i]);
        int [] newarr = new int[arr.length];
        for(int i=0;i<arr.length;i++){
            newarr[i] = queue.poll();
        }
        return newarr;
    }
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据之录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值