常见的排序

#include <stdio.h>
#include <stdlib.h>

void swap(int* a, int* b)
{
	int temp;
	temp = *a;
	*a = *b;
	*b = temp;
}

/*冒泡排序:时间复杂度O(N^2)    空间复杂度O(1)   稳定*/
void BubbleSort(int iarr[], int size)
{
	int i, j;
	// i 从0变到1后,即 j 从0到 size-i-1 后,最大数会被放到最后位置
	for (i = 0; i < size - 1; i++)
	{
		for (j = 0; j < size - i - 1; j++)
		{
			if (iarr[j] > iarr[j + 1])
			{
				swap(&iarr[j], &iarr[j + 1]);
			}
		}
	}
}


/*选择排序:时间复杂度O(N^2)    空间复杂度O(1)   不稳定*/
void SelectSort(int iarr[], int size)
{
	int i, j, k;
	for (i = 0; i < size - 1; i++)
	{
		k = i;            //变量 k 保存数组当前位置
		//从已经排序好的位置 i 开始,遍历后面未排序的
		for (j = i + 1; j < size; j++)       
		{
			if (iarr[j] < iarr[k])     
				k = j;      //寻找最小值的位置,并将该位置赋给 k
		}
		//交换当前位置 i 处和未排序的最小值位置上的数
		swap(&iarr[i], &iarr[k]);
	}
}


/*插入排序:时间复杂度O(N^2)    空间复杂度O(1)   稳定*/
void InsertSort(int iarr[], int size) 
{
	int i, j, temp;
	for (i = 1; i < size; i++)     //从位置 i 开始,即位置 i 前面的数已经排好
	{
		if (iarr[i] < iarr[i - 1])     //如果当前位置数比前面一个小,就要往前插入
		{
			temp = iarr[i];    //将当前位置数赋给temp
			//因为当前值要比前面的数小,所以要将当前值与前面的数进行比较并插入,直到比前面一个数大
			//循环条件:比较到第一个数且比前面的数小
			for (j = i - 1; j >= 0 && temp < iarr[j]; j--)      
			{
				iarr[j + 1] = iarr[j];     //比当前位置数大的元素都要向后挪一位
			}
			iarr[j + 1] = temp;       //当前位置数放到合适的位置,即比前面大且比后面小
		}
	}		
}


/*希尔排序:时间复杂度O(NlogN)    空间复杂度O(1)   不稳定*/
void ShellSort(int iarr[], int size)
{
	int i, j, temp, increment;
	//插入排序的改进版,以增量increment为间隔插入,相当于分成了increment组分别排序
	//该间隔每次减半,直到为1,排序完成
	for (increment = size / 2; increment > 0; increment /= 2)  
	{
		for (i = increment; i < size; i++)       //
		{
			temp = iarr[i];
			for (j = i - increment; j >= 0 && temp < iarr[j]; j -= increment)
			{
				iarr[j+increment] = iarr[j];
				iarr[j] = temp;
			}
		}
	}
}


/*归并排序:时间复杂度O(NlogN)    空间复杂度O(N)   稳定*/
//功能:将一个序列分成两个子序列,然后合并为一个有序的序列
//A为要排序的数组,TempA为临时存放排序好的数组
//R:右边子序列的首位置;rightEnd:右边子序列的尾部
void Merge(int A[], int TempA[], int L, int R, int rightEnd)
{
	//假设两个子序列是挨着的,则左序列的尾部就是右序列首部-1
	int leftEnd = R - 1;    
	int temp = L;
	int length = rightEnd - L + 1;    //求临时数组的长度
	//对两个子序列进行排序,依次将子序列中较小的数放到临时数组中
	//循环退出条件:其中一个序列中的数已经全部放到临时数组中
	//说明另一个序列中的数都大于临时数组中的数,直接复制剩下的数到临时数组中
	while (L <= leftEnd && R <= rightEnd)
	{
		if (A[L] <= A[R])
			TempA[temp++] = A[L++];
		else
			TempA[temp++] = A[R++];
	}
	while (L <= leftEnd)
		TempA[temp++] = A[L++];     //直接复制数组左边剩下的到临时数组中
	while (R <= rightEnd) 
		TempA[temp++] = A[R++];     //直接复制数组右边剩下的到临时数组中
	for (int i = 0; i < length; i++, rightEnd--)  
		A[rightEnd] = TempA[rightEnd];    //再把临时数组里面的数全部赋给数组A
}

//归并排序非标准接口,对整个序列进行递归划分并调用函数 Merge 进行排序
void MergeSortUnStandardInterface(int A[], int TempA[], int L, int rightEnd)
{
	int center;
	if (L < rightEnd)     //不满足时说明子序列只剩下一个元素
	{
		center = (L + rightEnd) / 2;
		MergeSortUnStandardInterface(A, TempA, L, center);
		MergeSortUnStandardInterface(A, TempA, center + 1, rightEnd);
		Merge(A, TempA, L, center + 1, rightEnd);
	}
}

