脑力编程题

本文介绍了多个编程题目,包括LeetCode和华为机考的题目,涵盖了排序、循环、四则运算、字符串处理、矩阵操作等算法。通过解析解题思路,展示了如何利用双指针、栈等数据结构解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

有些题,像脑筋急转弯。


一、Leetcode 769 最多能完成排序的块

这一题让我们找最多可以分成几块,以 2 3 1 0 5 4 为例,可以分成两块,因为 2 3 1 0 和 5 4 各一块。2 3 1 0的最大值为3,0的下标为3;5 4的最大值为5,4的下标为5。
所以这一题实际上问的是按顺序遍历一下,过程中,当前最大值等于数组下标时,就可以分成一个块。因此,代码如下:

int ans = 0, amax = -1;
for (int i = 0; i < arr.size(); ++i) {
	amax = max(amax, arr[i]);
  if (amax == i) ans++;
}
return ans;

二、Leetcode 202

这一题通过举例1-10的迭代过程,我们发现不能迭代到1的数都会进入循环,且循环中【一定包含4】。一旦认识到这一点,只要判断它经过多次迭代,得到了1还是4,就可以得到答案。

bool isHappy(int n) {
   int next = n;
   while (next != 1 && next != 4) {
       int temp1 = next; // 存储当前值,一步一步取出最高位
       int temp2 = 0; // 存储平方和
       while (temp1 / 10 > 0) {
           temp2 += (temp1 % 10) * (temp1 % 10);
           temp1 = temp1 / 10;
       }
       temp2 += temp1 * temp1;
       next = temp2;
   }
   return next == 1? true: false;
}

三、华为机考 6 质数因子

首先我们需要明确一点,才能开始做题:
(1)设数为n,那么大于sqrt(n)的质因子至多只有一个;
以10为例,最大质因子为5,大于sqrt(10);
以9为例,最大质因子为3,不大于sqrt(9).
(2)如果除1之外,n不存在小于等于sqrt(n)的质因子,那么n为质数。
以5为例,除1之外,质因子只有5,为它本身。

// ACM模式编程
int n, on;
cin >> n;
while (n != 1) { // n为1时不考虑
	on = n;
	for (int i = 0; i <= sqrt(on); ++i) {
		while (n % i == 0) { // 可能有多个相同的质因数,比如8 = 2 x 2 x 2
			cout << i << " ";
			n = n / i;
		}
	}
	if (on == n) { // 如果除1之外,n不存在小于等于sqrt(n)的质因子,那么n为质数
		cout << n;
	}
} 
return 0;

四、华为机考 50 四则运算

(1)这一题我们采用两个栈来存放数据:一个存放数字,一个存放计算符号;
(2)如果遇到-号,我们将-号赋给后面的数字,从而变成加法计算。这是为了避免后期从栈顶向下计算时出错,比如1-6+2,则会变成1-8;
(3)如果第一个字符为-,那么给数字栈补一个0进去,即-2变成0+(-2);
(4)遇到乘法*或除法/要先计算;
(4)遍历完成之后,如果只有一个数,直接返回该数;如果有多个数,则按顺序累加计算。

int cal(int l, int r, char c) {
    cout << "current calculation : " << l << c << r << endl;
    if (c == '+') return l + r;
    else if (c == '-') return l - r;
    else if (c == '*') return l * r;
    else return l / r;
}
int part(string s, int& idx) {
    stack<int> s1;  // 存放待计算数据
    stack<char> s2;  // 存放待计算符号
    int ans = 0;
    if (s1.empty() && s[idx] == '-') s1.push(0);   // 如果第一个字符是-,那么补一个0进去
    while(1) {
        cout << "s[idx] : " << s[idx] << endl;
        if (s[idx] == '(' || s[idx] == '[' || s[idx] == '{') {
            int temp = part(s, ++idx);  // 优先计算括号内
            cout << "part result : " << temp << endl;
            s1.push(temp);
        } else if (isdigit(s[idx])) {  // 如果是数字的话,要考虑它可能不是个位数
            int temp = s[idx] - '0';
            while (isdigit(s[idx+1])) {
                temp = temp * 10 + s[idx + 1] - '0';
                idx++;
            }
            cout << "total : " << temp << endl;
            s1.push(temp);
        } else if (s[idx] == '-') {  // 如果是-号,把-号给后面的数字,并存一个+进去
            if (s[idx + 1] == '(' || s[idx + 1] == '[' || s[idx + 1] == '{') {
                idx++;
                int temp = part(s, ++idx);
                cout << "part result : " << -temp << endl;
                s1.push(-temp);
            } else {
                int temp = s[++idx] - '0';
                while (isdigit(s[idx+1])) {
                    temp = temp * 10 + s[idx + 1] - '0';
                    idx++;
                }
                cout << "total : " << -temp << endl;
                s1.push(-temp);
            }
            s2.push('+');
        } else if (s[idx] == '+' || s[idx] == '*' || s[idx] == '/') {
            s2.push(s[idx]);
        } else {
            break;
        }
        //cout << "size " << s1.size() << " " << s2.size() << endl;
        if (!s2.empty() && s1.size() == s2.size() + 1 && (s2.top() == '*' || s2.top() == '/')) {
            // 如果有*/,要优先计算
            int r = s1.top(); s1.pop();
            int l = s1.top(); s1.pop();
            char k = s2.top(); s2.pop();
            s1.push(cal(l, r, k));
        }
        if ((unsigned long)idx == s.size() - 1) break;
        idx++;
    }
    int r = s1.top();
    s1.pop();
    ans = r;
    while (!s2.empty()) {
        int l = s1.top(); s1.pop();
        char c = s2.top(); s2.pop();
        ans = cal(l, r, c);
        r = ans;
    }
    // cout << "ans " << ans << endl;
    return ans;
}
int main() {
    string s;
    cin >> s;
    int idx = 0;
    int ans = part(s, idx);
    cout << ans;
    return 0;
}

