【归并排序,小和问题,利用C++实现】

1 归并排序(升序或降序只需在左右侧比较时修改即可)

  求数组左侧有序和右侧有序,然后合并左侧和右侧完成排序。

  对于数组a={1,4,2,5,3},mid=0+(4-0)/2=2,

第一步:从a的下标i=2处将a分为:左侧{1,4,2}右侧{5,3};

第二步:计算左侧的mid,将左侧{1,4,2}分为:左侧{1,4}右侧{2};

第三步:计算左侧的mid,将左侧{1,4}分为:左侧{1}右侧{4};

第四步:当不能进行分割时开始比较当前的左侧和右侧值进行排序,倒着执行到第一步完成数组a左侧的排序{1,2,4};

第五步:对数组a的右侧进行上述步骤,完成右侧排序{3,5};

最后:合并左侧{1,2,4}右侧{3,5},合并时若左侧a[i]<右侧a[j],先拷贝a[i],此为升序,若为降序则相反。 7-99999999999999

如图:

 

2 完整代码如下:

#include<iostream>
using namespace std;

//1.归并排序,利用递归,先将两侧排好序,再进行合并
//比较相邻序列
void merge(int arr[], int temp[], int start,int mid, int end)
{
	cout << "(start,mid,end)=" << "(" << start << "," << mid << "," << end << ")" << endl;
	int i = start, j = mid + 1, k = start;
	//将较小值放入temp
	while (i != mid+1 && j != end+1)
	{
		if (arr[i] <arr[j])
		{
			temp[k++] = arr[i++];
		}
		else
		{
			temp[k++] = arr[j++];
		}
	}
	//将多的数放到temp末尾
	while (i!= mid + 1)
	{
		temp[k++] = arr[i++];
	}
		
	while (j != end + 1)
	{
		temp[k++] = arr[j++];
	}
	
	//更新序列
	for (int i = start; i <= end; i++)
	{
		arr[i] = temp[i];
		cout << arr[i] << " ";
	}
	cout << endl;
}

//排序
void merge_sort(int arr[], int temp[], int start, int end)
{
	int mid = 0;
	if (start < end)
	{
		mid = start + (end - start) / 2;
		//递归调用
		merge_sort(arr, temp, start, mid);
		merge_sort(arr, temp, mid + 1, end);
		merge(arr, temp, start, mid, end);
	}
}

