C++经典题目解析

目录

第0题:

第1题:

第2题:

第3题:

冒泡排序:

 快速排序


这篇文章就来分析解释一下(分享——有趣的题目)这个提到的四个问题,见更多的题目,才能锻炼我们的思维和能力。详细代码请看上期内容。

第0题:


double gets(int n)
{
double r=0.0;
for(int i=1;i<n+1;i++)
{
r+=1.0/i;
}
return r;
}
 

关于这个题目没什么要提醒的,就是正常的一个函数然后调用罢了。我们只需要注意这个题目的数据类型就没问题了,因为是小数所以要用到浮点型,别用成整型导致输出错误就OK了。

第1题:


int ojld(int m,int n)
{
int r=m%n;
while(r!=0)
{
m=n;
n=r;
r=m%n;
}
return n;
}
 

这个题目我先来说一下数学模型,就是两个数m和n,将m除以n得到商,然后将被除数n赋值到m,然后将商赋值到除数,知道余数是零,就停止,最后将除数输出就是最大公约数。就是这样,也是十分的简单。我们利用了while循环就可以实现。


第2题:

#include <iostream>
using namespace std;
 
// 函数用于向左循环移位数组
void leftRotate(int arr[], int n, int d) {
    // 如果d是n的倍数,那么数组将保持不变
    if (d == 0) return;
    d = d % n; // 如果d大于n,则取模
 
    int temp[n]; // 用于临时存储数组
 
    // 先将第一部分的元素复制到temp中
    for (int i = 0; i < d; i++) {
        temp[i] = arr[n - d + i];
    }
两种方式:
    //1 然后将第二部分的元素复制到temp中,注意这里j从d开始
    for (int i = 0, j = d; i < n - d; i++, j++) {
        temp[j] = arr[i];
    }
                
    /* //2 复制剩余的元素到temp
    for (int i = d, j = 0; i < n; i++, j++) {
        temp[j] = arr[i - d];
    }
    */
    
    // 将temp中的元素复制回原数组
    for (int i = 0; i < n; i++) {
        arr[i] = temp[i];
    }
}
 
 
// 打印数组的函数
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++)
        cout << arr[i] << " ";
    cout << endl;
}
 
// 主函数
int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7};
    int n = sizeof(arr) / sizeof(arr[0]);
    int d = 3; // 向左移动3个位置
 
    cout << "原始数组: ";
    printArray(arr, n);
 
    leftRotate(arr, n, d);
 
    cout << "向左旋转 " << d << " 次后的数组: ";
    printArray(arr, n);
 
    return 0;
}

这个题目有些意思,我们首先定义了一个函数只是去实现这个功能,所以该函数是不需要任何返回值。我们的函数参数里传入了一个数组,数组的长度,还有要左移的次数。然后在函数第一步就对d进行判断,(可以节省我们后期代码处理的时间,减少了一些不必要的情况。)我们定义当d为0时直接结束函数(d==0时,数组不会移动,函数就没有运行的必要),如果d是n的倍数那么就对n取模再赋值给d(这一步可以简化我们的代码,提升效率)。然后我们定义一个数组temp(来进行临时存储)然后我们将数组的第一部分存储到temp中的后几位(temp[i] = arr[n - d + i];)。然后我们将剩下的几个元素传入temp数组。这里的d就是temp中的分界线(因为我们是左移循环,多所以我们将原数组的前几位输入到temp的后几位,然后将剩下的几个再输入就没问题了)。最后我们再将temp输入到原数组arr,函数到这里就结束了。之后就是打印数组的函数,然后就是函数的调用。

这里我要强调两个点:在代码中,if (d == 0) return; d = d % n; 这两行代码各自扮演着重要的角色,尤其是在处理数组旋转或任何需要周期性或循环性操作的场景中。
 1.  if (d == 0) return;
这行代码检查变量 d(通常表示要旋转的步数或次数)是否等于 0。如果是,函数立即返回,不执行任何旋转操作。这是因为旋转 0 次意味着数组应该保持不变,所以没有必要进行任何计算或修改。这可以提高代码的效率,避免不必要的计算。
 2.  d = d % n;
