快速排序、希尔排序、归并排序、堆排序、插入排序、冒泡排序、选择排序(递归、非递归)C语言详解

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

1.排序的概念及其运用

1.1排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

内部排序:数据元素全部放在内存中的排序。

外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

1.2排序运用
1.3 常见的排序算法
// 排序实现的接口

// 插入排序
void InsertSort(int* a, int n);

// 希尔排序
void ShellSort(int* a, int n);

// 选择排序
void SelectSort(int* a, int n);

// 堆排序
void AdjustDwon(int* a, int n, int root);
void HeapSort(int* a, int n);

// 冒泡排序
void BubbleSort(int* a, int n)

// 快速排序递归实现
// 快速排序hoare版本
int PartSort1(int* a, int left, int right);
// 快速排序挖坑法
int PartSort2(int* a, int left, int right);
// 快速排序前后指针法
int PartSort3(int* a, int left, int right);
void QuickSort(int* a, int left, int right);

// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right)

// 归并排序递归实现
void MergeSort(int* a, int n)
// 归并排序非递归实现
void MergeSortNonR(int* a, int n)

// 计数排序
void CountSort(int* a, int n)

// 测试排序的性能对比
void TestOP()
{
	srand(time(0));
	const int N = 100000;
	int* a1 = (int*)malloc(sizeof(int)*N);
	int* a2 = (int*)malloc(sizeof(int)*N);
	int* a3 = (int*)malloc(sizeof(int)*N);
	int* a4 = (int*)malloc(sizeof(int)*N);
	int* a5 = (int*)malloc(sizeof(int)*N);
	int* a6 = (int*)malloc(sizeof(int)*N);

	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];

	}

	int begin1 = clock();
	InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	SelectSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();

	int begin5 = clock();
	QuickSort(a5, 0, N-1);
	int end5 = clock();

	int begin6 = clock();
	MergeSort(a6, N);
	int end6 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("QuickSort:%d\n", end5 - begin5);
	printf("MergeSort:%d\n", end6 - begin6);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
}

######排序OJ(可使用各种排序跑这个OJ)OJ链接

2.常见排序算法的实现
2.1 插入排序
2.1.1基本思想:

直接插入排序是一种简单的插入排序法,其基本思想是:

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

实际中我们玩扑克牌时,就用了插入排序的思想

2.1.2直接插入排序:

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定
//插入排序
void InsertSort(int* a, int length)
{
	for (int i = 1; i < length; i++)
	{
		int end = i - 1;
		int num = a[i];
		while (end >= 0)
		{
			if (num < a[end])
			{
				a[end + 1] = a[end];//挪动数组
				end--;
			}
			else
			{
				break;//找到了要插入的点
			}
		}
		a[end + 1] = num;
	}
}
2.1.3 希尔排序( 缩小增量排序 )

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序

4

//希尔排序
void ShellSort(int* a, int length)
{
	//接近有序
	int gap = length;
	while (gap > 1)
	{
		gap /= 2;
		for (int i = 0; i < length - gap; i++)
		{
			int end = i;
			int num = a[i + gap];
			while (end >= 0)
			{
				if (a[end] > num)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = num;
		}
	}
}

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。

  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定:

《数据结构(C语言版)》— 严蔚敏

《数据结构-用面相对象方法与C++描述》— 殷人昆

因为咋们的gap是按照Knuth提出的方式取值的,而且Knuth进行了大量的试验统计,我们暂时就按照: O ( n 1.25 ) O(n^{1.25}) O(n1.25) O ( 1.6 ∗ n 1.25 ) O(1.6*n^{1.25}) O(1.6n1.25)来算。

  1. 稳定性:不稳定
2.2 选择排序

2.2.1基本思想:

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

2.2.2 直接选择排序:
  • 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素

  • 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换

  • 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

//选择排序
void SelectSort(int* a, int length)
{
	int left = 0, right = length - 1;
	while (left < right)
	{
		int maxi = left, mini = left;
		for (int i = left + 1; i <= right; i++)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}
			if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}
		Swap(&a[left], &a[mini]);
		if (left == maxi) maxi = mini;
		Swap(&a[right], &a[maxi]);
		left++;
		right--;
	}
}
直接选择排序的特性总结:
  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定
