排序算法总结

原创 2016年06月09日 19:36:20

不知道你有没有注意过,在我们身边,每天都在发生着各种各样的“排序”。每年期末的考试成绩在被排序,我们的年龄在被排序,一点一点过去的时间也是一种排序。作为一个计算机系的大学狗,博主最近也研究了一下几种常见的排序算法。 

1.插入排序

顾名思义,插入排序即找到一个位置将数据置入,而插入排序又分为以下两种

时间复杂度O(N^2)

a.直接插入排序

直接插入排序即我们每次规定一个数据作为key使用key和每一个位于其前面的数据做比较,当找到一个比它小的数据时就将其放在这个数据的后面,如果数据都大于key的值就将数据后移。key的起始是数组的第二个数据,每次循环一轮后将寻找范围增加一。

例如:第一次插入时key的值为第二个数据,第二次则变成第三个以此类推直到数组结束。这样每次我们在进行单轮插入时都可以保证数组是有序的。

代码如下:

void InsertSort(int *arr, int n)
{
	int end = 0;
	int tmp = 0;
	for (int i = 0; i < n - 1; i++)
	{
		end = i;
		tmp = arr[end + 1];
		while (end >= 0 && tmp < arr[end])
		{
			arr[end + 1] = arr[end];
			--end;
		}
		//此处由于已经减出范围所以需要++
		if (tmp < arr[++end])
			arr[end] = tmp;
	}
}
b.希尔排序

希尔排序实际上是直接插入排序的一种优化,它的key值不是每次和每一个数据进行比较,而是设置了一个gap值,每次跳动gap个数据进行比较。这样做的好处是能够在预排序阶段让小的数据尽量到前面,让大的数据尽量到后面。最后一次我们将gap值缩小到一,相当于做了一次直接插入排序

时间复杂度约为O(N^1.25)

代码如下:

void ShellSort(int *arr, int n)
{
	int end = 0;
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
			end = i;
			int tmp = arr[end + gap];
			while (end >= 0 && tmp < arr[end])
			{
				arr[end + gap] = arr[end];
				end -= gap;
			}
			arr[end + gap] = tmp;
		}
	}
}

2.选择排序

a.直接选择排序

选择排序,即每次选择最小的放在当前范围的第一个位置,或者每次选择一个最大的放在当前范围的最后一个位置。开始时,我们的范围是整个数组,每经过一个轮次之后就缩小一次数组的范围,在此我们为了稍稍优化一下,就可以每经过一个轮次同时选择最大的和最小的数据放在相应的位置。

时间复杂度O(N^2)

void SelectSort(int *arr, int n)
{
	int left = 0;
	int right = n - 1;
	int max = 0;
	int min = 0;
	while (left < right)
	{
		for (int i = left; i <= right; i++)
		{
			if (arr[i] > arr[max])
				max = i;
			if (arr[i] < arr[min])
				min = i;
		}
		swap(arr[left], arr[min]);
		if (max == left)
			max = min; 
		swap(arr[right], arr[max]);
		left++;
		right--;
	}
}
3.堆排序

我们在排序之前构造一个小堆这样就可保证每次最小的数据位于顶端,每次拿出一个数据放入数组。拿出时想小堆的顶端和末尾交换后拿出,再次调整小堆,继续选择,直到全部拿出,数组既有序

时间复杂度O(NlogN)

void AdjustDown(int *arr,int parent, int n)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && arr[child] < arr[child + 1])
			child++;
		if (arr[parent] < arr[child])
		{
			swap(arr[parent], arr[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}
void HeapSort(int *arr, int n)
{
	int index = (n - 2) / 2;
	for (int i = index; i >= 0; i--)
	{
		AdjustDown(arr, i, n);
	}
	int end = n - 1;
	while (end > 0)
	{
		swap(arr[0], arr[end]);
		AdjustDown(arr, 0, end);
		--end;
	}
}

4.交换排序

a.冒泡排序

比较相邻两个元素的大小,如果顺序错误就将他们的位置进行交换

时间复杂度O(N^2)

void BubbleSort(int *arr,int n)
{
//chage用于优化
	bool chage = false;
	int end = n - 1;
	for (int i = 0; i < n; i++)
	{
		chage = false;
		for (int j = 0; j < end-i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				swap(arr[j], arr[j + 1]);
				chage = true;
			}
		}
		if (chage = false)
			break;
	}

}
其中,chage用于优化,如果一次排序伦次中一次交换都没有发生即说明数组已经有序

b.快速排序

快速排序是一个值得我们注意的算法,他的基本思想即找一个基准位置,每次使得指针从选定范围的头尾开始寻找,从头开始的指针寻找大于基准的数,

从尾开始的指针找小于基准的数,找到之后将两个值得位置进行交换,这样保证每个排序轮次后基准的左边的数都小于他而右边的数都大于他,然后再递归。

时间复杂度O(NlogN)

1)标准快排

