数据结构丨排序

几乎没怎么写!只写了一些作业内容和老师要求"特别"掌握的。

基本概念

排序目的:提高查找效率。

排序算法的稳定性:即相对次序没发生改变——稳定,否则不稳定。

内排序与外排序

内排序:插入排序、选择排序、交换排序、归并排序、分配排序

排序算法的评价标准:

(1)排序的时间开销:数据比较次数与数据移动次数

(2)分析情况:平均情况、最好情况、最坏情况

算法执行时所需的附加存储:即辅助排序算法完成所需的存储空间。

排序的分类

一、插入排序
1. 直接插入排序
2. 折半插入排序
3. 希尔排序(Shell)

二、交换排序
1. 冒泡排序
2. 快速排序

三、选择排序
1. 直接选择排序
2. 锦标赛排序
3. 堆排序

四、归并排序
1. 二路归并

五、分配排序
1. 桶排序
2. 基数排序

排序方法

(?

插入排序 void InsertSort( );

交换排序 void SwapSort( );

选择排序 void SelectSort( );

归并排序 void MergeSort( );

分配排序 void AssignSort( );

快速排序 void QuickSort( );

一、插入排序

1. 直接插入排序

从第二个开始往右取基准,基准左边的为已经排好序的。依次往左边检查,插入到合适的位置。

麻将排序??

2. 折半插入排序

3. 希尔排序

二、交换排序

1. 冒泡排序🫧

void BubbleSort()

省流:元素两两相比,大的放后面,于是第一趟最大的就会在最后面。

时间复杂度:O(n^2)

空间复杂度:O(1)

代码: 

// 冒泡排序 // [0]作为temp
void BubbleSort(int* arr, int n) {
	for (int i = 1; i < n; i++) {
		// 一次冒泡
		bool flag = 0;
		for (int j = 1; j < n + 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				arr[0] = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = arr[0];
				flag = 1; // 有交换
			}
		}
		// 如果没有发生交换,说明已经排好了
		if (flag == 0) break;
		// Output(arr, n);
	}
}

2. 快速排序⭐

