数据结构——排序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<assert.h>
typedef  int int64_t;
void Swap(int* a, int* b) {
	int tmp = *a;
	*a = *b;
	*b = tmp;
	return;
}

void PrintArray(int array[], int64_t size) {
	printf("打印数组结果:\n");
	int64_t i = 0;
	for (; i < size; ++i) {
		printf("%d ", array[i]);
	}
	printf("\n");
}

///////////////////////////////////////////////////////
// 实现冒泡排序. 基于一个整数的数组进行排序
// 约定按照元素的大小升序排序
// 时间复杂度: O(N ^ 2)
// 空间复杂度: O(1)
// 稳定性: 稳定排序
///////////////////////////////////////////////////////
void BubbleSort(int array[], int64_t size) {
	if (size <= 1) {
		return;
	}
	int64_t bound = 0;
	// 采用每次循环找到一个最小的数字的方式完成冒泡
	// [0, bound) 就是当前的有序区间
	// [bound, size) 就是待排序区间
	for (; bound < size; ++bound) {
		// 每次循环一次, 有序区间就增加了一个元素
		// 本次循环的目的就是找到一个当前的最小数字
		int64_t cur = size - 1;
		for (; cur > bound; --cur) {
			if (array[cur] < array[cur - 1]) {
				// 此处交换两个元素
				Swap(&array[cur], &array[cur - 1]);
			}  // end if
		}  // end for
	} // end for
	return;
}

///////////////////////////////////////////////////////
// 选择排序
// 时间复杂度: O(N ^ 2)
// 空间复杂度: O(1)
// 稳定性: 不稳定
///////////////////////////////////////////////////////
void SelectSort(int array[], int64_t size) {
	if (size <= 1) {
		return;
	}
	int64_t bound = 0;
	// [0, bound) 有序区间
	for (; bound < size; ++bound) {
		// 找出当前区间中的最小元素
		// 使用 array[bound] 来作为擂台
		int64_t cur = bound;
		for (; cur < size; ++cur) {
			if (array[cur] < array[bound]) {
				Swap(&array[cur], &array[bound]);
			}
		}  // end for
	}  // end for
	return;
}

///////////////////////////////////////////////////////
// 插入排序
// 时间复杂度: O(N ^ 2)
// 空间复杂度: O(1)
// 稳定性: 稳定排序
///////////////////////////////////////////////////////
void InsertSort(int array[], int64_t size) {
	if (size <= 1) {
		return;
	}
	int64_t bound = 1;
	// [0, bound) 有序区间
	// [bound, size) 待排序区间
	// 把 bound 位置的元素尝试插入到前面有序线性表的合理位置
	for (; bound < size; ++bound) {
		int bound_value = array[bound];
		int64_t i = bound;
		for (; i > 0; --i) {
			if (array[i - 1] > bound_value) {
				// 进行搬运
				array[i] = array[i - 1];
			}
			else {
				// 已经找到了一个合理的位置. 
				break;
			}
		}
		// 下面这行代码表示两重含义: 
		// 1. 当前数组中的这个元素已经小于 bound_value, 就需要
		// 把 bound_value 放到这个元素的后面
		// 也就是放到下标为 i 的位置上
		// 2. 处理循环结束的情况. 如果 bound_value
		// 刚好是当前有序顺序表中
		// 最小的元素, 此时达到 i == 0 的时候, 循环就退出了.
		// 此时需要把 bound_value 设置到 下标为 0 的位置上.
		array[i] = bound_value;
	}
}

///////////////////////////////////////////////////////
// 堆排序
// 时间复杂度: O(NlogN)
// 空间复杂度: O(1)
// 稳定性: 不稳定排序
///////////////////////////////////////////////////////

