常见排序算法

这篇具有很好参考价值的文章主要介绍了常见排序算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

 

一 插入排序

1.直接插入排序

1.1基本思想

1.2代码实现

1.3图解分析

1.4特性

2.希尔排序

2.1基本思想

2.2代码实现

2.3图解分析

 2.4特性

二 选择排序

1.直接选择排序

1.1基本思想

1.2代码实现

1.3特性 

        2.堆排序

2.1基本思想

2.2相关概念图解

 2.3向下调整算法

2.4 图解分析

2.5代码实现 

2.6特性

三 交换排序 

1.冒泡排序

1.1.代码实现

1.2特性

        2.快速排序

2.1基本思想

2.2挖坑法图解

2.3挖坑法代码实现

2.4双指针法图解

2.5双指针法代码实现

2.6前后指针法图解

2.7前后指针法代码实现

2.8优化

2.9代码实现 

2.10代码易错分析图解

2.11特性 

四 归并排序

1.归并排序

1.1基本思想

1.2图解分析

1.3代码实现

1.4特性

五 非递归实现排序

5.1快速排序的非递归实现(栈)

5.2快速排序的非递归实现(队列)

5.3归并排序的非递归实现(循环)

六 其余排序 

6.1计数排序



一 插入排序

1.直接插入排序

1.1基本思想

直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐 个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。实际中我们玩扑克牌时,就用了插入排序的思想。

1.2代码实现

void InsertSort(int* a, int n)
{
	int i = 0;
	for (i = 0;i < n - 1;i++)
	{
		//end最多为n-2,如果为n-1 ,下标会越界
		int end = i;
		//将[0,end]有序 将end+1位置插入进去  使得[0,end+1]变为有序
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > tmp) 
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}

}

1.3图解分析

常见排序算法

1.4特性

1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度: O(N^2)
3. 空间复杂度: O(1) ,它是一种稳定的排序算法
4. 稳定性:稳定

2.希尔排序

2.1基本思想

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

2.2代码实现

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		// pag = pag / 2;  
		gap = gap / 3 + 1; 
		// gap > 1时都是预排序  接近有序  
		 // gap == 1时就是直接插入排序 有序  
		 // gap很大时,下面预排序时间复杂度O(N)
		 // gap很小时,数组已经很接近有序了,这时差不多也是O(N)
		 //间隔为pag的多组排序同时进行
		for (int i = 0;i < n - gap;i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

2.3图解分析

常见排序算法

 2.4特性

1. 希尔排序是对直接插入排序的优化。
2. gap > 1 时都是预排序,目的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序的 了,这样就会很快。这样整体而言,可以达到优化的效果。
3. 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N^1.3—
N^2
4. 稳定性:不稳定

二 选择排序

1.直接选择排序

1.1基本思想

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

1.2代码实现

void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while(begin<end)
	{
		int mini = begin;
		int maxi = begin;
		for (int i=begin;i<=end;i++)
		{
			if (a[i] < a[mini])
				mini = i;
			if (a[i] > a[maxi])
				maxi = i;
		}
		Swap(&a[begin], &a[mini]);
        //begin与maxi重合的处理
		if (begin== maxi)
		{
			maxi = mini;
		}
		Swap(&a[maxi], &a[end]);
		begin++;
		end--;
	}
}

常见排序算法

1.3特性 

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
2. 时间复杂度: O(N^2)
3. 空间复杂度: O(1)
4. 稳定性:不稳定

2.堆排序

2.1基本思想

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

2.2相关概念图解

常见排序算法

 2.3向下调整算法

常见排序算法

2.4 图解分析

常见排序算法

2.5代码实现 

