数据结构之----排序

1、冒泡排序

void BubbleSort(Sqlist &L)
{
	bool flag=0;//这里设置一个标志位,用来判断是否进行了交换,如果没有交换,说明不需要再排,这个就是冒泡排序的好处
	for(int i=1;i<L.length;i++)//只需要排L.length-1次
	{
		for(int j=L.length-1;j>=1;j--)//每一次都要从后往前找
		{
			if(L.r[j-1]>L.r[j])
				{
					swap(j-1,j,L);
					flag=1;
				}
		}
		if(!flag)
			break;
	}
}

思想:从后面两两比较,若是小的,就进行交换,这样每循环一次就冒泡出一个最小。同时其它的数进行了移位。

如果排序较好,那么需要移动的次数还是比较少的。

2、选择排序

#include "stdafx.h"
#include<iostream>
using namespace std;
const int maxsize=5;
struct Sqlist
{
 int r[maxsize];
 int length;
 Sqlist():length(0){}
};
void swap(int i,int j,Sqlist &L)
{
 int temp=L.r[i];
 L.r[i]=L.r[j];
 L.r[j]=temp;
}
void SlectSort(Sqlist &L)
{
 for(int i=0;i<L.length;i++)//
  
 { 
  int min=i;//这种游标法非常好,我很喜欢
  for(int j=i+1;j<=L.length;j++)
  {  
   if(L.r[j]<L.r[min])
    min=j;
  }
  if(i!=min)
   swap(i,min,L);
 }
}
int _tmain(int argc, _TCHAR* argv[])
{
 Sqlist L;
 for(int i=0;i<maxsize;i++)
 {
  cin>>L.r[i];//以后就用这种方法读入数据了
  L.length++;
 }
 SlectSort(L);
 for(int i=0;i<L.length;i++)
  cout<<L.r[i];//输出输入不加endl换行很方便</p><p>
 return 0;
}

思想:相当于是不断从后面找到最小的,放到前面相应的位置
 

3、直接插入排序

void InsertSort(Sqlist &L)
{
	for(int i=1;i<L.length;i++)//从第二个位置开始
	{
		int temp=L.r[i];
		int j;
		if(L.r[i]<L.r[i-1])
		{	for(j=i-1;L.r[j]>L.r[i];j--)//注意是停止条件,为>
				L.r[j+1]=L.r[j];
			L.r[j+1]=temp; //最后再插过去  这样节省时间,前面只要做覆盖移动即可
		}

	}
}

思想:向前插到合适的位置。


4、希尔排序

void shellsort(Sqlist &L)
{
	int i,j;
	int increment=L.length;
	do
	{
		increment=increment/3+1;
		for(i=increment+1;i<L.length;i++)
		{
			if(L.r[i]<L.r[i-increment])
			{
				int temp=L.r[i];//接下来和插入排序有点像了
				for(j=i-increment;L.r[j]>L.r[i];j-=increment)
					L.r[j+increment]=L.r[j];
				L.r[j+increment]=temp;
			}
		}
	}
	while(increment>1); //这个do while记住是有;的
}

思想:希尔排序是插入排序的改进版,只是更改了插入间隔为increment

(1)采用do while,从increment +1 遍历

(2)和前一个间隔值i-incremtnt比较,若小,则进入循环   (条件为,间隔值比i值大,)若是,则进行用前值 j 覆盖后值j+increment,最终用i值来刷新前值j+increment

(3)while 后面记得加上;

5、堆排序

void HeapAdjust(Sqlist &L,int s,int m)
{
	int temp,j;
	temp=L.r[s];
	for(j=2*s;j<=m;j*=2)//定位要调整的小堆的位置,
	{
		if(j<m && L.r[j]<L.r[j+1])//和被调整的数对比,看是哪一个分支,然后用分支覆盖掉,并滑向分支,最后插入调整数
			j++;
		if(temp>=L.r[j])
			break;
		L.r[s]=L.r[j];
		s=j;
	}
	L.r[s]=temp;
}
void HeapSort(Sqlist &L)
{
	L.length--;//从1开始排,若length为5则变成4,刚好是末端数
	int i;
	for(i=L.length/2;i>0;i--) //从第一个有子节点的位置开始调整为大顶堆,直到i=1
		HeapAdjust(L,i,L.length);
	for(i=L.length;i>1;i--)//不断交换,共交换L.length-1次
	{
		swap(i,1,L);//交换
		HeapAdjust(L,1,i-1);//调整大顶堆,由于其它的已经调整好,只有第一个没有调整,因此代入下去即可

	}
	L.length++;//恢复 这样size还是没有变,只是r[0]没有用到,这里主要是方便理解
}


堆排序的思想:

(1)全部调整堆,使其满足大顶堆的要求,这样堆顶的元素是最大的。(调整顺序:从length/2开始调整,刚好是最底下一个子堆)

(2) 交换和调整大顶堆n-1次,就完成了整个排序。

调整大顶堆的思想:

(1)存储要调整点的值

(2)找到子堆中,最大点的位置,

(3)如果最大点比调整点大,用最大点覆盖掉调整点,

(4)处理最大点所在的子堆,在里面找到调整点该放到哪个位置

(5)重复步骤(3)

(5)完毕后,将调整点插入到合适的位置。

 

6、归并排序

6.1 归并排序的递归实现

这个是自己根据大话数据结构改进的,跟以前排序方法,风格统一

