数据结构基础--排序

news2025/1/12 1:03:29

一、直接插入排序

思路:

直接插入排序是一种简单的插入排序法

其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一 个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

直接插入排序的特性总结:

1. 元素集合越接近有序,直接插入排序算法的时间效率越高

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1),它是一种稳定的排序算法

4. 稳定性:稳定

代码实现:

//插入排序
//最坏情况下时间复杂度为O(N^2),最好是O(N)
void InsertSort(int* a, int n)//n为数组大小
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			//从小向大排序
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}

二、希尔排序

思路:

希尔排序法又称缩小增量法。

希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个 组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工 作。当到达=1时,所有记录在统一组内排好序。

希尔排序的特性总结:

1. 希尔排序是对直接插入排序的优化

2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就 会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比

3. 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N^1.3—N^2)

4. 稳定性:不稳定

代码实现:

//希尔排序
//实际上就是间隔插入排序
//gap越大,大的数会更快的到后面,小的更快的到前面,越不接近有序
//gap越小,数据跳动越慢,越接近有序
//O(N^1.3)
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)//实现多次排序
	{
		gap = gap / 3 + 1;//+1为了保证最后一定是一步
		for (int i = 0; i < n - gap; i++)//单次排序
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

三、堆排序

思路:

详情可见之前的博客http://t.csdn.cn/EPBpC

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是 通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

代码实现:

//堆排序
//O(NlogN)
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
void AdjustDown(int* a, int n, int parent)
{
	assert(a);
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

四、选择排序

思路:

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的 数据元素排完

①在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素②若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换③在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

直接选择排序的特性总结:

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1)

4. 稳定性:不稳定

代码实现:

//选择排序
//O(N^2)
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin + 1; i <= end; i++)//循环找出最大值与最小值位置
		{
			if (a[i] > a[maxi])
			{
				maxi = i;
			}
			if (a[i] < a[mini])
			{
				mini = i;
			}
		}
		Swap(&a[begin], &a[mini]);//将最小的放在前面
		if (maxi == begin)
		{
			maxi = mini;
		}
		Swap(&a[end], &a[maxi]);//最大的放在后面
		begin++;
		end--;
	}
}

五、冒泡排序

思路:

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排 序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

冒泡排序的特性总结:

1. 冒泡排序是一种非常容易理解的排序

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1)

4. 稳定性:稳定

代码实现:

//冒泡排序
//O(N^2)
void BubbleSort(int* a, int n)
{
	for (int j = 0; j < n; j++)
	{
		int exchange = 0;
		for (int i = 1; i < n - j; i++)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}
		//如果某一趟排序没有发生交换,那么已经有序了
		if (exchange == 0)
		{
			break;
		}
	}
}

六、快速排序

思路:

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

将区间按照基准值划分为左右两半部分的常见方式有: 1. hoare版本 2. 挖坑法 3. 前后指针版本

 快速排序的特性总结:

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(logN)

4. 稳定性:不稳定

代码实现:

int GetmidIndex(int* a, int begin, int end)
{
	int mid = (begin + end) / 2;
	if (a[begin] < a[mid])
	{
		if (a[mid] < a[end])
		{
			return mid;
		}
		else if (a[begin] > a[end])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
	else // a[begin] > a[mid]
	{
		if (a[mid] > a[end])
		{
			return mid;
		}
		else if (a[begin] < a[end])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
}

//快排
//①霍尔方法:分割出左右区间,key固定位置,左小右大。
//左边做key,右边先走,保证相遇位置处值比key小
//最好O(NlogN),当每次key都是最大或者最小值(有序时)时间复杂度为O(N^2)
int PartSort1(int* a, int begin, int end)
{
	int mid = GetmidIndex(a, begin, end);
	Swap(&a[begin], &a[mid]);//使得key是数组中的中间值,避免有序时的排序消耗

	int left = begin, right = end;
	int keyi = left;
	while (left < right)
	{
		//右边先走,找小
		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}
		//左边后走,找大
		while (left > right && a[left] <= a[keyi])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);//此时left=right
	keyi = left;
	return keyi;
}

//挖坑法
//首先选取一个key值
int PartSort2(int* a, int begin, int end)
{
	int mid = GetmidIndex(a, begin, end);
	Swap(&a[begin], &a[mid]);
	int left = begin, right = end;
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		//右边找小,填左边
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		//左边找大,填右边
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;//退出循环时,left=right
	return hole;
}

//前后指针法
int PartSort3(int* a, int begin, int end)
{
	int prev = begin, cur = begin + 1;
	int keyi = begin;
	while (cur <= end)
	{
		//cur一直向后走,遇到比a[keyi]小的值,则交换数据
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	return keyi;
}




void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	//由于是递归调用,所以如果是小区间排序的话,递归占用资源过多
	//小区间采用直接插入排序,减少递归调用次数
	if ((end - begin + 1) < 15)
	{
		InsertSort(a + begin, end - begin + 1);
	}
	else
	{
		//int keyi=PartSort1(a,begin,end);
		//int keyi = PartSort2(a, begin, end);
		int keyi = PartSort3(a, begin, end);
		QuickSort(a, begin, keyi - 1);
		QuickSort(a, keyi + 1, end);
	}
}

非递归实现:

#include"Stack.h"
//快排非递归实现
//通过栈直接改成循环
//栈中存储排序位置的下标,通过下标位置进行排序操作
void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	StackInit(&st);
	StackPush(&st, begin);
	StackPush(&st, end);
	while (!StackEmpty(&st))
	{
		int right = StackTop(&st);
		StackPop(&st);
		int left = StackTop(&st);
		StackPop(&st);
		int keyi = PartSort1(a, left, right);
		//先右排序
		if (keyi + 1 < right)
		{
			StackPush(&st, keyi + 1);
			StackPush(&st, right);
		}
		//后左排序
		if (left < keyi - 1)
		{
			StackPush(&st, left);
			StackPush(&st, keyi - 1);
		}
	}
	StackDestory(&st);
}

七、归并排序

思路:

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有 序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

 归并排序的特性总结:

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(N)

4. 稳定性:稳定

代码实现:

//时间复杂度O(NlogN)
//空间复杂度O(N)
void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;
	// [begin, mid] [mid+1, end] 递归让子区间有序
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);

	// 归并[begin, mid] [mid+1, end]
	//...

	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}

	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}

	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
	tmp = NULL;
}

非递归实现:

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("malloc fail/n");
		exit(-1);
	}

	//归并每组数据个数,从1开始
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += gap * 2)
		{
			//除了begin1=i不会越界,剩余的三个变量都是有可能越界的
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int j = i;

			//越界修正
			if (end1 >= n)
			{
				end1 = n - 1;
				//范围一已经包括所有,begin2-end2区间不存在
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n)
			{
				//不存在区间
				begin2 = n;
				end2 = n - 1;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j] = a[begin1];
					j++;
					begin1++;
				}
				else
				{
					tmp[j] = a[begin2];
					j++;
					begin2++;
				}
			}
			while (begin1 <= end1)
			{
				tmp[j] = a[begin1];
				j++;
				begin1++;
			}
			while (begin2 <= end2)
			{
				tmp[j] = a[begin2];
				j++;
				begin2++;
			}
		}
		//整体归并完了,再拷贝
		memcpy(a, tmp, sizeof(int) * n);
		gap *= 2;
	}

	free(tmp);
	tmp = NULL;
}

八、整体实现

栈:

#pragma once
#include<stdio.h>
#include<malloc.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;//建立一个动态数组
	int top;//栈顶
	int capacity;//栈的容量
}ST;