// index 表示从当前下标开始进行调整
// 创建一个大堆, 要求父节点大于左右子树
void AdjustDown(int array[], int64_t size, int64_t index) {
	int64_t parent = index;
	int64_t child = 2 * parent + 1;  // 左子树
	while (child < size) {
		// 先拿左右子树进行比较, 看谁比较大, 然后再拿大的和
		// 父节点进行比较
		if (child + 1 < size
			&& array[child + 1] > array[child]) {
			// 右子树比左子树大, 就让 child 指向右子树
			child = child + 1;
		}
		if (array[child] > array[parent]) {
			Swap(&array[child], &array[parent]);
		}
		else {
			// 此时调整就调整完了~~
			break;
		}
		parent = child;
		child = 2 * parent + 1;
	}  // end while
	return;
}

void HeapCreate(int array[], int64_t size) {
	if (size <= 1) {
		return;
	}
	// 下沉式调整, 需要从后往前遍历,
	// 从最后一个非叶子节点开始遍历
	// size - 1表示最后一个元素的下标
	// 拿着这个下标 - 1 / 2 就得到了当前元素的父节点
	int64_t i = (size - 1 - 1) / 2;
	for (; i >= 0; --i) {
		AdjustDown(array, size, i);
	}
}

void HeapPop(int array[], int64_t size) {
	if (size <= 1) {
		return;
	}
	Swap(&array[0], &array[size - 1]);
	AdjustDown(array, size - 1, 0);
}

void HeapSort(int array[], int64_t size) {
	if (size <= 1) {
		return;
	}
	// 1. 建立大堆
	HeapCreate(array, size);
	// 2. 循环删除堆顶元素
	// 每次删除一个元素, 就把当前的最大值放到数组末尾了
	int64_t i = 0;
	for (; i < size; ++i) {
		HeapPop(array, size - i);
	}
}

///////////////////////////////////////////////////////
// 希尔(Shell)排序, 一种改进版本的插入排序
// 时间复杂度: 对于希尔序列, O(N ^ 2);
// 对于最优序列, 复杂度达到 O(N ^ 1.3)
// 空间复杂度: O(1)
// 稳定性: 不稳定排序
///////////////////////////////////////////////////////
void ShellSort(int array[], int64_t size) {
	if (size <= 1) {
		return;
	}
	// 此处我们使用希尔序列: N/2, N/4, N/8...1
	int64_t gap = size / 2;
	for (; gap >= 1; gap /= 2) {
		// 对当前的序列进行插入排序. 序列的组成类似于:
		// 0, gap, 2gap, 3gap...
		// 1, gap + 1, 2gap + 1, 3gap + 1
		// ...
		int64_t bound = gap;
		for (; bound < size; ++bound) {
			// 此处我们从1开始计数
			// 先插入第一组的第二个元素
			// 再插入第二组的第二个元素
			// 再插入第三组的第二个元素
			// ...
			// 再插入第一组的第三个元素
			// ... 
			// bound 位置的元素就是待插入元素
			int bound_value = array[bound];
			int64_t i = bound;
			for (; i >= gap; i -= gap) {
				// 第三重循环, 负责在当前组中找到一个合适位置,
				// 插入并进行搬运
				if (array[i - gap] > bound_value) {
					array[i] = array[i - gap];
				}
				else {
					// 找到合适的位置了, 需要把 bound_value 插入到
					// 当前的 i - 1 之后的位置, 也就是 i 的位置
					break;
				}
			}
			array[i] = bound_value;
		} // end for
	}  // end while
	return;
}

///////////////////////////////////////////////////////
// 归并排序
// 时间复杂度: O(NlogN)
// 空间复杂度: 对于数组来说, O(N)
// 稳定性: 稳定排序
///////////////////////////////////////////////////////
void MergeArray(int array[], int64_t beg, int64_t mid,
	int64_t end, int* tmp) {
	int64_t output_index = beg;
	int64_t cur1 = beg;
	int64_t cur2 = mid;
	while (cur1 < mid && cur2 < end) {
		if (array[cur1] < array[cur2]) {
			// 就把 cur1 位置的元素插入到结果序列中
			tmp[output_index++] = array[cur1++];
		}
		else {
			tmp[output_index++] = array[cur2++];
		}
	}
	// 都需要把剩下的数组的元素追加到 tmp 的末尾
	while (cur1 < mid) {
		tmp[output_index++] = array[cur1++];
	}
	while (cur2 < end) {
		tmp[output_index++] = array[cur2++];
	}
	// 需要把 tmp 中的元素拷贝回原有的数组中
	memcpy(array + beg, tmp + beg, sizeof(int)* (end - beg));
}

