常用排序算法

算法稳定性:若待排序表中有两个元素r1和r2,其对应的关键字相同,即key1=key2,且在排序前r1在r2的前面,若使用某一排序算法排序后,r1仍然排在r2前面,则称这个排序算法是稳定的。

排序目的:将无序的数据调整为有序序列

1、冒泡排序——O(n2)_O(1)_稳定

(1)基础实现

void bubble_sort(int *a, int n){
	int i, j, t;
	for(i = 0; i < n-1; i++){
		for(j = 0; j < n-1-i; j++){
			if(a[j] > a[j+1]){
				t = a[j];
				a[j] = a[j+1];
				a[j+1] = t;
			}
		}
	}
}

(2)简单优化

void bubble_sort_adv(int *a, int n){
	int i, j, t, flag = 0;  //标志未排好序
	for(i= 0; i < n-1 && flag == 0; i++){
		flag = 1; //假设排好序
		for(j = 0; j < n-1-i; j++){
			if(a[j] > a[j+1]){
				flag = 0;
				t = a[j];
				a[j] = a[j+1];
				a[j+1] = t;
			}
		}
	}
}

2、选择排序:序列左边都是有序的(相较冒泡排序减少了排序次数)O(n2)_不稳定

void select_sort(int *a, int n){
	int i, j, t;
	for(i = 0; i < n-1; i++){
		for(j = i+1; j < n; j++){
			if(a[i] > a[j]){
				t = a[i];
				a[i] = a[j];
				a[j] = t;
			}
		}
	}
}

3、插入排序

(1)直接插入排序:将无序序列插入有序序列(无序序列中第一个当作有序序列) O(n2)_O(1)_稳定

注:插入排序在什么时候效率高?(1)序列基本有序(2)数据较少

void insert_sort(int *a, int n){
	int i, j, t;
	for(i = 1; i < n; i++){
		if(a[i] < a[i-1]){
			t = a[i];
			for(j = i-1; j >= 0 && t < a[j]; j--){
				a[j+1] = a[j]; //填空位
			}
			a[j+1] = t;
		}
	}
}

(2)折半插入排序:在前面有序的序列中,使用折半查找找到待插入元素的正确位置 O(n2)

void insertSort(int a[], int n){
    int i, j, low, high, mid;
    for(i = 2; i <= n; i++){
        a[0] = a[i];  //将a[i]暂存到a[0]
        low = 1, high = i-1;
        while(low <= high){
            mid = (low+high) / 2;
            if(a[mid] > a[0]){
                high = mid-1;
            }else{
                low = mid+1;
            }
        }

        for(j = i-1; j >= high+1; --j){
            a[j+1] = a[j];  //统一后移元素,空出插入位置
        }

        a[high+1] = a[0];   //插入操作
    }
}

(3)希尔排序(插入排序升级版) 不稳定

注:当数据量很大时,希尔排序较插入排序效率高数倍

思路:先分组,然后对每一组数据分别进行插入排序

先按某一增量对数据进行分组后,对分得的每一组进行插入排序,最后合并为一组数据,再次进行插入排序

增量设置规则:上一轮增量/3+1
void shell_sort(int *a, int n){
	int i, j, t, k, increase = n;
	do{
		increase = increase/3 + 1;
		for(i = 0; i < increase; i++){ //增量变化
			for(j = i + increase; j < n; j += increase){
				if(a[j] < a[j-increase]){
					t = a[i];
					for(k = j - increase; k >= 0 && t < a[k]; k -= increase){
						a[k+increase] = a[k];
					}
					a[k+increase] = t;
				}
			}
		}
	}while(increase > 1);
}

4、快速排序(分治法:大问题分解成小问题,对小问题求解)O(nlogn)不稳定

//注:[left, right]
void quick_sort(int *a, int left, int right){
	int i, j, t, temp;
	if(left > right){
		return;
	}

	temp = a[left]; //首先找到一个基准数,temp存放的就是基准
	i = left;
	j = right;
	while(i != j){
		//顺序重要,要先从右往左找
		while(a[j] >= temp && i < j){
			j--;
		}

		//再从左往右走
		while(a[i] <= temp && i < j){
			i++;
		}

		if(i < j){
			t = a[i];
			a[i] = a[j];
			a[j] = t;
		}
	}

	//基准数归位
	a[left] = a[i];
	a[i] = temp;

	quick_sort(a, left, i-1);
	quick_sort(a, i+1, right);
	return;
}

5、归并排序:把每次排序结果放在第三方空间,用空间换时间  O(nlogn)_稳定

基本思想:将两个有序的序列合并成一个有序序列

把一个大的序列无限拆分成多个含一个元素的序列,此时序列有序,然后再向上合并直至序列有序

/*--------------------------------***merge_sort***--------------------------------------------
	归并排序分为拆、合两个步骤
*/
void merge(int *arr, int start, int mid, int end, int *tmp)
{
	int i = start;
	int j = mid + 1;
	int len = 0;

	while (i <= mid && j <= end)
	{
		if (arr[i] < arr[j])
		{
			tmp[len] = arr[i];
			len++;
			i++;
 		}
		else
		{
			tmp[len] = arr[j];
			len++;
			j++;
		}
	}

	while (i <= mid)
	{
		tmp[len] = arr[i];
		len++;
		i++;
	}

	while (j <= end)
	{
		tmp[len] = arr[j];
		len++;
		j++;
	}

	//回传到arr[]数组
	for (i = 0; i < len; i++)
	{
		arr[start + i] = tmp[i];
	}
}

void merge_sort(int *arr, int start, int end, int *tmp)
{
	int mid = (start + end) / 2;
	
	//递归出口
	if (start >= end)
	{
		return;
	}
	
	//左半边拆分
	merge_sort(arr, start, mid, tmp);
	//右半边拆分
	merge_sort(arr, mid + 1, end,tmp);
	//合并
	merge(arr, start, mid, end, tmp);
}

6、堆排序(利用完全二叉树和数组)O(nlogn)_不稳定

区别:每个节点都比它的左右节点大的叫大顶堆,都比它的左右节点小的叫小顶堆

//向下调整堆元素
void downAdjust(int *heap, int i,int num)
{
	int t, flag = 0;//flag 记录标记是否需要向下调整

	//当有左孩子结点,此时需要调整就执行循环
	while (i * 2 <= num && flag == 0)
	{
		if (heap[i] > heap[i * 2])
		{
			t = 2 * i;  //用t记录值较小的结点
		}
		else
		{
			t = i;
		}

		//有右孩子继续比较
		if (i * 2 + 1 <= num)
		{
			if (heap[t] > heap[2 * i + 1])
			{
				t = 2 * i + 1;
			}
		}

		//如果发现最小的结点已经不是自己本身
		if (t != i)
		{
			swap(heap, t, i);
			i = t; //交换了,把i置为t;
		}
		else
		{
			flag = 1;//否则说明不需要调整
		}
	}

	return;
}

//创建堆heap
void create_heap(int *heap, int num)
{
	int i;

	//从最后一个非叶子节点到第1个结点依次进行调整
	for (i = num / 2; i >= 1; i--) //为了便于使用,从序号1开始使用
	{
		downAdjust(heap, i, num);
	}

	return;
}

void heap_sort(int *heap, int num)
{
	int tmp = num;
	create_heap(heap, num);
	while (tmp > 1)
	{
		swap(heap, 1, tmp);
		tmp--;
		downAdjust(heap, 1, tmp);
	}

	return;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Star星屹程序设计

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值