void SelectMid(int *arr,int left,int right)
{
	int mid = (left + right) / 2;
	if (left < right)
	{
		if (arr[left] < arr[mid] && arr[mid] < arr[right])
			swap(arr[left], arr[left]);
		else if (arr[right] < arr[mid])
			swap(arr[left], arr[right]);
	}
	else
	{
		if (arr[mid] > arr[right] && arr[mid] <arr[left])
			swap(arr[left], arr[mid]);
	}
}
void QuikSort(int *arr, int left, int right)
{
	SelectMid(arr, left, right);
	if (right < left)
	{
		return;
	}
	int j = right;
	int i = left;
	//设置比较的基准值
	int base = arr[left];
	//一次交换必须直到相遇即停
	while (i != j)
	{
		//找从右边开始小于key值得数
		while (arr[j] >= base && i < j)
			j--;
		//从左起找第一个大于key值的数
		while (arr[i] <= base && i < j)
			i++;
	
		if (i < j)
		{
			swap(arr[i], arr[j]);
		}
	}
	//将key值归位
	swap(arr[left], arr[i]);
	QuikSort(arr, left, i - 1);
	QuikSort(arr, i + 1, right);
}
2)挖坑法快排

挖坑法的思想即第一次将key的位置置成一个坑然后从前找一个大于key的数据将其抛入坑中并在该数据的位置形成一个新的坑,然后从后面向前面

找一个小于key的数据在将其抛入坑中,然后再形成新的坑,如此循环就可以将数据置于正确的位置。

代码如下:

void QuikSort_OP1(int *arr, int start, int end)
{
	SelectMid(arr, start, end);
	if (start >=  end)
		return;
	int key = arr[end];
	int hoop = end;
	int left = start;
	int right = end;

	while (left < right)
	{
		while (right >left && arr[left] < key)
			++left;
		if (right >left)
		{
			arr[hoop] = arr[left];
			hoop = left;
		}
		while (right > left && arr[right] > key)
			--right;
		if (right > left)
		{
			arr[hoop] = arr[right];
			hoop = right;
		}
	}
	arr[hoop] = key;
	
	QuikSort_OP1(arr, start, hoop-1);
	QuikSort_OP1(arr, hoop+1,  end);
}

3)prev&cur快排

此种方法稍显难以理解,实际上它定义了两个指针cur和prev,每次让cur找比key小的数据,当数据连续的小于key值时prev紧跟cur,当其遇到大于

key时就会发现cur会连续的走若干步,prev总是指向了比key大的数的前一个。大家可以在纸上画一画

代码如下:

<span style="font-size: 14px;">void QuikSort_OP2(int *arr, int start, int end)
{
	SelectMid(arr, start, end);
	int  prev = start -1;
	int cur = start;
	while (cur < end)
	{
		while (arr[cur] > arr[end])
			cur++;
		if (cur < end )
			swap(arr[++prev], arr[cur++]);
	}
	swap(arr[end], arr[++prev]);
	QuikSort(arr, start, prev);
	QuikSort(arr, prev, end);
</span><span style="font-size:12px;">}</span>





版权声明:本文为博主原创文章,未经博主允许不得转载。

排序算法总结

  • 2015年07月04日 19:19
  • 390KB
  • 下载

排序算法总结

  • 2014年01月21日 13:52
  • 12KB
  • 下载

数据结构-八大排序算法总结

一.插入排序  1.直接插入算法    时间复杂度o(n^2)  空间复杂度o(1) #include #include #include #include /***************...

8个常见数据结构排序算法总结

  • 2013年11月15日 21:10
  • 23KB
  • 下载

各种排序算法总结

  • 2013年03月01日 13:21
  • 780B
  • 下载

各种排序算法的java实现及时间、空间复杂度、稳定程度总结

最近闲着没事,就随便看了看数据结构,看到各种排序算法时,突然心血来潮,就想,以前都是用C++实现的,能不能用java实现所有的排序算法呢?而且顺便练习一下递归的使用(因为我最不擅长使用的就是递归) ...

常用排序算法总结

  • 2013年06月01日 15:55
  • 4KB
  • 下载

第十三节:StringBuffer类_排序算法_自动装箱(个人总结)

StringBuffer类 StringBuffer在对字符串进行拼接时,不要再出现用"+"进行拼接了,当用+号进行两个变量代表的字符串拼接时,又会在常量池内产生新的对象以及新的垃圾,因此在使用...

C-C++排序算法总结

  • 2010年03月27日 13:06
  • 3KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:排序算法总结
举报原因:
原因补充:

(最多只允许输入30个字)