高频8. 数组排序 手撕十大排序算法及时空复杂度,稳定性分析,重点快排 堆排序 归并排序 Java codetop

本文详细介绍了多种排序算法,包括冒泡排序、快速排序、插入排序、希尔排序、选择排序、堆排序、归并排序、计数排序、桶排序和基数排序。每种排序算法的实现代码、时间复杂度和稳定性进行了分析。此外,还讨论了不同排序算法在面对特定数据特性(如大量重复元素、数据范围有限、稳定性需求等)时的适用性。对于计数排序、桶排序和基数排序这三种线性时间复杂度的排序算法进行了特别对比。
摘要由CSDN通过智能技术生成

题目描述

排序数组

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

输入:nums = [5,2,3,1]
输出:[1,2,3,5]

示例 1:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

各种解法

请添加图片描述

  • 比较排序:元素之间的次序依赖于他们之间的比较,比较之后才能确定自己的位置。优势在于不在于数据的分布,适用于一切需要排序的情况。
  • 非比较排序:通过确定每个元素的位置之前,应该有多少个元素,从而确定自己的位置。所以一次遍历就能实现排序。但是需要占用空间来确定唯一位置,对数据规模和数据分布有一定的要求。

1. 冒泡排序

  // 1. 冒泡排序
public int[] bubbleSort(int[] array){
  if(array.length == 0){
	  return array;
  }
  for(int i=0; i<array.length; i++){
	  for(int j=0; j<array.length-1-i; j++){
		  if(array[j]>array[j+1]){
			  swap(array, j, j+1);
		  }
	  }
  }
  return array;
}

2. 快速排序

// 2. 快速排序
public int[] quickSort(int[] array, int left, int right){
	int dp;
	if(left<right){
		dp = partition(array, left, right);
		quickSort(array, left, dp-1);
		quickSort(array, dp+1, right);
	}
	return array;
}
public int partition(int[] array, int left, int right){
	int pivot = array[left];
	while(left<right){
		while(left<right && array[right]>pivot) right--;
		if(left<right) array[left++] = array[right];
		while(left<right && array[left]<pivot) left++;
		if(left<right) array[right--] = array[left];
	}
	array[left] = pivot;
	return left;
}

3. 插入排序

// 3. 插入排序
public int[] insertSort(int[] array){
	if(array.length == 0) return array;
	int cur;
	for(int i=0; i<array.length-1; i++){
		cur = array[i+1];
		int preIndex = i;
		while(preIndex>=0 && array[preIndex]>cur){
			array[preIndex+1] = array[preIndex];
			preIndex--;
		}
		array[preIndex+1] = cur;
	}
	return array;
}

4. 希尔排序

 // 4. 希尔排序
public int[] shellSort(int[] array){
	if(array.length == 0) return array;
	int length = array.length;
	int temp, gap = length/2;
	while(gap>0){
		for(int i=gap; i<length; i++){
			temp = array[i];
			int preIndex = i-gap;
			while(preIndex>=0 && temp<array[preIndex]){
				array[preIndex+gap] = array[preIndex];
				preIndex -= gap;
			}
			array[preIndex+gap] = temp;
		}
		gap /= 2;
	}
	return array;
}

5. 选择排序

 // 5. 选择排序
public int[] selectionSort(int[] array){
	if(array.length == 0) return array;
	for(int i=0; i<array.length; i++){
		int minIndex = i;
		for(int j=i; j<array.length; j++){
			if(array[j]<array[minIndex]) minIndex=j;
		}
		swap(array, minIndex, i);
	}
	return array;
}

6. 堆排序

// 6. 堆排序
public int[] heapSort(int[] array){
	if(array.length == 0) return array;
	for(int i=array.length/2-1; i>=0; i--){
		adjustHeap(array, i, array.length);
	}
	for(int j=array.length-1; j>0; j--){
		swap(array, 0, j);
		adjustHeap(array, 0, j);
	}
	return array;
}
public void adjustHeap(int[] array, int i, int length){
	int temp = array[i];
	for(int k=2*i+1; k<length; k=2*k+1){
		if(k+1<length && array[k+1]>array[k]) k++;
		if(array[k]>temp){
			swap(array, i, k);
			i = k;
		}else break;
	}
}