//接口函数
void StackInit(ST* ps);//初始化
void StackDestory(ST* ps);//销毁
void StackPush(ST*ps,STDataType x);//入栈,不分头插尾插,因为栈只能在栈顶操作
void StackPop(ST* ps);//出栈
STDataType StackTop(ST* ps);//取栈顶数据
int StackSize(ST* ps);//得到栈的数据个数
bool StackEmpty(ST* ps);//判断栈是否为空
#include"Stack.h"
//栈:一种特殊的线性表,只允许在固定的一端进行插入与删除元素操作。
//进行数据插入和删除操作的一段,称为栈顶,另一端称为栈底
//栈中的数据元素遵循后进先出LIFO的原则
//压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
//出栈:栈的删除操作叫做出栈。出数据在栈顶

//如果用数组实现,相当于顺序表的尾插尾删,用尾去做了栈顶,唯一的缺陷就是空间不够需要增容
//如果用链表实现,如果用链表尾做栈顶,那么用双向链表更方便
//如果要用单链表实现,那么就用头去做栈顶更好,入栈出栈效率都是O(1)

void StackInit(ST* ps)
{
	assert(ps);
	ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a==NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	ps->capacity = 4;
	ps->top = 0;
	//初始top=0,意味着top指向栈顶元素的下一个
	//初始top=-1,意味着top指向栈顶元素
}

void StackDestory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

void StackPush(ST* ps,STDataType x)
{
	assert(ps);
	if (ps->top==ps->capacity)
	{
		STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDataType));
		if (tmp==NULL)
		{
			printf("realloc fail\n");
			exit(-1);//终止程序
		}
		else
		{
			ps->a = tmp;
			ps->capacity *= 2;
		}
	}
	ps->a[ps->top] = x;
	ps->top++;
}

void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top>0);//空栈时调用top直接终止程序报错
	ps->top--;//直接将栈顶数据删除,下一次有数据入栈会覆盖top位置
}

STDataType StackTop(ST* ps)//取栈顶元素
{
	assert(ps);
	assert(ps->top>0);
	return ps->a[ps->top - 1];
}

int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;//如果top为零,说明栈中没有元素,为空
	//返回布尔值为1
}

头文件:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<time.h>
#include<assert.h>
#include<string.h>

void InsertSort(int* a, int n);//插入排序,接近有序情况下最优
void ShellSort(int*a ,int n);//希尔排序
void HeapSort(int* a, int n);//堆排序
void SelectSort(int* a, int n);//选择排序
void BubbleSort(int* a, int n);//冒泡排序
void QuickSort(int* a, int begin,int end);//快排
void QuickSortNonR(int* a, int begin, int end);//快排非递归实现
void MergeSort(int* a, int n);//归并排序
void MergeSortNonR(int* a, int n);//归并排序非递归

源文件:

#include"sort.h"


//插入排序
//最坏情况下时间复杂度为O(N^2),最好是O(N)
void InsertSort(int* a, int n)//n为数组大小
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			//从小向大排序
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}


//希尔排序
//实际上就是间隔插入排序
//gap越大,大的数会更快的到后面,小的更快的到前面,越不接近有序
//gap越小,数据跳动越慢,越接近有序
//O(N^1.3)
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)//实现多次排序
	{
		gap = gap / 3 + 1;//+1为了保证最后一定是一步
		for (int i = 0; i < n - gap; i++)//单次排序
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}


//堆排序
//O(NlogN)
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
void AdjustDown(int* a, int n, int parent)
{
	assert(a);
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}


//选择排序
//O(N^2)
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin + 1; i <= end; i++)//循环找出最大值与最小值位置
		{
			if (a[i] > a[maxi])
			{
				maxi = i;
			}
			if (a[i] < a[mini])
			{
				mini = i;
			}
		}
		Swap(&a[begin], &a[mini]);//将最小的放在前面
		if (maxi == begin)
		{
			maxi = mini;
		}
		Swap(&a[end], &a[maxi]);//最大的放在后面
		begin++;
		end--;
	}
}


//冒泡排序
//O(N^2)
void BubbleSort(int* a, int n)
{
	for (int j = 0; j < n; j++)
	{
		int exchange = 0;
		for (int i = 1; i < n - j; i++)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}
		//如果某一趟排序没有发生交换,那么已经有序了
		if (exchange == 0)
		{
			break;
		}
	}
}

