C++ 算法题解 (1)


1. n个人围成圈,依次报数,每次数到m的人淘汰,求最后剩下的人

双向链表实现:

	#include <cstdio>
	#include <iostream>
	using namespace std;
	const int MAX = 100 + 10;
	int a[MAX],b[MAX];
	int main()
	{
	    int m,n;
	    cin >> n >> m;
	    for (int i = 1; i <= n-1; i++) a[i]=i+1,b[i]=i-1;
	    a[n]=1,b[1]=n;
	    int c=1;
	    for (int i = 1; i <= n-1; i++)
	    {
	        for(int t=1;t<=m-1;t++) c=a[c];
	        //c被淘汰
	        printf("第%d个淘汰:%d\n", i , c );
	        b[a[c]]=b[c];  //c下一个人的上一个(原本是c)指向c的上一个
	        a[b[c]]=a[c];  //c上一个人的下一个(原本是c)指向c的下一个
	        c=a[c];        //从c的下一个人开始
	    }
	    cout<<"最后剩下了:"<<c<<endl;
	    cin>>n;   //只是为了暂停
	    return 0;
	}

2. 整数反转

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

解题思路
一开始我还无耻地试图用try catch解决溢出问题:只要error直接return 0
结果发现leetcode的编译器不吃这套,照样runtime error
那么只好中规中矩地写了,检验溢出的方法就是先定义一个MAX_INT/10的值,反转后每次加新的值上去之前先除10看看会不会溢出(如果不除10直接加上去就error了)
先分离符号位,避免不要的麻烦,最后再加上就是了。
跑个while循环计算一下有几位。
如果是个个位数就直接返回,省的跑一遍
还有一个要注意,如果是-231次方,去掉符号位的时候会直接溢出
然后循环就一位一位地剥离,%10(取最后一位数字), /=10(去掉最后一位数字),乘以相应的位数,加到ans里(加之前先把二者除10加一下看看会不会超过maxn)

代码

#include <cmath>
class Solution {
public:
    const int maxn=214748365;  //max_int / 10 ,构建的数除以10必须小于这个数,否则溢出
    const int pow[11]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
    int reverse(int x) {
        if(abs(x)<10) return x;
        if(x==-2147483648) return 0;
        int ans = 0,n = 0,t = 0, flag = x/abs(x);
        x = abs(x);
        int y = x;
        while(y)  //数x的位数
        {
            y/=10;
            n++;
        }
        for(int i=n;i>=1;i--)
        {
            t = x % 10;
            if(t*pow[i-1] + ans/10 >= maxn) return 0;
            ans += t*pow[i];
            x/=10;
        }
        return ans*flag;
    }
};

3.回文数

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

  • 转字符串解法:
    在这里插入图片描述
#include <string>
using namespace std;
class Solution {
public:
    bool isPalindrome(int x) {
        if(x<0) return false;
        if(x>=0 && x<10) return true;
        string s = to_string(x);
        for(int i=0;i<=s.length()/2;i++)
            if(s[i]!=s[s.length-1-i]) return false;
        return true;
    }
};
  • 纯数字解
    在这里插入图片描述

class Solution {
public:
    const int pow[11]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
    bool isPalindrome(int x) {
        if(x<0) return false;
        if(x>=0 && x<10) return true;
        int y=x,n=0;
        while(y){   //数y的位数
            y/=10;
            n++;
        }
        for(int i=1;i<=n;i++)
            if( (x/pow[i]) % 10 != (x/pow[n-i+1])%10) return false;
        return true;
    }
};

4. 两数相加

给出两个非空的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储一位数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

在这里插入图片描述

// Definition for singly-linked list.
#include <string>
using namespace std;
  struct ListNode {
      int val;
      ListNode *next;
      ListNode(int x) : val(x), next(NULL) {}
  };

