java面试算法汇总-排序问题

时间复杂度汇总:

在这里插入图片描述
稳定性:在排序过程中相同元素的相对位置保持不变

稳定的排序算法:

冒泡排序(Bubble Sort):冒泡排序是一种简单的比较排序算法,它比较相邻元素并交换它们,如果相邻元素相等时不进行交换,因此它是稳定的。

插入排序(Insertion Sort):插入排序也是一种简单的比较排序算法,它将一个元素逐个插入到已排序的部分中,当遇到相等的元素时,不改变它们的相对顺序,因此它是稳定的。

归并排序(Merge Sort):归并排序是一种分治排序算法,它将数组分成两个子数组,分别排序,然后将它们合并成一个有序数组。在合并过程中,相等元素的相对顺序保持不变,所以归并排序是稳定的。

不稳定的排序算法:

快速排序(Quick Sort):快速排序是一种分治排序算法,它不保证相等元素的相对顺序在排序后保持不变,因此是不稳定的。

选择排序(Selection Sort):选择排序是一种简单的比较排序算法,它每次选择最小的元素并将其放在已排序部分的末尾,相等元素可能会发生交换,所以是不稳定的。

希尔排序(Shell Sort):希尔排序是插入排序的改进版,它不保证相等元素的相对顺序在排序后保持不变,因此是不稳定的

堆排序也是不稳定的排序。

稳定排序

冒泡排序

冒泡排序(Bubble Sort)算法是所有排序算法中最简单、最基础的一个,它的实现思路是通过相邻数据的交换达到排序的目的。
冒泡排序的执行流程是:

  • 对数组中相邻的数据,依次进行比较;
  • 如果前面的数据大于后面的数据,则把前面的数据交换到后面。经过一轮比较之后,就能把数组中最大的数据排到数组的最后面了;再用同样的方法,把剩下的数据逐个进行比较排序,最后得到就是从小到大排序好的数据
  • 冒泡排序比较一次就比较好了那么就是O(n) ,具体来说,当冒泡排序开始遍历数组时,如果在一轮遍历中没有发生任何元素的交换操作,那么说明数组已经按照升序或降序排列,这时排序过程可以提前终止。因此,当输入数组本身已经有序时,冒泡排序的时间复杂度是O(n)。(你可以在内层循环中添加一个标志(flag)来判断是否有元素发生了交换。如果没有发生交换,就可以提前终止排序。)
       public static void bubbleSort(int[] arr) {
	        
	        int n = arr.length;
	        
	        for (int i = 1; i < n; i++) {
	            for (int j = 0; j < n - i; j++) {
	                if (arr[j] > arr[j + 1]) {
	                    int temp = arr[j];
	                    arr[j] = arr[j + 1];
	                    arr[j + 1] = temp;
	                }
	            }
	        }
    }

直接插入排序

将无序队列中的数依次插入到有序队列中去
在这里插入图片描述
时间复杂度:正序时O(n) 逆序时O(n[^2])

当待排元素已经为正序时,或者接近正序时所比较的次数和移动次数比较少。对于小规模来说,插入排序是一个快速的排序法。

空间复杂度:O(1) 只需要一个监视哨arr[0]

import java.util.Arrays;
 
public class InsertSort {
    public static void main(String[] args){
        int[] a={12,15,9,20,6,31,24};
        Sort(a);//调用方法
    }
 
	 public static void insertionSort(int[] a) {
	 
        int n = a.length;
        
        for (int i = 1; i < n; i++) {
				int key = a[i];
				int j = i-1;
				while(j>=0&&a[j]>key)
				{
				     a[j+1] = a[j];
				     j--;
				}
				a[j+1] = key;
        }
    }
}

归并排序

归并排序是一种分治排序算法,它将数组分成两个子数组,分别排序,然后将它们合并成一个有序数组。在合并过程中,相等元素的相对顺序保持不变,所以归并排序是稳定的。

归并排序的最好最坏的时间复杂度都是0(nlogn), 空间复杂度是O(n)