void _MergeSort(int array[], int64_t beg,
	int64_t end, int* tmp) {
	if (end - beg <= 1) {
		// 处理只有一个元素的情况
		// 对于区间中只有一个元素来说, 此时就一定是有序的.
		return;
	}
	int64_t mid = beg + (end - beg) / 2;
	// 保证左右两个区间都是有序区间
	_MergeSort(array, beg, mid, tmp);
	_MergeSort(array, mid, end, tmp);
	// 在左右都有序的基础上才能进行归并
	// [beg, mid), [mid, end)
	MergeArray(array, beg, mid, end, tmp);
}

// 递归的层数 logN
// 总的递归的次数 2logN 

void MergeSort(int array[], int64_t size) {
	// 先创建好缓冲区, 以备后面使用, 缓冲区的大小需要和
	// 原有的数组长度相同.
	int* tmp = (int*)malloc(sizeof(int)* size);
	// 下划线版本的函数辅助进行递归
	// 第一个参数, 输入数组的起始位置.
	// 第二个第三个参数表示当前进行归并排序的区间
	// [0, size)
	// 第四个参数表示用于归并的缓冲区起始位置
	_MergeSort(array, 0, size, tmp);
	free(tmp);
}

// 非递归版本的归并排序
void MergeSortByLoop(int array[], int64_t size) {
	if (size <= 1) {
		return;
	}
	int* tmp = (int*)malloc(sizeof(int)* size);
	// 分别对长度为1, 2, 4, ... N/2 数组进行合并
	int64_t  gap = 1;  // 当前处理序列长度为1的数组
	for (; gap <= size / 2; gap *= 2) {
		// 把当前步长情况下的所有区间两两合并
		int64_t i = 0;
		for (; i < size; i += 2 * gap) {
			// 每次循环处理相邻的两个区间
			// [i, i + gap)
			// [i+ gap, i + 2*gap)
			int64_t beg = i;
			int64_t mid = i + gap;
			if (mid >= size) {
				mid = size;
			}
			int64_t end = i + 2 * gap;
			if (end >= size) {
				end = size;
			}
			MergeArray(array, beg, mid, end, tmp);
		}
	}
	free(tmp);
}

///////////////////////////////////////////////////////
// 快速排序
// 时间复杂度: 最坏 O(N ^ 2)(如果数组是逆序的) 平均水平 O(NlogN)
// 空间复杂度: O(N) 递归. 最坏情况下要递归 N 层
// 稳定性: 不稳定排序
///////////////////////////////////////////////////////

int64_t Partion(int array[], int64_t beg, int64_t end) {
	int64_t left = beg;
	int64_t right = end - 1;
	int tmp = array[end - 1];   // 基准值
	while (left < right) {
		// 1. 先让 left 从左往右找, 找到第一个大于基准值的元素
		while (left < right && array[left] <= tmp) {
			++left;
		}
		// 此循环结束之时, left 就指向了第一个大于 基准值 的元素

		// 2. 再让 right 从右往左找, 找到第一个小于基准值的元素
		while (left < right && array[right] >= tmp) {
			--right;
		}
		// 此循环结束之时, right 就指向了第一个小于 基准值 的元素

		if (left < right) {
			Swap(&array[left], &array[right]);
		}
	}
	// left 和 right 重合之后, 就把基准值和left指向的元素
	// 再进行交换
	// 如何证明 left 和 right 重合之后, left 指向的元素一定
	// 大于等于基准值呢?
	// 情况有两种:
	// 1. 由于 ++left, 导致 left 和 right 重合
	//    此时就看 right 之前指向的元素是否是大于等于基准值的元素
	//    结合第二个循环和swap操作, right也指向了一个大于基准值的元素   
	// 2. 由于 --right, 导致 right 和 left 重合
	//    此时就看 left 之前指向的元素是否是大于等于基准值的元素
	//    结合第一个循环, 确实 left 指向了一个大于等于基准值的元素
	Swap(&array[left], &array[end - 1]);
	return left;
}