//向下调整算法
void AdjustDwon(int* a, int n, int root)
{
	//条件  左右子树都为小堆
	int parent = root;
	//默认为左
	int child = parent * 2 + 1;
	while (child < n)
	{
		//左右哪一个孩子小
        //child + 1 < n   如果child为最后一个元素 child+1 会越界
		if (child + 1 < n && a[child + 1] < a[child])
		{
			child++;
		}

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

//堆排序
void HeapSort(int* a, int n)
{
	//从末端开始  建堆  O(N)
	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特性

1. 堆排序使用堆来选数,效率就高了很多 logN。
2. 时间复杂度: O(N*logN)
3. 空间复杂度: O(1)
4. 稳定性:不稳定

三 交换排序 

1.冒泡排序

1.1.代码实现

void BullSort(int* a, int n)
{
	int i, end = n - 1;
	while (end>0)
	{
		//优化
		int chanxn = 0;
		for (i = 1;i <= end;i++)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				chanxn = 1;
			}
		}
		if (chanxn == 0)
		{
			break;
		}
		end--;
	}
}

1.2特性

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

2.快速排序

2.1基本思想

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

2.2挖坑法图解

常见排序算法

 2.3挖坑法代码实现

//挖坑法
int PartSort1(int* a, int left, int right)
{
	int begin = left, end = right;
	int pivot = begin;
	int key = a[begin];
	while (begin < end)
	{
		//右边找小
		while (a[end] >= key && begin < end)
		{
			end--;
		}
		a[pivot] = a[end];
		pivot = end;
		while (a[begin] <= key && begin < end)
		{
			begin++;
		}
		a[pivot] = a[begin];
		pivot = begin;
	}
	pivot = begin;
	a[pivot] = key;
	return pivot;
}

void QuickSort(int* a,int left,int right)
{ 
    //  区间不存在或者只有一个元素
	if (left >= right)
	{
		return;
	}
	int pivot = PartSort3(a,left,right);
	//   [left,pivot-1]  pivot  [pivot+1,right]

	QuickSort(a, left, pivot - 1);
	QuickSort(a, pivot + 1, right);
	
}

2.4双指针法图解

常见排序算法

 2.5双指针法代码实现

//双指针法
int PartSort2(int* a, int left, int right)
{
	int begin = left, end = right;
	int keyi = begin;
	while (begin < end)
	{
		while (a[end] >= a[keyi]&& begin < end)
		{
			end--;
		}
		while (a[begin] <= a[keyi] && begin < end)
		{
			begin++;
		}
		Swap(&a[end], &a[begin]);
	}
	Swap(&a[keyi], &a[end]);
	return end;
}


void QuickSort(int* a,int left,int right)
{ 
	if (left >= right)
	{
		return;
	}
	int pivot = PartSort3(a,left,right);
	//   [left,pivot-1]  pivot  [pivot+1,right]
	QuickSort(a, left, pivot - 1);
	QuickSort(a, pivot + 1, right);
}

2.6前后指针法图解

常见排序算法

 

 2.7前后指针法代码实现

//前后指针法
int PartSort3(int* a, int left, int right)
{
	int keyi = left;
	int prev = left, cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < a[keyi]
			&& ++prev != cur)
		{
			Swap(&a[prev], &a[cur]);
		}

		++cur;
	}

	Swap(&a[keyi], &a[prev]);
	return prev;
}

void QuickSort(int* a,int left,int right)
{ 
	if (left >= right)
	{
		return;
	}
	int pivot = PartSort3(a,left,right);
	//   [left,pivot-1]  pivot  [pivot+1,right]

	QuickSort(a, left, pivot - 1);
	QuickSort(a, pivot + 1, right);
	
}

2.8优化

小区间优化以及三数取中

常见排序算法

2.9代码实现 

//三数取中
int GetMidIndex(int* a, int left, int right)
{
	int mid = (left + right) >> 1;
	if (a[left] > a[mid])
	{
		if (a[right] > a[left])
			return left;
		else if (a[mid] > a[right])
			return mid;
		else
			return right;
	}
	else  //a[left] <= a[mid]
	{
		if (a[right] < a[left])
			return left;
		else if (a[mid] < a[right])
			return mid;
		else
			return right;
	}
}



void QuickSort(int* a,int left,int right)
{ 
	if (left >= right)
	{
		return;
	}
	int pivot = PartSort3(a,left,right);
	//   [left,pivot-1]  pivot  [pivot+1,right]
	//小区间优化
	if (pivot - 1 - left >10)
	{
		QuickSort(a, left, pivot - 1);
	}
	else
	{
		InsertSort(a + left, pivot - 1 - left+1);
	}
	if (right - (pivot+1) > 10)
	{
		QuickSort(a, pivot + 1, right);
	}
	else
	{
		InsertSort(a+ pivot + 1, right - (pivot + 1)+1);
	}
}

2.10代码易错分析图解

常见排序算法

