排序

排序算法的稳定性: A和B的关键字相等,排序后A、B的先后次序保持不变,则称这种排序算法是稳定的(进行元素交换/插入时时候跨区间,若跨区间,则不稳定)

插入排序

算法思想: 每一步将一个待排序的对象,按其关键字大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。
稳定性: 稳定
应用场景: 接近有序,数据量小

代码实现
void InsertSort(DataType* arr, int size)
{
	for (int i = 1; i < size; i++) //默认手里有一个数据已经排好,即在0号位置
	{
		int key = arr[i];  //保存待排数据
		int end = i - 1;//拿到已经排好的序列的最后一个元素的下标
		//找带插入元素的位置  搬移数据
		while (end >= 0 && key < arr[end])  //保证手中有数据
		{
			//所有元素向后挪移一个
			arr[end + 1] = arr[end];  
			end--;
		}
		arr[end + 1] = key;  //找到位置,插入到end的后面
	}
}

希尔排序

算法思想: 先将整个待排记录序列分割成若干个子序列(各种方式),分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录进行依次直接插入排序
稳定性: 不稳定
应用场景: 数据量大,不接近有序

代码实现
void ShellSort(DataType* arr, int size)
{
	int gap = 3;  //对数据分组
	while (gap > 0)
	{
		//插入排序
		for (int i = gap; i < size; i++) //i++时,向后走一步,但是在不同分组之间排序
		{
			int key = arr[i]; 
			int end = i - gap;
			while (end >= 0 && key < arr[end]) 
			{
				arr[end + gap] = arr[end];  
				end -= gap;
			}
			arr[end + gap] = key;  //end<=key,找到位置,插入到end的后面
		}
		gap--;	//每次把gap--,分组间距小于1,直到gap = 1 时则把所有的都放在一起排序
	}
}

选择排序

算法思想: 找到最大的元素,与最后的元素交换
稳定性: 不稳定
应用场景: 元素重复性高

代码实现
void Swap(DataType * x, DataType* y)
{
	DataType tmp;
	tmp = *x;
	*x = *y;
	*y = tmp;
}
void SelectSort(DataType* arr, int size)  //每次所传的size-1
{
	for (int i = 0; i < size - 1; i++)  //遍历数组 
	{
		int Maxpos = 0;  //默认开头位置元素最大
		for (int j = 1; j < size - 1 - i; j++)  //找到最大的元素
		{
			if (arr[Maxpos] < arr[j])  
				Maxpos = j;
		}
		if (Maxpos != size - 1 -i)
			Swap(&arr[Maxpos], &arr[size - 1 - i]); //每一次都需要向前挪动一个
	}
}
优化
//一次遍历找到最大值和最小值,放在合适的位置
void SelectSort_OP(DataType* arr, int size)
{
	int begin = 0, end = size - 1;
	while (begin < end)
	{
		int Maxpos = begin;
		int Minpos = begin;
		int i = begin + 1;
		//找元素位置
		while (i <= end)
		{
			if (arr[Maxpos] < arr[i])  
				Maxpos = i;
			if (arr[Minpos] > arr[i])  
				Minpos = i;
			i++;
		}
		if (Maxpos != end)
			Swap(&arr[Maxpos], &arr[end]);
		//交换时,若最小元素的位置为最大元素所要交换的位置,最大元素交换完成后,再交换最小元素时回出现错误,所以需要进行判断,重新定位最小值的位置
		if (Minpos == end)
			Minpos  = Maxpos;
		if (Minpos != 0)
			Swap(&arr[Minpos], &arr[begin]);
		begin++;
		end--;

	}
}

堆排序

算法思想:

  1. 将无序序列构建成堆
  2. 把堆顶元素放到序列的最后
  3. 把剩余的n-1个元素重新调整成为堆
  4. 重复,直到得到一个有序序列

稳定性: 不稳定

代码实现
void HeapAdust(DataType *arr, int  size, int parent)  //大堆
{
	int child = (parent << 1) + 1;  //找到孩子
	while (child && child <= size - 1)
	{
		//左右孩子中找最大的
		if (child + 1 < size &&arr[child] < arr[child+1])
		{
			child += 1;
		}
		if (arr[parent] < arr[child])
		{
			Swap(&arr[child], &arr[parent]);
			//继续向下判断
			parent = child;
			child = (parent << 1) + 1;
		}
		else
			return;
	}
}