int GetmidIndex(int* a, int begin, int end)
{
	int mid = (begin + end) / 2;
	if (a[begin] < a[mid])
	{
		if (a[mid] < a[end])
		{
			return mid;
		}
		else if (a[begin] > a[end])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
	else // a[begin] > a[mid]
	{
		if (a[mid] > a[end])
		{
			return mid;
		}
		else if (a[begin] < a[end])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
}

//快排
//①霍尔方法:分割出左右区间,key固定位置,左小右大。
//左边做key,右边先走,保证相遇位置处值比key小
//最好O(NlogN),当每次key都是最大或者最小值(有序时)时间复杂度为O(N^2)
int PartSort1(int* a, int begin, int end)
{
	int mid = GetmidIndex(a, begin, end);
	Swap(&a[begin], &a[mid]);//使得key是数组中的中间值,避免有序时的排序消耗

	int left = begin, right = end;
	int keyi = left;
	while (left < right)
	{
		//右边先走,找小
		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}
		//左边后走,找大
		while (left > right && a[left] <= a[keyi])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);//此时left=right
	keyi = left;
	return keyi;
}

//挖坑法
//首先选取一个key值
int PartSort2(int* a, int begin, int end)
{
	int mid = GetmidIndex(a, begin, end);
	Swap(&a[begin], &a[mid]);
	int left = begin, right = end;
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		//右边找小,填左边
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		//左边找大,填右边
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;//退出循环时,left=right
	return hole;
}

//前后指针法
int PartSort3(int* a, int begin, int end)
{
	int prev = begin, cur = begin + 1;
	int keyi = begin;
	while (cur <= end)
	{
		//cur一直向后走,遇到比a[keyi]小的值,则交换数据
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	return keyi;
}




void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	//由于是递归调用,所以如果是小区间排序的话,递归占用资源过多
	//小区间采用直接插入排序,减少递归调用次数
	if ((end - begin + 1) < 15)
	{
		InsertSort(a + begin, end - begin + 1);
	}
	else
	{
		//int keyi=PartSort1(a,begin,end);
		//int keyi = PartSort2(a, begin, end);
		int keyi = PartSort3(a, begin, end);
		QuickSort(a, begin, keyi - 1);
		QuickSort(a, keyi + 1, end);
	}
}

#include"Stack.h"
//快排非递归实现
//通过栈直接改成循环
//栈中存储排序位置的下标,通过下标位置进行排序操作
void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	StackInit(&st);
	StackPush(&st, begin);
	StackPush(&st, end);
	while (!StackEmpty(&st))
	{
		int right = StackTop(&st);
		StackPop(&st);
		int left = StackTop(&st);
		StackPop(&st);
		int keyi = PartSort1(a, left, right);
		//先右排序
		if (keyi + 1 < right)
		{
			StackPush(&st, keyi + 1);
			StackPush(&st, right);
		}
		//后左排序
		if (left < keyi - 1)
		{
			StackPush(&st, left);
			StackPush(&st, keyi - 1);
		}
	}
	StackDestory(&st);
}


//时间复杂度O(NlogN)
//空间复杂度O(N)
void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;
	// [begin, mid] [mid+1, end] 递归让子区间有序
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);

	// 归并[begin, mid] [mid+1, end]
	//...

	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}

	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}

	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
	tmp = NULL;
}


void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("malloc fail/n");
		exit(-1);
	}

	//归并每组数据个数,从1开始
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += gap * 2)
		{
			//除了begin1=i不会越界,剩余的三个变量都是有可能越界的
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int j = i;

			//越界修正
			if (end1 >= n)
			{
				end1 = n - 1;
				//范围一已经包括所有,begin2-end2区间不存在
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n)
			{
				//不存在区间
				begin2 = n;
				end2 = n - 1;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j] = a[begin1];
					j++;
					begin1++;
				}
				else
				{
					tmp[j] = a[begin2];
					j++;
					begin2++;
				}
			}
			while (begin1 <= end1)
			{
				tmp[j] = a[begin1];
				j++;
				begin1++;
			}
			while (begin2 <= end2)
			{
				tmp[j] = a[begin2];
				j++;
				begin2++;
			}
		}
		//整体归并完了,再拷贝
		memcpy(a, tmp, sizeof(int) * n);
		gap *= 2;
	}

	free(tmp);
	tmp = NULL;
}