2.11特性 

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫 快速 排序
2. 时间复杂度: O(N*logN)
3. 空间复杂度: O(logN)
4. 稳定性:不稳定

四 归并排序

1.归并排序

1.1基本思想

归并排序( MERGE-SORT )是建立在归并操作上的一种有效的排序算法 , 该算法是采用分治法(Divide and Conquer )的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序 列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并排序也叫 外排序 ,可以对磁盘上的数据进行排序。

1.2图解分析

常见排序算法

1.3代码实现

//归并子函数
void _MergeSort(int* a, int left, int right, int* tmp)
{
	if (left >= right)
		return;

	int mid = (left + right) >> 1;
	// 假设 [left, mid] [mid+1, right]
	有序,那么我们就可以归并了
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid+1, right, tmp);

	// 归并
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else
		{
			tmp[index++] = a[begin2++];
		}
	}
    //将剩余数据拷贝到tmp中
	while (begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}
	
	// 拷贝回去
	for (int i = left; i <= right; ++i)
	{
		a[i] = tmp[i];
	}
}


void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}

1.4特性

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

五 非递归实现排序

在前面快速排序以及归并排序都使用递归实现的,然而递归存在一定的缺陷,最为主要的就是当递归的深度足够深的时候,会发生栈溢出。  

改为非递归的两种方式,一是改循环,二是借用数据结构的栈,队列等来模拟递归的过程。

5.1快速排序的非递归实现(

void QuickSortNonR(int* a,int n)
{
	ST st;
	StackInit(&st);
    //右
	StackPush(&st, n - 1);
    //左
	StackPush(&st, 0);

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

		int right = StackTop(&st);
		StackPop(&st);
		int pivot = PartSort1(a, left, right);
		// [left pivot-1] pivot [pivot+1  right] 
        //判断左右区间是否需要入栈
        //右
		if (pivot + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, pivot + 1);
		}
		//左
		if (left < pivot - 1)
		{
			StackPush(&st, pivot - 1);
			StackPush(&st, left);
		}
	}
 
	StackDestroy(&st);
}

 5.2快速排序的非递归实现(队列

void QuickSortNonRQueue(int* a, int n)
{
	Queue q;
	QueueInit(&q);
	//左
	QueuePush(&q, 0);
	//右
	QueuePush(&q, n - 1);

	while (!QueueEmpty(&q))
	{
		int left = QueueFront(&q);
		QueuePop(&q);

		int right = QueueFront(&q);
		QueuePop(&q);
		int pivot = PartSort2(a, left, right);
		// [left pivot-1] pivot [pivot+1  right] 
		//左
		if (left < pivot - 1)
		{
			QueuePush(&q, left);
			QueuePush(&q, pivot - 1);
		}
		//右
		if (pivot + 1 < right)
		{
			QueuePush(&q, pivot+1);
			QueuePush(&q,right);
		}
	}
	//销毁
	QueueDestory(&q);
}

5.3归并排序的非递归实现(循环

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0;i < n;i += 2 * gap)
		{

			//[i i+gap-1]  [i+gap i+2*gap-1]
			//归并
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//右半区间不存在
			if (begin2 >= n)
				break;

			//归并的时候右半区间算多了
			if (end2>=n)
			{
				end2 = n - 1;
			}

			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
			//归并多少拷贝多少
			for (int j = i;j <= end2;j++)
			{
				a[j] = tmp[j];
			}
		}
		gap *= 2;
	}

	free(tmp);
}

常见排序算法

六 其余排序 

6.1计数排序