//每一次都会将一个最大/最小的元素放到最后
void heap(DataType* arr, int size)
{
	//建堆
	int root = ((size - 2) >> 1);  //最后一个非叶子结点
	int end = size - 1;
	for (; root >= 0; root--)  //构建一个大堆
		HeapAdust(arr, size, root);
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);//交换堆顶与最后一个元素
		HeapAdust(arr, end, 0);  //只把end-1个元素再一次调整成堆  end相当于size 
		end--;
	}
}

冒泡排序

算法思想 每趟不断将记录两两比较,并按大小顺序交换
稳定性: 稳定的

代码实现
void BubbleSort(DataType *arr, int size)
{
	for (int i = 0; i < size - 1; i++)  //最后一个元素不需要比较
	{
		int ISChange = 0;
		for (int j = 0; j < size - i - 1; j++)
		{
			if (arr[j]> arr[j + 1])
			{
				ISChange = 1;
				Swap(&arr[j], &arr[j + 1]);
			}
		}
		//在一次排序中,若未发生交换,则说明已经有序
		if (!ISChange)
		{
			return;
		}
	}
}

快速排序

算法思想 任取一个元素(如第一个)为中心,所有比他小的元素一律前放,比它大的元素一律后放,形成左右两个子表,对各子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩一个

  1. 在待排序的序列 [left,right) 中找一个基准值(任意位置)——最右侧的值
  2. 根据基准值将序列分割左右两个部分(前面的比基准值小,后面的比基准值大)
  3. 递归分别对左右两个部分排序

稳定性: 不稳定
应用场景: 数据随机、数据量大

代码实现
//优化——降低每次划分后取到极大值或极小值的概率
int GetMiddleIndex(DataType *arr, int left, int right)//三数取中间
{
	//返回中间值的一个数
	int mid = left + (right - left) / 2;
	if (arr[left] < arr[right - 1])
	{
		if (arr[mid] < arr[left])
			return left;
		else if (arr[mid] > arr[right - 1])
			return right - 1;
		else    //mid位于中间
			return mid;
	}
	else
	{
		if (arr[mid] > arr[left])
			return left;
		else if (arr[mid] < arr[right - 1])
			return right - 1;
		else    //mid位于中间
			return mid;
	}
}
int Partion1(DataType *arr, int left, int right)
{
	//找基准值,找到中间的
	DataType index = GetMiddleIndex(arr, left, right);
	if (index != right - 1)
	{
		Swap(&arr[index], &arr[right]);
	}
	//改变基准值的找法
	DataType key = arr[right - 1];
	//双指针,一指针从头开始,二从尾开始
	int begin = left, end = right - 1;
	//一指针从头向后找一个比基准值大的,二指针从后向前找一个比基准值小的
	while (begin< end)
	{
		//防止越界访问
		while (begin < end && arr[begin] <= key) //确定begin的值
			begin++;
		while (begin < end && arr[end] >= key) //确定end的值
			end--;
		//交换一二指针,重复上一步,直到两指针相遇
		if (begin < end)	//区间有效
			Swap(&arr[begin], &arr[end]);
	}
	//交换相遇位置与最右位置(基准值)的数据交换 
	if (begin != right - 1)
		Swap(&arr[begin], &arr[right - 1]);
	//返回基准值的位置
	return begin;
}

//挖坑法
int Partion2(DataType *arr, int left, int right)
{
	//找基准值
	DataType key = arr[right - 1];   //end的位置为坑
	//双指针,一指针从头开始,二从尾开始
	int begin = left, end = right - 1;
	while (begin < end)
	{
		//从左向右找比基准值大的位置
		while (begin < end && arr[begin] <= key) //确定begin的值
			begin++;
		//把end的值换成begin
		if (begin < end)	//区间有效
		{
			arr[end] = arr[begin];     //填坑,begin的位置成新坑
			end--;
		}
		//从右向左找比基准值小的位置
		while (begin < end && arr[end] >= key) //确定end的值
			begin++;
		//把begin的值换成end
		if (begin < end)	//区间有效
		{
			arr[begin] = arr[end];     //填坑,end的位置成新坑
			begin++;
		}
	}
	//相遇,用key填最后的坑
	arr[begin] = key;
	//返回
	return begin;
}