void _QuickSort(int array[], int64_t beg, int64_t end) {
	if (end - beg <= 1) {
		return;
	}
	// 借助 Partion 函数完成基准值的选择以及数字的交换过程
	// 返回值表示基准值最终的位置
	int64_t mid = Partion(array, beg, end);
	// 基准值左右的区间分别是
	// [beg, mid), [mid+1, end)
	_QuickSort(array, beg, mid);
	_QuickSort(array, mid + 1, end);
}

void QuickSort(int array[], int64_t size) {
	// 借助 _QuickSort 完成递归. 
	// [0, size) 表示要对哪部分数据进行快速排序
	_QuickSort(array, 0, size);
}

// 非递归版本的快速排序
// 操作思路和归并排序很像, 都是倒腾区间
// 借助一个栈
typedef struct SeqStack{
	int64_t array[100];
	int top;
}SeqStack;
void SeqStackInit(SeqStack *pstack)
{
	assert(pstack != NULL);
	pstack->top = 0;
}
void SeqStackPush(SeqStack *pstack, int64_t beg)
{
	pstack->array[pstack->top - 1] = beg;
	pstack->top++;
}
void SeqStackPop(SeqStack *pstack)
{
	pstack->top--;
}
int SeqStackTop(SeqStack *pstack)
{
	return pstack->array[pstack->top--];
}
int SeqStackEmpty(SeqStack *pstack)
{
	return pstack->top == 0 ? 1 : 0;
}
void QuickSortByLoop(int array[], int64_t size) {
	if (size <= 1) {
		return;
	}
	// 定义一个栈
	SeqStack stack;
	SeqStackInit(&stack);
	int64_t beg = 0;
	int64_t end = size;
	SeqStackPush(&stack, beg);
	SeqStackPush(&stack, end);
	while (!SeqStackEmpty(&stack)) {
		SeqStackTop(&stack, &end);
		SeqStackPop(&stack);
		SeqStackTop(&stack, &beg);
		SeqStackPop(&stack);
		if (end - beg <= 1) {
			continue;
		}

		int64_t mid = Partion(array, beg, end);
		// 此时又得到了两个待整理区间:
		// [beg, mid)
		// [mid+1, end)
		SeqStackPush(&stack, beg);
		SeqStackPush(&stack, mid);
		SeqStackPush(&stack, mid + 1);
		SeqStackPush(&stack, end);
	}
}

///////////////////////////////////////////////////////
// 以下代码为测试代码
///////////////////////////////////////////////////////
void TestBubbleSort() {
	int array[] = { 9, 5, 2, 7 };
	int tmp[20];
	//BubbleSort(array, sizeof(array) / sizeof(array[0]));
	//SelectSort(array, sizeof(array) / sizeof(array[0]));
	//InsertSort(array, sizeof(array) / sizeof(array[0]));
	//AdjustDown(array, sizeof(array) / sizeof(array[0]), 2);
	//ShellSort(array, sizeof(array) / sizeof(array[0]));
	//MergeArray(array, 0, 2, 3, &tmp);
	//QuickSortByLoop(array, sizeof(array) / sizeof(array[0]));
	Partion(array, 0, 4);
	PrintArray(array, sizeof(array) / sizeof(array[0]));
	
}

int main() {
	TestBubbleSort();
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/CDatreides/article/details/81481972
今日推荐