停更阶段学习算法题的一些总结

一星题

快速排序

题目链接:快速排序
在这里插入图片描述

其实这道题在停更之前的八大排序中已经涉及过了,下面来看看y总的代码。

#include <iostream>

using namespace std;

const int N = 100010;

int q[N];

void quick_sort(int q[], int l, int r)
{
   
    if (l >= r) return;

    int i = l - 1, j = r + 1, x = q[l + r >> 1];
    while (i < j)
    {
   
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }

    quick_sort(q, l, j);
    quick_sort(q, j + 1, r);
}

int main()
{
   
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);

    quick_sort(q, 0, n - 1);

    for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);

    return 0;
}

y总的代码其实是很简便和优雅的,但由于我比较习惯之前的实现方式,所以遇到快速排序时一直都是用的我自己的那套写法。

void QuickSort1(int* a, int left, int right)
{
   
	if (left >= right)//当只有一个数据或是序列不存在时,不需要进行操作
		return;

	int begin = left;//L
	int end = right;//R
	int key = left;//key的下标
	while (left < right)
	{
   
		//right先走,找小
		while (left < right&&a[right] >= a[key])
		{
   
			right--;
		}
		//left后走,找大
		while (left < right&&a[left] <= a[key])
		{
   
			left++;
		}
		if (left < right)
		{
   
			swap(&a[left], &a[right]);
		}
	}
	int meet = left;//L和R的相遇点
	swap(&a[key], &a[meet]);//交换key和相遇点的值

	QuickSort1(a, begin, meet - 1);//key的左序列进行此操作
	QuickSort1(a, meet + 1, end);//key的右序列进行此操作
}

这只是快速排序的一种实现方法:Hoare法。其他的方法可以看这篇文章:八大排序


归并排序

题目链接:归并排序
在这里插入图片描述

这里也不多结束了,之前的那篇八大排序中也涉及到了此排序。
y总的代码:

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int a[N], tmp[N];

void merge_sort(int q[], int l, int r)
{
   
    if (l >= r) return;

    int mid = l + r >> 1;

    merge_sort(q, l, mid), merge_sort(q, mid + 1, r);

    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
        else tmp[k ++ ] = q[j ++ ];
    while (i <= mid) tmp[k ++ ] = q[i ++ ];
    while (j <= r) tmp[k ++ ] = q[j ++ ];

    for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}

int main()
{
   
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);

    merge_sort(a, 0, n - 1);

    for (int i = 0; i < n; i ++ ) printf("%d ", a[i]);

    return 0;
}

自己平常写的:

void  MergeSort(int* a, int left, int right,int* temp)
{
   
	if (left >= right)
		return;
	int mid = left + (right - left) / 2;
	int begin1 = left, end1 = mid;
	int begin2 = mid+1, end2 = right;
	int i = left,j=0;  //i一定要写成left不能写成0,如果是递归左序列还可以适用,但是递归到右序列时就不行了
	MergeSort(a, left, mid, temp);  //这里要注意区间的划分,不然可能出现死递归
	MergeSort(a, mid + 1, right, temp);
	while (begin1 <= end1&&begin2 <= end2)
	{
   
		if (a[begin1] < a[begin2])
		{
   
			temp[i++] = a[begin1++];
		}
		else
		{
   
			temp[i++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
   
		temp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
   
		temp[i++] = a[begin2++];
	}
	for (j = left; j < right + 1; j++)
	{
   
		a[j] = temp[j];
	}
}


高精度加法

其实这就是一道模拟题,并不涉及什么算法,注意到细节也就相对容易了。
题目链接:高精度加法
在这里插入图片描述
代码实现:

#include <iostream>
#include <vector>

using namespace std;

vector<int> add(vector<int> &A, vector<int> &B)
{
   
    if (A.size() < B.size()) return add(B, A);

    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++ )
    {
   
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }

    if (t) C.push_back(t);
    return C;
}

int main()
{
   
    string a, b;
    vector<int> A, B;
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');

    auto C = add(A, B);

    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
    cout << endl;

    return 0;
}

注意:这里写代码时要时刻想清楚哪个时候vector里面存放的是正向排序,什么时候是逆序的。

由于减法其实就是加上一个负数,其实如果你愿意,也可以把减速的运算过程模拟出来,因为之前已经介绍了加法,所以减法这里就不过多介绍了。


高精度乘法

题目链接:高精度乘法
在这里插入图片描述
代码实现:

#include <iostream>
#include <vector>

using namespace std;


vector<int> mul(vector<int> &A, int b)
{
   
    vector<int> C;

    int t = 0;
    for (int i = 0; i < A.size() || t; i ++ )
    {
   
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();  //用于解决前导0的问题

    return C;
}


int main()
{
   
    string a;
    int b;

    cin >> a >> b;

    vector<int> A;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');

    auto C = mul(A, b);

    for (int i = C.size() - 1; i >= 0; i -- ) printf("%d", C[i]);

    return 0;
}

注意:乘法前导0的问题。


前缀和

题目链接:前缀和
在这里插入图片描述
这道题如果用暴力解法的话,时间可能会超时。但是用了前缀和的话,从O(n)的复杂度可以变为O(1)的复杂度。下面我们先来了解一下前缀和是什么,其实这和高中学的数列的知识有些关系,前缀和其实是一个S[N]的数组,其中N为元素的个数,S[1]表示a1,S[2]表示a1+a2,S[3]表示a1+a2+a3,也就是说i是几,S[i]就是前几项的和,这里我们第一个元素在数组中下标并不是从0开始的,而是从1开始的。
代码实现:

#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int a[N], s[N];

int main()
{
   
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);

    for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i]; // 前缀和的初始化

    while (m -- )
    {
   
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", s[r] - s[l - 1]); // 区间和的计算
    }

    return 0;
}

