【数据结构】排序(Java代码实现)

选择排序

每趟从未排序的序列中选出最小值,放到已排序序列的末尾。

private void selectSort(int[] arr) {
	int len = arr.length;
	for (int i = 0; i < len - 1; i++) {
		// 每一趟从未排序的序列中选出最小值的下标ail
		int minIndex = i;
		for (int j = i + 1; j < len; j++) {
			if (arr[j] < arr[minIndex]) {
				minIndex = j;
			}
		}
		// 选出的最小值成为已排序序列的末尾,这通过一次交换实现
		int temp = arr[i];
		arr[i] = arr[minIndex];
		arr[minIndex] = temp;
	}
}

// tip:第一趟i指向0,然后从1开始找最小值,交换二者

 
 

插入排序

对于当前的数字,在已排序的序列中从后向前扫描,找到正确的位置并插入。

private void insertionSort(int[] arr) {
	int len = arr.length;
	for (int i = 1; i < len; i++) {
		int temp = arr[i];	 // 暂存arr[i]的值,因为它会因为后挪而被覆盖
		int index = i;		 // index指向arr[i]应该存放的位置
		while (index - 1 >= 0 && arr[index - 1] > temp) {
			arr[index] = arr[index - 1];
			index--;
		}
		arr[index] = temp;
	}
}

// tip:第一个元素不用插入到前面,所以遍历从1开始

 
 

冒泡排序

相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最大元素“冒泡”到这趟的最后。

