第8章 排序

本笔记是对《王道数据结构》中各章节涉及的基础知识进行整理。本笔记主要用以应对夏令营面试中可能会问到的数据结构方面的问题,比较泛泛而谈,如果您对这些内容感兴趣,建议参考原书。大佬可自行绕路

更多章节内容请参见:保研复习——数据结构篇-CSDN博客

目录

知识框架:

总结:

插入排序:

直接插入排序:

希尔排序:

折半插入排序:

交换排序:

冒泡排序:

快速排序:

代码:

选择排序:

简单选择排序:

堆排序:

归并排序和基数排序:

归并排序:

代码:

基数排序:

外部排序:

总结:


知识框架:

总结:

插入排序:

直接插入排序:

直接插入排序(Insertion Sort)是一种简单且直观的排序算法,其核心思想是构建一个有序序列,对于未排序的数据,在已排序序列中从后向前扫描,找到相应位置并插入时间复杂度是O(n^2)

希尔排序:

希尔排序(Shell Sort),也称为递减增量排序算法,是插入排序的一种改进版本。其核心思想是通过将原序列分割成若干个子序列分别进行插入排序,从而提高排序效率。随着排序过程的推进,子序列的规模逐渐减小,直到最后进行一次常规的插入排序(增量为1),此时整个序列已经接近有序,因此插入排序的效率较高。时间复杂度为O (n^ (1.3-2))

折半插入排序:

折半插入排序(Binary Insertion Sort)是一种对插入排序进行优化的排序算法。其核心思想是在插入未排序元素时,通过二分查找法找到插入位置,从而减少比较操作的次数。尽管比较次数减少了,但移动元素的次数并没有改变,因此整体时间复杂度仍然是 O(n^2)

交换排序:

冒泡排序:

冒泡排序是一种简单直观的排序算法,其核心思想是多次遍历待排序序列每次遍历时比较相邻的元素,如果顺序不对就交换它们的位置,直到整个序列排序完成时间复杂度为O(n^2)

快速排序:

快速排序(Quick Sort)是一种基于分治法的高效排序算法。其核心思想是每次排序前先找到一个枢纽值,然后通过一次排序将待排序序列分成两个子序列,其中一个子序列的所有元素都小于(或等于)另一个子序列的所有元素,然后递归地对这两个子序列进行快速排序。时间复杂度为O(nlogn)

代码:

void quick_sort(int q[], int l, int r) {
	if (l >= r) return;

	int i = l - 1, j = r + 1, x = q[(l + r) / 2];

	while (i < j) {
		do i++; while (q[i] < x);
		do j--; while (q[j] > x);
		if (i < j) swap(q[i], q[j]);
	}
	quick_sort(q, l, j), quick_sort(q, j + 1, r);
}

选择排序:

简单选择排序:

简单选择排序(Selection Sort)是一种直观且容易理解的排序算法。其核心思想是每次从未排序部分中选择最小(或最大)的元素,将其放在已排序部分的末尾,直到整个序列排序完成。时间复杂度为O(n^2)

堆排序:

堆排序的核心思想是:将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点,将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成构造成一个堆,这样就会得到n个元素中的次大值。如此反复执行,便能得到一个有序序列了。时间复杂度是O(nlogn)

归并排序和基数排序:

归并排序:

  1. 分解:归并排序开始于将待排序的数组不断地“一分为二”,直到每个子数组只包含一个元素。这个过程是递归进行的,即每个子数组也会继续被分解成更小的子数组,直到每个子数组只包含一个元素。
  2. 递归排序与合并
    • 在分解过程完成后,递归地开始合并这些子数组。合并时,会取出两个相邻的子数组,并将它们合并成一个有序的新数组。
    • 合并过程中,会比较两个子数组中的元素,并按照大小顺序依次放入新数组中,直到两个子数组中的所有元素都被考虑完毕。
    • 这个合并过程是递归进行的,每次合并两个子数组,生成的新有序数组又会被视为新的子数组,继续参与后续的合并过程。

时间复杂度为O(nlogn) 

代码:

void merge_sort(int q[], int l, int r) {
	if (l >= r) return;

	int mid = (l + r) >> 1;
	merge_sort(q, l, mid);
	merge_sort(q, mid + 1, r);

	int k = 0, i = l, j = mid + 1;
	while (i <= mid && j <= r) {
		if (q[i] <= q[j]) tmp[k++] = q[i++];
		else tmp[k++] = q[j++];
	}

	while (i <= mid) tmp[k++] = q[i++];
	while (j <= r) tmp[k++] = q[j++];

	for (i = l, j = 0;i <= r;i++, j++) q[i] = tmp[j];
}

基数排序:

基数排序,基数排序的思想是把位数相同的一组数组依次从后往前比较其每一位上的大小,经过几轮比较使得数据达到有序的做法。比较的次数跟数据的位数有关系。比如要比较一组手机号码从小到大排列,可以比较手机号每一位大小,然后比较11次,手机号达到有序。

注意:基数排序每次位的比较可以使用线性排序的方式,比如桶排序或者计数排序,因为它们的时间复杂度为O(n),而且每轮的比较需要保证每次比较数据的稳定性,不然基数排序就无法完成。

基数排序每一位的比较可以使用线性排序,比如桶排序或者计数排序,当然需要保证如计数排序的稳定性。每次排序时间复杂度O(n),那么如果有k位,则时间复杂度为O(k*n),如果k不大比如手机号11位,那么时间复杂度就是O(n) 。

外部排序:

总结:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值