2.2.3 堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

6

//堆排序
void AdjustDown(int* a, int sz, int parent)
{
	//调大堆
	assert(a);
	int child = parent * 2 + 1;
	while (child < sz)//儿子节点要存在
	{
		//找左右儿子中最大的那个
		if (child + 1 < sz && a[child] < a[child + 1])
		{
			child++;//找到了最大的那个
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void SetHeap(int* a, int sz)
{
	assert(a);
	for (int i = (sz - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, sz, i);
	}
}

void heap_sort(int* a, int sz)
{
	int num = sz;
	SetHeap(a, sz);
	while (num)
	{
		Swap(&a[0], &a[num-1]);
		num--;
		AdjustDown(a, num, 0);
	}
}
直接选择排序的特性总结:
  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定
2.3 交换排序

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

2.3.1冒泡排序
//冒泡排序
void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		bool exchange = false;
		for (int j = 0; j < n - i - 1; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(&a[j], &a[j + 1]);
				exchange = true;
			}
		}
		if (exchange == false)
		{
			break;
		}
	}
}

冒泡排序的特性总结:

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定
2.3.2 快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{
  if(right - left <= 1)
      return;

  // 按照基准值对array数组的 [left, right)区间中的元素进行划分
  int div = partion(array, left, right);

  // 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)
  // 递归排[left, div)
  QuickSort(array, left, div);

  // 递归排[div+1, right)
  QuickSort(array, div+1, right);
}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,同学们在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

将区间按照基准值划分为左右两半部分的常见方式有:

  1. hoare版本
//hoare版本
void QuickSort(int* a, int left,int right)
{
	if (left >= right) return;
	
	int begin = left, end = right;
	int keyi = left;
	int mid = GetMid(a, left, right);
	Swap(&a[left], &a[mid]);
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}

		while (left < right && a[left] <= a[keyi])
		{
			left++;
		}

		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);
	keyi = right;
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}
  1. 挖坑法
//挖坑法
void QuickSort(int* a, int left, int right)
{
	if (left >= right) return;
	
	int key = a[left];
	int begin = left, end=right;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[left] = a[right];
		
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[right] = a[left];
	}
	a[left] = key;
	int keyi = left;
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}
  1. 前后指针版本
//前后指针快速排序
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	
	int cur = left + 1, prev = left;
	int keyi = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi] && cur > prev)
		{
			prev++;
			Swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	Swap(&a[prev], &a[keyi]);
	int mid = prev;
	QuickSort(a, left, mid - 1);
	QuickSort(a, mid+1, right);
}

2.3.2 快速排序优化

  1. 三数取中法选key
int GetMid(int* a,int left,int right)
{
	int mid = left + right >> 1;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else//a[left]>a[mid]
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] < a[mid])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}
  1. 递归到小的子区间时,可以考虑使用插入排序
