天天看点

基础排序算法及代码实现一.交换类二.插入排序类三.选择排序类四.分治类排序

一.交换类

冒泡排序:

void BubbleSort(int*src, int n)//冒泡
{
	int i, j, flag = 0;
	for (i = 0; i < n-1; i++)//最后一次不需要,因为出最后最后只剩一个元素待排
	{
		flag = 0;
		for (j = 0; j < n - i - 1; j++)
		{
			if (src[j] > src[j + 1])
			{
				int tmp = src[j];
				src[j] = src[j + 1];
				src[j + 1] = tmp;
				flag = 1;
			}
		}
		if (0 == flag)
		{
			break;
		}
	}
}
           

冒泡排序类似于海浪,一波一拨,每次将无序中的最值冒到最边上,一趟一趟重复进行。

选择排序:

void SelectSort(int* src, int n)//选择排序
{
	int i, j;
	int min;//始终标记接下来遍历中最小的数据下标
	for (i = 0; i < n-1; i++)
	{
		min = i;
		for (j = i; j < n; j++)
		{
			if (src[min] > src[j])
			{
				min = j;
			}
		}
		if (min != i)
		{
			int tmp = src[i];
			src[i] = src[min];
			src[min] = tmp;
		}
	}
}
           

该算法是从头开始不断遍历后面的未排序列,找到最值,并与本节点交换,每次得到未排序列的最值

二.插入排序类

直接插入排序:

void InsertSort(int*src, int n)//插入排序
{
	int i, j;
	for (i = 1; i < n; i++)
	{
		int tmp = src[i];
		if (src[i - 1] > tmp)//前面已经有序,如果当i-1小于等于tmp,则说明前面所有数据均小于等于tmp,故不用找位置插入
		{
			for (j = i; j > 0 && src[j - 1]> tmp; j--)//j前面数据已经有序,只需要找到第一个比tmp小的数据,插在它之后
			{
				src[j] = src[j - 1];
			}
			src[j] = tmp;
		}
	}
}

           

直接插入排序是从第二个元素为基准点,然后在前面已经有序的数列中寻找合适的位置插入,若没有合适位置就保持不动,基准点后移。图解:

基础排序算法及代码实现一.交换类二.插入排序类三.选择排序类四.分治类排序

注意:

  • 当数据规模越小,直接插入排序越优
  • 当数组越接近于有序,该排序算法越快

希尔排序:

void ShellSort(int* src, int n)//希尔排序:分组插排
{
	int gap = n / 2;
	int i, j;
	for (gap=n /2; gap >= 1; gap /= 2)//gap为每次分的组数
	{
		for (i = gap; i < n; i++)
		{
			int tmp = src[i];
			for (j = i; j - gap >= 0 && src[j -gap] > tmp; j -= gap)
			{
				src[j] = src[j-gap];
			}
			src[j] = tmp;
		}
	}
}
           

希尔排序是直接插入排序进阶版本,它先将数据分组插排,使得数组趋于有序化,当分得组内个数特别小时,数组排序完成。图解

基础排序算法及代码实现一.交换类二.插入排序类三.选择排序类四.分治类排序

三.选择排序类

堆排序:

//向下调整算法,将下标为i的节点向下调整
void adjustDown(int* arr, int size, int n)//n为下标c
{
	int cur = n;
	while ((cur + 1) * 2 <= size)//只要有左孩子存在就要进入循环
	{
		if ((cur + 1) * 2 + 1 > size)
		{
			n = cur * 2 + 1;//没有右孩子,左孩子为孩子节点的最大值
		}
		else
		{
			if (arr[cur * 2 + 1] > arr[cur * 2 + 2])//找出右节点和左节点中的较大节点,并用n标记
			{
				n = cur * 2 + 1;
			}
			else
			{
				n = cur * 2 + 2;
			}
		}
		if (arr[cur] < arr[n])//当左右孩子节点大于该节点时,向下调整该节点,否则,就已经形成大顶根,不再继续循环
		{
			int tmp = arr[cur];
			arr[cur] = arr[n];
			arr[n] = tmp;
			cur = n;//每次交换完之后将cur更新到n下标,使后面继续向下调整
		}
		else
		{
			break;//孩子节点均小于根节点,已经构成大顶堆
		}
	}
}
void HeapSort(int* src, int n)
{
	int i = n / 2 + 1;
	//int j ;
	for (i = n / 2-1; i >= 0; i--)
	{
		adjustDown(src, n, i);	//构成大顶堆
	}
	//每次将堆中最大的元素(对顶)换下来放在最后,然后再调整一个大的上去,重复
	for (i = n - 1; i > 0; i--)
	{
		SwapArgs(src + i, src);
		adjustDown(src, --n, 0);//每次对交换后的节点向下调整,n--
	}
}
           

主要借助向下调整算法构造大根堆,再不断将最大元素出堆,得到排序。图助理解:

基础排序算法及代码实现一.交换类二.插入排序类三.选择排序类四.分治类排序

四.分治类排序

归并排序:

void Merging(int* src,int* tmp,int start,int end)
{
	if (start >= end)
	{
		return;
	}
	int mid = start + (end - start) / 2;
	int i = start, j = mid+1;
	int k = start;
	Merging(src, tmp, start, mid );
	Merging(src,tmp,mid+1,end);
	while (i<=mid && j<=end)
	{
		if (src[i] < src[j])
		{
			tmp[k] = src[i];
			i++;
			k++;
		}
		else
		{
			tmp[k] = src[j];
			k++;
			j++;
		}
	}
	while (i <= mid)
	{
		tmp[k] = src[i];
		i++;
		k++;
	}
	while (j <= end)
	{
		tmp[k] = src[j];
		k++;
		j++;
	}
	for (i = start; i <= end; i++)//将合并好的数据回执到src内
		//这里end最后的下标,start是第一个下标,下标范围易错
	{
		src[i] = tmp[i];
	}
}
void MergeSort(int* src, int n)//归并排序
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	Merging(src, tmp, 0, n-1);
	free(tmp);
}
           

归并思路类似与树的后序遍历,先调整左右,再有序归并在一起。相比于其他排序有一定的空间牺牲。

基础排序算法及代码实现一.交换类二.插入排序类三.选择排序类四.分治类排序

快速排序:

int PointerWay(int* src, int start, int end)
{
	int point = src[start];
	while (start<end)
	{
		while (start< end && src[end] >= point)
		{
			end--;
		}
		SwapArgs(&src[start], &src[end]);
		while ( start<end && src[start] <= point)
		{
			start++;
		}
		SwapArgs(&src[start], &src[end]);
	}
	return start;
}
void Quicking(int*src, int start, int end)
{
	int mid;//作为基准点
	if (start < end)//满足条件,快速排序递归调用
	{
		mid = PointerWay(src,start,end);
		Quicking(src, start, mid-1);
		Quicking(src, mid + 1, end);
	}
}
void QuickSort(int* src, int n)//快速排序
{
	Quicking(src,0, n-1);
}
           

快速排序基础实现如上将第一个作为基准值,后续比较,将比基准值小的放在一侧,比基准值大的放在另一侧,再缩小排序范围将左右已同样方法排序。

基础排序算法及代码实现一.交换类二.插入排序类三.选择排序类四.分治类排序

最后说一个细节,对于插入排序,有的地方说不稳定,有地方说稳定,我通过简短的学习认为选择排序的是否稳定与实现方法有关。例如上面的实现就是稳定的,而如果将第六行和第八行代码中的src[i - 1] > tmp改为src[i - 1] >= tmp之后,稳定性就不一定了!

继续阅读