基础数据结构15:冒泡排序和归并排序

1.请添加图片描述
2. 冒泡排序和二路归并排序

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//八大排序   排序分为升序和降序   我们默认使用升序
//算法的描述  算法的实现  算法的评价(时间复杂度,空间复杂度,稳定性)
//什么是稳定性:如果排序前A在A`的前面,排序之后A孩子A`的前面,则排序算法稳定
//如何判断其稳定性:看算法中是否存在跳跃交换
//第一个排序算法:直接插入排序:每次从待排序队列中取一个值,放到已排序好的队列,再次保持有序,重复这样的动作,直到把待排序队列中的值全部取完  时间复杂度O(n^2) 空间复杂度O(1)  稳定的
//第二个排序算法:希尔排序(缩小增量排序):是一个特殊的直接插入排序,相当于多次调用直接插入排序,每一次的增量保持互素,并且最后一个增量一定位1,为1才能保证其完全有序   时间复杂度O(n^1.3~1.5) 空间复杂度O(1)  不稳定
//第三个排序算法:冒泡排序(沉石排序):两两比较,大的向后挪动,小的向前  时间复杂度O(n^2) 空间复杂度O(1)  稳定的(不存在跳跃交换)
//第四个排序算法:二路归并排序(非递归形式):两两合并成一个组,当合并后的组能容纳arr所有值的时候,则退出,因为此时已经组内完全有序   时间复杂度O(nlogn)  空间复杂度O(nlogn)  不稳定

void InsertSort(int arr[], int len)
{
	assert(arr != NULL);
	if (arr == NULL)
	{
		return;
	}

	int count = 0;
	int tmp;
	int j;//将j的生存周期提高,保证break后的的代码arr[j+1] = tmp;有效
	for (int i = 1; i < len; i++)//每次从待排序队列中取的值
	{
		tmp = arr[i];//用tmp保存待插入的值
		for (j = i - 1; j >= 0; j--)//从右向左找不比tmp大的值
		{
			if (arr[j] > tmp)//如果比tmp大  则向右放一格
			{
				arr[j + 1] = arr[j];
				count++;
			}
			else//如果不比tmp大
			{
				break;
			}
		}

		arr[j + 1] = tmp;
	}
	printf("swap: %d\n", count);
}
void Show(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}


int count2 = 0;
static void Shell(int arr[], int len, int gap)
{
	int tmp;
	int j;//将j的生存周期提高,保证break后的的代码arr[j+1] = tmp;有效
	for (int i = gap; i < len; i++)//每次从待排序队列中取的值
	{
		tmp = arr[i];//用tmp保存待插入的值
		for (j = i - gap; j >= 0; j = j - gap)//从右向左找不比tmp大的值
		{
			if (arr[j] > tmp)//如果比tmp大  则向右放一格
			{
				arr[j + gap] = arr[j];
				count2++;
			}
			else//如果不比tmp大
			{
				break;
			}
		}

		arr[j + gap] = tmp;
	}
}
void ShellSort(int arr[], int len)
{
	//assert

	int gap[] = { 5, 3, 1 };
	int lengap = sizeof(gap) / sizeof(gap[0]);
	for (int i = 0; i < lengap; i++)
	{
		Shell(arr, len, gap[i]);//5 3 1
	}


}

void BubbleSort(int arr[], int len)
{
	bool tag = true;//优化标记   如果当前这轮存在一次交换  则tag变成FALSE   
					 //当tag为true不就代表着不存在前面比后面大 
					 //则已经完全有序  那直接退出即可  剩余轮次不需要执行
	int count = 0;
	for (int i = 0; i < len - 1; i++)//少一轮
	{
		tag = true;
		for (int j = 0; j + 1 < len - i; j++)//j<len-1-i
		{
			if (arr[j] > arr[j + 1])//两两比较 前面大于后面 则交换两个值
			{
				tag = false;
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
		count++;
		if (tag)
		{
			break;
		}
	}
	printf("跑了%d趟\n", count);
	}

//一次融合的代码   时间复杂度O(n)
static void Merge(int arr[], int len, int gap)//gap->几几合并的几
{
	int* brr = (int*)malloc(sizeof(int) * len);//申请额外的辅助空间brr
	assert(brr != NULL);
	int i = 0;//i指向brr的下标

	//申请四个指针  用low1 high1来表示左边的组的边界  用low2 high2来表示右边的组的边界 
	int low1 = 0;
	int high1 = low1 + gap - 1;
	int low2 = high1 + 1;
	int high2 = low2 + gap - 1 < len ? low2 + gap - 1 : len - 1;
	//low2+gap-1是我应得的   len如果大于我应得的  则该给我多少就给多少  如果小则将你剩余的都给我

	while (low2 < len)//我抓的两个组都存在   low2<len则代表右手没抓空  而右手没抓空 则左手肯定也没抓空  则证明两手都抓中了
	{
		while (low1 <= high1 && low2 <= high2)//两个组都还有数据  则接着比较向下取到brr内
		{
			if (arr[low1] <= arr[low2])
			{
				brr[i++] = arr[low1++];
			}
			else
			{
				brr[i++] = arr[low2++];
			}
		}
		//此时肯定有一组数据没了 那么将另一组数据直接挪到brr内即可
		//但此时需要判断左右手哪个抓的组里没数据了
		while (low1 <= high1)//左手抓的组里面还有数据 在直接挪到brr内
		{
			brr[i++] = arr[low1++];
		}
		while (low2 <= high2)//右手抓的组里面还有数据 在直接挪到brr内
		{
			brr[i++] = arr[low2++];
		}

		low1 = high2 + 1;
		high1 = low1 + gap - 1;
		low2 = high1 + 1;
		high2 = low2 + gap - 1 < len ? low2 + gap - 1 : len - 1;

	}

	//此时肯定不能同时两手都抓到组   而且可以得到一个信息  那就是(只可能右手抓的组是空的)
	//左手抓上一个组 右手抓空  接下来只需要将左手的组原样挪下来即可
	while (low1 < len)//左手抓的组里面还有数据 在直接挪到brr内
	{
		brr[i++] = arr[low1++];
	}

	//最后一步:将辅助空间brr里的数据全部重新拷贝到arr里
	for (int i = 0; i < len; i++)
	{
		arr[i] = brr[i];
	}
	free(brr);//最后记得将malloc来的brr释放掉  避免内存泄露
	brr = NULL;

}

void MergeSort(int arr[], int len)
{
	//assert
	for (int i = 1; i < len; i *= 2)//11融合 22融合  44融合  88融合  直到融合后的组包含了arr所有值
	{
		Merge(arr, len, i);//logn次 * O(n)  = nlogn
	}
}

int main()
{
	int arr[] = { 1,3,2,4,6,5,7,9,8 };
	int len = sizeof(arr) / sizeof(arr[0]);

	//InsertSort(arr, len);
	//ShellSort(arr, len);
	//printf("swap = %d\n", count2);

	//BubbleSort(arr, len);
	MergeSort(arr, len);
	Show(arr, len);


	return 0;
}

4.(1)冒泡排序(沉石排序):冒泡排序轮次少一轮,冒泡排序的优化,加入一个标志,如果某一轮已经完全有序,则不用继续进行比较交换。时间复杂度O(n^2),空间复杂度O()
(2)归并排序:二路归并排序,分治策略,空间复杂度为O(n),需要借助额外空间帮助融合。最开始将数组单个分为一组,则每一组局部有序,再两两合并,直到能把所有数据放到同一个组内完成。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值