//小区间优化
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	if ((right - left + 1) > 10)
	{
		int cur = left + 1, prev = left;
		int keyi = left;
		while (cur <= right)
		{
			if (a[cur] < a[keyi] && cur > prev)
			{
				prev++;
				Swap(&a[cur], &a[prev]);
			}
			cur++;
		}
		Swap(&a[prev], &a[keyi]);
		int mid = prev;
		QuickSort(a, left, mid - 1);
		QuickSort(a, mid + 1, right);
	}
	else
	{
		InsertSort(a + left, right - left + 1);
	}
}
2.3.2 快速排序非递归
void QuickSortNonR(int* a, int left, int right)
{
	Stack st;
	StackInit(&st);
	StackPush(&st, left);
	StackPush(&st, right);

	while (StackEmpty(&st) != 0)
	{
		right = StackTop(&st);
		StackPop(&st);
		left = StackTop(&st);
		StackPop(&st);

      if(right - left <= 1)
          continue;

		int div = PartSort1(a, left, right);
       // 以基准值为分割点,形成左右两部分:[left, div) 和 [div+1, right)
      StackPush(&st, div+1);
	    StackPush(&st, right);

		StackPush(&st, left);
		StackPush(&st, div);
	}

  StackDestroy(&s);
}
int OnceSort(int* a, int left, int right)
{
	if (left > right)
	{
		return;
	}
	int key = a[left];
	while (left < right)
	{
		//先算右边,右边找大
		while (left<right&&a[right] >= key)
		{
			right--;
		}
		//找到了就交换
		a[left] = a[right];
		while (left<right&&a[left] <= key)
		{
			left++;
		}
		a[right] = a[left];
	}
	a[left] = key;//将key放在正确的位置上
	int meeti = left;//相遇的点
	return meeti;
}

void QuickSort(int* a, int left, int right)
{
	ST st;//创建一个栈来模拟递归的过程
	STInit(&st);
	STPush(&st,right);
	STPush(&st,left);
	while (!STEmpty(&st))
	{
		//左区间
		int begin = STTop(&st);
		STPop(&st);
		int end = STTop(&st);
		STPop(&st);
		int mid = OnceSort(a, begin, end);
       
		if(end > mid + 1)
		{
			STPush(&st, end);
			STPush(&st, mid + 1);
		}
       //如果left>=mid-1说明左边已经排完序了
       if(begin < mid - 1)
		{
			STPush(&st,mid - 1);
			STPush(&st, begin);
		}
	}
	STDestroy(&st);
}

在这里判断if语句的条件为什么不取=号呢?

假如我们取了等于号:

会出现很多没必要的判断,begin和end相等的时候就是只有一个元素,一个元素是不需要排序的,所以不用取=

如果没有取=号的话:

快速排序的特性总结:
  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

  2. 时间复杂度:O(N*logN)

15

  1. 空间复杂度:O(logN)

  2. 稳定性:不稳定

2.4 归并排序

基本思想:

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并排序核心步骤:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-svQBGJGJ-1683775858015)(C:/Users/19735/Desktop/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%88%9D%E9%98%B6V5-2021%E4%BF%AE%E8%AE%A2/Lesson6–%E6%8E%92%E5%BA%8F/12.jpg)]

//归并排序递归
void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
	{
		return;
	}
	
	int mid = begin + end >> 1;
	_MergeSort(a, begin, mid,tmp);
	_MergeSort(a, mid+1, end,tmp);
	
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	
	//处理剩余的
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}

	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}

void MergeSort(int* a, int sz)
{
	int* tmp = (int*)malloc(sizeof(int) * sz);
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	_MergeSort(a, 0, sz - 1, tmp);
	
	free(tmp);
}
//归并排序非递归
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	int gap = 1;//分组,组间距
	while (gap < n)
	{
		for (int i = 0; i < n; i += gap * 2)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int j = i;
			if (begin1>=n||end1 >= n || begin2 >= n)
			{
				break;
			}

			if (end2 >= n)
			{
				end2 = n - 1;
			}
			printf("[%d %d] [%d %d]\n", begin1, end1, begin2, end2);
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
		}
		gap *= 2;
	}
	free(tmp);
}

如果不对区间进行修正:

可以看到原数组本来只有9个元素,有效区间是[0,8]但上述运行中明显有超过这个区间的区间,那这是什么原因导致的呢?

框框中的代码当gap很大的时候就会产生越界,但由于begin2依旧满足小于end2所以程序会继续进行

代码运行过程:

归并排序的特性总结:
  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定
2.5 非比较排序

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
操作步骤:

  1. 统计相同元素出现次数
  2. 根据统计的结果将序列回收到原来的序列中13