省流:省不了哇,非常复杂的!(或者说想出这个算法的人很天才?

一次划分:

0. 首先要设定low和high两个指针(不是真的指针,就是单纯用来记录数组下标位置);

     low指向第一个元素;low指向的数扔到[0]里,把low空出来;[0]里装的是基准数。

1. 因为low是空的,所以让high一格一格向low移动。如果碰到high指向的数小于[0],把high数扔进空位置,把high位空出来。

2. 因为此时high是空的,所以......(对low操作,方法类似于1.)

3. 直到low==high,一次划分结束!此时low==high==mid。

4. 把[0]搬回[mid];return mid(方便确认下次划分范围)。

总体:

递归划分,划分完一次之后开始划分左右子表,直到最小的子表(low==high)

代码:

void QuickSort(int low, int high, int* arr); // 快速排序 // 要用[0]作为一个机动人员
int  Partition(int low, int high, int* arr); // 一次划分 // 返回mid
// 快速排序 // 要用[0]作为一个机动人员
// 从arr[low]到arr[high]做快排
// low不包括[0]
void QuickSort(int low, int high, int* arr) {
	int mid;
	if (low < high) {
		mid = Partition(low, high, arr); // 划分
		QuickSort(low, mid - 1, arr);    // 划分左子表
		QuickSort(mid + 1, high, arr);   // 划分右子表
	}
}

// 划分 // 返回应该存放的位置
int Partition(int low, int high, int* arr) {
	arr[0] = arr[low]; // 基准数
	while (low < high) // low==high停止,得出mid
	{
		// 从右向左扫描(一开始low是空的,所以先做这一边)
		while (low < high && arr[high] >= arr[0]) // 移动到 low==high 或 high<p 为止
			high--;
		arr[low] = arr[high];

		// 从左向右扫描
		while (low < high && arr[low] <= arr[0]) // 移动到 low==high 或 low>p 为止
			low++;
		arr[high] = arr[low];
	}
	// 此时low==mid==high
	arr[low] = arr[0]; // 把基准数搬回mid位
	return low;
}
// 划分(更符合本人脑子版)
int Partition(int low, int high, int* arr) {
	arr[0] = arr[low]; // 基准数。存入[0]
	while (low < high) { // low==high时停止
		while (low < high) { // low==high时停止
			if (arr[high] < arr[0]) { // 和基准数比较大小,判断是否要挪动
				arr[low] = arr[high]; // [low]是空的,把[high]放入[low]
				break; // 换另一边往中间移
			}
			high--; // 不挪动的话,指针继续往中间走
		}
		while (low < high) { // 同理
			if (arr[low] > arr[0]) {
				arr[high] = arr[low];
				break;
			}
			low++;
		}
	} // 此时 low==high,也就是mid
	arr[low] = arr[0]; // 把基准数搬回mid
	return low; // mid
}

三、选择排序

1. 直接选择排序🎃

省流:

1. 第一次选最大的放一边,第二次选第二大的放最大的的旁边

2. 注意这里的"放一边"不是插入的放,而是交换的放

时间复杂度:O(n^2)(anytime)

空间复杂度:O(1)

稳定性:不稳定

// 直接选择排序
void SelectSort(int* arr, int n) {
	for (int i = 1; i < n; i++) {
		// 一次选择 // 选一个最大的放右边
		arr[0] = arr[1]; // 设置一个max(数值)
		int maxIndex = 1; // I can't believe I still need one more variable TTT
		for (int j = 1; j <= n + 1 - i; j++) {
			if (arr[j] > arr[0]) {
				arr[0] = arr[j];
				maxIndex = j;
			}
		}
		// 交换 [n+1-i] 和 [maxIndex]
		// 此时 [0] 已经没用啦。用它当temp
		arr[0] = arr[n+1-i];
		arr[n + 1 - i] = arr[maxIndex];
		arr[maxIndex] = arr[0];
		// Output(arr, n);
	}
}

2. 锦标赛排序

3. 堆排序⭐

要用到堆。

二叉树的顺序存储:(此时[0]作为机动人员。

i的左孩子:2i

i的右孩子:2i+1

大根堆(大顶堆):完全二叉树中,根>=左、右

小——同理。

过程:

1. 建立大根堆,检查分支节点,从最下层开始调整。

代码:

// SeqList 顺序表(数组)
class SeqList {
	int* data;   // 存放数组
	int maxSize; // 最大容量
	int last;    // 末位下标 // 比如 0 1 2 3 // last=3
public:
	SeqList(int size);            // 构造函数
	~SeqList() { delete[] data; } // 析构函数

	void Input(int size); // 输入
	void Output();        // 输出

	void BuildMaxHeap();             // 建立大根堆
	void HeadAdjust(int v, int len); // 将以v为根的子树调整为大根堆
	void HeapSort();                 // 堆排序
};
// 建立大根堆
void SeqList::BuildMaxHeap() {
	// 从最后一个分支节点[last/2]开始调整
	for (int i = last / 2; i > 0; i--) {
		HeadAdjust(i, last);
	}
}

// 将以v为根的子树调整为大根堆
// 有len是因为之后要处理变长的数组
void SeqList::HeadAdjust(int v, int len) {
	data[0] = data[v]; // 暂存根节点的值

	int i;
	while (2 * v <= len) // 掉到没得掉为止(无左/右子树)
	{
		// 根结点v
		i = 2 * v; // 左结点i
		// 如果有右结点
		if (i < len) {
			// 判断左右结点大小
			if (data[i] < data[i + 1]) {
				i = i + 1; // i:左右结点中更大的下标
			}
			// cout << "data[i]:" << data[i] << endl;
		}
		// 判断根结点和左右结点大小
		if (data[0] >= data[i]) break; // 符合的话就不用接着下坠了。
		// 用data[0],不要用data[v]。data[v]应该是个空的
		// 不符合
		else {
			// 交换
			data[v] = data[i];
			// data[i] = data[0]; // 先别急着放回去。找到那个不用换的根结点[v]再换。
		}
		// 不符合才要用新结点
		v = i; // 根节点v
	}
	data[v] = data[0];
}

// 堆排序
void SeqList::HeapSort() {
	// 建立初始堆
	BuildMaxHeap();
	// cout << "初始堆:";
	// Output();
	for (int i = last; i > 1; i--) {
		// 堆顶和末尾互换
		data[0] = data[1];
		data[1] = data[i];
		data[i] = data[0];
		// cout << "交换后:";
		// Output();
		// 再整理剩下的堆
		HeadAdjust(1, i - 1); // 记得-1
		// cout << "last:" << i - 1 << endl;
		// cout << "整理堆:";
		// Output();
	}
}

四、归并排序

1. 二路归并

五、分配排序

1. 桶排序

2. 基数排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值