c++实现的一些基础算法题
前言
主要用于记录写到的一些基础算法题
题目来源说明
排序算法
目前记录了两种排序算法
题目描述 对n个数据进行快速排序。 输入格式 包含多组测试数据。第一行输入一个T,代表T组数据。
每组数据第一行为一个整数n,接下来为n个整数。
输出格式 输出排序后的数组。 输入样例
1
5
4 3 7 1 5
快速排序
代码实现
#include<bits/stdc++.h>
using namespace std;
int n;
void getData(int arr[]){
for(int i=0;i<n;i++)
cin>>arr[i];
}
void quickSort(int arr[],int start,int end){
if(start>=end)return;
int i=start,j=end;
int temp = arr[start];
while(i < j){
while(i<j&&arr[j]>temp)j--;
arr[i]=arr[j];
while(i<j&&arr[i]<=temp)i++;
arr[j]=arr[i];
}
arr[i]=temp;
quickSort(arr,start,i-1);
quickSort(arr,i+1,end);
}
void viewData(int arr[]){
for(int i=0;i<n;i++)
cout<<arr[i]<<" ";
cout<<endl;
}
int main(){
int m;
cin>>m;
while(m--){
cin>>n;
int arr[n+1];
memset(arr,0,sizeof(arr));
getData(arr);
quickSort(arr,0,n-1);
viewData(arr);
}
}
归并排序
代码实现
#include<bits/stdc++.h>
using namespace std;
int n;
int Counter;
int data[100008];
void getData(){
memset(data,0,sizeof(data));
for(int i=0;i<n;i++)
cin>>data[i];
}
void viewArr(){
for(int i=0;i<n;i++)
cout<<data[i]<<" ";
cout<<endl;
}
void mergeSort(int left, int right){
if(left>=right)return;
int mid = (left + right) / 2;
int lfLen = mid - left + 1;
int rLen = right - mid;
int leftIndex=0;
int rightIndex=0;
int leArr[lfLen + 1],riArr[rLen + 1];
for(int i=left;i<=mid;i++)
leArr[i-left] = data[i];
for(int i=mid+1;i<=right;i++)
riArr[i-mid-1] = data[i];
for(int i=left;i<=right;i++){
if(leftIndex!=lfLen&& rightIndex!=rLen){
if(leArr[leftIndex] <= riArr[rightIndex]){
data[i] = leArr[leftIndex++];
} else {
data[i] = riArr[rightIndex++];
Counter += lfLen - leftIndex;
}
} else if(leftIndex!=lfLen){
data[i] = leArr[leftIndex++];
} else {
data[i] = riArr[rightIndex++];
}
}
}
void merge(int left, int right){
if(left < right){
merge(left, (left + right) / 2);
merge((left + right) / 2 + 1 , right);
mergeSort(left,right);
}
}
int main(){
while(cin>>n){
getData();
merge(0,n-1);
viewArr();
}
}
动态规划
最长上升子序列
题目描述
设有由n(1≤n≤200)个不相同的整数组成的数列,记为:b(1)、b(2)、……、b(n)且b(i)≠b(j)(i≠j),若存在i1<i2<i3<…<ie 且有b(i1)<b(i2)<…<b(ie)则称为长度为e的上升序列。程序要求,当原数列出之后,求出最长的上升序列。 例如13,7,9,16,38,24,37,18,44,19,21,22,63,15。例中13,16,18,19,21,22,63就是一个长度为7的不下降序列,同时也有7 ,9,16,18,19,21,22,63组成的长度为8的上升序列。
输入格式
第一行为n,第二行为用空格隔开的n个整数。
输入样例
14
13 7 9 16 38 24 37 18 44 19 21 22 63 15
输出样例
max=8
代码实现
#include<bits/stdc++.h>
using namespace std;
int n,maxCount=1;
int data[100004];
void getData(){
cin>>n;
memset(data,0,sizeof(data));
for(int i=0;i<n;i++)
cin>>data[i];
}
int main(){
getData();
int dp[n+1];
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++){
dp[i] = 1;
for(int j=0;j<i;j++){
if(data[i] > data[j]){
dp[i] = max(dp[i], dp[j] + 1);
}
}
if(maxCount< dp[i]){
maxCount = dp[i];
}
}
cout<<"max="<<maxCount<<endl;
}
最长公共子序列
题目描述
一个给定序列的子序列是在该序列中删去若干元素后得到的序列。 例如,序列Z=<B,C,D,B>是序列X=<A,B,C,B,D,A,B>的子序列,相应的递增下标序列为<2,3,5,7>。给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。例如,若X=<A,B,C,B,D,A,B>和Y=<B,D,C,A,B,A>,则序列<B,C,A>是X和Y的一个公共子序列,序列 <B,C,B,A>也是X和Y的一个公共子序列。而且,后者是X和Y的一个最长公共子序列.因为X和Y没有长度大于4的公共子序列。 给定两个序列X=<x1,x2,…,xm>X=<x1,x2,…,xm>和Y=<y1,y2….yn>Y=<y1,y2….yn>.要求找出X和Y的一个最长公共子序列。
** 输入格式**
共有两行。每行为一个由大写字母构成的长度不超过1000的字符串,表示序列X和Y。
输出格式
第一行为一个非负整数。表示所求得的最长公共子序列的长度。若不存在公共子序列.则输出文件仅有一行输出一个整数0。
输入样例
ABCBDAB
BDCABA
输出样例
4
代码实现
#include<bits/stdc++.h>
using namespace std;
char a[1001], b[1001];
int dp[1001][1001], len1, len2;
void lcs(int i,int j){
for(i=1; i<=len1; i++){
for(j=1; j<=len2; j++){
if(a[i-1] == b[j-1])
dp[i][j] = dp[i-1][j-1] + 1;
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
int main(){
while(~scanf(" %s",a)){
scanf(" %s", b);
memset(dp, 0, sizeof(dp));
len1 = strlen(a);
len2 = strlen(b);
lcs(len1, len2);
printf("%d\n", dp[len1][len2]);
}
return 0;
}
采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 如果你是辰辰,你能完成这个任务吗?
输入格式
每组输入数据的第一行有两个整数T(1<=T<=1000)和M(1<=M<=100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。 数据规模: 对于30%的数据,M<=10; 对于全部的数据,M<=100。
输出格式
每组输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
输入样例
70 3
71 100
69 1
1 2
输出样例
3
代码实现
#include<bits/stdc++.h>
using namespace std;
struct yaowu{
int time,val;
}data[10000001];
int timeMax,n;
void getData(){
for(int i=0;i<n;i++){
cin>>data[i].time>>data[i].val;
}
}
int main(){
cin>>timeMax>>n;
getData();
int dp[timeMax + 1];
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
for(int j=timeMax;j>0;j--){
if(data[i].time <= j){
dp[j] = max(dp[j], dp[j - data[i].time] + data[i].val);
}
}
cout<<dp[timeMax]<<endl;
}
思路
将问题按照物品数划分为 n 个阶段,通过dp 这个数组可以记录在每个对应的背包容量在不同的阶段所能装载的最大价值。
跳跃游戏 II
题目来源 跳跃游戏 II
题目描述
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 假设你总是可以到达数组的最后一个位置。
输入示例
nums = [2,3,1,1,4]
输出示例
2
代码实现
class Solution {
public:
int jump(vector<int>& nums) {
int dataLen = nums.size();
int dp[dataLen + 1];
memset(dp,0,sizeof(dp));
for(int i=0;i<dataLen;i++){
for(int j=i+1;j<dataLen;j++){
if(nums[i] + i < j)
break;
if(dp[j]!=0)
dp[j] = min(dp[j], dp[i] + 1);
else
dp[j] = dp[i] + 1;
}
}
return dp[dataLen-1];
}
};
思路
从前到后的递推,通过dp 数组记录到达每个位置需要的最小步数。
贪心算法
看电视-会议安排
** 题目描述**
暑假到了,小明终于可以开心的看电视了。但是小明喜欢的节目太多了,他希望尽量多的看到完整的节目。 现在他把他喜欢的电视节目的转播时间表给你,你能帮他合理安排吗?
输入格式
输入包含多组测试数据。每组输入的第一行是一个整数n(n<=100),表示小明喜欢的节目的总数。 接下来n行,每行输入两个整数si和ei(1<=i<=n),表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。 当n=0时,输入结束。
输出格式
对于每组输入,输出能完整看到的电视节目的个数。
** 输入样例**
12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
0
输出样例
5
代码实现
#include<bits/stdc++.h>
using namespace std;
int n;
int Mycount=0;
struct tv{
int start,end;
}data[1001];
void getData(){
int start,end;
tv temp;
for(int i=0;i<n;i++){
cin>>start>>end;
temp.start = start;
temp.end = end;
data[i] = temp;
}
}
bool cmp(tv a,tv b){
return a.end < b.end;
}
void makeTask(){
int nowStart=0;
for(int i=0;i<n;i++){
if(data[i].start >= nowStart){
nowStart = data[i].end;
Mycount++;
}
}
}
int main(){
cin>>n;
while(n!=0){
Mycount=0;
getData();
sort(data,data+n,cmp);
makeTask();
cin>>n;
cout<<Mycount<<endl;
}
}
跳跃游戏
题目来源 跳跃游戏
题目描述
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标。
输入样例
nums = [2,3,1,1,4]
输出样例
true
代码实现
class Solution {
public:
bool canJump(vector<int>& nums) {
int dataLen = nums.size();
int counter = 0;
for(int i=0;i<dataLen;i++){
if(i <= counter){
counter = max(counter, i + nums[i]);
}
if(counter>=dataLen-1)
return true;
}
return false;
}
};
思路
从起点开始跳,然后每次记录能跳跃的最远距离,然后从能跳到的范围内继续找能跳到的最远距离,最后可以得到全局的跳跃的最远距离。
排列组合问题
无重复全排列
题目描述
输出N个数的无重复全排列
输入格式
输入一个数值N 1<=N=50
输出格式
输出N个数的无重复全排列,每个数之间用空格隔开 最后一行输出无重复全排列的个数。
输入样例
3
输出样例
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
Total=6
代码实现
方案一: 用递归,并采用map进行判重
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll counter=0;
int n;
int data[1002],tempData[1002];
map<int,bool> flags;
void getData(){
for(int i=0;i<n;i++){
flags[i] = false;
data[i] = i;
}
memset(tempData,0,sizeof(tempData));
}
void viewData(int arr[],int len){
for(int i=0;i<n;i++)
cout<<arr[i] + 1<<" ";
cout<<endl;
}
void pac(int start){
if(start == n){
viewData(tempData,n);
counter++;
return ;
}
for(int i=0;i<n;i++){
if(!flags[i]){
flags[i] = true;
tempData[start] = data[i];
pac(start+1);
flags[i] = false;
}
}
}
int main(){
cin>>n;
getData();
pac(0);
cout<<"Total="<<counter;
return 0;
}
方案二; 采用数组中的数据交换,但无法直接满足无重复
只能实现全排列,但不满足无重复
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll counter=0;
int n;
int data[1002];
void getData(){
for(int i=0;i<n;i++)
data[i] = i;
}
void viewData(int arr[],int len){
for(int i=0;i<n;i++)
cout<<arr[i] + 1<<" ";
cout<<endl;
}
void camp(int *a,int *b){
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
void pac(int start){
if(start == n){
viewData(data,n);
counter++;
return ;
}
for(int i=start;i<n;i++){
camp(&data[start], &data[i]);
pac(start+1);
camp(&data[start], &data[i]);
}
}
int main(){
cin>>n;
getData();
pac(0);
cout<<"Total="<<counter;
return 0;
}
组合数
题目描述
输出N个数中M个数的组合
输入格式
输入一个数值N,M 1<=N=50, 1<= M <= N
输出格式
输出N个数中M个数的组合,每个数之间用空格隔开 最后一行输出无重复全排列的个数。
输入样例
5 2
输出样例
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
Totl=10
代码实现
基于上面的全排列的方法一进行改进
#include<bits/stdc++.h>
using namespace std;
int n,m,counter=0;
int data[10001],temp[10001];
map<int,bool> states;
void getData(){
cin>>n>>m;
memset(temp,0,sizeof(temp));
for(int i=0;i<n;i++){
data[i] = i;
states[i] = false;
}
}
void viewData(int arr[], int len){
for(int i=0;i<len;i++){
cout<<arr[i] + 1<<" ";
}
cout<<endl;
}
void getCC(int start,int step){
if(step == m){
viewData(temp, m);
counter++;
return;
}
for(int i=start;i<n;i++){
if(!states[i]){
temp[step] = data[i];
states[i] = true;
getCC(i+1, step+1);
states[i] = false;
}
}
}
int main(){
getData();
getCC(0, 0);
cout<<"Totl="<<counter<<endl;
}
双指针
两数之和
题目来源 LeetCode两数之和
题目描述
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。
输入样例
nums = [2,7,11,15], target = 9
输出样例
[0,1]
代码实现
struct nums2index{
int index,num;
}Mydata[10005];
bool camp(nums2index a, nums2index b ){
return a.num < b.num;
}
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> res;
int len = nums.size();
int left = 0;
int right = len - 1;
for(int i=0;i<len;i++){
Mydata[i].index = i;
Mydata[i].num = nums[i];
}
sort(Mydata, Mydata + len, camp);
for(;;){
if(left == right){
break;
}
if(Mydata[left].num + Mydata[right].num == target){
res.push_back(Mydata[left].index);
res.push_back(Mydata[right].index);
break;
} else if (Mydata[left].num + Mydata[right].num < target){
left ++;
} else
right--;
}
return res;
}
};
思路
先对原始数据进行排序,采用两个指针分别指向 left 和right ,根据大小比比较决定向中间移动左指针还是右指针
盛最多水的容器
题目来源LeetCode盛最多水的容器
题目描述
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明:你不能倾斜容器。
输入样例
[1,8,6,2,5,4,8,3,7]
输出样例
49
代码实现
class Solution {
public:
int maxArea(vector<int>& height) {
int resVec = 0;
int left = 0;
int right = height.size() - 1;
while(left != right){
if(height[left] > height[right]){
resVec = max(resVec, (right - left) * height[right]);
right--;
} else {
resVec = max(resVec, (right - left) * height[left]);
left++;
}
}
return resVec;
}
};
思路
此题采用了贪心的思想与双指针策略结合,与上一题的不同是此题无法先进行排序,因此在决定移动双指针的时候优先移动较小的那个指针从而保证最大的容量一定会被我们遍历到。
总结
总结就是多提升思维,起码要了解基础的算法
|