public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {64, 34, 25, 12, 22, 11, 90};
        mergeSort(arr);
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }

    public static void mergeSort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return; // 数组为空或只有一个元素时无需排序
        }
        mergeSort(arr, 0, arr.length - 1);
    }

    private static void mergeSort(int[] arr, int left, int right) {
        if (left < right) {
            int mid = (left + right) / 2;
            
            // 递归分割左右子数组
            mergeSort(arr,left,mid);
            mergeSort(arr,mid+1,right);
            merge(arr,left,mid,right);
        }
    }

    private static void merge(int[] arr, int left, int mid, int right) {
        int n1 = mid-left+1;
        int n2 = right-mid;
        
        int[] leftArr = new int[n1];
        int[] rightArr = new int[n2];
		
		for(int i=0;i<n1;i++)
		{
		    int leftArr[i] = arr[left+i];
		}
		for(int j=0;j<n2;j++)
		{
		    int rightArr[j] = arr[mid+j+1];
		}
		
		int i=0,j=0,k=left;
		
		while(i<n1&&j<n2)
		{
           if(leftArr[i]<rightArr[j])
           {
                 arr[i] = leftArr[i];
                 i++;
			}
			else
			{
			     arr[j] = rightArr[j];
                 j++;
			}
			k++;
		}
	   
	   while(i<n1)
	   {
          arr[k] = leftArr[i];
          i++;
          k++;
		}
		while(j<n2)
		{
		  arr[k] = rightArr[j];
          j++;
          k++;
		}	
		
    }
}

不稳定的排序

选择排序

它的基本思想是在未排序的部分中选择最小(或最大)的元素,然后将其放在已排序部分的末尾。它的最好最坏时间复杂度是O(n^2)

public class SelectionSort {

    public static void main(String[] args) {
        int[] arr = {64, 34, 25, 12, 22, 11, 90};
        selectionSort(arr);
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }

    public static void selectionSort(int[] arr) {
        
        int n = arr.length;
        
		for(int i=0;i<n-1;i++)
		{
           int minIndex = i;
           for(int j=i+1;j<n;j++)
           {
				if(arr[minIndex]>arr[j])
				{
				   minIndex = j;
				}

			}
            // 交换最小元素和当前位置的元素
            int temp = arr[minIndex];
            arr[minIndex] = arr[i];
            arr[i] = temp;
		}
            


}

快速排序

  • 首先设定一个分界值,通过该分界值把数组分为左右两个部分;
  • 将大于等于分界值的元素放到分界值的右边,将小于分界值的元素放到分界值的左边;
  • 然后对左右两边的数据进行独立的排序,在左边数据中取一个分界值,把小于分界值的元素放到分界值的左边,大于等于分界值的元素,放到数组的右边;右边的数据也执行同样的操作;
  • 重复上述操作,当左右各数据排序完成后,整个数组也就完成了排序。

快速排序的平均时间复杂度为O(n log n),其中n是数组的长度。这是快速排序在大多数情况下的性能表现,因此它通常被认为是一种高效的排序算法。

快速排序的最好情况时间复杂度是O(n log n),当输入数据分布均匀随机时,快速排序的表现最佳,每次划分都能将数组均匀地分成两半。

然而,快速排序的最坏情况时间复杂度是O(n^2),当输入数据已经完全有序或者接近有序时,每次划分都可能将数组分成一个较大的部分和一个较小的部分,导致递归深度增加,性能下降

quickRow(quickNums, 0, quickNums.length - 1);

public static void quickRow(int[] array, int low, int high){
	   int i,j,pivot;
	   //结束条件
	   if (low >= high) {
	       return;
	   }
	   i = low;
	   j = high;
	   //选择的节点,这里选择的数组的第一数作为节点
	   pivot = array[low];
	   while (i < j){
	       //从右往左找比节点小的数,循环结束要么找到了,要么i=j
	       while (array[j] >= pivot && i < j){
	           j--;
	       }
	       //从左往右找比节点大的数,循环结束要么找到了,要么i=j
	       while (array[i] <= pivot && i < j){
	           i++;
	       }
	       //如果i!=j说明都找到了,就交换这两个数
	       if (i < j){
	           int temp = array[i];
	           array[i] = array[j];
	           array[j] = temp;
	       }
	   }
	   //i==j一轮循环结束,交换节点的数和相遇点的数
	   array[low] = array[i];
	   array[i] = pivot;
	   //数组“分两半”,再重复上面的操作
	   quickRow(array,low,i - 1);
	   quickRow(array,i + 1,high);
	}
	

堆排序

堆排序(Heap Sort)算法是利用堆结构和二叉树的一些特性来完成排序的。 其实每次堆排序就是将无序的堆排成大根堆或者小根堆。

堆排序的时间复杂度在最好情况和最坏情况下都是O(n log n),其中n是数组的长度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值