int Partion3(DataType *arr, int left, int right)
{
	//找基准值
	DataType key = arr[right - 1];
	//双指针
	int cur = left, prev = cur -1;
	while (cur < right)  //cur未越界
	{
		
		//cur比key大,则prev不动      第一个条件不成立则第二个不判断
		//cur比key小,prev向后走一步看是否在一个位置,
			//若在一个位置,则cur之前没有比key大的元素
			//若不再一个位置,则prev向后走过的位置肯定比cur大,需要交换
		if (arr[cur] <= key && ++prev != cur)  
		{
			Swap(&arr[cur], &arr[prev]); 
		}
		++cur;
	}
	if (++prev != right - 1)
		Swap(&arr[prev], &arr[right - 1]);
	return prev;
}
//递归
void QuickSort(DataType *arr, int left, int right)
{
	//划分到一定数据量(16)时,可以直接使用插入排序
	if (right - left > 1)  //只剩一个元素时不需要排序
	{
		//在待排序列中找一个基准值——区间   在left,right找基准值划分区间,最后返回基准值在区间中的位置
		int boundary = Partion1(arr, left, right);
		//排基准值左边
		QuickSort(arr, left, boundary);
		//排基准值左边
		QuickSort(arr, boundary + 1, right);
	}
}

//循环
void QuickSortNor(DataType *arr,int size)
{
	int left = 0;
	int right = size;
	Stack s;
	StackInit(&s);
	StackPush(&s, right);
	StackPush(&s, left);
	while (!StackEmpty(&s))
	{
		left = StackTop(&s);		//左边界
		StackPop(&s);
		right = StackTop(&s);		//右边界
		StackPop(&s);
		if (left < right)
		{
			int boundary = Partion_OP(arr, left, right);
			//排左侧, 把右半部分区间压栈
			StackPush(&s, right);
			StackPush(&s, boundary + 1);
			//排右侧   把左半部分区间压栈
			StackPush(&s, boundary);
			StackPush(&s, left);
		}
	}
}

归并排序

稳定性: 稳定
应用场景: 外部排序

代码实现
void MergeData(DataType *arr, int left, int mid, int right, DataType* temp)
{
	int begin1 = left, end1 = mid;
	int begin2 = mid, end2 = right;
	int index = left;
	while (begin1 < end1 && begin2 < end2)
	{
		if (arr[begin1] <= arr[begin2])
		{
			temp[index++] = arr[begin1++];
		}
		else
		{
			temp[index++] = arr[begin2++];
		}
	}
	while (begin1 < end1)
	{
		temp[index++] = arr[begin1++];
	}
	while (begin2 < end2)
	{
		temp[index++] = arr[begin2++];
	}
}
void _MergeSort(DataType *arr, int left, int right, DataType* temp)
{
	if (right - left > 1)
	{
		int mid = left + (right - left) / 2;   //划分区间
		_MergeSort(arr, left, mid, temp);
		_MergeSort(arr, mid, right, temp);
		MergeData(arr, left, mid, right, temp);
		memcpy(arr+left, temp+left, right - left);
	}
}
void MergeSort(DataType *arr, int size)
{
	int *temp = (DataType*)malloc(sizeof(DataType)* size);
	if (temp == NULL)
	{
		assert(0);
	}
	_MergeSort(arr, 0, size, temp);
	free(temp);
	temp = NULL;  
}
二路归并

二路归并的过程

  1. 初始序列看成 n 个有序子序列,每个子序列长度为1
  2. 两两合并,得到 n/2 个长度为 2或1 的有序子序列
  3. 重复直至得到一个长度为 n 的有序序列为止
代码实现
void MergeSortNor(DataType *arr, int size)
{
	int *temp = (DataType*)malloc(sizeof(DataType)* size);
	if (temp == NULL)
	{
		assert(0);
	}
	int gap = 1;
	while (gap < size)
	{
		for (int i = 0; i < size; i += 2 * gap)
		{
			//计算左右两半部分区间
			int left = i;
			int mid = i + gap;
			int right = mid + gap;
			//检测左右半部分区间的合法性
			if (mid > size)
			{
				mid = size;
			}
			if (right > size)
			{
				right = size;
			}
			MergeData(arr, left, mid, right, temp);
		}
		memcpy(arr , temp, size*arr[0]);
		gap *= 2;
	}	
	free(temp);
	temp = NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值