数据结构之排序算法(C语言)

1、冒泡排序

 冒泡排序(Bubble Sort)是一种简单的排序算法,其基本思想是通过交换相邻两个数的位置,将当前序列中的最大值逐步“冒泡”到末尾。

冒泡排序的基本操作:依次比较两两相邻的元素的值,若为逆序,则交换它们的位置,直到数据序列排完为止;

代码实现:

void bubble(int A[], int len)
{
	for (int i = 1; i < len; i++) //循环次数
	{
		int j = 0;
		for (j = 0; j < len-i; j++) //数据之间比较的次数
		{
			if (A[j] < A[j+1]) //数据交换
			{
				int temp = A[j];
				A[j] = A[j+1];
				A[j+1] = temp;
			}
		}
	}
}
int main()
{
	int A[10] = { 14,6,7,23,75,12,98,43,64,57 };
	bubble(A, 10);
	for (int i = 0; i < 10; i++)
		printf("%d ", A[i]);
	return 0;
}

2、插入排序

概念:每次将一个待排序的数据按照其大小插入到已排好序的子序列中,直到全部数据插入成功;

算法思想:首先,使用temp保存第i个元素,然后,对数据进行比较,当temp小于i-1个元素时,第i-1个元素复制到第i个位置,temp继续与往左边的元素对比,直到temp大于第n个元素时,temp的数据放在第n+1个位置;(若temp是序列中最小的元素,则放在最开头位置)

代码实现:

void insert(int A[], int len)
{
	int i, j, temp;
	for (i = 1; i < len; i++) // A[0]为已排好序的序列
	{
		if (A[i] < A[i - 1]) //若A[i]小于前驱值
		{
			temp = A[i]; //用temp保存A[i]
			for (j = i - 1; j >= 0 && A[j] > temp; j--) //检查排好序的序列
				A[j + 1] = A[j]; // 若temp < A[j],则j元素往后移动
			A[j + 1] = temp; //循环结束,temp插入序列中
		}
	}
}
int main()
{
	int A[10] = { 6,75,8,32,65,99,43,16,45,28 };
	insert(A, 10);
	for (int i = 0; i < 10; i++)
		printf("%d ", A[i]);
	return 0;
}

优化思路:可以在查找插入位置时,使用折半查找来寻找插入位置;在查找到插入位置时,将插入位置后的序列往前进一位,空出位置给新数据插入;

折半查找代码:

void insert(int A[], int len ) //查找插入位置使用折半查找
{
	for (int i = 1; i < len; i++)
	{
		if (A[i] < A[i - 1])
		{
			int temp = A[i];
			int per = 0;
			int last = i - 1;
			int mid = (per + last) / 2;
			while ( per <= last) //终止条件
			{
				mid = (per + last) / 2;
				if (A[mid] > temp)
					last = mid - 1;
				else
					per = mid + 1;
			}
			while (per <= (i - 1)) //插入位置找到后,per到(i-1)的元素往后移动
			{
				A[i] = A[i - 1];
				i--;
			}
			A[per] = temp; //插入数据
		}
	}
}

3、希尔排序

希尔排序(Shell Sort)是一种插入排序的改进算法,也称为缩小增量排序。它通过将待排元素分成若干个小组,对每个小组进行插入排序,不断缩小组的大小,最终完成排序。希尔排序的核心在于定义增量序列,即分组的方式。在效率和稳定性之间取得了一个平衡,适用于面对大规模数据的排序。

希尔排序特征:先追求数据序列中元素的部分有序,再逐渐逼近全局有序;

算法思想:设置一个增量d,用来分隔数据序列,将相隔距离为d的元素看作一个子表,对各个子表分别进行直接插入排序,接着缩小增量d(最好每次缩小一半),重复上述过程,直到d=1为止;

注意:希尔排序只适用于顺序表,不适用于链表;

代码实现:

