C++学习第一篇——排序算法

排序算法

排序就是将数据按一定顺序重新排列。它是许多算法的基础,可以让数据变得更容易处理。而对于算法就要了解其的时间复杂度空间复杂度,排序算法更要考虑其的稳定性稳定性是指,当数据中存在2个或2个以上键值相等的元素时,这些元素在排序处理前后顺序不变。
如图所示,几种排序算法的时间空间复杂度,以及稳定性。
在这里插入图片描述
首先,介绍几种初等排序。

插入排序法(Insertion Sort)

C++实现:

void Insertion(int arr[],int n)
{
	for(int i=1;i<n;i++)
	{
		int num=arr[i];
		int k=i-1;
		while(k>=0)
		{
			if(arr[k]>num)
			{
				arr[k+1]=shu[k];
			}
			else
			{
				break;
			}
			k--;
		}
		arr[k+1]=num;
	}
}

在插入排序中,我们只将比取出的值大的元素向后平移,不相邻的元素不会直接交换位置,因此整个排序算法十分稳定。
插入排序法是一种很有趣的算法,输入数据的顺序能大幅影响它的复杂度,之前表中对应其的复杂度是O(N^2),也仅指输入数据问降序排列的情况。如果输入数据为升序排列,那么程序只需要经历N次比较便可执行完毕。可见,插入排序法的优势就在于能快速处理相对有序的数据。

冒泡排序法(Bubble Sort)

C++实现

void Bubble(int arr[],int n)
{
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n-1;j++)
		{
			if(arr[j]>arr[j+1])
			{
				swap(arr[j],arr[j+1]);
			}
		}
	}
}

冒泡排序法仅对数组中的相邻元素进行比较和交换,因此键相同的元素不会改变顺序。所以冒泡排序法也属于一种稳定排序的算法,但如果将原有比较的<或>进行取等操作,那么该算法就会失去稳定性。

选择排序法(Selection Sort)

C++实现

void Selection(int arr[],int n)
{
	int count=0;
	for(int i=0;i<n;i++)
	{
		int mini = i;
		for(int j=i;j<n;j++)
		{
			if(arr[mini]>arr[j])
			{
				mini = j;
			} 
		} 
		swap(arr[i],arr[mini]);
	}
}

由于选择排序法会直接交换两个不相邻的元素,所以属于不稳定的排序算法。

希尔排序法(Shell Sort)

C++实现

vector <int> G;
//interval = g
void insertion(int arr[],int n ,int g)
{
	for(int i=g;i<n;i++)
	{
		int v=arr[i];
		int j=i-g;
		while(j>=0&&arr[j]>v)
		{
			arr[j+g]=arr[j];
			j-=g;
			cnt++;
		} 
		arr[j+g]=v;
	} 
}
void shell(int arr[],int n)
{
	int h=1;
	while(h<=n)
	{
		G.push_back(h);
		h=3*h+1;
	}
	for(int i=G.size()-1;i>=0;i--)
	{
		insertion(arr,n,G[i]);
	}
}

insertion是仅以间隔为g的元素为对象进行的插入排序,shell是进行insertion的循环并在每轮循环后缩小g的范围,G数组使用来存g的范围的。
以上是四种初级排序,冒泡排序法和选择排序法不依赖数据,即比较运算的次数(算法复杂度)不受输入数据的影响,而插入算法在执行时却依赖数据,处理某些数据时具有很高的效率。希尔排序也只是变相的插入排序,更改循环间隔,达到时间复杂度的减小,但也是一种不稳定排序。

在对于一些体积庞大的数组面前,初等排序算法时间复杂度高达O(n^2),那么对于这些数据就要用到高等算法。下面介绍几种高等排序

归并排序(Merge Sort)

C++实现

#define MAX INT_MAX
int l[250005],r[250005];//定义两个数组用来存分割开的数组
void merge(int A[],int n,int left,int mid,int right)
{
	int n1=mid-left;//l数组的大小
	int n2=right-mid;//r数组的大小
	for(int i=0;i<n1;i++)//赋值到l数组
	{
		l[i]=A[left+i];
	}
	for(int i=0;i<n2;i++)//赋值到r数组
	{
		r[i]=A[mid+i];
	}
	l[n1]=r[n2]=MAX;//设置一个整数最大值来表明数组界限,可以不设置
	int i=0,j=0;
	/*
	遍历数组判断左侧是否比右侧小,进行合并,这里函数调用关系类似于dfs,那么他会从左至右逐渐合并,每次合并都两个数组。如果要降序排序需要将赋值左右反向更换。
	*/
	for(int k=left;k<right;k++)
	{
		if(l[i]<=r[j])
		{
			A[k]=l[i++];
		}
		else
		{
			A[k]=r[j++];
		}
	}
}
void mergesort(int A[],int n,int left,int right)
{
	if(left+1<right)
	{
		int mid=(left+right)/2;
		mergesort(A,n,left,mid);
		mergesort(A,n,mid,right);
		merge(A,n,left,mid,right);
	}
}

在归并排序法中,合并两个已排序数组的merge是整个算法的基础,要将包含n1个的数组和包含n2的数组合并在一起变成A。mergesort中,将整个数组进行分割,分为两个数组,然后将结果合并。

快速排序(Quick Sort)

C++实现

int partition(int A[],int n,int p,int r)
{
	int i,j;
	int x;
	x=A[r];
	i=p-1;
	for(int j=p;j<r;j++)
	{
		if(A[j]<=x)
		{
			i++;
			swap(A[i],A[j]);
		}
	}
	swap(A[i+1],A[r]);
	return i+1;
}
void quicksort(int A[],int n,int p,int r)
{
	int q;
	if(p<r)
	{
		q=partition(A,n,p,r);
		quicksort(A,n,p,q-1);
		quicksort(A,n,q+1,r); 
	}
}

快速排序与归并排序一样基于分治法,但执行分割时就已在原数组中完成了排序,因此不需要归并排序中那种手动的合并处理。但与归并排序不同的是,在分割的过程中会交换不相邻元素,因此是一种不稳定排序。

桶排序(Bucket Sort)

又称作基数排序和箱排序
C++实现

int c[VMAX+1],b[VMAX+1],a[VMAX+1];
	//a数组用来存储原有数组
	//b数组用来记录结果数组 
	//c数组用来记录原有数组中每种数字出现的频率
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)//存入数组a并且记录数字频率至c 
	{
		scanf("%d",&a[i+1]);
		c[a[i+1]]++;
	}
	for(int i=1;i<=VMAX;i++)//c数组下标是当前数字,值是在当前数字之前的数字数量,从小到大累加 
	{
		c[i]=c[i]+c[i-1];
	}
	for(int j=1;j<=n;j++)//遍历数组a,寻找一个适当的位置来存放每一个遍历过的数字,这个位置也就是在他之前数字(比他小)的数量 
	{
		b[c[a[j]]]=a[j];
		c[a[j]]--;//减小当前数字前的数字个数 
	}

桶排序从数组末尾的元素开始选择,属于一种稳定的排序算法。其运行所需的时间及内存空间也与数组的最大值成正比。因为数组的值越大,所需数组的大小也就越大,但其时间复杂度仅有O(n+k),是一种高效且稳定的算法。
另外一种堆排序会在以后进行补充。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值