五、Leetcode76 最小覆盖字串

用flags和needchar去记录t字符串中字符是否出现以及出现的次数。
每次找到一段能够覆盖t的子串时,不停增大左边界,从而找到最小的覆盖子串。

string minWindow(string s, string t) {
	if (s.size() < t.size()) return "";
    vector<bool> flags(128, false);
    vector<int> needchar(128, 0);
    for (char c: t) {
    	flags[c] = true;
        needchar[c]++;
    }
    int l = 0, minsz = s.size() + 1, minl = 0, cnt = 0;
    for (int i = 0; i < s.size(); ++i) {
    	if (flags[s[i]]) {
        	needchar[s[i]]--;
        	// 如果说是s[i]在t中重复多次,那么每减1次就要cnt++,如果s中提供多次,cnt不应该++
        	if (needchar[s[i]] >= 0) cnt++;
    	}
    	while (cnt == t.size()) {
        	// 更新长度
        	if (i - l + 1 < minsz) {
        		minsz = i - l + 1;
            	minl = l;
        	}
        	// 试一试扔掉一个
        	if (flags[s[l]] && needchar[s[l]] <= 0) {
        		// 如果s中提供多个s[l],那么去掉多余的cnt不--;如果去掉这个就没有更多的了,那么就要--
            	if (needchar[s[l]] == 0) cnt--;
            	needchar[s[l]]++;
        	}
        	l++;
    	}
 	}
 	// 如果从来没有进入过循环,minsz不会发生变化,这时候应该输出空字符串
 	return minsz == s.size() + 1 ? "" : s.substr(minl, minsz);
}

六、Leetcode 50 Pow(x, n)

这里要用到加速,因为一个个连乘很慢。

    double myPow(double x, int n) {
        long N = n;
        return n > 0 ? quickM(x, N) : 1.0 / quickM(x, -N);
    }

    double quickM(double x, long n) {
        if (n == 0) return 1.0;
        if (n == 1) return x;
        double y = quickM(x, n / 2);
        return n % 2 == 0 ? y * y : y * y * x;
    }

七、Leetcode 56 合并区间

记录当前区间的右端和重叠区间的右端的最大值,无法再延伸时加入答案中。

vector<vector<int>> merge(vector<vector<int>>& intervals) {
	sort(intervals.begin(), intervals.end());
	vector<vector<int>> ans;
	for (int i = 0; i < intervals.size();) {
		int l = intervals[i][0], r = intervals[i][1];
		i++;
		while (i < intervals.size() && intervals[i][0] <= r) {
			r = max(r, intervals[i][1]);
			i++;
		}
		ans.push_back(vector<int>{l, r});
	}
	return ans;
}

八、Leetcode 73 矩阵置0

这个方法不消耗额外的空间,但是不是很好。
最好的方法可以用两个数组记录某行某列有没有0,然后两次遍历。

    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (matrix[i][j] == 0) setm1(matrix, i, j, m, n);
            }
        }
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (matrix[i][j] == -11) matrix[i][j] = 0;
            }
        }
    }
    void setm1(vector<vector<int>>& matrix, int r, int c, int m, int n) {
        for (int i = 0; i < m; ++i) {
            if (matrix[i][c] == 0) q.push({i, c});
            matrix[i][c] = -11;
        }
        for (int i = 0; i < n; ++i) {
            if (matrix[r][i] == 0) q.push({r, i});
            matrix[r][i] = -11;
        }
        while (!q.empty()) {
            auto [x, y] = q.front();
            q.pop();
            setm1(matrix, x, y, m, n);
        }
    }
	
}

九、双指针

9.1 Leetcode 80 删除有序数组中的重复项 II

用双指针,l指向待修改的节点,r指向新节点。

    int removeDuplicates(vector<int>& nums) {
        int n = nums.size();
        if (n <= 2) return n;
        int l = 2, r = 2;
        while (r < n) {
            if (nums[l - 2] != nums[r]) {
                nums[l] = nums[r];
                l++;
            }
            r++;
        }
        return l;

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值