常见排序算法详解(上)

插入排序

基本思想:

直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
当插入第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. 稳定性:稳定
代码:
#include<stdio.h>

void InsertSort(int* a,int n)
{
	for(int i=0;i<n-1;i++)
	{
		int end=i;
		int tmp=a[end+1];
		while(end>=0)
		{
			if(tmp<a[end])
			{
				a[end+1]=a[end];
				end--; 
			}
			else
			{
				break;
			}
		}
		a[end+1]=tmp;
	}
}

int main()
{
	int a[6]={2,4,1,6,3,7};
	InsertSort(a,6);
	for(int i=0;i<6;i++)
	{
		printf("%d ",a[i]); 
	}
	printf("\n");
}

希尔排序

基本思想:

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

在这里插入图片描述

希尔排序特性总结:
  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就
    会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N1.3—N2)
  4. 稳定性:由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
代码:
void ShellSort(int* a,int n)
{
	int gap=n;
	while(gap>1)
	{
		gap=gap/3+1;
		for(int i=0;i<n-gap;i++)
	{
		int end=i;
		int tmp=a[end+gap];
		while(end>=0)
		{
			if(tmp<a[end])
			{
				a[end+gap]=a[end];
				end-=gap;
			}
			else
			{
				break;
			}
		}
		a[end+gap]=tmp;
	}
	}
}

选择排序

基本思想:

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

直接选择排序特性总结:
  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
    选择排序的交换操作介于 0 和 (n - 1)次之间。选择排序的比较操作为 n (n - 1) / 2 次之间。选择排序的赋值操作介于 0 和 3 (n - 1) 次之间。比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+…+1=n*(n-1)/2。交换次数O(n),最好情况是,已经有序,交换0次;最坏情况交换n-1次,逆序交换n/2次。交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定
    选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果一个元素比当前元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中两个5的相对前后顺序就被破坏了,所以选择排序是一个不稳定的排序算法。
代码:
void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void SelectSort(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	while (begin<end)
	{
		int mini = begin;
		int maxi = end;
		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位置处,经过前面的交换,最大值已经杯换到最小下标处
		if (maxi == begin)
		{
			maxi = mini;
		}
        swap(&a[maxi], &a[end]);
		end--;
		begin++;
	}
}

冒泡排序

在这里插入图片描述

原理:
  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
特性总结:
  1. 时间复杂度:O(N^2)
  2. 空间复杂度:O(1)
  3. 稳定性:稳定
    冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,是不会再交换的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

代码:

void swap(int* a,int* b)
{
	int tmp=*a;
	*a=*b;
	*b=tmp;
}
void BubbleSort(int* a,int n)
{
	int end=n;
	int exchange=0;
	while(end>0)
	{
		for(int i=1;i<end;i++)
		{
			if(a[i-1]>a[i])
			{
				swap(&a[i-1],&a[i]);
				exchange=1;
			}
		}
		end--;
		if(exchange==0)
		break;
	}
} 

堆排

简介:

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

在这里插入图片描述

堆排特性总结:
  1. 时间复杂度:O(N*logN)
  2. 空间复杂度:O(1)
  3. 稳定性:不稳定
代码:
//向下调整算法
void ADjustDown(int* a,int n,int root)
{
	int parent=root;
	int child=parent*2+1;
	while(child<n)
	{
		//child指向孩子中大的那一个 
	    if(child+1<n&&a[child]<a[child+1])
	{
		child++;
	}
	    if(a[child]>a[parent])
	{
		int tmp;
		tmp=a[child];
		a[child]=a[parent];
		a[parent]=tmp;
		
		parent=child;
		child=child*2+1;
	}
	else
	{
		break;
	}
	}
	
}

void HeapSort(int* a,int n)
{
	//建立大堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		ADjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		int tmp = a[end];
		a[end] = a[0];
		a[0] = tmp;

		ADjustDown(a, end, 0);
		end--;
	}

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值