C语言排序

 C语言排序技术

注:代码使用C99编译器

选取3000个(int)随机数进行各种排序并测时间:

#include <stdio.h>
#include <windows.h>
#define N 3000 //取3000个数据进行测量
//测量时间
typedef struct Get_T {
	long start;
	long end;
	long Dt;
}GetTime;
GetTime T1;
void MeasureTime(GetTime* T)
{
	static char i = 0;
	if (!i)
	{
		i++;
		T->start = GetTickCount();//windows.h
	}	
	else
	{
		T->end = GetTickCount();
		printf("%ldms\n", (T->end) - (T->start)); //ms
		i = 0;
	}
}

void main(void)
{
    int    data[N]={0};
    //测试函数准确度:
	/*
	MeasureTime(&T1);
	Sleep(1000);
	MeasureTime(&T1);
	*/
	for (int i = 0; i < N; i++)
		data[i]= rand()%N;//产生N个0-N之间的随机数

	MeasureTime(&T1);
    //Cmp1(data);
	//INS1(data);
    //INS2(data);
    //Sec1(data);
    //Sec2(data);
	MeasureTime(&T1);
	for (int i = 0; i < N; i++)
		printf("%d",*(data+i));//查看排序结果
    system("pause");
}


交换类排序

冒泡排序:一种最简单的交换类排序方法,它是通过两两相邻数据元素的交换逐步将线性表变成有序。冒泡排序每-遍的从前往后扫描都把排序范围内的最大元素沉到了表的底部,每-遍的从后往前扫描,都把排序范围内的最小元素像气泡一样浮到了表的最前面。冒泡排序的名称也由此而来。在最坏情况下,对长度为n的线性表排序,冒泡排序需要经过n/2遍的从前往后的扫描和n/2遍的从后往前扫描,需要比较的次数为n (n-1) /2。

//冒泡排序法
//如果只是向右或向左消除逆序,那么循环次数为N-1
void Comp1(int* p) {
	int i, t,j;
	for (j = 0; j < N/2; j++)
	{
		for (i = 0; i<N - 1; i++) //向右消除逆序
			if (*(p + i) >= *(p + i + 1))
			{
				t = *(p + i + 1);
				*(p + i + 1) = *(p + i);
				*(p + i) = t;
			}
		for (i = N - 1; i >=0; i--)//向左消除逆序
		{
			if (*(p + i) <= *(p + i - 1))
			{
				t = *(p + i - 1);
				*(p + i - 1) = *(p + i);
				*(p + i) = t;
			}
		}
	}
}

原理:

                                         


快速排序:由于冒泡排序法在扫描过程中只对相邻两个元素进行比较,因此,互换两个相邻元素时只能消除一个逆序。如果通过两个(不相邻)元素的交换,能够消除线性表中的多个逆序,就会大大加快排序的速度。下面介绍的快速排序法可以实现通过一次交换而消除多个逆序。(快速排序暂时未实现,给个demo)

//快速排序
void Cmp2(char *ptr)
{
	char* i=0, *j=0;
	char t = 0,a=0;
	char *low = ptr, *high = ptr;
	char *data = ptr;

	i = ptr + N - 1;
	j = ptr;

	while (flag < 4)
	{
		for (; *i;)
		{
			if (*(j) >= *i)//向左
			{
				t = *i;
				*i = *j;
				*j = t;
				high = i;
				break;
			}
			i--;
		}

	for(;*j;)
		{
			if ( *(i)<= *j )
			{
				t = *j;
				*j = *i;
				*i = t;
				low = j;
				printf("*low = %d\n", *low);
				printf("*high = %d\n", *high);
				break;
			}
			j++;

		}
		if (high == low)
		{
			for (a = 0; a < N; a++) //---->取出最大
				printf("%d\n", *(a + ptr));
			i = ptr + N - 1;
			j = ptr;
			printf("high == left \n");
			flag++;
			break;
		}

	}
	//	system("pause");调试
	return;
}

原理:

 


插入类排序

简单插入类排序:把n个待排序的元素看成是- -个有序表和一个无序表。开始时,有序表只包含一个元素,而无序表包含另外n-1个元素,每次取无序表中的第一一个元素插入到有序表中的正确位置,使之成为增加一个元素的新的有序表。插入元素时,插入位置及其后的记录依次向后移动。最后有序表的长度为n,而无序表为空,此时排序完成。

                                                                    

void INS1(int* Des)
{
	for (int i=1; i < N; i++)
	{
			for (int j = i; j>0 ; j--)
			{
				if (Des[j] < Des[j-1])
				{
					int t;
					t = Des[j - 1];
					Des[j-1] = Des[j];
					Des[j] = t;
				}
			}
	}
}

