1.前言
本文主要围绕常见的八大算法由浅入深进行讲解,分别有:冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、 归并排序、计数排序。
预备知识:读者应该对C语言语法有基本的认识,能看懂代码的程度即可!
2.算法讲解
动图演示:

冒泡排序主要思想是通过遍历数据,将最小或者最大的数移至最后。
2.1、冒泡排序
2.1.1 代码展示
void BubbleSort(int* arr, int sz)
{
  //变量 i 控制遍历的次数 -- 每次遍历一遍排列一个数据,总共遍历 n - 1次
	for (int i = 0; i < sz; i++)
	{
		for (int j = 0; j < sz - i - 1; j++)
		{
		//变量 j 控制待排序的前后两个数据,j 应随 i 的变化而变化,公式为 j < sz - i - 1
			if (arr[j] > arr[j + 1])
				flag = 0, Swap(&arr[j], &arr[j + 1]);
		}
	}
}
2.1.2 思路讲解
排升序,冒泡排序的做法是利用两层循环,内层遍历的同时与相邻数进行比较,循环结束后必能将最大的数移至数组末端,每次循环都能将一个数据排序完毕,直至循环结束。
2.1.3 优化建议
数据已有序时,循环无法停止,将会导致遍历一直持续下去,这样会很消耗时间,因此我们可以从时间复杂度上进行优化,这里提供的思路是在外层循环内部新增一个flag变量,用于记录循环过程中是否交换数据。代码演示如下:
void BubbleSort(int* arr, int sz)
{
  //变量 i 控制遍历的次数 -- 每次遍历一遍排列一个数据,总共遍历 n - 1次
	for (int i = 0; i < sz; i++)
	{
		//flag用于标记是否排列完成,若未排列完成,值为1;若排列完成,值置为0
		int flag = 1;
		for (int j = 0; j < sz - i - 1; j++)
		{
		//变量 j 控制待排序的前后两个数据,j 应随 i 的变化而变化,公式为 j < sz - i - 1
			if (arr[j] > arr[j + 1])
				flag = 0, Swap(&arr[j], &arr[j + 1]);
		}
		if (flag)
			break;
	}
}
2.2、插入排序
2.2.1 动图演示

2.2.2 代码展示
void InsertSort(int arr[], int size)
{
	//默认认为i之前的元素都已排列整齐
	for (int i = 0; i < size - 1; i++)
	{
		int end = i + 1;
		int tmp = arr[end];
		while (end > 0)
		{
			if (arr[end - 1] > arr[end])
			{
				arr[end] = arr[end - 1];
				arr[end - 1] = tmp;
			}
			else
			{
				break;
			}
			end--;
		}
	}
}
2.2.3 思路讲解
插入排序主要思想是,假设变量 i 之前的数据已经有序的前提下,利用 i 遍历整个数组,每遍历一个数据将其有规则地插入到 i 之前的子数组中,确保 i 之前的子数组有序遍历完整个数组。
2.3、希尔排序
希尔排序的过程实际上是分组插入排序的过程,希尔排序引入了一个遍历gap,用于指定两组子序列间的间隔,需要注意的是,gap是一个随着排序的进行逐渐减少的变量,当gap为1时为普通插入排序。
//希尔排序的实现
void ShellSort(int* arr, int size)
{
	int gap = size;
	while (gap > 1)
	{
		//gap每次排序前除3是大量数据综合来看时间复杂度最优的,加1确保gap属于[0,1]
		gap = gap / 3 + 1;
		//以下套用插入排序的逻辑
		for (int i = 0; i < size - gap; i += gap)
		{
			int end = i + gap;
			int tmp = arr[end];
			while (end > 0)
			{
				if (arr[end - gap] > arr[end])
				{
					arr[end] = arr[end - gap];
					arr[end - gap] = tmp;
				}
				else
				{
					break;
				}
				end -= gap;
			}
		}
	}
}
2.4、选择排序

选择排序主要思想,单趟完成遍历找出数组中的最小值,单趟结束后将最小值与begin交换,直至多趟结束。
void SelectSort(int* arr, int sz) //待优化版本
{
	int begin = 0, end = sz - 1;
	while (begin < end)
	{
		int min = begin;
		//遍历找出最小值
		for (int i = begin + 1; i <= end; i++)
		{
			if (arr[min] > arr[i])
				//更新最小值
				min = i;
		}
		//交换最小值
		Swap(&arr[min], &arr[begin]);
		begin++;
	}
}
2.5、堆排序