void shellsort(int A[], int n)
{
	int temp, step, i, j;
	for (step = n / 2; step > 0; step = step / 2) //步长变化
	{
		for (i = step; i < n; i++) 
		{
			if (A[i] < A[i - step]) //比较
			{
				temp = A[i]; //将小值暂存在temp
				for (j = i - step; j >= 0 && temp < A[j]; j -= step)
					A[j + step] = A[j]; //记录后移,查找插入的位置
				A[j + step] = temp; //插入
			}
		}
	}
}

int main()
{
	int A[10] = { 76,34,65,18,45,99,32,11,21,88 };
	shellsort(A, 10);
	for (int i = 0; i < 10; i++)
		printf("%d ", A[i]);
	return 0;
}

4、快速排序

快速排序是一种常用的排序算法,基本思路是选择一个元素作为pivot(枢轴),然后通过一趟排序将待排序列分为两部分,一部分比pivot小,一部分比pivot大。然后再对这两部分分别递归地进行快速排序,直到整个序列有序。

操作步骤:使用一个temp来存放pivot,使用双指针从序列的两端开始往中间扫描,当右端元素小于pivot,与左部分进行交换,左指针往右移动,反之,操作相反,直到左右指针同时指向同一个位置为止;

递归用来改变边界指针,对不同的序列进行划分;

代码实现:

int quicksort2(int A[], int low,int high)
{
	int point = A[low]; //确定基准元素
	while (low < high) // 退出循环条件
	{
		while (low < high && A[high] >= point) //从右边开始寻找比point值小的元素
			high--;
		A[low] = A[high]; //换到左边
		while (low < high && A[low] <= point)//从左边开始寻找比point值大的元素
			low++;
		A[high] = A[low]; //换到右边
	}
	A[low] = point; //插入point值最终的位置
	return low; //返回low坐标
}
void quicksort1(int A[], int low, int high)
{
	if (low < high) //递归退出条件
	{
		int lowpoint = quicksort2(A, low, high); //对序列进行划分
		quicksort1(A, low, lowpoint - 1); //左子表
		quicksort1(A, lowpoint + 1, high); //右子表
	}
}
int main()
{
	int A[10] = { 44,23,75,34,98,21,58,65,91,11 };
	int low = 0; int high = 9;
	quicksort1(A, low, high);
	for (int i = 0; i < 10; i++)
		printf("%d ", A[i]);
	return 0;
}

5、选择排序

选择排序(Selection Sort)是一种简单直观的排序算法。其基本思想是在未排序的数列中,每次选择最小(或最大)的数,放置到数列的起始位置,然后再在剩余未排序的数列中选择最小(或最大)的数,放置到已排序数列的末尾。重复这样的操作,直到整个数列有序。选择排序算法的时间复杂度稳定在 O(n^2),因为每个元素都要比较多次。尽管如此,选择排序算法也是一种简单易懂、易于实现的排序算法。

5.1、简单选择排序

代码实现:

void choosesort(int A[], int n)
{
	int i, j;
	for ( i = 0; i < n - 1; i++) //遍历次数
	{
		int min = i;
		for ( j = i+1; j < n; j++) //遍历元素个数
		{
			if (A[j] < A[min]) //寻找最小值
				min = j;
		}
		if (min != i) //下标不同则交换
		{
			int temp = A[min];
			A[min] = A[i];
			A[i] = temp;
		}
	}
}
int main()
{
	int A[10] = { 23,45,76,43,23,11,98,87,66,34 };
	choosesort(A, 10);
	for (int i = 0; i < 10; i++)
		printf("%d ", A[i]);
	return 0;
}

5.2、堆排序

堆排序本身是一种完全二叉树,可以理解为一棵顺序存储的完全二叉树;

分为两种关键字序列:
1、根节点都大于或等于其左右孩子节点的值的树——大根堆;

2、根节点都小于或等于其左右孩子节点的值的树——小根堆;

堆排序的重要参数(i为节点的数组下标)

i的左孩子——2i;

i的右孩子——2i+1;