原理:


希尔排序:将整个无序序列分割成若干个小的子序列后分别进行插入排序

子序列的分割方法为:将相隔某个增量d的元素构成-一个子序列。在排序过程中,逐次减小这个增量,最后当d减至1时,进行一次插入排序,排序就完成。

增量序列一般取dt=n/2k (k=1, 2,.,. [log2n]) ,其中n为待排序序列的长度。


//增量序列 Dt=N/2k(取整) (k=1,2,.....[log2N])
//不适用于很多数据  测试数据超过2000个时卡死,低于2000没有问题
void INS2(int* data)
{
#define k 1
	for(int dt = N;dt>=0; dt = dt / (2 * k) - 1)//改变增量
		for (int i = dt; i < N; i += dt)//分割子序列
		{
			if (*(data + i) < *(data + i - dt))
			{
				for (int j = i;j>=0; j -= dt)//对子序列进行个排序
				{
					if (*(data + j ) < *(data + j-dt))
					{
						int t;
						t = *(data + j -dt);
						*(data + j - dt) = *(data + j);
						*(data + j) = t;
					}
				}
			}
		}	
}

原理:


选择类排序

简单选择排序:首先从所有n个待排序的数据元素中选择最小的元素,将该元素与第1个元素交换,再从剩下的n-1个元素中选出最小的元素与第2个元素交换。重复这样的操作直到所有的元素有序为止。

//简单选择排序
void Sec1(int* data)
{
	int* min = data;
	for (int j = N; j > 0; j--)//未排序的元素
	{
		for (int i = 1; i < j; i++)
		{
			if (*min > *(data + i))
			{
				int t = *min;
				*min = *(data + i);
				*(data + i) = t;
			}
		}
		min = data++;
	}

}

原理:



堆排序:首先将一个无序序列建成堆,然后将堆顶元素(序列中的最大项)与堆中的最后一个元素交换。不考虑已经换到最后的那个元素,将剩下的n- 1个元素重新调整为堆,重复执行此操作,直到所有元素有序为止。(先考虑大根堆情况)

对于数据元素较少的线性表来说,堆排序的优越性并不明显,但对于大量的数据元素来说,堆排序是很有效的。在最坏情况下,堆排序需要比较的次数为0(nlog2n)。

//堆排序
//适合用于大数据
void heapify(int *data, int n, int i)//从(叶结点)父节点出发,比较父结点和子结点的数值 并交换
{
	if (i >= n) return;//数据溢出,递归出口
	int max = i;//父节点,记录父结点的下标
	int left= 2 * i + 1;//子结点
	int right= 2 * i + 2;//子节点
	if (left<n  && *(data+left) > *(data + max))//如果子节编号大于N 溢出
		max = left;//左子节点大于父结点
	if (right<n && *(data + right) > *(data + max))//如果子节编号大于N 溢出
		max = right;//右子节点大于父结点

	if (max != i)//父结点和左或右子节点交换
	{
		int t = *(data +i);
		*(data+i) = *(data + max);
		*(data + max) = t;
		heapify(data,n,max);//当每次向上建立完堆时,需要保证之前的堆(例如叶子节点-堆)任然满足堆的性质,从下往上递归
	}
}
/*
关于堆:
定义:
1.完全二叉树 从上往下 从左往右
2.父节点的内容大于子节点的内容
性质:
*父结点=(i-1)/2  i:子节点
*叶子结点最大标号		N-1
*叶子结点的父结点父结点	((N-1)-1)/2
*左子结点=2*i+1
*右子结点=2*i+2
*/
void Sec2(int *data)
{
	for (int i = (N - 2) / 2; i >= 0; i--)//建立堆(从父结点出发)
		heapify(data, N, i);//从下到上 
	for (int i = N - 1; i >= 0; i--) //先左后右 先下后上
	{
		int t = *(data+i);
		*(data + i) = *data;
		*data= t;//破坏了根节点的堆
		heapify(data, i, 0);//恢复根节点堆的性质
	}
}

 常用的排序方法时间和空间复杂度比较

方法平均时间最坏情况时间辅助存储
冒泡排序O(n^2)O(n^2)O(1)
快速排序

O(nlog2n)

O(n^2)O(log2n)
插入排序O(n^2)O(n^2)O(1)
选择排序O(n^2)O(n^2)O(1)
堆排序O(nlog2n)O(n^2)O(1)

程序测试:

 使用希尔排序时数据大于2000不稳定

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值