编程题—面试必刷101题
牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网
☆数学
左移与右移/与运算
int i = 1;
i = i << 2; //把i里的值左移2位
//000...0001 左移两位变成000... 0100
//求二进制中1的个数
int hammingWeight(uint32_t n) {
int ans=0;
while(n){
ans+=n&1;
n>>=1;
}
return ans;
}
//巧妙应用n&(n−1)
//(n−1) 解析: 二进制数字 n最右边的 1变成 0,此 1右边的 0都变成 1。
//n&(n - 1)解析: 二进制数字 n最右边的 1变成 0,其余不变
int hammingWeight(uint32_t n) {
int ans=0;
while(n){
n&=(n-1);//消除掉最右边的1
ans++;
}
return ans;
}
二进制
可以转到字符串的第二题
//数组法 将整型用数组储存 然后将数组的值转为整型
int main()
{
int n;
cin>>n;
vector<int>ans;
int ansz;
while(n)
{
ans.push_back(n%2);
n=n/2;
}
for(int j=ans.size()-1; j>=0 ; j--)
{
cout<<ans[j];
ansz+=ans[j]*pow(10,j);
}
cout<<"ansz:"<<ansz;
return 0;
}
//字符串法 将整型用数组储存 将数组的值转为整型太麻烦了 知道这个方法就行
int main()
{
int n;
cin>>n;
string s;
while(n)
{
s+=(n%2==0?'0':'1') ;
n/=2;
}
//反转字符串 两种方法皆可
for(int i=0,j=s.size()-1; i<s.size()/2; i++,j--)
{
swap(s[i],s[j]);
}
//reverse(s.begin(),s.end());
cout<<s<<endl;
return 0;
}
最大公因素与最小公倍数
一个长度为n的数组[n1,n2…nn],求计算出他们的最大公因数
int cal_max_common_factor(int* L, int LLen) {//l是长度
if(LLen==0) return -1;
//求出数组中最小的那个元素
vector<int>v;
for(int i=0;i<LLen;i++){
v.push_back(L[i]);
}
sort(v.begin(),v.end());
int minimum=v[0];
// 以最小的那个元素当做最大公因子
for(int i=minimum;i>0;i--){
bool flag=true;
//L数组中的元素去除,若有余数 则不是最大公因子
for(int j=0;j<LLen;j++){
if(L[j]%i!=0){
flag=false;
break;
}
}
if(flag==true) {return i;}
}
return 0;
}
//辗转除余法
int gys(int a,int b){
if(a==0||b==0) return 0;
while(b!=0){
int k=a%b;
a=b;
b=k;
}
return a;
}
int cal_max_common_factor(int* L, int LLen) {
// write code here
if(LLen==0) return -1;
int k;
k=gys(L[0], L[1]);
for(int i=2;i<LLen;i++){
k=gys(k, L[i]);
}
return k;
}
//最小公倍数 两个数的乘积/最大公约数
int gbs(int a,int b){
int t=gys(a,b);
return a*b/t;}
各位相加为个位数
int main()
{
int X;
cin>>X;
while(X>=10){
int sum=0;
while(X){
sum+=X%10;
X/=10;
}
X=sum;
}
cout <<X<< endl;
return 0;
}
某一个数只经过*2与-1得到另一个数的最小操作数
int solution(int X, int H) {
int count = 0;
while (X != H) {
if (X < H) {
if (H < 2 *X-1) {
X -= 1;
}
else {
X = 2 * X;
}
count++;
}
else if(X>H){
count+=X-H;
X=H;
}
}
return count;
}
int main()
{
int X,H;
cin>>X>>H;
int n=solution( X, H);
cout << n<< endl;
return 0;
}
字符串
左旋转字符串
剑指 Offer 58 - II. 左旋转字符串 - 力扣(LeetCode)
string reverseLeftWords(string s, int n) {
//前n个翻转
//n+1到s.size()翻转
//全部翻转
reverse(s.begin(),s.begin()+n);
reverse(s.begin()+n,s.begin()+s.size());
reverse(s.begin(),s.begin()+s.size());
return s;
}
翻转字符串
剑指 Offer 58 - I. 翻转单词顺序 - 力扣(LeetCode)
string reverseWords(string s) {
string res;
int n = s.size();
if(n == 0) return res;
int right = n - 1;
while(right >= 0){
//从后往前寻找第一字符
while(right >= 0 && s[right] == ' ') right--;
if(right < 0) break;
//从后往前寻找第一个空格
int left = right;
while( left >= 0 && s[left] != ' ' ) left--;
//添加单词到结果
res += s.substr(left + 1, right - left);
res += ' ';
//继续往前分割单词
right = left;
}
//去除最后一个字符空格
if (!res.empty()) res.pop_back();
return res;
}
def reverseWords(self, s: str) -> str:
return " ".join(reversed(s.split()))
void reverseString(vector<char>& s) {
for(int i=0,j=s.size()-1;i<s.size()/2;i++,j--){
swap(s[i],s[j]);
}
//大小写翻转
for(int i=0;i<n;i++){//遍历
if(s[i]>='A'&&s[i]<='Z'){
s[i]+='a'-'A';
}
else if(s[i]>='a'&&s[i]<='z'){
s[i]-='a'-'A';
}
cout<<s[i];
}
判断是否是子序列
给定两个字符串 s和 t ,判断 s是否为 t 的子序列。字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
#include<iostream>
using namespace std;
bool help(string s, string t, int l1, int l2){
if(l1 >= s.length()) return true;
if(l2 >= t.length()) return false;
if(s[l1] == t[l2]) return help(s, t, l1 + 1,l2 + 1);
else return help(s, t, l1,l2 + 1);
}
int main(){
string s,t;
cin >> s >> t;
if(help(s, t, 0, 0)) cout << "true";
else cout << "false";
}
最短转化为a的字符串(蚂蚁笔试)
x个连续字母a组成字符串,两个连续相同的字母可以转换成其下一个相邻字母,问最短的可以转换成x个字母a的字符串
#include <iostream>
#include <algorithm>
#include<bits/stdc++.h>
using namespace std;
char letterMap[26] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
int main()
{
string res="";
int x;
cin>>x;
while(x)
{
int tmp=x;
int cnt=0;
while(tmp)
{
tmp>>=1;
cnt++;
}
x-=pow(2,cnt-1);
res+=letterMap[cnt-1];
}
cout << res << endl;
return 0;
}
//法二 转换为数组再求解
int main()
{
int n;
cin>>n;
vector<int>ans;
string s;
int ansz;
while(n)
{
ans.push_back(n%2);
n=n/2;
}
int j=ans.size()-1;
while(j>=0)
{
if(ans[j]==1) {s+=(j+'a');j--;}
else j--;
}
cout<<s<<endl;
return 0;
}
多个空格转为1个空格(9.17深圳大普笔试)
将字符串中连续的多个空格替换成一个空格
int main()
{
char str[]=" how are you ";
int i=0,j=0;//判断是否首字母为空格
//删除开头为空格的情况
while(str[i]!='\0'){
if(str[i]!=' ')break;
else i++;
}
//处理中间的空格 多个空格变为一个
while(str[i]!='\0'){
if(str[i]!=' ') str[j++]=str[i++];
else{
str[j++]=str[i++];
while(str[i]==' ') i++;
}
}
//处理最后的空格
if(str[j-1]==' ') str[j-1]='\0';
else str[j]='\0';
int k=0;
//将中间多余的空格删除,并将其中的空格变成其他字符
while(str[k]!='\0'){
if(str[k]==' ') str[k]=';';
else k++;
}
cout<<str<<endl;
return 0;
}
转换字符串的最少操作次数
2027. 转换字符串的最少操作次数 - 力扣(LeetCode)
把字符串中的‘X’转为‘O’;一次 操作 定义为从 s 中选出 三个连续字符 并将选中的每个字符都转换为 ‘O’ 。注意,如果字符已经是 ‘O’ ,只需要保持 不变;返回最少的操作次数
int minimumMoves(string s) {
//从头遍历字符数组,如果该字符为'O'直接跳过,否则就对后面三个进行修改
int n=s.size(),i=0,cnt=0;
while(i<n){
if(s[i]=='O') {i++;continue;}
int k=3;
while(k>0&&i<n){
s[i]='O';
i++;
k--;
}
cnt++;
}
return cnt;
}
统计text.txt中单词总数,以及每个单词出现的次数 单词频率
面试题 16.02. 单词频率 - 力扣(LeetCode)
(单词之间以空格分隔 文章区分大小)
unordered_map<string,int> mydic;
WordsFrequency(vector<string>& book) {
for(string i:book)
mydic[i]++;
}
int get(string word) {
return m[word];
}
有效字母异位词
bool isAnagram(string s, string t) {
//t 是 s 的异位词等价于「两个字符串中字符出现的种类和次数均相等」
//定义一个新的数组record,大小为26,因为字符串中有26个小写字母
//遍历s,每出现一个字母在相应的索引下标+1;遍历t,每出现一个字母在相应的索引下标-1;
//如果record所有元素都为0,则返回true
//定义一个新的数组record 初始化为0
int record[26]={0};
//遍历s,每出现一个字母在相应的索引下标+1;
for(int i=0;i<s.length();i++){
record[s[i]-'a']++;
}
//遍历t,每出现一个字母在相应的索引下标-1;
for(int j=0;j<t.length();j++){
record[t[j]-'a']--;
}
//判断record所有元素都为0,则返回true
for(int k=0;k<26;k++){
if(record[k]!=0)
return false;
}
return true;
}
有效的括号
//括号匹配是使用栈解决的经典问题
//不匹配的情况有三:左括号多余、括号类型匹配不上、右括号多余
//技巧:在匹配左括号的时候,右括号先入栈,就只需要比较当前元素和栈顶相不相等就可以了,比左括号先入栈代码实现要简单的多了!
bool isValid(string s) {
stack<int> st;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '(') st.push(')');
else if (s[i] == '{') st.push('}');
else if (s[i] == '[') st.push(']');
// 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false
// 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false
else if (st.empty() || st.top() != s[i]) return false;
else st.pop(); // st.top() 与 s[i]相等,栈弹出元素
}
// 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
return st.empty();
}
动态规划
适合于很多重叠的子问题,动态规划中每一个状态一定是由上一个状态推导出来的(一般求多种可能性有递推性质)
解题步骤
- 确定dp数组(dp table)以及下标的含义:dp[i]的定义为:第i个数的斐波那契数值是dp[i]
- 确定递推公式:如状态转移方程 dp[i] = dp[i - 1] + dp[i - 2];
- dp数组如何初始化:dp[0] = 0;dp[1] = 1;
- 确定遍历顺序:从前往后或从后往前
- 举例推导dp数组
找问题的最好方式就是把dp数组打印出来,看看究竟是不是按照自己思路推导的!
做动规的题目,写代码之前一定要把状态转移在dp数组的上具体情况模拟一遍,心中有数,确定最后推出的是想要的结果。
爬楼梯的最小成本
剑指 Offer II 088. 爬楼梯的最少成本 - 力扣(LeetCode)
//基础
int minCostClimbingStairs(vector<int>& cost) {
//dp[i]的定义:到达第i个台阶所花费的最少体力为dp[i]
//公式dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];
vector<int> dp(cost.size());
dp[0] = cost[0];
dp[1] = cost[1];
for (int i = 2; i < cost.size(); i++) {
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];
}
// 注意最后一步可以理解为不用花费,所以取倒数第一步,第二步的最少值
return min(dp[cost.size() - 1], dp[cost.size() - 2]);
}
//进阶版
int minCostClimbingStairs(vector<int>& cost) {
int n=cost.size();
int dp0=cost[0],dp1=cost[1];
for(int i=2;i<n;i++){
int dpi=min(dp0,dp1)+cost[i];
dp0=dp1;
dp1=dpi;
}
return min(dp0,dp1);
}
//斐波那契数列
//F(0) = 0,F(1) = 1
//F(n) = F(n - 1) + F(n - 2),其中 n > 1
int fib(int n) {
if(n<2) return n;
return fib(n-1)+fib(n-2);
}
//进阶
int fib(int n) {
if(n<2) return n;
int dp0=0,dp1=1;
for(int i=2;i<=n;i++){
int sum=dp0+dp1;
dp0=dp1;
dp1=sum;
}
return dp1;
}
三步问题
面试题 08.01. 三步问题 - 力扣(LeetCode)
int waysToStep(int n) {
if(n<3) return n;
if(n==3) return 4;
int dp1=1,dp2=2,dp3=4;
int MOD=1000000007;
for(int i=4;i<=n;i++){
int dpn=((dp1+dp2)%MOD+dp3)%MOD;//前两个相加可能超限制
dp1=dp2;
dp2=dp3;
dp3=dpn;
}
return dp3;
}
连续子数组的最大和
剑指 Offer 42. 连续子数组的最大和 - 力扣(LeetCode)
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
int maxSubArray(vector<int>& nums) {
int n=nums.size();
int dp=nums[0];
int ma=dp;
for(int i=1;i<n;i++){
dp=max(dp+nums[i],nums[i]);
ma=max(dp,ma);
}
return ma;
}
BM64 最小花费爬楼梯
最小花费爬楼梯_牛客题霸_牛客网 (nowcoder.com)
int minCostClimbingStairs(vector<int>& cost) {
// write code here
int n=cost.size();
int dp0=cost[0],dp1=cost[1];//初始化
for(int i=2;i<n;i++){
int dpi=min(dp0,dp1)+cost[i];//递推序列
dp0=dp1;
dp1=dpi;
}
return min(dp0,dp1);
}
BM65 最长公共子序列(二)/及长度
最长公共子序列(二)_牛客题霸_牛客网 (nowcoder.com)
剑指 Offer II 095. 最长公共子序列 - 力扣(LeetCode)
子序列:不要求位置在原串中连续,例如:“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列
二维动态规划问题
//最长公共子序列 具体值
//方法一 动态规划记下字符串 内存超限制 一组用例不过
string LCS(string s1, string s2) {
// write code here
int m=s1.size(),n=s2.size();
vector<vector<string>>dp(m+1,vector<string>(n+1));//因为要加1行0以及一列0
if(m==0||n==0) return "-1";
for(int i=1;i<=m;i++){
char c1=s1.at(i-1);
for(int j=1;j<=n;j++){
char c2=s2.at(j-1);
if(c1==c2) {
dp[i][j]=dp[i-1][j-1]+s1.at(i-1);//若相等 则加上斜对角的值
}
else {
//dp[i][j]=max(dp[i-1][j],dp[i][j-1]);//不相等,则左边和上边取最大值
dp[i][j]=dp[i-1][j].length()>dp[i][j-1].length()?dp[i-1][j]:dp[i][j-1];
}
}
}
if(dp[m][n]=="") return "-1";
return dp[m][n];
}
//方法二 存长度,再反查得到字符串,ok!
string LCS(string s1, string s2) {
// write code here
int m=s1.size(),n=s2.size();
vector<vector<int>>dp(m+1,vector<int>(n+1));//因为要加1行0以及一列0
if(m==0||n==0) return "-1";
string ans;
//存取最长长度
for(int i=1;i<=m;i++){
char c1=s1.at(i-1);
for(int j=1;j<=n;j++){
char c2=s2.at(j-1);
if(c1==c2) {
dp[i][j]=dp[i-1][j-1]+1;//若相等 则加上斜对角的值
}
else {
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);//不相等,则左边和上边取最大值
}
}
}
//反查得到字符串
for(int i=m,j=n;dp[i][j]>=1;){
if(s1[i-1]==s2[j-1]){
ans+=s1[i-1];
--i;
--j;
}
else if(dp[i-1][j]>=dp[i][j-1]) --i;
else --j;
}
reverse(ans.begin(), ans.end());
return ans.empty()?"-1":ans;
}
//最长公共子序列的长度
int longestCommonSubsequence(string text1, string text2) {
int m=text1.size(),n=text2.size();
vector<vector<int>>dp(m+1,vector<int>(n+1));//因为要加1行0以及一列0
for(int i=1;i<=m;i++){
char c1=text1.at(i-1);
for(int j=1;j<=n;j++){
char c2=text2.at(j-1);
if(c1==c2) dp[i][j]=dp[i-1][j-1]+1;//若相等 则斜对角+1
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);//不相等,则左边和上边取最大值
}
}
return dp[m][n];
}
最长公共子串
最长公共子串_牛客题霸_牛客网 (nowcoder.com)
string LCS(string str1, string str2) {
// write code here
int m=str1.size(),n=str2.size();
vector<vector<int>>dp(m+1,vector<int>(n+1));//因为要加1行0以及一列0
int max=0,pos=0;
if(m==0||n==0) return "-1";
//存取最长长度
for(int i=1;i<=m;i++){
char c1=str1.at(i-1);
for(int j=1;j<=n;j++){
char c2=str2.at(j-1);
if(c1==c2) {
dp[i][j]=dp[i-1][j-1]+1;//若相等 则加上斜对角的值
}
else {
dp[i][j]=0;//不相等,则赋值为0
}
//更新最大长度
if(dp[i][j]>max){
max=dp[i][j];
pos=i-1;
}
}
}
return str1.substr(pos-max+1,max);
}
BM71 最长上升子序列(一)
最长上升子序列(一)_牛客题霸_牛客网 (nowcoder.com)
int LIS(vector<int>& arr) {
// write code here
int n=arr.size();
vector<int>dp(n,1);//初始化为1
int res=0;//最大长度
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
//如果前面的数小于当前arr[i],则以当前数字结尾的最长上升子序列可能更新
if(arr[i]>arr[j]) dp[i]=max(dp[j]+1,dp[i]);
}
}
for(int i=0;i<n;i++){
if(dp[i]>res) res=dp[i];
}
return res;
}
最长回文子串
最长回文子串_牛客题霸_牛客网 (nowcoder.com)
//中心扩散法
int expandAroundCenter(string s, int left, int right)
{
int L = left, R = right;
while (L >= 0 && R < s.length() && s[L] == s[R])
{// 计算以left和right为中心的回文串长度
L--;
R++;
}
return R - L - 1;
}
string longestPalindrome(string s)
{
if (s.length() < 1)
{
return "";
}
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++)
{
int len1 = expandAroundCenter(s, i, i);//一个元素为中心
int len2 = expandAroundCenter(s, i, i + 1);//两个元素为中心
int len = max(len1, len2);
if (len > end - start)
{
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substr(start, end - start + 1);
}
int getLongestPalindrome(string A) {
// write code here
int n=A.size();
string s=longestPalindrome(A);
return s.size();
}
背包问题
01背包、完全背包->(物品个数的不同)->应用->暴力,dp(一维 二维)
(0,i)的物品放进容量为j的背包里
数组
三色球
最小没出现的正整数
给定一个无序的正整数数组,输出数组中没有出现过的最小正整数。比如[1,2,4] 返回 3,时间复杂度O(n)时间。
//排序的时间复杂度是O(nlogn),寻找的时间复杂度是O(n),因此是O(nlogn)
int missingNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
int n = nums.size();
for (int i = 0,j=1; i <n,j<=n; i++,j++) {
if (nums[i] != j) {
return j;
}
}
return n+1;
}
//将数组的元素加入到哈希集合中,依次检查1-n的每个整数是否在哈希集合中
//哈希集合的每次添加元素和查找元素的时间复杂度都是 O(1),因此总时间复杂度是 O(n)。
int missingNumber(vector<int>& nums) {
unordered_set<int> set;
int n = nums.size();
for (int i = 0; i < n; i++) {
set.insert(nums[i]);
}
int missing = -1;
for (int i = 1; i <= n; i++) {
if (!set.count(i)) {
missing = i;
break;
}
}
return missing;
}
合并排序的数组
面试题 10.01. 合并排序的数组 - 力扣(LeetCode)
//直接合并后排序 没有利用两个数组已经排序的优势
void merge(vector<int>& A, int m, vector<int>& B, int n) {
for (int i = 0; i < n; ++i) {
A[m + i] = B[i];
}
sort(A.begin(), A.end());
}
//双指针 将两个数组看作队列,每次从两个数组头部取出比较小的数字放到结果中,slow作呕一部,fast走两步
void merge(vector<int>& A, int m, vector<int>& B, int n) {
int a=0,b=0;//当做队列的头部指针
int sor[m+n],cur;
while(a<m||b<n){
if(a==m) cur=B[b++];
else if(b==n) cur=A[a++];
else if(A[a]<B[b]) cur=A[a++];
else cur=B[b++];
sor[a+b-1]=cur;
}
for(int i=0;i<m+n;i++){
A[i]=sor[i];
}
}
数组相对排序
剑指 Offer II 075. 数组相对排序 - 力扣(LeetCode)
vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
//统计arr1中元素出现的次数
int upper = *max_element(arr1.begin(), arr1.end());
vector<int> frequency(upper + 1);
for (int x: arr1) {
++frequency[x];
}
vector<int> ans;
//arr2按照插入
for (int x: arr2) {
for (int i = 0; i < frequency[x]; ++i) {
ans.push_back(x);
}
frequency[x] = 0;
}
//此时还剩下没在arr2中出现的元素
for (int x = 0; x <= upper; ++x) {
for (int i = 0; i < frequency[x]; ++i) {
ans.push_back(x);
}
}
return ans;
}
两数之和
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map <int,int> map;
for(int i = 0; i < nums.size(); i++) {
auto iter = map.find(target - nums[i]);//自动类型判断
if(iter != map.end()) {
return {iter->second, i};//iter.first会得到key,iter.second会得到value。 // 如果差值已经存在过,直接返回对应的索引
}
map.insert(pair<int, int>(nums[i], i));//在map中插入找到的元素 // 否则存起来
}
return {};
}
};
查找插入的位置
剑指 Offer II 068. 查找插入位置 - 力扣(LeetCode)
int searchInsert(vector<int>& nums, int target) {
//成立的条件nums[pos−1]<target≤nums[pos]
//即在一个有序数组中找第一个大于等于 target 的下标
int l=0,h=nums.size()-1,ans=nums.size();//ans 初值设置为数组长度可以省略边界条件的判断
while(l<=h){
int m=l+(h-l)/2;
if(nums[m]>=target) {
ans=m;
h=m-1;
}
else l=m+1;
}
return ans;
}
字符串中的变位词
剑指 Offer II 014. 字符串中的变位词 - 力扣(LeetCode)
//方法一:滑动窗口
bool checkInclusion(string s1, string s2) {
//变位词即字符个数相等即可
//记 s1 的长度为 n,我们可以遍历 s2中的每个长度为 n的子串,判断子串和 s1 中每个字符的个数是否相等
int n = s1.length(), m = s2.length();
if (n > m) return false;
vector<int>cnt1(26,0),cnt2(26,0);
//先记录前两个字符
for(int i=0;i<n;i++){
cnt1[s1[i]-'a']++;
cnt2[s2[i]-'a']++;
}
if(cnt1==cnt2) return true;
//开始接下来的字符 cnt2开始往右滑动 右边+,左边- 始终保持滑动窗口为2
for(int j=n;j<m;j++){
cnt2[s2[j]-'a']++;
cnt2[s2[j-n]-'a']--;
if(cnt1==cnt2) return true;
}
return false;
}
最小操作次数(美团9.17笔试)
最少操作数使数组递增
输入n和数组的值,最少需要多少次操作可以把数组变成顺子。
比如3。3 7 6 输出2;即经过两次操作可以变成5 6 7
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
vector<int> vec;
int ans=0;
for(int i=0;i<n;i++){
int ii;
cin>>ii;
vec.push_back(ii);
}
sort(vec.begin(),vec.end());
int m1=vec.size()/2;
int m=vec[m1];
int mi=m-m1;//顺子中最小的值;
vector<int> vec1;//存去重后的数组
for(int i=0;i<n;i++){
ans+=abs(vec[i]-mi);
mi++;
}
cout<<ans<<endl;
return 0;
}
呜呜呜 我在力扣找到了类似的 1827. 最少操作使数组递增 - 力扣(LeetCode)
数组原有的位置顺序不能变
int minOperations(vector<int>& nums) {
int ans=0,temp;
int n=nums.size();
if(n==1) return 0;
for(int i=1;i<n;i++){
if(nums[i]<=nums[i-1]){
temp=nums[i-1]-nums[i]+1;
nums[i]+=temp;
ans+=temp;
}
}
return ans;
}
最小操作次数使得数组元素相等
453. 最小操作次数使数组元素相等 - 力扣(LeetCode)
每次操作都会使得n-1个元素增加1
int minMoves(vector<int>& nums) {
//只需要考虑数组中元素相对大小的变化
//使 n−1 个元素增加 1,也可以理解使 1个元素减少 1
//计算使得所有元素减少到最小值所需的次数
int n=nums.size(),ans=0;
//求最小值的方法
//方法一
// sort(nums.begin(),nums.end());
// int mi=nums[0];
//方法二
//int mi=INT_MAX;
// for(int i=0;i<n;i++) mi=min(mi,nums[i]);
//方法三
int mi=*min_element(nums.begin(),nums.end());
for(int i=0;i<n;i++) ans+=nums[i]-mi;
return ans;
}
数组内乘积小于k的数(百度笔试)-滑动窗口
剑指 Offer II 009. 乘积小于 K 的子数组 - 力扣(LeetCode)
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
int l=0,r=0,n=nums.size(),ans=0;
int cur=1;
while(l<=r&&r<n){
cur*=nums[r];
while(l<=r&&cur>=k){
cur/=nums[l];
l++;
}
ans+=r-l+1;
r++;
}
return ans;
}
链表
链表反转
ListNode* reverseList(ListNode* head) {
//双指针法 从前往后翻转指向
//cur指针,指向头结点,再定义一个pre指针,初始化为null
//while循环中翻转&cur和pre指针更新
ListNode*tmp;//暂存
ListNode*cur=head;//头指针赋值给cur
ListNode*pre=NULL;
while(cur){//当目前的指针不为空 翻转操作
tmp=cur->next;
cur->next=pre;
//更新
pre=cur;
cur=tmp;
}
return pre;
}
判断链表的环并找出环节点BM6 +BM7
BM6 判断链表中是否有环
判断链表中是否有环_牛客题霸_牛客网 (nowcoder.com)
step 1:设置快慢两个指针,初始都指向链表头。
step 2:遍历链表,快指针每次走两步,慢指针每次走一步。
step 3:如果快指针到了链表末尾,说明没有环,因为它每次走两步,所以要验证连续两步是否为NULL。
step 4:如果链表有环,那快慢双指针会在环内循环,因为快指针每次走两步,因此快指针会在环内追到慢指针,二者相遇就代表有环。
bool hasCycle(ListNode *head) {
if(head==NULL) return false;//判断链表为空的情况
//定义快慢指针
ListNode*f=head;
ListNode*s=head;
//若没有环快指针会先走到链表尾部;
while(f!=NULL && f->next !=NULL){
f=f->next->next;//快指针走两步;
s=s->next;//慢指针走一步
if(f==s)return true;//相遇有环
}
return false;//到末尾没有环
}
BM7 链表中环的入口结点
链表中环的入口结点_牛客题霸_牛客网 (nowcoder.com)
剑指 Offer II 022. 链表中环的入口节点 - 力扣(LeetCode)
//方法一 哈希表
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode *> visited;
while (head != nullptr) {
//对特定元素进行计数,若重复则返回1,否则返回0
if (visited.count(head)) return head;
visited.insert(head);
head = head->next;
}
return nullptr;
}
//方法二 快慢指针
ListNode *detectCycle(ListNode *head) {
if(head==NULL) return NULL;//判断链表为空的情况
//定义快慢指针
ListNode*f=head; ListNode*s=head;
//若没有环快指针会先走到链表尾部;
while(f!=NULL && f->next !=NULL){
f=f->next->next;//快指针走两步;
s=s->next;//慢指针走一步
if(f==s){
//ptr和slow相遇的位置就是入环点,slow是顺时针走,ptr是从表头出发
ListNode*ptr=head;
while(ptr!=s){
ptr=ptr->next;
s=s->next;
}
return ptr;
}
}
return NULL;//到末尾没有环
}
两个链表是否相交
遍历链表headA,并将链表 headA 中的每个节点加入哈希集合中。然后遍历链表 headB,对于遍历到的每个节点,判断该节点是否在哈希集合中:
如果当前节点不在哈希集合中,则继续遍历下一个节点;
如果当前节点在哈希集合中,则后面的节点都在哈希集合中,即从当前节点开始的所有节点都在两个链表的相交部分,因此在链表headB 中遍历到的第一个在哈希集合中的节点就是两个链表相交的节点,返回该节点。
如果链表headB 中的所有节点都不在哈希集合中,则两个链表不相交,返回null。
https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
//法一:哈希表
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode*>visited;
while(headA!=nullptr){
visited.insert(headA);
headA=headA->next;
}
while(headB!=nullptr){
if(visited.count(headB)) return headB;
headB=headB->next;
}
return nullptr;
}
//法二:双指针
设「第一个公共节点」为 node ,「链表 headA」的节点数量为 a ,「链表 headB」的节点数量为 bb,
「两链表的公共尾部」的节点数量为 c
构建两个节点指针 A , B 分别指向两链表头节点 headA , headB
此时指针 A , B 重合,并有两种情况:a+(b−c)=b+(a−c)
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *A = headA, *B = headB;
while (A != B) {
A = A != nullptr ? A->next : headB;
B = B != nullptr ? B->next : headA;
}
return A;
}
栈
最小栈
class MinStack {
private:
stack<int>st;
stack<int>st_min;
public:
MinStack() {
st_min.push(INT_MAX);//必须初始化
}
void push(int val) {
st.push(val);
if(st_min.top()>=val) st_min.push(val);
}
void pop() {
if(st.top()==st_min.top()) st_min.pop();
st.pop();
}
int top() {
return st.top();
}
int getMin() {
return st_min.top();
}
};
模拟:边界与实际
BM97 旋转数组
类似于三次翻转,左右两边各翻转一次,整体再翻转一次。需要考虑m>n的情况
vector<int> solve(int n, int m, vector<int>& a) {
// write code here
m=m%n;
reverse(a.begin(), a.end());
reverse(a.begin(), a.begin()+m);
reverse(a.begin()+m, a.end());
return a;
}
旋转数组中的最小数字
即寻找右排序的首个元素,即寻找旋转点,前面的元素小于后一个,那么后一个即为旋转点。
剑指 Offer 11. 旋转数组的最小数字 - 力扣(LeetCode)
int minArray(vector<int>& numbers) {
for(int i=1;i<numbers.size();i++){
if(numbers[i]<numbers[i-1]) return numbers[i];
}
return numbers[0];
}
BM98 螺旋矩阵
给你一个正整数 n
,生成一个包含 1
到 n x n
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
if(n==0) return {};//为空则返回空
// 创建大小固定的二维矩阵
vector<vector<int>> matrix(n,vector<int>(n,0));
int l=0,r=n-1,t=0,b=n-1;//上 右 下 左
int cnt=1;//初始化
while(cnt<=n*n){
//向右
for(int i=l;i<=r;i++) matrix[t][i]=cnt++;
if(++t>b) break;
//向下
for(int i=t;i<=b;i++) matrix[i][r]=cnt++;
if(--r<l) break;
//向左
for(int i=r;i>=l;i--) matrix[b][i]=cnt++;
if(--b<t) break;
//向上
for(int i=b;i>=t;i--) matrix[i][l]=cnt++;
if(++l>r) break;
}
return matrix;
}
};
顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字
https://leetcode-cn.com/problems/spiral-matrix/
思路没问题,注意边界值,以及i值的书写与加加减减
class Solution {
public:
vector<int> spiralOrder(vector<vector<int> > &matrix) {
if(matrix.empty()) return {};//为空则返回空
int l=0,r=matrix[0].size()-1,t=0,b=matrix.size()-1;//上 右 下 左
vector<int> ans;//记录答案;
while(true){
//向右
for(int i=l;i<=r;i++) ans.push_back(matrix[t][i]);
if(++t>b) break;
//向下
for(int i=t;i<=b;i++) ans.push_back(matrix[i][r]);
if(--r<l) break;
//向左
for(int i=r;i>=l;i--) ans.push_back(matrix[b][i]);
if(--b<t) break;
//向上
for(int i=b;i>=t;i--) ans.push_back(matrix[i][l]);
if(++l>r) break;
}
return ans;
}
};
BM98 顺时针旋转矩阵
有一个nxn整数矩阵,请编写一个算法,将矩阵顺时针旋转90度,并输出旋转后的矩阵。
顺时针旋转矩阵_牛客题霸_牛客网 (nowcoder.com)
//按列倒序存取
vector<vector<int> > rotateMatrix(vector<vector<int> > mat, int n) {
vector<vector<int> >ans;
vector<int>ans1;
for(int i=0;i<n;i++){
for(int j=n-1;j>=0;j--){
ans1.push_back(mat[j][i]);
}
ans.push_back(ans1);
ans1.clear();
}
return ans;
}
//对角线交换 再将元素内部翻转
vector<vector<int> > rotateMatrix(vector<vector<int> > mat, int n) {
// write code here
for (int i=0;i<n;++i){
for (int j=0;j<i;++j){
swap(mat[i][j],mat[j][i]);//先交换
}
}
for (int i=0;i<n;++i)
reverse(mat[i].begin(),mat[i].end());//再将内部元素翻转
return mat;
}
扑克牌中的顺子
从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
链接:https://leetcode.cn/problems/bu-ke-pai-zhong-de-shun-zi-lcof
思路:要保证5张牌是顺子,1.除大小王外,其他无重复;2.除大小王外,最大为max,最小为min,max-min<5.
1.遍历,遇到大小王就跳过;2.判重,set的时间复杂度为O(1);3,获取最大最小牌
class Solution {
public:
bool IsContinuous( vector<int> numbers ) {
set<int>repeat;//去重集合
int max=0,min=14;//最大值最小值
for(int i:numbers){//遍历找最大值、最小值 去重
if(i==0) continue;//跳过大小王
max=max>i?max:i;
min=min<i?min:i;
if(repeat.find(i)!=repeat.end()) return false;//若有重复,提前返回false
repeat.insert(i);//没有重复,添加到set中
}
return max-min<5;
}
};
蜗牛往上爬
一口井深len米,蜗牛白天爬m米,晚上滑下去n米,几天到井口?
int daycost(int len, int m, int n) {
if(m<=n&&len>m) return -1;//注意处理好边界问题,
//if(len<=m||len==0) return 1;
int day=0;
while(len>0){
day+=1;
len-=m;
if(len<=0) return day;
len+=n;
}
return 0;
}