如果你一开始并不能怎么看懂这段代码,其实你举一个示例,然后对着代码看,其实很容易理解。下面我们来提升一下难度,这里是一维数组的前缀和,下面看看二维数组的前缀和。


子矩阵的和(二维数组的前缀和)

题目链接:子矩阵的和
在这里插入图片描述
代码实现:

#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int s[N][N];

int main()
{
   
    scanf("%d%d%d", &n, &m, &q);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &s[i][j]);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];  //这里自己举个比较简单的二维数组可以很简单的分析出来

    while (q -- )
    {
   
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]); //对应到下面图中用荧光笔涂鸦的部分
    }

    return 0;
}

这段代码想比前面的一维数组前缀和,难度又提升了,不过大致的思路还是一样的。这里博主就不具体介绍了,因为这只是一篇用于个人总结的博客。下面附上一张我分析时的图片。
在这里插入图片描述


移除元素

题目链接:移除元素
在这里插入图片描述
代码实现:

class Solution {
   
public:
    int removeElement(vector<int>& nums, int val)
    {
   
       if(nums.size()==0)
       {
   
           return 0;
       }
       vector<int>::iterator begin=nums.begin();
       vector<int>::iterator end=nums.end()-1;
       while(begin<end)
       {
   
           while(begin<end&&(*begin)!=val)
           {
   
               begin++;
           }
           while(begin<end&&(*end)==val)
           {
   
               end--;
           }
           swap((*begin),(*end));
       }
       int sum=0;
       for(vector<int>::iterator i=nums.begin();i!=nums.end();i++)
       {
   
           if((*i)!=val)
           {
   
               ++sum;
           }
       }
       return sum;
    }
};

更优的做法(双指针法):

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
   
public:
    int removeElement(vector<int>& nums, int val) {
   
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
   
            if (val != nums[fastIndex]) {
   
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

二分查找

题目链接:二分查找
在这里插入图片描述
代码实现:

class Solution {
   
public:
    int search(vector<int>& nums, int target)
    {
   
      int right=nums.size()-1;
      int left=0;
      while(left<=right)
      {
   
          int mid=left+(right-left)/2;
          if(nums[mid]>target)
          {
   
              right=mid-1;
          }
          else if(nums[mid]<target)
          {
   
              left=mid+1;
          }
          else
          {
   
              return mid;
          }
      }
      return -1;
    }
};

第二种写法:

// 版本二
class Solution {
   
public:
    int search(vector<int>& nums, int target) {
   
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
        while (left < right) {
    // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
   
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
   
                left = middle + 1; // target 在右区间,在[middle + 1, right)中
            } else {
    // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

回文数

题目链接:回文数
在这里插入图片描述

bool isPalindrome(int x){
   
    if(x<0|(x%10==0&&x!=0))
    {
   
        return false;
    }
    if(x==0)
    {
   
        return true;
    }
    int temp=0;
    int nums=0;
    while(x>temp)
    {
   
        temp=temp*10+x%10;
        x/=10;
    }
    if(temp/10==x||temp==x
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一个数学不怎么好的程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值