测试:

#include"sort.h"

void testInsertSort()
{
	int a[] = { 7,6,555,48,5,23,7,8,3 };
	InsertSort(a, sizeof(a) / sizeof(int));
	for (int i=0;i<(sizeof(a)/sizeof(int));i++)
	{
		printf("%d ", a[i]);
	}
}

void testShellSort()
{
	int a[] = { 7,6,555,48,5,23,7,8,3 };
	ShellSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < (sizeof(a) / sizeof(int)); i++)
	{
		printf("%d ", a[i]);
	}
}

void testHeapSort()
{
	int a[] = { 7,6,555,48,5,23,7,8,3 };
	HeapSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < (sizeof(a) / sizeof(int)); i++)
	{
		printf("%d ", a[i]);
	}
}

void testSelectSort()
{
	int a[] = { 7,6,555,48,5,23,7,8,3 };
	SelectSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < (sizeof(a) / sizeof(int)); i++)
	{
		printf("%d ", a[i]);
	}
}


void testBubbleSort()
{
	int a[] = { 7,6,555,48,5,23,7,8,3 };
	BubbleSort(a,sizeof(a)/sizeof(int));
	for (int i = 0; i < (sizeof(a) / sizeof(int)); i++)
	{
		printf("%d ", a[i]);
	}
}

void testQuickSort()
{
	int a[] = { 7,6,555,48,5,23,7,8,3 };
	//int a[] = { 23,4,8,9,2,86,3,48 };
	//由于快排需要知道排序的范围,数组角标比长度小一
	QuickSort(a, 0, sizeof(a) / sizeof(int)-1);
	for (int i = 0; i < (sizeof(a) / sizeof(int)); i++)
	{
		printf("%d ", a[i]);
	}
}

void testMergeSort()
{
	int a[] = { 7,6,555,48,5,23,7,8,3 };
	MergeSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < (sizeof(a) / sizeof(int)); i++)
	{
		printf("%d ", a[i]);
	}
}

void testMergeSortNonR()
{
	int a[] = { 7,6,555,48,5,23,7,8,3 };
	MergeSortNonR(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < (sizeof(a) / sizeof(int)); i++)
	{
		printf("%d ", a[i]);
	}
}



void TestOP()
{
	srand(time(0));
	const int N = 1000000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	int* a6 = (int*)malloc(sizeof(int) * N);
	int* a7 = (int*)malloc(sizeof(int) * N);
	int* a8 = (int*)malloc(sizeof(int) * N);

	int j = 0;
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand()+1;

		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
		a7[i] = a1[i];
		a8[i] = a1[i];
	}
	printf("循环随机赋值完毕\n");

	int begin1 = clock();
	//InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	//SelectSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();

	int begin7 = clock();
	//BubbleSort(a7, N);
	int end7 = clock();

	int begin5 = clock();
	QuickSort(a5, 0, N - 1);
	int end5 = clock();

	int begin6 = clock();
	MergeSort(a6, N);
	int end6 = clock();

	int begin8 = clock();
	MergeSortNonR(a8, N);
	int end8 = clock();
	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("BubbleSort:%d\n", end7 - begin7);

	printf("QuickSort:%d\n", end5 - begin5);
	printf("MergeSort:%d\n", end6 - begin6);
	printf("MergeSortNonR:%d\n", end8 - begin8);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
	free(a7);
	free(a8);
}