//ac
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
    {
        string s1,s2,a,b;  //a > b
        
        while(l1 != NULL) 
        {
            s1.append(to_string(l1->val));
            l1=l1->next;
        }
        while(l2 != NULL) 
        {
            s2.append(to_string(l2->val));
            l2=l2->next;
        }
        if(s1.length()>s2.length())  //保证a的长度比b长
        {   
            a=s1;
            b=s2;
        }
        else{
             a=s2;
             b=s1;
        }
        while(a.length()>b.length())   //给b后面补0补到和a一样的位置
            b.append("0");
        
        ListNode *ans=new ListNode(0);
        ListNode *t = ans;
        int tmp,add=0;
        for(int i=0;i<a.length();i++)
        {
            tmp=a[i]-'0' + b[i]-'0' + add;
            add=tmp/10;    	//add保存是否进位
            tmp %= 10;
            t->val=tmp;
            if(i<a.length()-1)
            {
                t->next = new ListNode(0);
                t=t->next;
            }
        }
        if(add) t->next=new ListNode(add);
        return ans;
        
    }
};


5. 无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

滑动窗口法:
对于字符串s , 我们维护一个长度为width = r - l +1的子字符串,称之为滑动窗口,初始l=0,r=0,width=1,初始窗口是s[0]。每次从右边拉一个字符进来,如果此时窗口中有重复字符,那么从左端去掉一个字符: l++,如果还是重复,那么一直去到无重复字符为止,以上述规则跑完全程,始终保持窗口内无重复,其间每次操作检测窗口长度维护一个最大值。即可以O(n)的复杂度完成

在这里插入图片描述

#include <string>
using namespace std;
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s=="") return 0;
        int width;
        int ans = 0;
        int l=0,r=0;
        string w;
        for( ; r < s.length() ; r++)
        {   
            width = r - l + 1;
            if(width>ans) ans=width;
            w = s.substr(l,width);
            for(int i=0;i<w.length();i++)
                if(r+1<s.length() && w[i]==s[r+1])  
                {
                    while(s[l]!=s[r+1]) l++;  //找到与s[r+1](即将移入左端的字符)重复的字符位置
                    l++;                      //左端移除重复的字符
                    break;
                }
        }
        return ans;
        
    }
};


6. 输入若干个字符串,统计其中每个字符串的出现次数
  • 解题思路
    我们使用priority_queue来维护一个字典序排序的string堆,就可以在线性时间内完成全部的统计工作
#include <iostream>
#include <queue>
using namespace std;
int main()
{
    priority_queue<string> heap;
    string s;
    while (true)
    {
        cin >> s;
        if (s == "0") break;
        heap.push(s);
    }
    string last=heap.top();
    int count = 1;
    heap.pop();
    while (!heap.empty())
    {
        if (heap.top() == last) count++;
        else 
        {
            cout << last << ":" << count << endl;
            count = 1;
            last = heap.top();
        }
        heap.pop();
    }
    cout << last << ":" << count << endl;
}


  1. 取两个递增链表的交集
#include <stdlib.h>
#include <stdio.h>

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(NULL) {}
    void push_back(int data)    //在尾部添加元素
    {
        ListNode* p = this;
        while (p->next != NULL) p = p->next;
        p->next = new ListNode(data);
    }

};
int main()
{
    ListNode* A = new ListNode(1);
    ListNode* B = new ListNode(2);
    ListNode* pA = A;
    ListNode* pB = B;
    ListNode* ans=new ListNode(0); int flag = 0;
    while (pA != NULL && pB != NULL)
    {
        while (pA != NULL && pA->val < pB->val) pA = pA->next;
        if (pA->val == pB->val)
        {
            if (!flag) { ans->val=pA->val; flag = 1; }
            else ans->push_back(pA->val);
            pA = pA->next;
        }
        pB = pB->next;
    }
    return;
}