private void bubbleSort(int[] arr) {
	int len = arr.length;
	for (int i = 0; i < len - 1; i++) {
		for (int j = 0; j < len - i - 1; j++) {
			if (arr[j] > arr[j + 1]) {
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}

// tip:for(i < len - 1)和for(j < len - i - 1)要背熟

 
 

希尔排序

将序列依据增量分组,对每组使用直接插入排序算法;当增量二分减小到1时,对序列进行最后一次排序。

private void shellSort(int[] arr) {
	int len = arr.length;
	for (int gap = len / 2; gap > 0; gap /= 2) {	// 增量二分减小
		for (int i = gap; i < len; i++) {			// 多组插入排序交叉进行(这里需要理解)
			int temp = arr[i];
			int index = i;
			while (index - gap >= 0 && arr[index - gap] > temp) {
				arr[index] = arr[index - gap];
				index -= gap;
			}
			arr[index] = temp;
		}
	}
}

// tip:gap是不断二分的;内层的for循环是for(int i = gap; i < len; i++)

 
 

快速排序

从序列中选出一个基准(最左侧元素),走一躺之后,基准被换到了中间,基准左侧的元素都比基准要小,基准右侧的元素都比基准要大,然后递归排序左侧和右侧的序列。

private void quickSort(int[] arr, int L, int R) {
	if (L >= R) {
		return;
	}
	int left = L;
	int right = R;
	// 基准
	int temp = arr[left];	
	while (left < right) {
		// 找到右侧第一个比基准小的元素
		while (left < right && arr[right] >= temp) {
			right--;
		}
		arr[left] = arr[right];
		// 找到左侧第一个比基准大的元素
		while (left < right && arr[left] <= temp) {
			left++;
		}
		arr[right] = arr[left];
	}
	arr[left] = temp;
	// 最后left和right会收缩到基准点的正确位置处
	quickSort(arr, L, left - 1);
	quickSort(arr, left + 1, R);
}

// arr[a]=arr[b],则arr[b]处冗余,等待被覆盖

 
 

归并排序

使用分治的思想,先排左子序列,再排右子序列,然后二路归并;对于左子序列/右子序列,分别递归。

private void mergeSort(int[] arr, int left, int right) {
	if (left >= right) {
		return;
	}
	int mid = left + (right - left) / 2;	// 二分为左右子序列
	mergeSort(arr, left, mid);				// 左子序列排序
	mergeSort(arr, mid + 1, right);			// 右子序列排序
	merge(arr, left, mid, right);			// 二路归并
}

private void merge(int[] arr, int left, int mid, int right) {
	int[] temp = new int[arr.length];		// 辅助数组,可提升作用域防止重复开辟
	int i = left;
	int j = mid + 1;
	int t = left;
	while (i <= mid && j <= right) {
		if (arr[i] < arr[j]) {
			temp[t++] = arr[i++];
		} else {
			temp[t++] = arr[j++];
		}
	}
	while (i <= mid) {
		temp[t++] = arr[i++];
	}
	while (j <= right) {
		temp[t++] = arr[j++];
	}
	while (left <= right) {
		arr[left] = temp[left];
		left++;
	}
}

// tip:二路归并merge方法需要三个指针参数;注意mid指向的位置时偏左的

 
 

堆排序

先通过多次调整堆构建出大顶堆,然后不断进行交换堆顶和堆尾元素、调整大顶堆的操作。

private void heapSort(int[] arr) {
	int len = arr.length;
	// 构建大顶堆,即从第一个非叶子结点从下至上、从右至左调整堆结构
	for (int i = len / 2 - 1; i >= 0; i--) {
		adjustHeap(arr, i, len);
	}
	// 交换堆顶和堆尾元素+调整大顶堆
	for (int j = len - 1; j > 0; j--) {
		int temp = arr[j];
		arr[j] = arr[0];
		arr[0] = temp;
		adjustHeap(arr, 0, j);
	}
}

// 调整大顶堆
private void adjustHeap(int[] arr, int i, int len) {
	int temp = arr[i];
	for (int k = i * 2 + 1; k < len; k = k * 2 + 1) {
		if (k + 1 < len && arr[k] < arr[k + 1]) {   // 保证k指向i的两个子结点的最大者
			k++;
		}
		if (arr[k] > temp) {    // 本次调整可能破坏下面的子堆,所以将i置为k继续调整
			arr[i] = arr[k];
			i = k;
		} else {
			break;
		}
	}
	arr[i] = temp;
}

// tip:13526的建堆过程极其经典

 
 

计数排序

计数排序不是基于比较的,而是将元素存储到额外的存储空间中,然后取出。

private void countingSort(int[] arr, int min, int max) {
	int[] count = new int[max - min + 1];
	for (int n : arr) {
		count[n - min]++;
	}
	int index = 0;
	for (int i = 0; i < count.length; i++) {
		while (count[i] > 0) {
			arr[index++] = min + i;
			count[i]--;
		}
	}
}

// tip:注意偏移

 
 

桶排序

将元素放到有限数量的桶里,在每个桶再分别排序。

private void bucketSort(int[] arr, int min, int max) {
	int len = arr.length;                       // 桶的容量
	int bucketNum = (max - min) / len + 1;      // 桶的数量

	// 初始化桶
	List<List<Integer>> buckets = new ArrayList<>();
	for (int i = 0; i < bucketNum; i++) {
		buckets.add(new ArrayList<>());
	}

	// 元素放入桶
	for (int n : arr) {
		int id = (n - min) / len;
		buckets.get(id).add(n);
	}

	// 桶内排序
	for (List<Integer> bucket : buckets) {
		Collections.sort(bucket);
	}

	int index = 0;
	for (List<Integer> bucket : buckets) {
		for (int n : bucket) {
			arr[index++] = n;
		}
	}
}

// tip:可能一个桶要装所有的元素,所以桶的容量为arr.length;为了保证最大值由桶可装,桶的数量向上+1

 
 

基数排序

先根据个位进行桶排序,再根据十位进行桶排序,再根据百位进行桶排序…整个序列逐渐完成排序。

private void radixSort(int[] arr, int max) {
	int len = arr.length;
	int[][] buckets = new int[10][len];     // 桶
	int[] bucketSize = new int[10];         // 每个桶的实际容量
	
	for (int i = 0, mod = 1; i < String.valueOf(max).length(); i++, mod *= 10) {
		// 元素入桶
		for (int n : arr) {
			int id = n / mod % 10;
			buckets[id][bucketSize[id]++] = n;
		}
		// 元素出桶
		int index = 0;
		for (int j = 0; j < 10; j++) {
			for (int k = 0; k < bucketSize[j]; k++) {
				arr[index++] = buckets[j][k];
			}
			bucketSize[j] = 0;
		}
	}
}

// tip:基数排序是个逐渐有序的过程

 
 
 
 
 
 
 
 

 
 

复杂度与稳定性

时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性
选择排序O(n²)O(n²)O(n²)O(1)不稳定
插入排序O(n²)O(n²)O(n)O(1)稳定
冒泡排序O(n²)O(n²)O(n)O(1)稳定
希尔排序O(n)~O(n²)O(1)不稳定
快速排序O(nlog2n)O(n²)O(nlog2n)O(log2n)不稳定
归并排序O(nlog2n)O(nlog2n)O(nlog2n)O(n)稳定
堆排序O(nlog2n)O(nlog2n)O(nlog2n)O(1)不稳定
计数排序O(n+k)O(n+k)O(n+k)O(k)稳定
桶排序O(n+k)O(n²)O(n)O(k)稳定
基数排序O(n*r)O(n*r)O(n*r)O(k)稳定

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

☘️

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值