int main()
{
	TestOP();
	//testInsertSort();
	//testShellSort();
	//testHeapSort();
	//testSelectSort();
	//testBubbleSort();
	//testQuickSort();
	//testMergeSort();
	//testMergeSortNonR();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/94619.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二分查找算法

目录 一 算法简介 1&#xff09;算法解释 2&#xff09;前提 3&#xff09;思想 4&#xff09;分类 5&#xff09;算法模板 mid的计算的实现方法 二分法模板 求某个数的平方根: 二 算法实践 1&#xff09;问题引入 2&#xff09;问题解答 1)解法一&#xff1a;左闭…

[附源码]Node.js计算机毕业设计关山社区居民信息管理系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

Nacos认证绕过漏洞(CVE-2021-29441)

Nacos认证绕过漏洞&#xff08;CVE-2021-29441&#xff09; 指纹识别 titlenacos漏洞范围 nacos1.2.0版本-nacos1.4.0版本 漏洞复现 靶机ip:192.168.1.4 默认的nacos登录界面 http://192.168.1.14:8848/nacos/#/login利用如下请求包查看只有一个nacos用户 GET /nacos/v…

ZBC陆续在主要CEX开启Staking,锁定市场大部分流通量成大利好

从2022年Q3开始&#xff0c;Zebec生态开始不断的迎来新的利好&#xff0c;比如以 10 亿美元的完全稀释估值筹集了 850 万美元&#xff0c;使其历史融资额超过4000万美元&#xff0c;引发ZBC通证的一波上涨。而在此后&#xff0c;Zebec 生态开启了从Solana生态的迁移&#xff0c…

Eclipse+Java+Swing+mysql实现学生宿舍管理系统

EclipseJavaSwingmysql实现学生宿舍管理系统一、系统介绍1.环境配置二、系统展示1.登录页2.学生主页面3.学生端-登记页面4.学生端-学生信息修改5.学生端-寝室信息查询6.学生端-学生信息查询7.学生端-退出登录8.管理员-主页面9.管理员-宿舍信息修改10.管理员-宿舍信息删除11.管理…

JQuery | 系统性学习 | 无知的我费曼笔记

无知的我已经复盘完成JQuery 。。。 文章目录JQuery概述入口函数特性-隐式迭代Dom和JQuery区别互相转化JQuery选择器基本和层级选择器筛选选择器后缀筛选方法筛选应用排他思想应用链式编程JQuery操作样式修改样式CSS修改类名JQuery效果基础效果显示效果隐藏效果切换效果滑动效果…

Springboot 使用redis检测浏览量,评论量,点赞量的变化并完成与mysql的交互(有具体实现,有具体需求)

目录 依赖 准备实体类与业务类 开始正题 实现一览 流程一览 具体实现 1 初始化 2 写浏览量增加的方法 3 在切面处检测浏览器变化 4 新增文章时将新的数据写入redis 5 删除文章时将数据从Redis中删除 6 书写将数据写入mysql数据库的方法 7 销毁的时候将数据写入my…

一文搞懂百万富翁问题

百万富翁问题1. 解决方案2. 协议描述3. 协议说明4. 协议举例两个百万富翁Alice和Bob想知道他们两个谁更富有&#xff0c;但他们都不想让对方及其他第三方知道自己财富的任何信息&#xff0c;这是由中国计算机科学家、2000年图灵奖获得者姚启智教授于1982年在论文《Protocols fo…

新手小白做跨境电商有哪些注意的地方?

近两年&#xff0c;受疫情刺激&#xff0c;线上电商出现前所未有的高速增长&#xff0c;中国品牌纷纷出海&#xff0c;跨境电商腾飞。此外&#xff0c;国内电商市场发展趋于平淡&#xff0c;市场需求不断萎缩&#xff0c;也让越来越多的大卖家和平台盯上了这块大蛋糕。不仅中小…

300左右半入耳蓝牙耳机推荐:南卡、漫步者、JBL蓝牙耳机谁值得入手?

现在的年轻人&#xff0c;出门都会随身携带的一副蓝牙耳机&#xff0c;所以很多品牌商加入其中&#xff0c;导致大多数人选购难度变大&#xff0c;很多人总是不知道哪个不知道品牌蓝牙耳机最好&#xff0c;半入耳式蓝牙耳机相比入耳式蓝牙耳机有着天然的舒适性&#xff0c;因而…

如何利用MOS管设计一个LED亮度可调电路

首先大家可以看下下面的MOS管亮度可调的演示视频 如何利用MOS管设计一个LED亮度可调电路这个电路大致电路图如下 MOS管的栅极放了一个电容&#xff0c;当按按键1的时候&#xff0c;电源通过R1给电容充电&#xff0c;&#xff0c;MOS管栅极的电压慢慢增大&#xff0c;流过MOS管的…

计算机毕业设计HTML+CSS+JavaScript——基于HTML花店购物网站项目的设计与实现

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…

Essential singularity

In complex analysis, an essential singularity of a function is a “severe” singularity near which the function exhibits odd behavior. The category essential singularity is a “left-over” or default group of isolated singularities that are especially unm…

跳转指令 —— B、BL

跳转指令可以跳转到标号的下一条指令&#xff0c;本质就是修改了PC寄存器的值。&#xff08;标号并非指令&#xff0c;只是用来定位&#xff0c;相当于记录了当前位置的下一条指令的地址&#xff09; 这里的MAIN就是一个标号 MAIN: MOV R1, #1 这里的FUNC就是一个标…

实验3 路由器基本配置及路由配置

实验3 路由器基本配置及路由配置一、实验目的二、实验要求三、实验步骤&#xff0c;数据记录及处理四&#xff0e;实验总结一、实验目的 1、路由器几种模式。 2、基本的配置命令。 3、路由器各接口的配置方法。 4、会查看检测接口状态。 二、实验要求 写出自己学习使用了哪些…

Redis框架(十五):大众点评项目 共同关注方案实现?双指针筛选DB数据:Redis取交集

大众点评项目 好友关注 共同关注需求&#xff1a;好友关注 共同关注业务逻辑展示点击关注功能实现判断当前用户是否关注了此博主共同好友列表查询业务逻辑实现双指针筛选DB数据Redis取交集总结SpringCloud章节复习已经过去&#xff0c;新的章节Redis开始了&#xff0c;这个章节…

字节一面:select......for update会锁表还是锁行?

select查询语句是不会加锁的&#xff0c;但是select .......for update除了有查询的作用外&#xff0c;还会加锁呢&#xff0c;而且它是悲观锁。 那么它加的是行锁还是表锁&#xff0c;这就要看是不是用了索引/主键。 没用索引/主键的话就是表锁&#xff0c;否则就是是行锁。…

sklearn基础篇(十)-- 非负矩阵分解与t-SNE

1 非负矩阵分解(NFM) NMF(Non-negative matrix factorization)&#xff0c;即对于任意给定的一个非负矩阵V\pmb{V}VVV&#xff0c;其能够寻找到一个非负矩阵W\pmb{W}WWW和一个非负矩阵H\pmb{H}HHH&#xff0c;满足条件VW∗H\pmb{VW*H}VW∗HVW∗HVW∗H,从而将一个非负的矩阵分解…

物联网架构实例—解决Linux(Ubuntu)服务器最大TCP连接数限制

1.前言&#xff1a; 在对物联网网关进行压测的时候&#xff0c;发现在腾讯云部署网关程序&#xff0c;设备接入数量只能达到4000多个长连接&#xff0c;之后就再也无法接入终端了。 之前在阿里云部署的时候明明可以到达2万左右&#xff0c;而且腾讯云的这个服务器比阿里云的硬…

蓝桥杯嵌入式综合实验真题-联网电压测量系统设计与实现

目录 实验要求&#xff1a; 实验思路&#xff1a; 核心代码&#xff1a; &#xff08;1&#xff09;变量声明 &#xff08;2&#xff09;函数声明 &#xff08;3&#xff09;main主函数 &#xff08;4&#xff09;按键&#xff08;长按/短按&#xff09; &#xff08;5&…