int main()
{
	int a[5] = {1,4,2,5,3};
	int t[5];
	merge_sort(a, t, 0, 4);
	for (int i = 0; i < 5; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;

	system("pause");
	system("cls");
	return 0;
}

3 小和问题 

 问题描述:在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和

以上述中a={1,4,2,5,3}为例:记小和为:res=0

(1)1左边比1小的数为0(1左边没有数字),res=0+0;

(2)4左边比4小的数为1,                                 res=0+1

(3)2左边比2小的数为1,                                  res=0+1+1

(4)5左边比5小的数为1,4,2,                            res=0+1+1+1+4+2

(5)3左边比3小的数为1,2,                               res=0+1+1+1+4+2+1+2=12

从上述可以得出比1的右侧比1大的个数为4,比4大的个数为1,比2大的数的个数为2,比5大的数的个数为0,比3大的数的个数为0

  小和  res=1*4 + 4*1 + 2*2 + 5*0 + 3*0=12

因此小和问题只需在归并排序过程中记录比当前数a[i]大的数的个数 num    res+=a[i]*num

注意:当左侧数等于右侧数,即:a[i]==a[j] 时应先拷贝右侧数,即 :temp=a[j],否则统计不出比当前数字大的数的个数.

4 小和详细代码:

#include<iostream>
using namespace	std;

/*
小和问题描述:
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和
*/
//分段排序
int merge(int arr[], int temp[], int start, int mid, int end)
{
	cout << "(start,mid,end)=" << "(" << start << "," << mid << "," << end << ")" << endl;
	int i = start, j = mid+1, k = start;
	int res = 0;
	while(i != mid + 1 && j != end + 1)
	{
		//若左侧等于右侧先拷贝右侧,否则算不出比当前数大的数的个数
		if (arr[i] == arr[j])
		{
			temp[k++] = arr[j++];
		}
		else if (arr[i] < arr[j]) //注意此处不要写为arr[i++]<arr[j++]
		{
			res += (end - j + 1) * arr[i]; //num=end-j+1为比当前数a[i]大的数的个数
			temp[k++] = arr[i++];
		}
		else
		{
			temp[k++] = arr[j++];
		}
	}
	//将多余的数放到temp末尾
	while (i != mid + 1)
	{
		temp[k++] = arr[i++];
	}
	while (j != end + 1)
	{
		temp[k++] = arr[j++];
	}
	//更新a[]
	for (int i=start;i<= end;i++)
	{
		arr[i] = temp[i];
		cout << arr[i] << " ";
	}
	cout << endl;
	return res;
}

//递归排序
int merge_sort(int arr[], int temp[], int start, int end)
{
	int mid = 0;
	if (start == end) //此时不记录小和
	{
		return 0;
	}
	mid = start + (end - start) / 2;
	//递归调用
	return merge_sort(arr,temp,start,mid)
	+merge_sort(arr, temp, mid + 1, end)
	+merge(arr, temp, start, mid, end);
}

int main()
{
	//准备数组{ 2, 3, 4, 1, 2, 3, 8, 9, 0 };小和=5*2+3*3+2*4+4*1+3*2+2*3+1*8=
	int a[5] = { 1,4,2,5,3 };
	int t[5];
	int Sum = 1 * 4 + 4 * 1 + 2 * 2 + 5 * 0 + 3 * 0;

	int sum=merge_sort(a, t, 0, 4);
	cout << "a=";
	for (int i = 0; i < 5; i++)
	{
		cout << a[i] << ",";
	}
	cout << endl;
	cout << "验证Sum=" << Sum << endl;
	cout << "小和sum=" << sum << endl;
	return 0;
}

结果: 

这是作者的学习笔记,在此分享,仅供参考。因为作者水平有限,如有疏漏之处,还望批评指正。 

要使用8254定时/计数器输出4KHz的方波,首先要理解其工作模式和地址解析。8254拥有三种基本工作模式,其中模式3支持方波输出。在模式3下,计数器会在计数到一半时切换输出电平,产生方波。为得到4KHz的方波输出,需要使用外部时钟频率的适当分频。 参考资源链接:[南邮微机原理课后习题解析:接口与定时/计数器](https://wenku.youkuaiyun.com/doc/646310685928463033bcee32) 首先,我们需要知道外部时钟频率。假设使用的是8MHz时钟,因为8254的计数器在输入时钟下计数,我们可以计计数器需要的初值。4KHz方波意味着周期为250微秒,因此如果时钟频率为8MHz,即每微秒有8个时钟脉冲,则250微秒内应有2000个时钟脉冲。计数器需要计数1000次(因为是从0开始计数到1000,然后从1000计数到0),所以初值设置为8000-1000=7000(十六进制的1B58)。 接下来,进行地址解析。8254通常有三个计数器端口和一个控制端口,端口地址通常由译码电路确定。对于PC系列,控制端口地址通常是0x43,而计数器0、1、2的端口地址分别是0x40、0x41和0x42。如果系统采用了片选信号,可能还需要额外的译码逻辑。 初始化过程包括设置控制字和加载初值。控制字决定了计数器的选择、读写操作的类型、计数器工作模式以及计数器的时钟频率。对于8MHz时钟和模式3,控制字可能设置为0x36(二进制的***),这指示计数器0以模式3工作,使用低字节、高字节方式读取计数器值。然后向计数器0写入初值7000。 通过以上步骤,即可实现使用8254定时/计数器产生4KHz方波输出。在这个过程中,对CPU接口电路的理解和对I/O端口地址解析的正确应用是关键。这份详细解析过程中,涵盖了接口电路的功能、I/O端口分类、计数模式、中断管理等关键知识点,不仅有助于理解微机原理的基本概念,也为实际应用提供了指导。欲深入了解这些概念及其在微机原理中的具体应用,建议参考《南邮微机原理课后习题解析:接口与定时/计数器》。 参考资源链接:[南邮微机原理课后习题解析:接口与定时/计数器](https://wenku.youkuaiyun.com/doc/646310685928463033bcee32)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

冲冲冲@chong

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

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

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

打赏作者

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

抵扣说明:

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

余额充值