8. 面试题13. 机器人的运动范围

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0]
的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于 k 的格子。例如,当 k 为18时,机器人能够进入方格
[35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

输入:m = 2, n = 3, k = 1 输出:3

输入:m = 3, n = 1, k = 0 输出:1
提示: 1 <= n,m <= 100 0 <= k <= 20

直接遍历每个格子,位数之和不能大于k那也就是
条件1: i % 10 + i / 10 + j % 10 + j / 10 <= k
注意机器人只能四个方向移动,那么第i,j个格子能到达的必要条件还有一个:
条件2: map[ i - 1][ j ] || map[ i ][ j - 1]
(从小到大遍历,只能从上面或者左边来)
满足两个条件,即可标记 map[i][j] = 1 然后答案+1
注意初始条件:map[0][0] = 1,因此答案最小是1

class Solution {
public:
    int movingCount(int m, int n, int k) {
        int count = 1;
        int map[100][100]= { 0 };
        map[0][0] = 1;   //第一个格子必定可以到
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
            {
                if(i==0&&j==0) continue;
                if(i%10 + i/10 + j%10 + j/10 <= k )
                    if((i>0 && map[i-1][j]) || (j>0 && map[i][j-1]))
                    {
                        map[i][j]=1;
                        count++;
                    }
            }
        return count;
    }
};

9. 最长回文子串
#include <string>
using namespace std;

class Solution {
public:
    string longestPalindrome(string s) {
        string ans(" ");
        ans[0] = s[0];
        for(int i=0;i<s.length();i++)
        {
            for(int j=s.length()-1 ; j>i ; j--)
            {
                if(s[i] == s[j])
                {
                    int flag = 1;
                    for(int r=i,l=j; r<l ; r++,l--)
                        if(s[r]!=s[l]){flag = 0 ; break;}
                    
                    if(flag) 
                    {
                        if(ans.length() < j - i + 1 )  //如果比已有的回文子串更长
                            ans = s.substr(i,j-i+1);
                        break;
                    }
                }  

            }
        }
        return ans;
    }
};

10. 全排列

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

递归回溯,每个位置尝试填入原排列1~N位置上的数字,填了一次就标记,返回时再把标记擦除。同时传递一个一维向量,保存每一次递归构造的排列,当digit = N时(已经构造到最后一位数)放入保存答案的二维向量中即可。

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> tmp;    
        memset(vis,0,sizeof(vis));
        dfs(nums.size(),1,tmp,nums);
        return seq;
    }
private:
    vector<vector<int>> seq;
    int vis[1000];
    void dfs(int n,int digit,vector<int> tmp,vector<int> & nums)
    {
        for (int i = 1; i <= n; i++)
        {
            if (!vis[i])
            {
                tmp.emplace_back(nums[i-1]);
                vis[i] = 1;
                if(digit < n)  
                    dfs(n, digit + 1, tmp,nums);
                else {
                    seq.emplace_back(tmp);
                    vis[i] = 0;
                    return;
                }
                tmp.pop_back();
                vis[i] = 0;
            }
            
        }
    }
};

11. 走迷宫

输入一个用01矩阵表示的迷宫,0表示地面,1表示障碍物。起点为左上顶点,终点为右下顶点。输出最少步数或者无解。

方法1DFS

#include <stdio.h>
#define MAXN 100
int map[MAXN][MAXN];
int vis[MAXN][MAXN];
int m,n;
int ans;
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };

void dfs(int x,int y,int dep)
{
	printf("(%d , %d)\n", x, y);
	if (x == m && y == n)
	{
		if (dep < ans) ans = dep;
		return;
	}
	vis[x][y] = 1;
	for (int i = 0; i < 4; i++)
	{
		int xx = x + dx[i], yy = y + dy[i];
		if (xx >= 1 && yy >= 1 && xx <= m && yy <= n && vis[xx][yy]==0 && map[xx][yy] == 0)
			dfs(xx, yy, dep + 1);
	}
	vis[x][y] = 0;
}

void init()
{
	
	scanf_s("%d%d", &m,&n);
	ans = m * n;
	for(int i = 1; i <= m;i++)
		for (int j = 1; j <= n; j++)
		{
			scanf_s("%d", &map[i][j]);  //0表示可以走,1表示不能走
			vis[i][j] = 0;
		}
}

int main()
{
	init();
	dfs(1,1,0);
	if (ans == m * n) printf("No Solution.");
	else printf("Ans = %d", ans);
	getchar();getchar();
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值