常用七种排序算法(C语言实现,含图解)

常用排序算法

一、冒泡算法

1.1 基本思想:

相邻两两比较,较大的下沉,较小的上升,第一轮之后最小的数就被放到了第一个位置,以此类推。

1.2 优化:

若某一次完了之后已经排好序,则没必要进行到len-1次,可用一个flag,没有交换之后就没必要再进行下去了 。

1.3 测试代码

#include 
#include 
void bubbleSort(int a[],int length)
{
	int len = length;
	int temp;//中间变量
	bool flag;//优化标志,判断是否有交换 
	for(int i=0;ii; j--)
		{
			if(a[j]

二、选择排序

2.1 基本思想:

一开始就从原始序列中找到最小的元素,放到序列的起始位置作为已排序序列,然后在剩下的未排序的元素中继续寻找最小的元素,放到已排序的序列末尾,以此类推,直到所有的元素排列完毕。

2.2 测试代码(main函数参考冒泡排序的测试代码)

void selectionSort(int a[],int length)
{
	int len = length;
	int minIndex;
	int temp;
	for(int i=0;i

三、插入排序

3.1 基本思想:

在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。 插入排序在实现上,常采用in-place排序,因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

3.2 具体算法描述:

  1. 从第一个元素开始,该元素可以认为已经被排序;
  2. 取出下一个元素,在已经排序好的元素序列中从后往前扫描;
  3. 如果该元素(已排序)大于新元素,将该元素移到下一个位置;
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  5. 将新元素插入到该位置之后;
  6. 重复步骤2~5.
    小规模数据或者基本有序时十分高效,即数据有序程度越高,越高效

3.2 测试代码(main函数参考冒泡排序的测试代码)

void insertSort(int a[],int length)
{
	int len = length;
	int temp;
	int i;//已排好序的下一个元素(待排元素)的下标
	int j;//有序序列的末尾 
	
	for(i=1;i=0&&a[j]>temp) 
		{//遍历有序序列,与要插入的元素比较 
			a[j+1] = a[j];//将元素后移,满足条件后移一个位置 
			j--;
		}
		a[j+1] = temp;//插入 
	} 
}

四、希尔排序

4.1 基本思想:

将无序数组分割为若干个子序列,子序列不是逐段分割的,而是相隔特定的增量的子序列,对各个子序列进行插入排序;然后再选择一个更小的增量,再将数组分割为多个子序列进行排序…最后选择增量为1,即使用直接插入排序,使最终数组成为有序。

4.2 具体图解描述:

  • 首先它把较大的数据集合分割成若干个小组(逻辑上分组),然后对每一个小组分别进行插入排序,此时,插入排序所作用的数据量比较小(每一个小组),插入的效率比较高
  • 下面有颜色的是逻辑上的分组,并没有实际地进行分组操作,在数组中的位置还是原来的样子,只是将它们看成这么几个分组。
    常用七种排序算法(C语言实现,含图解)_第1张图片
  • 可以看出,他是按下标相隔距离为4分的组,也就是说把下标相差4的分到一组,比如这个例子中a[0]与a[4]是一组、a[1]与a[5]是一组…,这里的差值(距离)被称为增量
    常用七种排序算法(C语言实现,含图解)_第2张图片
  • 每个分组进行排序后,各个分组就变成有序的了(整体不一定有序)
    常用七种排序算法(C语言实现,含图解)_第3张图片
  • 此时,整个数组变得部分有序了(有序程度肯不是很高)
    常用七种排序算法(C语言实现,含图解)_第4张图片
  • 然后缩小增量为上个增量的一半:2,继续划分分组,此时,每个分组元素个数多了,但是,数组变的部分有序了,插入排序效率同样比高
    常用七种排序算法(C语言实现,含图解)_第5张图片
  • 同理对每个分组进行排序(插入排序),使其每个分组各自有序
    常用七种排序算法(C语言实现,含图解)_第6张图片
  • 最后设置增量为上一个增量的一半:1,则整个数组被分为一组,此时,整个数组已经接近有序了,插入排序效率高
    常用七种排序算法(C语言实现,含图解)_第7张图片
  • 注意点
    对各组进行插入的时候并不是先对一个组进行排序完再对另一个组进行排序,而是轮流对每个组进行插入排序

常用七种排序算法(C语言实现,含图解)_第8张图片

4.3 测试代码(main函数参考冒泡排序的测试代码)

void insertI(int a[],int gap,int i)
{
	int inserted = a[i];
	int j=i-gap;
	//插入的时候按组进行插入(组内元素两两相隔gap)
	while(j>=0&& a[j]>inserted) 
	{
		a[j+gap] = a[j];
		j-=gap;
	}
	a[j+gap] = inserted;
}

void shellSort(int a[],int length)
{
	int len= length;
	//进行分组,最开始的增量 (gap)为数组长度的一半 
	for(int gap = len/2;gap>0;gap/=2)
	{
		//对各个分组进行插入排序
		for(int i = gap; i

五、归并排序

5.1 基本思想:

归并排序的实现分为递归实现非递归(迭代)实现

  • 递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。
  • 非递归(迭代)实现的归并排序首先进行的是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

5.2 操作步骤:

  1. 申请空间,使用临时数组来保存排好序的元素;
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
  4. 重复步骤3直到某一指针到达序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾。
    常用七种排序算法(C语言实现,含图解)_第9张图片

5.3 测试代码(main函数参考冒泡排序的测试代码)

//递归 
void mergeSort1(int a[],int left,int right,int *temp)
{//"分"的实现 
	int mid = 0;
	if(left

六、堆排序

6.1 基本思想:

是一种近似完全二叉树的结构(通常堆是通过一维数组来实现的),并满足性质:以最大堆(也叫大根堆、大顶堆)为例,其中父结点的值总是大于它的孩子节点。

6.2 堆排序的过程:

  1. 由输入的无序数组构造一个最大堆,作为初始的无序区;
  2. 把堆顶元素(最大值)和堆尾元素互换;
  3. 把堆(无序区)的尺寸缩小1,并调用heapify(A, 0)从新的堆顶元素开始进行堆调整;
  4. 重复步骤2,直到堆的尺寸为1。

6.3 测试代码(main函数参考冒泡排序的测试代码)

void Swap(int A[], int i, int j)
{
    int temp = A[i];
    A[i] = A[j];
    A[j] = temp;
}
 
void Heapify(int A[], int i, int size)  // 从A[i]向下进行堆调整
{
    int left_child = 2 * i + 1;         // 左孩子索引
    int right_child = 2 * i + 2;        // 右孩子索引
    int max = i;                        // 选出当前结点与其左右孩子三者之中的最大值
    if (left_child < size && A[left_child] > A[max])
        max = left_child;
    if (right_child < size && A[right_child] > A[max])
        max = right_child;
    if (max != i)
    {
        Swap(A, i, max);                // 把当前结点和它的最大(直接)子节点进行交换
        Heapify(A, max, size);          // 递归调用,继续从当前结点向下进行堆调整
    }
}
 
int BuildHeap(int A[], int n)           // 建堆,时间复杂度O(n)
{
    int heap_size = n;
    for (int i = heap_size / 2 - 1; i >= 0; i--) // 从每一个非叶结点开始向下进行堆调整
        Heapify(A, i, heap_size);
    return heap_size;
}
 
 
void heapSort(int a[],int len){
	int heap_size = BuildHeap(a, len);    // 建立一个最大堆
	while (heap_size > 1) // 堆(无序区)元素个数大于1,未完成排序
    {
        // 将堆顶元素与堆的最后一个元素互换,并从堆中去掉最后一个元素
        // 此处交换操作很有可能把后面元素的稳定性打乱,所以堆排序是不稳定的排序算法
        Swap(a, 0, --heap_size);
        Heapify(a, 0, heap_size);     // 从新的堆顶元素开始向下进行堆调整,时间复杂度O(logn)
    }
}

七、快速排序

7.1 基本思想:

使用分治策略来把一个序列分为两个子序列。

7.2 算法步骤:

  1. 从序列中挑出一个元素,作为"基准"(pivot);
  2. 把所有比基准值小的元素放在基准前面;
  3. 所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作,
  4. 对每个分区递归地进行步骤1~2。

7.3 测试代码(main函数参考冒泡排序的测试代码)

//方法一
void Swap(int A[], int i, int j)
{
    int temp = A[i];
    A[i] = A[j];
    A[j] = temp;
}
int Partition(int A[], int left, int right)  // 划分函数
{
    int pivot = A[right];               // 这里每次都选择最后一个元素作为基准
    int tail = left - 1;                // tail为小于基准的子数组最后一个元素的索引
    for (int i = left; i < right; i++)  // 遍历基准以外的其他元素
    {
        if (A[i] <= pivot)              // 把小于等于基准的元素放到前一个子数组末尾
        {
            Swap(A, ++tail, i);
        }
    }
    Swap(A, tail + 1, right);           // 最后把基准放到前一个子数组的后边,剩下的子数组既是大于基准的子数组
                                        // 该操作很有可能把后面元素的稳定性打乱,所以快速排序是不稳定的排序算法
    return tail + 1;                    // 返回基准的索引
}
 
void quickSort1(int a[],int left,int right){
	if (left >= right)
		return;
    int pivot_index = Partition(a, left, right); // 基准的索引
    quickSort(a, left, pivot_index - 1);
    quickSort(a, pivot_index + 1, right);
}




//方法二
void quickSort2(int a[],int n){
	int i,j;
	int pivot = a[0];	//设置第一个数为基准值 
	i = 0;
	j = n - 1;
	while(i < j){
		//大于基准值者保持在原位置 
		while(ipivot){j--;}
		if(i1)
	{
		quicksort(a,i);  //左序列进行快排
	}
	if(n-i-1>1)
	{
		quicksort(a+i+1,n-i-1);//右序列进行快排
	}
} 

参考原文:
1、https://blog.csdn.net/a951030393/article/details/80209490
2、https://blog.csdn.net/qq_39207948/article/details/80006224
3、https://blog.csdn.net/Moralin_/article/details/80491901

你可能感兴趣的:(数据结构,排序算法,冒泡,插入,选择,希尔,归并,堆,快速,C语言,数据结构)