//计数排序
void CountSort(int* a, int n)
{
	int max = a[0], min = a[0];
	for (int i = 1; i < n; ++i)
	{
		if (a[i] > max)
		{
			max = a[i];
		}

		if (a[i] < min)
		{
			min = a[i];
		}
	}

	int range = max - min + 1;
	int* countA = (int*)malloc(sizeof(int) * range);
	if (countA == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	memset(countA, 0, sizeof(int) * range);

	// 计数
	for (int i = 0; i < n; i++)
	{
		countA[a[i] - min]++;
	}

	// 排序
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (countA[i]--)
		{
			a[j++] = i + min;
		}
	}

	free(countA);
}
计数排序的特性总结:
  1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
  2. 时间复杂度:O(MAX(N,范围))
  3. 空间复杂度:O(范围)
  4. 稳定性:稳定
3.排序算法复杂度及稳定性分析

16

17

4.选择题练习
1. 快速排序算法是基于(    )的一个排序算法。
A 分治法
B 贪心法
C 递归法
D 动态规划法

2.对记录(54,38,96,23,15,72,60,45,83)进行从小到大的直接插入排序时,当把第8个记录45插入到有序表时,为找到插入位置需比较( )次?(采用从后往前比较)
A 3
B 4
C 5
D 6

3.以下排序方式中占用O(n)辅助存储空间的是
A 选择排序
B 快速排序
C 堆排序
D 归并排序

4.下列排序算法中稳定且时间复杂度为O(n2)的是( )
A 快速排序
B 冒泡排序
C 直接选择排序
D 归并排序

5.关于排序,下面说法不正确的是
A 快排时间复杂度为O(N*logN),空间复杂度为O(logN)
B 归并排序是一种稳定的排序,堆排序和快排均不稳定
C 序列基本有序时,快排退化成冒泡排序,直接插入排序最快
D 归并排序空间复杂度为O(N), 堆排序空间复杂度的为O(logN)

6.下列排序法中,最坏情况下时间复杂度最小的是( )
A 堆排序
B 快速排序
C 希尔排序
D 冒泡排序

7.设一组初始记录关键字序列为(65,56,72,99,86,25,34,66),则以第一个关键字65为基准而得到的一趟快速排序结果是()
A 3456256586997266
B 2534566599867266
C 3456256566998672
D 3456256599867266
答案:
1.A
2.C
3.D
4.B
5.D
6.A
7.A
【C语言排序详解——冒泡排序 前言: 冒泡排序可以说是排序系列中最简单也最基础的一种排序的方式,尽管它十分的简单易懂,但依旧会有一些小问题是大家可能忽略的,因此我打算将不同排序分成单独的文章进行讲解,这样既不会显得臃肿,同时也可以讲得更加仔细,废话不多说,开干。 一.概念简述 从上图我们不难看出冒泡排序应该有两个循环:第一个循环是小循环,该循环的作用是——在某个数组内依次进行两个数的大小比较;第二个循环是大循环,改循环的作用是——决定小循环的次数 那么概括来说就是,在一定次数内,数组按照一定的大小顺序进行两两比较,满足顺 阅读详情

相关推荐

IO多路转接 ——— select、poll、epoll

select是系统提供的一个多路转接接口。select函数的函数原型如下: 参数说明:参数timeout的取值:返回值说明:select调用失败时,错误码可能被设置为:fd_set结构与sigset_t结构类似,fd_set本质也是一个位图,用位图中对应的位来表示要监视的文件描述符。 调用select函数之前就需要用fd_set结构定义出对应的文件描述符集,然后将需要监视的文件描述符添加到文件描述符集当中,这个添加的过程本质就是在进行位操作,但是这个位操作不需要用户自己进行,系统提供了一组专门的接口,用

2021dragon的博客 7915

C语言——冒泡排序

冒泡排序的原理是:从左到右,相邻元素进行比较。每次比较一轮,就会找到序列中最大的一个或最小的一个。这个数就会从序列的最右边冒出来。 以从小到大排序为例,第一轮比较后,所有数中最大的那个数就会浮到最右边;第二轮比较后,所有数中第二大的那个数就会浮到倒数第二个位置……就这样一轮一轮地比较,最后实现从小到大排序。 代码如下 #include<stdio.h> void BBsort(int arr[], int size) { int j,i,tem; for (i = 0; i ..