//使用排序统一接口int iarry[], int size
void MergeSort(int iarr[], int size)
{
	int* TempA;
	//申请临时数组空间,这样只需要一个临时数组
	//如果在子函数中申请,会需要反复的申请空间和释放空间,浪费资源
	TempA = malloc(size * sizeof(int));   
	if (TempA != NULL)
		MergeSortUnStandardInterface(iarr, TempA, 0, size - 1);
	else
		printf("空间不足!");
}


/*快速排序:时间复杂度O(NlogN)    空间复杂度O(logN)   不稳定*/
//选取主元也就是基准,这里选取数组左中右的中间值
int SelectPivot(int iarr[], int left, int right)
{
	int center = (left + right) / 2;
	if (iarr[left] > iarr[center])
	{
		swap(&iarr[left], &iarr[center]);
	}
	if (iarr[left] > iarr[right])
	{
		swap(&iarr[left], &iarr[right]);
	}
	if (iarr[center] > iarr[right])
	{
		swap(&iarr[center], &iarr[right]);
	}
	//将主元放到(right-1)的位置上,这样只需要对(left+1)到(right-2)的元素进行比较排序
	//因为通过之前的比较,位置left的元素比主元小,位置right的元素比主元大
	swap(&iarr[center], &iarr[right - 1]);  
	return iarr[right - 1];
}

//定义阈值
#define cutoff 100

//快速排序非标准接口,将大于主元的数全放在主元的右边,小于主元的数全放在主元的左边
void QuickSortUnStandardInterface(int iarr[], int left, int right)
{
	//数组长度大于所定义阈值则用快速排序,否则直接用插入排序,因为快排适合大数据
	if (cutoff <= right - left)
	{
		//选取基准
		int pivot = SelectPivot(iarr, left, right);
		//定义两个“指针”i 指在左边,j 指在右边
		int i = left, j = right - 1;
		//一直循环,直到 i>j 时退出循环
		for (;;)
		{
			//左边的数小于主元则指针 i 一直往右移,直到碰到大于主元的数退出
			while (iarr[++i] < pivot);
			//右边的数大于主元则指针 j 一直往左移,直到碰到小于主元的数退出
			while (iarr[--j] > pivot);
			//这时 i 停在大于主元的位置,j 停在小于主元的位置,不符合要求,则交换它们的位置
			if (i < j)
				swap(&iarr[i], &iarr[j]);
			else
				break;
		}
		//退出for循环时正好从位置 i 往右的数都大于主元,从 i 往前的数都小于主元
		//所以交换主元位置和位置 i,主元就被放在了正确的位置
		swap(&iarr[i], &iarr[right - 1]);
		//依次递归对主元左右两边的序列进行排序
		QuickSortUnStandardInterface(iarr, left, i - 1);
		QuickSortUnStandardInterface(iarr, i + 1, right);
	}
	else
	{
		//数组长度小于所定义阈值直接用插入排序,这样效率更高
		InsertSort(iarr + left, right - left + 1);
	}
}

//快速排序标准接口
void QuickSort(int iarr[], int size)
{
	QuickSortUnStandardInterface(iarr, 0, size - 1);
}


/*堆排序:时间复杂度O(NlogN)    空间复杂度O(1)   不稳定*/
//将N个元素的数组中以A[p]为根的子堆调整为最大堆
void PercDown(int A[], int p, int N)
{
	int Parent, Child;
	int X = A[p];  //取出根结点存放的值
	//将父节点依次向下层比较,直到比较到最后一层退出
	for (Parent = p; (Parent * 2 + 1) < N; Parent = Child)
	{
		//父节点从位置0开始,则位置为 i 的父节点的子节点位置分别是(2i+1)和(2i+2)
		Child = Parent * 2 + 1;
		//Child指向左右子结点的较大者
		if ((Child != N - 1) && (A[Child] < A[Child + 1]))
			Child++;  
		//如果父节点大于子节点则不用调整
		if (X >= A[Child]) 
			break;
		//否则将子节点赋给父节点
		else  
			A[Parent] = A[Child];   
	}
	//最后将父节点的值放到正确的位置
	A[Parent] = X;
}

void HeapSort(int A[], int size)
{
	int i;
	//从倒数第一个有儿子的节点开始调整,使每个节点的子树变成最大堆
	for (i = size / 2 - 1; i >= 0; i--)
		PercDown(A, i, size);

	for (i = size - 1; i > 0; i--)
	{
		//交换第一个和最后一个节点的值,将最大值放到最后一个节点的位置,最大值已放到正确的位置
		swap(&A[0], &A[i]);
		//再调整除最后一个位置的堆,使其变成最大堆
		PercDown(A, 0, i);
	}
}



void main()
{
	int iarry[10] = { 33,98,89,8,84,768,890,23,28,67 };
	//BubbleSort(iarry, 10);
	//SelectSort(iarry, 10);
	//InsertSort(iarry, 10);
	//ShellSort(iarry, 10);
	//QuickSort(iarry, 10);
	//MergeSort(iarry, 10);
	HeapSort(iarry, 10);
	printf("The result of sorted:\n");
	for (int i= 0; i < 10; i++)
		printf("%d ", iarry[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值