目录
一星题
快速排序
题目链接:快速排序
其实这道题在停更之前的八大排序中已经涉及过了,下面来看看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