记录学习点滴 47万+

【Docker】常见容器问题解决#OCI runtime create failed: container_linux.go:349: starting container process

ERROR: for nginx Cannot start service nginx: OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \"rootfs_linux.go:58: mounting \\\"/data/docker-compose/n

liu_chen_yang的博客 5465

C语言实现冒泡排序

冒泡排序作为学习排序最基本的算法,具有稳定性与实用性。 下面是C语言冒泡排序的源代码 #include<stdio.h> int main(void) { int a[10]={6,4,3,2,7,8,9,10,1,5}; int i,k,w; for(i=0;i<9;i++) { for(k=0;k<9-i;k++) { if(a[k]>a[k+1]) { ...

wantsnowfly的博客 9万+

冒泡排序算法详解之C语言

冒泡排序是一种常用的排序算法,属于稳定排序法,其时间复杂度为O(n^2)冒泡排序法的原理就是从前向后依次比较相邻两个元素的大小,大元素后沉,类似于水中的泡泡逐步上浮的过程,随着泡泡逐渐接近水面,水中压强逐渐减小,水泡体积逐渐增大,因此成为冒泡排序

sunnyoldboy博客 5万+

C语言——冒泡排序(分析详解)

排序也称排序算法(Sort Algorithm)排序是将一组数据,依指定的顺序进行排列的过程。冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐 向上冒。因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。

m0_53415522的博客 1万+

C语言实现八大排序算法详解插入排序希尔排序选择排序堆排序冒泡排序快速排序递归非递归)、归并排序递归非递归)和计数排序

本篇文章使用C语言实现了数据结构中常见的八大排序算法,它们分别是插入排序希尔排序选择排序堆排序冒泡排序快速排序归并排序和计数排序

m0_50655389的博客 2079

数据结构算法——排序(C语言实现)

排序详细知识点详解

weixin_74967884的博客 1535

八大排序算法—源代码(c语言)

插入排序 希尔排序 选择排序 堆排序 冒泡排序 快速排序 归并排序 计数排序八大排序算法插入排序 希尔排序 选择排序 堆排序 冒泡排序 快速排序 归并排序 计数排序八大排序算法插入排序 希尔排序 选择排序 堆排序 冒泡排序 快速排序 归并排序 计数排序八大排序算法插入排序 希尔排序 选择排序 堆排序 冒泡排序 快速排序 归并排序 计数排序八大排序算法插入排序 希尔排序 选择排序 堆排序 冒泡排序 快速排序 归并排序 计数排序八大排序算法的C语言实现。...

Brant_zero的博客 3654

C语言实现常见排序算法

插入排序是模拟的打扑克中的理牌顺序,每次在已排序列中从后往前排,找到相应的位置并插入。插入排序稳定的排序算法,相等元素的相对位置在排序后不会改变O1O(1)O1时间复杂度:On2O(n^2)On2OnO(n)OnOn2O(n^2)On21、预排序–接近有序2、插入排序具体来说:间隔gap分为一组,总计gap个子序列,一次走gap,然后对gap每组进行分别插入排序

m0_74996548的博客 1411

C语言数据结构算法(排序)详细版

排序在日常生活中,随处可见,大家一定要好好掌握o。

2301_81978155的博客 2507

七大排序算法详解:从原理到实现(希尔/堆排/快排/归并等)

两部分,每次从未排序部分取出第一个元素,在已排序部分从后向前扫描,找到合适的位置插入。时间复杂度:平均O(n log n),最坏O()(可通过随机选pivot优化)。:选基准值(pivot),将数组分为“小于pivot”和“大于pivot”两部分。基准值(pivot),将数组分为“小于pivot”和“大于pivot”两部分。:交换堆顶元素(最大值)与末尾元素,缩小堆范围并重新调整堆,重复直至有序。时间复杂度:最好O(n)(已有序),平均O()。时间复杂度:最好O(n)(已有序),平均O()