这行代码对 d 进行模 n 操作,其中 n 是数组的长度。模运算 % 的结果是 d 除以 n 的余数。这个操作有几个目的:
确保 d 的值在有效范围内:如果 d 大于或等于 n,那么实际上只需要旋转 d % n 次就能达到相同的效果。例如,如果数组有 5 个元素(n = 5),并且你想旋转 7 次(d = 7),那么实际上只需要旋转 2 次(7 % 5 = 2),因为旋转 5 次或更多次将会使数组回到原始状态。
避免无效操作:通过确保 d 的值小于 n,可以避免执行无效或多余的旋转操作。
这两行代码在数组旋转函数中非常重要,它们确保了函数的效率和正确性。首先,它们检查是否需要执行旋转操作(d 是否为 0)。然后,它们确保旋转的次数在有效范围内(d 是否小于 n),从而避免了不必要的计算或无效操作。


第3题:

#include <iostream>
#include <ctime>
#include <cmath>
#include <cassert>
using namespace std;
 
void BubbleSort( int *r , int n ) ; 
void QuickSort(int *data , int first, int last);
int Partition( int *data, int first, int last );
int *generateRadomArray(int n , int rangeL , int rangeR);
 
int main()
{
	int i = 4;            
	int n = pow(10,i);                  //待排序的数据规模
	
	clock_t startTime, endTime;
	int *arr = generateRadomArray(n,0,n);
	startTime = clock();
	BubbleSort( arr , n );
	endTime = clock();
	cout << "data size: " << n << endl;
	cout << "Time cost: " << double(endTime-startTime)/CLOCKS_PER_SEC << "s" << endl; 
 
	startTime = clock();
	QuickSort( arr, 0, n-1 );
	endTime = clock();
	cout << "data size: " << n << endl;
	cout << "Time cost: " << double(endTime-startTime)/CLOCKS_PER_SEC << "s" << endl;   
 
	delete[] arr;	
	return 0;
}
 
void BubbleSort( int *r , int n ) 
{	
	int temp, bound, exchange = n-1;
	while( exchange != 0)
	{
		bound = exchange;
		exchange = 0;
		for( int j = 0; j < bound; j++ )  //排序区间[1,bound] 
			if(r[j] > r[j+1])
			{
				temp = r[j]; 
                r[j] = r[j+1];	
                r[j+1] = temp;
				exchange = j; 
			}
	}
}
	
int Partition( int *data, int first, int last )
{
	int i = first, j = last, temp;              //初始化一次划分的区间
	while (i < j)	
	{
		while (i < j && data[i] <= data[j]) 
			j--;      //右侧扫描
		if (i < j) { 
			temp = data[i];	data[i] = data[j]; data[j] = temp; 
			i++; 
		}
		while (i < j && data[i] <= data[j]) 
			i++;     //左侧扫描
		if (i < j) {
			temp = data[i]; data[i] = data[j]; data[j] = temp;  
			j--; 
		}
	}
	return i;           // i为轴值记录的最终位置
}
	
void QuickSort(int *data , int first, int last)
{	
	if (first >= last) 
  		return;                            //区间长度为1,递归结束
	else 
	{
		int pivot = Partition(data, first, last);     //一次划分
		QuickSort(data, first, pivot-1);         //对左侧子序列进行快速排序
		QuickSort(data, pivot+1, last);         //对右侧子序列进行快速排序	
	}
}
 
int *generateRadomArray(int n , int rangeL , int rangeR) 
{
	assert( n > 0 && rangeL <= rangeR );     	//断言处理 
	int *arr = new int[n];                      //数据规模为n
	srand(time(NULL));
	for(int i = 0 ; i < n ; i++ )
	{
		arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;
	}	
	return arr;
}

看这个题目之前我们我要先理解一下什么是冒泡排序和快速排序。

冒泡排序:

算法步骤

  1. 比较相邻的元素:如果第一个比第二个大,就交换它们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
 快速排序

算法步骤

  1. 选择基准(Pivot):从数列中挑出一个元素,称为"基准"(pivot),
  2. 分区(Partitioning):重新排序数列,所有比基准值小的摆放在基准前面,所有比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归(Recursion):递归地把小于基准值元素的子数列和大于基准值元素的子数列排序。

 上面的代码已经写的十分清楚,大家可以自行思索,由于篇幅和时间有限,这个解析我会放在下一期的题目分享中。感谢大家阅读,求一个免费的赞。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值