7. 归并排序

// 7. 归并排序
public int[] mergeSort(int[] array, int left, int right){
	if(array.length == 0) return array;
	if(left<right){
		int mid = left+(right-left)/2;
		mergeSort(array, left, mid);
		mergeSort(array, mid+1, right);
		merge(array, left, mid, right);
	}
	return array;
}
public void merge(int[] array, int left, int mid, int right){
	int[] tempArr = new int[array.length];
	int leftStart = left;
	int rightStart = mid+1;
	int tempIndex = left;

	while(leftStart<=mid && rightStart<=right){
		if(array[leftStart]<array[rightStart]){
			tempArr[tempIndex++] = array[leftStart++];
		}else{
			tempArr[tempIndex++] = array[rightStart++];
		}
	}
	while(leftStart<=mid){
		tempArr[tempIndex++] = array[leftStart++];
	}
	while(rightStart<=right){
		tempArr[tempIndex++] = array[rightStart++];
	}
	while(left<=right){
		array[left] = tempArr[left++];
	}
}

8. 计数排序

// 8. 计数排序
public int[] countSort(int[] array){
	int max = array[0];
	int min = array[0];
	for(int i=0; i<array.length; i++){
		if(array[i]>max){
			max = array[i];
		}
		if(array[i]<min){
			min = array[i];
		}
	}
	int d = max-min;
	// 每个数的个数
	int[] countArray = new int[d+1];
	for(int i=0; i<array.length; i++){
		countArray[array[i]-min]++;
	}
	// 每个数的个数加上前面所有数的个数之和
	for(int i=1; i<countArray.length; i++){
		countArray[i]+=countArray[i-1];
	}
	// 排好序的数组
	int[] sortedArray = new int[array.length];
	for(int i=array.length-1; i>=0; i--){
		sortedArray[countArray[array[i]-min]-1] = array[i];
		countArray[array[i]-min]--;
	}
	for(int i=0; i<array.length; i++){
		array[i] = sortedArray[i];
	}
	return array;
}

9. 桶排序

// 桶排序
public static void bucketSort(int[] a, int max) {
	int[] buckets;

	if (a==null || max<1)
		return ;

	// 创建一个容量为max的数组buckets,并且将buckets中的所有数据都初始化为0。
	buckets = new int[max];

	// 1. 计数
	for(int i = 0; i < a.length; i++) 
		buckets[a[i]]++; 

	// 2. 排序
	for (int i = 0, j = 0; i < max; i++) {
		while( (buckets[i]--) >0 ) {
			a[j++] = i;
		}
	}

	buckets = null;
}

10. 基数排序

// 10. 基数排序
public int[] radixSort(int[] array){
	int max = array[0];
	int exp;
	for(int arr: array){
		if(arr>max) max = arr;
	}
	// 从个位开始
	for(exp=1; max/exp>0; exp*=10){
		int[] temp = new int[array.length];
		int[] buckets = new int[10];

		for(int value: array){
			buckets[(value/exp)%10]++;
		}
		for(int i=1; i<10; i++){
			buckets[i] += buckets[i-1];
		}
		for(int i=array.length-1; i>=0; i--){
			temp[buckets[(array[i]/exp)%10]-1] = array[i];
			buckets[(array[i]/exp)%10]--;
		}
		System.arraycopy(temp, 0, array, 0, array.length);  
	}
	return array;
}

基数排序 vs 计数排序 vs 桶排序
  • 基数排序:根据键值的每位数字来分配桶
  • 计数排序:每个桶只存储单一键值
  • 桶排序:每个桶存储一定范围的数值

排序算法复杂度及稳定性

  1. 稳定:两个相等的数,在排序后,相对位置不变
  2. 不稳定:两个相等的数,在排序后,相对位置变化
    在这里插入图片描述

各排序算法适用的情况

  • 包含大量重复的元素:三路快排
  • 数据的取值范围非常有限:计数排序
  • 要求稳定排序:归并就比快排好了
  • 数据的存储情况的区别:
    • 快排依赖于数组的随机存储
    • 链表存储更适合归并排序
    • 数据量很大或者内存很小,不足以装载在内存中,使用外排序算法。不用全部装载到内存中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值