基础算法-2.21

本文介绍了递归的概念、递归和循环的比较,以及几种常见的排序算法(冒泡排序、选择排序、插入排序、快速排序和归并排序)。通过实例展示了如何在C++中实现这些算法。
摘要由CSDN通过智能技术生成

目录

模拟

递归

递归和循环的比较

排序

冒泡排序

选择排序

​编辑

插入排序

快速排序

归并排序


模拟

#include <bits/stdc++.h>
using namespace std;

int s2i(string s)  //string 转换为int 
{
  int res=0;
  for(const auto & i:s)
    res= res*10+i -'0';
  return res;
}

string i2s(int x,int w)//int 转换为指定位数的string
{ 
  string res;
  while(x)  res+=(x%10)+'0',x/=10;
  while(res.length()<w)  res+='0';
  reverse(res.begin(),res.end());
  return res;
}

bool isLeapYear(int year)//判断闰年
{
  return (year%4==0&&year%100!=0)||(year%400==0);
}

bool isok(int year,int month,int day)//判断日期是否合法
{
  int days[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
  if(isLeapYear(year))  days[2]=29;//是否闰年
  return day<=days[month];
}

bool isPa(string s)//是否回文
{
  for(int i=0;i<s.length()/2;++i)
  {
    if(s[i]!=s[s.length()-1-i])  return false;
  }
  return true;
}

bool isPa2(string s)//是否为ABABBABA的回文
{
  if(!isPa(s))  return false;
  return s[0]==s[2]&&s[1]==s[3];//   ?
}

int main()
{
  string s; cin>>s;//substr(n,m),从n位置开始往后取m位
  int year=s2i(s.substr(0,4)),month=s2i(s.substr(4,2)),day=s2i(s.substr(6,2));

  bool ans1=false,ans2=false;//用于是否找到了答案
 for(int i=year;i<=9999;++i)
 {
   for(int j=1;j<=12;++j)
   {
     if(i==year&&j<month)  continue;
    for( int k=1;k<=31;++k)
    {
      if(i==year&&j==month&&k<=day)  continue;

      if(!isok(i,j,k))  continue;
      string date=i2s(i,4)+i2s(j,2)+i2s(k,2);//拼接字符
      if(!ans1&&isPa(date))
      {
        cout << date<<'\n';
        ans1=true;
      }
       if(!ans2&&isPa2(date))
      {
        cout << date<<'\n';
        ans2=true;
      }

    }
   }
 }
 
  return 0;
}

递归

递归指函数直接或间接调用自身的过程

递归和循环的比较

递归的特点:

1.直观、简洁、易于理解和实现。

2.适用于问题规模可以通过调用调用不断减小的情况。

3.可以处理复杂的数据结构和算法,如图的遍历和树。

4.存在栈溢出风险,因此递归层数不宜过深。

循环的特点:

1.直接控制流程,效率较高。

2.适用于问题规模没有明显的缩减,或者需要一定的迭代。

3.大部分的动态规划。

斐波那契数列:

//斐波那契数列
/*
	f(1)=f(2)=1;f(n)=f(n-1)+f(n-2),结果对1e9+7取模
*/

#include <iostream>
using namespace std;
using ll = long long;

const int N = 1e5 + 9;
const ll p= 1e9 + 7;

ll dp[N];//带备忘录

int f(int x)
{
	//计算过的就不再重复计算,时间复杂度为O(n)
	if (dp[x]) return dp[x];
	if (x <= 2) return 1;
	return dp[x]=(f(x - 1) + f(x - 2)) % p;

}

int main()
{
	int n; cin >> n;
	for (int i = 1; i <= n; ++i)  cout << f(i) << '\n';

	return 0;
}

例题:

排序

冒泡排序

冒泡排序的思想是每次将最大的一下一下运到右边,然后将最右边这个确定下来,再来确定第二大的。

冒泡排序一般用双层循环实现。

选择排序

选择排序的思想和冒泡排序类似,是每次找出最大的然后直接放到右边对应位置,然后将最右边这个确定下来(而不是一个一个的交换)(同一个题,方法不同)

插入排序

插入排序是一种简单直观的排序算法,基本思想是将待排序的元素逐个插入到已经排序的序列的合适位置,使得已排序序列逐渐扩大,从而构成有序序列,最终得到完全有序的序列。类似打扑克的手牌。

例题

快速排序

快速排序是一种基于分治法的排序方法,原理是将一个数组分成两个子数组,其中一个子数组的所有元素都小于另一个子数组的元素,然后递归的对两个数组进行排序。

思想:不断的将数组分成两个子数组,递归的对子数组进行排序,最终得到一个有序的数组。

通过选择合适的基准和分区操作实现

时间复杂度:O(nlogn)

归并排序

原理是将一个数组分成两个子数组,将子数组向下递归的排序后(当数组中仅有一个元素值无需再排序,直接返回),得到两个有序数组,然后进行O(n)的合并,最终合并成有序的原数组。

两个有序数组合并成一个原数组,方法是依次对比两个数组的值,将较小的值放到数组中。

递归主体MergeSort();传入参数为要排序的数组和区间的左右端点。

注意:当区间为一时,直接返回。

void MergeSort(int a[], int l, int r)
{
	if (l == r) return;
	int mid = (l + r) / 2;//默认向下取整
	MergeSort(a, l, mid);
	MergeSort(a, mid + 1, r);
	//排序完成后a[l,mid],a[mid+1,r]都是分别有序的

	//将a[l,r]两部分一个个放入b[l,r]中
	int pl = 1, pr = mid + 1, pb = l;
	while (pl <= mid || pr <= r)
	{
		if (pl > mid)
		{
			//左半边已经放完
			b[pb++] = a[pr++];
		}
		else if (pr > r)
		{
			//右半边已经放完
			b[pb++] = a[pl++];
		}
		else
		{
			//两边都有元素,取小的放进去
			if (a[pl] < a[pr]) b[pb++] = a[pl++];
			else b[pb++] = a[pr++];
		}
	}
	//完成后复制
	for (int i = 1; i <= r; ++i) a[i] = b[i];
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值