tanyongxi66的博客 1479

用c语言实现各种排序(插入、希尔、选择、堆排、冒泡、递归非递归快排、归并)

用c语言实现各种排序(插入、希尔、选择、堆排、冒泡、递归非递归快排、归并) 由于快排的非递归实现需要用到堆,所以我们先导入堆的代码 Stack.h #pragma once #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <stdbool.h> typedef int STDataType; typedef struct Stack { STDataType* a;

weixin_56667446的博客 195

C语言实现九种排序

文章目录C语言实现九种排序头文件C文件测试主函数 C语言实现九种排序 头文件 #ifndef _MYSORT_H_ #define _MYSORT_H_ /*网站 : sorting.at*/ typedef enum {UP,DOWN} SORTWAY; void myswap(int *a,int *b); void arrayswap(int a[],int i,int j); ...

qq_40144572的博客 322

数据结构】八大排序!(C语言

八大排序!万字手撕!

m0_65140642的博客 773

数据结构算法(第五章_八大排序)(完整代码+动图+详解+对比)

数据结构算法⑰(第五章_八大排序)(完整代码+动图+详解+复杂度和稳定性对比+关于排序选择题)此篇是此专栏最后一篇了,还有些高阶数据结构在后面点再更新。下一篇开始C++。

参考书籍:《C语言程序设计》《数据结构》《C++ Primer》《Effective C++》《STL源码剖析》《现代操作系统》《UNIX环境高级编程》《图解TCP/IP》《图解HTTP》《Linux高性能服务器编程》《剑指Offer》《算法导论》 1931

[C语言]常见的多种排序方式和实现

C语言实现,快排的三种实现方式及其优化,插入排序,选择排序,交换排序,归并排序,计数排序

sewerperson的博客 193

初阶数据结构的各种排序方法——冒泡,直接插入,希尔,快排,选择,归并(C语言

交换排序基本思想: 所谓交换,就是根据序列中两个记录键值的⽐较结果来对换这两个记录在序列中的位置 交换排序的特点是:将键值较⼤的记录向序列的尾部移动,键值较⼩的记录向序列的前部移动。

2401_83456040的博客 837

数据结构排序入门(3):核心排序归并排序,归并非递归排序,计数排序排序扫尾复杂度分析)+八大排序源码汇总

归并排序采用分治思想,通过递归将数组对半拆分至单个元素后合并,非递归版本通过gap变量控制归并区间大小逐步扩大。计数排序通过统计元素出现频次实现排序,先确定数据范围,建立计数数组统计频率,最后按顺序重构数组。两种算法均需额外空间,归并排序时间复杂度为O(nlogn),计数排序为O(n)但受数据范围限制。关键实现包括归并的有序合并过程和计数排序的频次统计与重构步骤。

asciiletters的博客 531

数据结构排序算法全解析 —— 从原理到实现,一篇搞定

本文全面解析了C语言实现的八大排序算法插入排序希尔排序选择排序堆排序冒泡排序快速排序归并排序和计数排序。通过图文并茂的方式详细阐述了各算法的原理、实现代码(均附可运行示例)、时间复杂度分析(从O()到O(nlogn))及稳定性讨论。特别对比了不同算法的适用场景。文中还提供了10万量级的性能测试数据,直观展示各算法效率差异,并给出优化建议。最后总结了算法选择指南,强调排序作为算法基础的重要性,建议读者动手实践以加深理解。

xxwxx__的博客 408

排序及模拟实现>——《Data Structure in C Train》

排序及模拟实现>:对插入排序直接插入)、 希尔排序堆排序、直接选择排序冒泡排序快速排序归并排序、计数排序等八大排序的模拟实现。

m0_57859086的博客 777
上一篇: 单链表(C语言)
下一篇: C++引用(&)笔记
Hayaizo
博客等级 码龄4年 635粉丝 59原创
评论 43
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值