void CountSort(int* a, int n)
{
	int max = a[0], min = a[0];
	for (int i = 0;i < n;i++)
	{
		if (a[i] < min)
			min = a[i];
		if (a[i] > max)
			max = a[i];
	}
	int range = max - min + 1;
	int* count = (int*)malloc(sizeof(int) * range);
	memset(count, 0, sizeof(int) * range);
	//统计次数
	for (int i = 0;i < n;i++)
	{
		//相对位置
		count[a[i] - min]++;
	}

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

常见排序算法

计数排序的时间复杂度为O(N+range),也就是说明它适用于范围集中的一组整形数据排序,当range较大的话,速度就比较慢了。 

稳定性

排序结束后相同数据相对位置不会发生改变

常见排序算法文章来源地址https://www.toymoban.com/news/detail-443472.html

到了这里,关于常见排序算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 十大排序算法(上)直接插入排序、希尔排序、直接选择排序、堆排序

    排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 稳定性 :假设在待排序的序列中,存在多个具有相同内容的元素,如果经过排序,这些元素的相对位置并不发生改变,则称这种排序算法是稳定的。 稳定的排序可以变成不稳定的

    2024年02月05日
    浏览(35)
  • 数据结与算法之排序-插入排序(直接插入/折半插入/希尔)

    文章目录 目录 前言 一、什么是插入排序 1.直接插入排序 2.折半插入排序          3.希尔排序 总结 理解三种排序,并将三种排序用C++实现,借鉴了王卓老师和没有难学的知识的图例 提示:以下是本篇文章正文内容,下面案例可供参考         插入排序是简单直观的排序方

    2024年02月04日
    浏览(30)
  • 排序算法 —— 直接插入排序(图文超详细)

    直接插入排序是一个比较简单的排序算法。作用是将一组数排序成升序的。 元素集合越接近有序,直接插入排序算法的时间效率越高。 时间复杂度:O(n^2) 空间复杂度:0(1),它是一种稳定的排序算法。 稳定性:稳定 下面以 5 4 3 6 2 这组数作为例子来讲解。 直接插入排序就像

    2024年02月08日
    浏览(27)
  • 直接插入排序、希尔排序、直接选择排序、堆排序、冒泡排序——“数据结构与算法”

    各位CSDN的uu们你们好呀,今天小雅兰的内容是数据结构与算法啦,是排序!!!下面,让我们进入七大排序的世界吧!!! 排序:所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 稳定性:假定在待排序的记录序列中,存在

    2024年02月15日
    浏览(44)
  • C语言--直接插入排序【排序算法|图文详解】

    直接插入排序又叫简单插入排序,是一种 简单直观 的排序算法,它通过构建有序序列,对于未排序的数据,在已排序序列中从后向前扫描,找到相应位置并插入。 算法描述: 假设要排序的列表为arr,列表的第一个元素arr[0]默认已经是有序序列。 从第二个元素开始,即arr[

    2024年01月19日
    浏览(30)
  • 【数据结构与算法】直接插入排序和希尔排序

    进入了初阶数据结构的一个新的主题——排序。所谓排序,就是一串记录, 按照其中的某几个或某些的大小(一定的规则) , 递增或递减排列起来的操作 。 排序的 稳定性 :在一定的规则下,两个值相等的元素,在排序算法处理前后的相对位置是否发生变化,如果相

    2024年04月13日
    浏览(34)
  • 【数据结构】详解七大排序算法(直接插入排序、希尔排序、直接选择排序、堆排序、冒泡排序、快速排序)

    1、基本思想    把待排序的数按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所以的记录插入完为止,得到一个新的有序序列。    实际中我们玩扑克牌时,就用到了插入排序的思想 基本步骤:    当插入第i个元素时,前面的arr[0]、arr[2]…arr

    2024年02月04日
    浏览(52)
  • 稳定的排序算法:直接插入排序和冒泡排序 (c++实现)

    1.直接插入排序: 插入排序:就是把数组分为左右两个序列,保持左边序列中所有元素是有序的,(当左边序列只有第一个元素时,本身就是有序的,)每次往后移一个,将这个元素保存起来,跟左边的元素进行比较,直到找到第一个小于它的元素后停下,此时的位置是这个

    2024年02月10日
    浏览(33)
  • 【排序算法】一、排序概念和直接插入排序(C/C++)

    「前言」文章内容是排序算法之 直接插入排序 的讲解。(所有文章已经分类好,放心食用) 「归属专栏」排序算法 「主页链接」个人主页 「笔者」枫叶先生(fy) 排序的概念 排序 :所谓排序,就是使一串记录,按照其中的某个或某些的大小, 递增或递减 的排列起来

    2024年01月22日
    浏览(33)
  • c语言编写排序算法——直接插入排序(附详细代码)

    记号说明: a[k:r] 是指序列 a[k] a[k+1] a[k+2] … a[r] 。 为了讨论简单,假设待排序的每个记录是一个整数,这个整数就是排序码。 直接插入排序 :先将第一个记录看作是一个有序的记录序列,然后从第二个记录开始,依次将未排序的记录插入到这个有序的记录序列中去,直到整

    2024年02月11日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包