判断i是否为分支节点/叶节点——i>(n/2)+1;

构建大根堆思路:把所有根节点检查一遍,是否满足大根堆的要求,若不满足,则将当前节点与其左右孩子节点的最大值交换位置,若元素互换破环了下一级堆,则采用相同的方法继续往下调整;

排序思路:首先将待排序的数组构造成一个大根堆,整个数组的最大值为数组首位数,接着将顶端的数与数组末尾的数交换,此时,末尾的数变为最大值,待排序的数组个数减一,继续将剩余的数构建成大根堆,然后交换,重复上述操作,便能得到有序数组;

注意:升序用大根堆,降序用小根堆;

该代码从数组下标为1处开始,下标为0的空间被弃用;

代码实现:

void headAdjust(int A[], int k, int len) //以k为根节点的树调整为大根堆
{
	int temp = A[k]; //存放根节点
	for (int i = 2 * k; i < len; i = i * 2) 
	{
		if (i < len && A[i] < A[i + 1]) //寻找根节点的左右孩的最大值
			i++;
		if (temp >= A[i]) //根节点大于左右孩,则表示是大根堆
			break;
		else  //否则—>进行替换
		{
			A[k] = A[i];
			k = i; //修改k值,继续向下的树检查
		}
		for (int j = 1; j <= 10; j++)
			printf("%d ", A[j]);
		printf("\n");
	}
	A[k] = temp; //检查完毕,把值放入最终位置
}

void Buildhead(int A[], int len)
{
	for (int i = len / 2; i > 0; i--) //从后往前调整每个根节点
	{
		headAdjust(A, i, len);
	}
}

void headsort(int A[], int len)
{
	Buildhead(A, len); //初始化大根堆
	for (int i = len; i > 1; i--) 
	{ 
		int temp = A[i];
		A[i] = A[1];
		A[1] = temp;  //堆顶元素与堆底元素交换
		for (int j = 1; j <= 10; j++)
			printf("%d ", A[j]);
		printf("\n");
		headAdjust(A, 1, i - 1); //把剩余的元素整理为大根堆
	}
}

int main()
{
	int n = 10;
	int i;
	int a[11];
	for (i = 1; i <= n; i++)
		scanf_s("%d", &a[i]);
	headsort(a, n);
	for (int j = 1; j <= n; j++)
		printf("%d ", a[j]);
	return 0;
}

6、归并排序

归并排序是一种基于分治法的排序算法。它的基本思想是将待排序的元素序列分成若干个子序列,每个子序列都是有序的,然后将这些子序列合并成一个有序的序列。具体实现时,采用递归的方式将序列分成两半,然后将两个有序子序列合并成一个有序序列,最后得到完整的有序序列。

代码实现:

int arr[10]; //创建额外空间来存储
void Merge(int A[], int low, int mid, int high)
{
	int i, j, k;
	for (k = low; k <= high; k++) //将数据序列复制到arr数组
		arr[k] = A[k];
	for (i = low, j = mid + 1, k = i; i <= mid && j <= high; k++) //i,j指向arr数组的两个子数组的开头,k指向A数组
	{
		if (arr[i] <= arr[j]) //数据比较
			A[k] = arr[i++];
		else
			A[k] = arr[j++];
	} //某个子数组为空,另一个子数组不为空,将剩余数据填入A数组;
	while (i <= mid)
		A[k++] = arr[i++];
	while (j <= high)
		A[k++] = arr[j++];
}

void Mergesort(int A[], int low, int high)
{
	if (low < high) 
	{
		int mid = (low + high) / 2; //从中间划分
		Mergesort(A, low, mid); //对左数组进行划分
		Mergesort(A, mid + 1, high); //对右数组进行划分
		Merge(A, low, mid, high); //进行归并
	}
}

int main()
{
	int A[10] = { 23,12,43,30,99,84,76,66,54,4 };
	Mergesort(A, 0, 9);
	for (int i = 0; i < 10; i++)
		printf("%d ", A[i]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值