IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 几种常见的排序算法 -> 正文阅读

[数据结构与算法]几种常见的排序算法

目录

1、冒泡排序

方法一:循环实现冒泡排序

方法二:递归实现冒泡排序?

?2、选择排序

?*优化

3、 插入排序

*优化

4、希尔排序

5、快速排序

1、挖坑填数+分治

2、hoarea法

6、归并排序


1、冒泡排序

首先从数组的第一个元素开始到数组最后一个元素为止,对数组中相邻的两个元素进行比较,如果位于数组左端的元素大于数组右端的元素,则交换这两个元素在数组中的位置,此时数组最右端的元素即为该数组中所有元素的最大值。算法的时间复杂度为O(n^2)。

*优化:每趟冒泡排序时判断一下是否交换过元素,如果没有交换过元素,证明已经有序,排序提前结束。

方法一:循环实现冒泡排序

//循环实现冒泡排序
void BubbleSort1(int arr[], int length) {
	int ifswap;//每趟排序中是否交换过元素,未交换为0,交换为1
	for (int i = 0; i < length - 1; i++) {//i为排序的趟数
		ifswap = 0;//每趟排序需初始化交换标志
		for (int j = 0; j < length - i - 1; j++) {
			if (arr[j] > arr[j + 1]) {//若前面的值大于其后的值,进行交换
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
				ifswap = 1;
			}
		}
		if (ifswap == 0) return;
	}
}

方法二:递归实现冒泡排序?

//递归实现冒泡排序
void BubbleSort2(int arr[], int length) {
	int ifswap = 0;//每趟排序中是否交换过元素,未交换为0,交换为1
	if (length < 2) return;//数组小于2个元素不需要排序
	for (int i = 0; i < length - 1; i++) {
		if (arr[i] > arr[i + 1]) {
			int temp = arr[i];
			arr[i] = arr[i + 1];
			arr[i + 1] = temp;
			ifswap = 1;
		}
	}
	if (ifswap == 0) return;
	BubbleSort2(arr, --length);
}

?2、选择排序

从头至尾扫描序列,找出最小的元素,和第一个交换,接着从剩下的元素中继续查找交换,直至得到一个有序序列。算法的时间复杂度为O(n^2)。

//选择排序
void SelectionSort(int arr[], int length){
	int flag;
	for (int i = 0; i < length-1; i++) {//一共进行n-1趟比较
		flag = i;//记录最小元素的下标
		for (int j = i+1; j < length; j++) {//i之前的元素是已经排列好的
			if (arr[j] < arr[flag]) {
				flag = j;
			}
		}
		//如果本趟循环的最小值不是起始位置,则交换
		if (flag != i) {
			int temp = arr[i];
			arr[i] = arr[flag];
			arr[flag] = temp;
		}
	}
}

?*优化:将最小值和最大值都选出来,最小放在当前序列的最左边,最大放在当前序列的最右边,循环趟数可减半。

//选择排序的优化
void SelectionSort(int arr[], int length) {
	int left, right;//每趟排序的最左和最右的位置
	int minflag, maxflag;//记录最小和最大元素的下标
	int i;
	left = 0; right = length - 1;
	while (left < right) {
		minflag = maxflag = left;
		for (i=left; i <= right; i++) {//i之前的元素是已经排列好的
			if (arr[i] < arr[minflag])  minflag = i;
			if (arr[i] > arr[maxflag])  maxflag = i;
		}
		//如果本趟循环的最小值不是最左位置,则交换
		if (minflag != left) {
			int temp = arr[left];
			arr[left] = arr[minflag];
			arr[minflag] = temp;
		}
		if (maxflag == left) maxflag = minflag;
		//如果本趟循环的最大值不是最右位置,则交换
		if (maxflag != right) {
			int temp = arr[right];
			arr[right] = arr[maxflag];
			arr[maxflag] = temp;
		}
		left++, right--;
	}
}

3、 插入排序

插入排序的基本思想就是将无序序列插入到有序序列中。对于未排序的数据,在已排序的序列中从后向前扫描,找到相应的位置后插入。该算法的时间复杂度为O(n^2)。

//插入排序
void InsertSort(int arr[], int length) {
	int tmp;//当前需要排序元素的值
	int i, j;
	for (i = 0; i < length; i++) {
		tmp = arr[i];//待排序元素
		for (j = i - 1; j >= 0; j--) {
			if (arr[j] <= tmp) break;
			arr[j + 1] = arr[j];//逐个元素后移
		}
		//插入当前排序元素
		arr[j + 1] = tmp;
	}
}

*优化:1、对已排序的序列进行二分查找法;2、携带多个元素;3、数据链表化

下面仅展示通过二分查找法实现的折半插入排序

//折半插入排序
void InsertSort(int arr[], int length) {
	int i, j, mid, left, right, t;
	for (i = 1; i < length; i++) {
		t = arr[i];
		for (left = 0, right = i - 1; left <= right;) {
			mid = (left + right) / 2;
			if (t < arr[mid]) right = mid - 1;
			else left = mid + 1
				;
		}
		for (j = i - 1; j >= left; j--)
			arr[j + 1] = arr[j];
		arr[left] = t;
	}
}

4、希尔排序

是对于直接插入排序的一种优化。先将待排记录序列分割成为若干子序列分别进行插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行一次直接插入排序。适用于较为庞大的数据排序