!!!学习堆排序前必须确保自己对二叉树有充分的认识!!!,需要预先掌握的知识:建堆、向上调整算法、向下调整算法、另外还需要对二叉树结构有一定的认识。
堆排序的思想,排升序建大堆,将堆顶与end进行交换后,再将堆顶进行向下调整。
void AdjustDwon(int* a, int n, int root)
{
	int child = root * 2 + 1;//假设左孩子比较大
	while (child < n)
	{
		if ((child + 1 < n) && a[child] < a[child + 1])
			child++;
		if (a[root] < a[child])//排升序建大堆
		{
			Swap(&a[root], &a[child]);
			root = child;
			child = root * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int* a, int n)
{
	//首先建堆 -- 1)排升序建大堆, 2)排降序建小堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDwon(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon(a, end, 0);
		end--;
	}
}
2.6、快速排序

快速排序主要思想,单趟固定一个媒介key,远离key的一方先走,找小于key的值后停下,靠近key的一方开始走找大于key的值,将二者的值互换,继续重复以上步骤。直至左右双方相遇,此时将相遇值与key交换位置,该key的位置整好是有序时的位置,并且将数组分为左右两棵子树。
int PartSort(int* arr, int left, int right)
{
	int key = left;
	int begin = left, end = right;
	while(begin < end)
	{
		//右边找小 -- 远离key的节点先移动
		while (begin < end && arr[end] >= arr[key])
			end--;
		//左边找大
		while (begin < end && arr[begin] <= arr[key])
			begin++;
		Swap(&arr[begin], &arr[end]);
	}
	//交换key和相遇点的值
	Swap(&arr[key], &arr[begin]);
	return begin;
}
void QuickSort(int* arr, int left, int right)
{
	if (left >= right)
		return;
	int key = PartSort(arr, left, right);
	QuickSort(arr, left, keyi - 1);
	QuickSort(arr, keyi + 1, right);
}
2.7、归并排序

归并排序的主要是利用分治法来实现,分治法可分为三个步骤:分解、解决和合并。其中分解是利用递归来将原数组分为两半,直至每一个子数组只剩一个元素;对最小的子数组视为已排序;合并就是将两个已排序的子数组合并成一个新的有序数组,直到最终合并成完整的有序数组。
void _MergeSort(int* arr, int* tmp, int begin, int end)
{
	if (begin >= end)
		return;
	int mid = (begin + end) / 2;
	//对左右两边进行排序 --- 结束条件:left==right
	_MergeSort(arr, tmp, begin, mid);
	_MergeSort(arr, tmp, mid + 1, end);
	//合并两个数组
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] <= arr[begin2])
			tmp[i++] = arr[begin1++];
		else
			tmp[i++] = arr[begin2++];
	}
	//查漏补缺
	while (begin1 <= end1)
		tmp[i++] = arr[begin1++];
	while (begin2 <= end2)
		tmp[i++] = arr[begin2++];
	memcpy(arr + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}
void MergeSort(int* arr, int sz)
{
	int* tmp = (int*)malloc(sz * sizeof(int));
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	//创建成功 -- 进行归并排序
	_MergeSort(arr, tmp, 0, sz - 1);
	free(tmp);
	tmp = NULL;
}
2.8、计数排序

计数排序的主要思想是,统计元素出现的次数来实现排序。具体来讲,统计原数组中每个数出现的次数,将每个数出现的次数作为新数组中待存的值,新数组下标则表示原数组的值或者相对映射值
void CountSort(int* arr, int sz)
{
	int min = arr[0], max = arr[0];
	//遍历原数组,找出最大值和最小值确认大小范围
	for (int i = 0; i < sz; i++)
	{
		if (min > arr[i])
			min = arr[i];
		if (max < arr[i])
			max = arr[i];
	}
	
	//注意此处求的范围是带开辟数组的大小,假设最小值为数组下标起始位置,
	//那么max表示数组的最后一个数据的下标,故有求范围最后需要+1
	int range = max - min + 1;
	int* count = (int*)calloc(range, range * sizeof(int));
	//检查count数组是否开辟成功
	if(count == NULL)
	{
		perror("calloc fail");
		return;
	}
	//遍历arr数组并统计数组arr中每个数据出现的次数,并存到count数组中
	for (int i = 0; i < sz; i++)
	{
		count[arr[i] - min]++;
	}
	
	// 排序
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		//注意count内的数据对应的是arr数组中该数出现的个数,
		//另外要记得count数组的下标(i)就是arr数组中数据的映射值(映射关系为,i = arr[] - min)
		while (count[i]--)//此处i充当count数组的下标
		{
			//此处i充当arr数组中元素的映射值
			arr[j++] = i + min;
		}
	}
	free(count);
}
 
                   
                   
                   
                   
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                  
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            