void merge(Sqlist &L,int i,int m,int n)
{
	int j,k,l;
	int ti=i;
	int tsize=n-i+1;
	int *S=new int[n-i+1];//初始化数组,用来存储排序,做缓存用
	for(int t=0;t<tsize;t++)
		S[t]=0;
	for(j=m+1,k=0;i<=m && j<=n;k++)
	{
		if(L.r[i]<L.r[j])//看大哪边大就哪哪边的,这种i++的方式非常好
			S[k]=L.r[i++];
		else
			S[k]=L.r[j++];

	}
	if(i<=m)//若j那边取完了i这边还有剩的,那么就把剩下的直接加在后面
	{
		for(l=0;l<=m-i;l++)
			S[k+l]=L.r[i+l];
	}
	if(j<=n)
	{
		for(l=0;l<=n-j;l++)
			S[k+l]=L.r[j+l];
	}
	for(int a=0;a<tsize;a++)
		L.r[ti+a]=S[a];
	delete S;//不要忘了释放动态数组

}
void msort(Sqlist &L,int s,int t)//s为待排序数组的第一个元素下标,t为待排序数组最后一个元素的下标
{
	int m;
	if(s==t)
		return;//直接返回,无需归并
	else if(s+1==t)
	{
		if(L.r[s]>L.r[t])//只要交换两个元素的位置,就已经做好了排序,不需要在做归并
			swap(s,t,L);
		return;
	}
	else
	{
		m=(s+t)/2;
		msort(L,s,m);
		msort(L,m+1,t);
		merge(L,s,m,t);
	}
}
void MergeSort(Sqlist &L)
{
	msort(L,0,L.length-1);
}

排序思想:

(1)要想将一个数组排序,那么就是从中间分开,将两边排好序之后,再归并,就完成排序。

(2)而两边要想排好序,也是要做同样的工作。

(3)因此用到递归

(4)递归的工作是,分成两边,两边排好序(递归),归并(return)

(5)递归的出口是,当数组中元素个数为1时,说明已经排好序,且无需做分开,归并的工作,就能返回(return)。当元素个数为2的时候,也无需分开,只需要简单排序就能返回(return)。

归并思想:

(1)对于一个数组,其中i...m已经排好序,m+1...n已经排好序,将其归并i...n要做的工作是。

(2)定义一个i到n长度的动态数组,不断比较两边的大小,哪边大,就将哪边的值放到动态数组中,同时那边的游标往前走一个。

(3)当一边已经完成,而另外一边还没有放完时,就将剩下的接在数组后面。

(4)将原来数组中i...n部分的值更新为动态数组中的值。

6.2 归并排序的非递归实现

7、快速排序

int partition(Sqlist &L,int low,int high)//简单来讲
{
	int pivotkey;
	pivotkey=L.r[low];
	while(low<high)
	{
		while(low<high && L.r[high]>=pivotkey)
			high--;
		swap(low,high,L);
		while(low<high && L.r[low]<=pivotkey)
			low++;
		swap(low,high,L);
	}
	return low;

}

void qsort(Sqlist &L,int low,int high)
{
	int pivot;
	if(low<high)
	{
		pivot=partition(L,low,high);
		qsort(L,low,pivot-1);
		qsort(L,pivot+1,high);
	}
}
void QuickSort(Sqlist &L)
{
	qsort(L,0,L.length-1);
}


快速排序的思想是:

(1)要想将一个数组排序,可以将其分成两部分,较大值部分可较小值部分,再将两部分做同样的工作后,就完成了排序。

(2)因此还是用到递归

(3)递归的工作是,分成两边,左边为小值部分,右边为大值部分。

(3)递归出口,当小数组只有一个元素的时候直接返回(return),当有两个元素的时候,交换排序后返回(return)

分部实现:

(1)选取一个关键字,为第一个值,排序结果是,让一部分比它小,一部分比它大。

(2)不另外建立缓存

(3)采用左右移动的方式完成,先比较高端部分,高端有值比关键字小,那么将高端值交换到低端来,

(4)再比较低端,若低端有值比关键字大,那么将其交换到高端。

(5)如此往复,就完成了分部工作。

这种交换方法,省空间,而且特别巧妙。

 优化方法:

(1)优化选取关键字

(2)优化不必要的交换,相当于是备份了一个,temp然后用覆盖的方式  ,就和堆排序里面有点像。

(3)优化递归操作

总结

可以将算法非为两类:

简单算法:冒泡、简单选择、直接插入

改进算法:希尔、堆、归并、快排

总的来说,改进算法中堆和归并排序各种情况算法时间复杂度都是nlogn。

而希尔和快排最坏情况是n2,快排最好是nlogn,希尔最坏是n1.5这里看取的增量而定。

递归需要占用logn的空间,而归并排序同样需要一个数组来存储。耗费n的空间。

冒泡和直接插入最好情况时间复杂度为n,是排序很好的情况。

 

从平均情况来看,后面三种算法要比希尔排序好,但是比前面三种简单算法好

从最好情况来看,冒泡排序和直接插入排序又更胜一筹,如果排序基本有序,那么就不要考虑复杂算法。

从最坏情况来看,堆排序与归并排序又强过快排以及其它排序

可以看出:堆排序与归并排序,表现的比较稳定

但是快排:有时候表现的特别好,有时候比较差

空间复杂度来讲:归并排序和快速排序都是用的递归,所以对空间的要求比较大,反而堆排序堆空间要求比较低。

稳定性来看:归并排序最好

从排序个数来看:当个数较少时采用简单排序法

总之,经过优化的快速排序是性能最好的排序方法,但是要根据不同的场合来考虑不同的算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值