void groupsort(int arr[], int length, int pos, int step) {
	int t;//当前需要排序的元素的值
	int i, j;
	for (i = pos + step; i < length; i += step) {
		t = arr[i];
		//从已排序的最右开始,把大于当前排序的元素后移
		for (j = i - step; j >= 0; j -= step) {
			if (arr[j] <= t) break;
			arr[j + step] = arr[j];//逐个元素后移
		}
		arr[j + step] = t;//插入当前元素
	}
}
//希尔排序
void ShellSort(int arr[], int length) {
	int step, i;
	step = length / 2;//初始化时间间隔
	while (step > 0) {
		for (i = 0; i < step; i++) {
			groupsort(arr, length, i, step);
		}
		step /= 2;
	}
}

5、快速排序

通过一趟排序将待排记录分割成独立的两部分,从待排序列中任意选取一个数(通常选取第一个)作为基准值,然后将比它小的都安置在它的位置之前,将比它大的都安置在它的位置之后。

1、挖坑填数+分治

1、选出一个数据(一般是最左边或是最右边的)存放在key变量中,在该数据位置形成一个坑。
2、还是定义一个L和一个R,L从左向右走,R从右向左走。(若在最左边挖坑,则需要R先走;若在最右边挖坑,则需要L先走)。
3、在走的过程中,若R遇到小于key的数,则将该数抛入坑位,并在此处形成一个坑位,这时L再向后走,若遇到大于key的数,则将其抛入坑位,又形成一个坑位,如此循环下去,直到最终L和R相遇,这时将key抛入坑位即可。(选取最左边的作为坑位)

//快速排序(递归、挖坑法)
void QuickSort(int arr[], int start, int end) {
	if (start >= end) return;
	int left = start, right = end;
	int baseval = arr[start];//设置基准数
	while (left < right) {
		//right向左,找小
		while (left < right && arr[right] >= baseval)
			right--;
		if (left < right) {
			arr[left] = arr[right];
			left++;
		}
		//left向右,找大
		while (left < right && arr[left] <= baseval)
			left++;
		if (left < right) {
			arr[right] = arr[left];
			right--;
		}
	}
	//把基准数放到合适的位置
	arr[left] = baseval;
	//递归
	QuickSort(arr, start, left - 1);//递归基准数左边的元素
	QuickSort(arr, left + 1, end);//递归基准数左边的元素
}

2、hoarea法

1、选出一个key,一般是最左边或是最右边的。
2、定义一个L和一个R,L从左向右走,R从右向左走。(需要注意的是:若选择最左边的数据作为key,则需要R先走;若选择最右边的数据作为key,则需要L先走)。
3、在走的过程中,若R遇到小于key的数,则停下,L开始走,直到L遇到一个大于key的数时,将L和R的内容交换,R再次开始走,如此进行下去,直到L和R最终相遇,此时将相遇点的内容与key交换即可。(选取最左边的值作为key)

//hoare法实现快速排序
void Swap(int* x, int* y) {
	int temp = *x;
	*x = *y;
	*y = temp;
}
void QuickSort(int arr[], int start, int end) {
	//当只有一个数据或是序列不存在时,不需要进行操作
	if (start >= end) return;
	int left = start, right = end;
	int pos = left;//基准数下标
	while (left < right) {
		//right向前走,找小
		while (left < right && arr[right] >= arr[pos])
			right--;
		//left向后走,找大
		while (left < right && arr[left] <= arr[pos])
			left++;
		//交换left和right位置的值
		if (left < right)
			Swap(&arr[left], &arr[right]);
	}
	int meetpos = left;//left和right相等时的下标
	Swap(&arr[pos], &arr[meetpos]);//交换相遇点和基准数的值
	QuickSort(arr, start, meetpos - 1);//基准数左边的序列排序
	QuickSort(arr, meetpos + 1, end);//基准数右边的序列排序
}

6、归并排序

归并排序是典型的递归和分治的例子,具体是将已有序的子序合并,从而得到完全有序的序列,即先使每个子序有序,再使子序列段间有序。时间复杂度O(NlogN)。

void _MergeSort(int arr[], int arrtmp[], int start, int end) {
	if (start >= end)  return;//	区间元素少于两个,递归停止
	int mid = start + (end - start) / 2;

	int startpos1 = start, endpos1 = mid;//左区间内的首元素、最后一个元素位置
	int startpos2 = mid + 1, endpos2 = end;//右区间内的首元素、最后一个元素位置

	_MergeSort(arr, arrtmp, startpos1, endpos1);//对左区间递归排序
	_MergeSort(arr, arrtmp, startpos2, endpos2);//对右区间递归排序

	int i = start; 

	//把区间左右两边数列合并到已排序数列arrtmp中 
	while (startpos1 <= endpos1 && startpos2 <= endpos2)
		arrtmp[i++] = arr[startpos1] < arr[startpos2] ? arr[startpos1++] : arr[startpos2++];
	//把左边序列剩余部分追加到arrtmp中
	while (startpos1 <= endpos1) arrtmp[i++] = arr[startpos1++];
	//把右边序列剩余部分追加到arrtmp中
	while (startpos2 <= endpos2) arrtmp[i++] = arr[startpos2++];
	//把已排序数组arrtmp中的元素复制到arr中
	memcpy(arr + start, arrtmp + start, (end - start + 1) * sizeof(int));
}

//归并排序主函数
void MergeSort(int arr[], int length) {
	if (length < 2) return;//小于两个元素则不需要排序
	int* arrtmp = (int*)malloc(sizeof(int) * length);//申请一个与原数组大小相同的空间
	if (arrtmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	_MergeSort(arr, arrtmp, 0, length - 1);//归并排序
	free(arrtmp);//释放空间
}

?

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-11-26 09:06:11  更:2021-11-26 09:08:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/9 16:08:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码