剑指offer经典题目(七)
目录
动态规划
字符串相关
排序思想相关
链表相关
动态规划
题目1:输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值。OJ地址
图示如下。
题目解析: 如果单纯的从如何求解最大值这一点出发,依次的去累加连续的值并进行比较,这个题目实际上是非常复杂的,因为我们要遍历出所有可能得的子数组,然后依次的比较每个字数组的和,最终找出最大的子数组,这样去解决这个问题会比较麻烦,所以我们可以采用动态规划的方法去解决这道题。
动态规划分为三步。
- 定义状态。
- 列状态转移方程。
- 设置初始值。
定义状态。
我们认为,f(i) 就表示,以 i 下标,包含 i 下标结尾的子数组的最大值,比如 f(2) 就表示以下标为 2 的元素作为结尾的子数组的最大值。
列状态转移方程。
f(i) = max ( f(i-1) + arr[i] , arr[i] )
子数组可能只包含一个元素,也可能包含多个元素,所以以当前下标结尾的子数组的最大值,就可以认为是以前一个下标结尾的子数组的最大值加上当前元素的值之后与当前下标的元素进行比较的较大值。
设置初始值。
f(0) = arr[0]
以 0 下标结尾的子数组的最大值就是数组的第一个元素。
编码如下。
#include <algorithm>
class Solution {public:int FindGreatestSumOfSubArray(vector<int>& array) {if (array.size() == 0) {return 0;}int* dp = new int[array.size()];//dp[i]表示以下标i结尾的子数组的最大值dp[0] = array[0];int max_value = dp[0];for (int i = 1; i < array.size(); i++) {//状态转移方程dp[i] = max(dp[i - 1] + array[i], array[i]);if (max_value < dp[i]) {max_value = dp[i];}}delete []dp;return max_value;}
};
字符串相关
题目2:给定一个仅由小写字母组成的字符串。现在请找出一个位置,删掉那个字母之后,字符串变成回文。请放心总会有一 个合法的解。如果给定的字符串已经是一个回文串,那么输出-1。OJ地址
题目图示如下。
题目解析: 题目已经告诉了我们,如果这个字符不是一个回文串,那么就有且一个字符导致了这个字符不是一个回文串,所以只要删除了这个字符,那么删除这个字符之后的字符串一定是一个回文串。这便是此题的核心切入点。我们可以通过判断这个字符串是不是回文串,如果这个字符不是一个回文串,找到两个字符不相等的下标,然后删除一个下标对应的字符,如果删除之后对应的字符串是回文串,那么这个下标对应的字符其实就是我们要找的字符,最终返回该字符对应下标即可,如果不是返回另一个字符对应的下标即可,需要注意的就是我们得提前保存好对应字符的下标,具体操作看下述代码。
编码如下。
#include <iostream>
using namespace std;
#include<vector>
#include<string>static bool IsPalind(const string& str, int& start, int& end) {while (start < end) {if (str[start] != str[end]) {return false;} else {start++;end--;}}return true;
}int main() {int num;cin >> num;while (num) {num--;string s;cin >> s;int start = 0;int end = s.size() - 1;if (IsPalind(s, start, end)) {cout << -1 << endl;} else {int result1 = start;int result2 = end;s.erase(start, 1);start = 0;end = s.size() - 1;if (IsPalind(s, start, end)) {cout << result1 << endl;} else {cout << result2 << endl;}}}return 0;
}
排序思想相关
题目3:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。OJ地址
题目图示如下。
先问大家一个问题,求组合的最小的数,一定是将数组中的数从小到大组合吗?
这是我们普遍的一个误区,如果直接按照整型的大小进行组合,组合出的数不一定是组合最小的数。所以在进行组合时,我们是按照整数转为字典序之后进行比较,然后根据字符串比较的结果进行组合,此时组合的数才是整个数组组合之后的最小的数。
题目解析:将数组中的相邻两个整型转为字符串之后进行拼接,然后根据不同的顺序,保留最终字典序小的组合,然后根据这个组合调整数组中每个元素的位置。
编码如下。
#include <string>
class Solution {public:static bool comp(int x, int y) {//x,y构成的序列,小的放在前面string xs=to_string(x);string ys=to_string(y);string s1=xs;s1+=ys;string s2=ys;s2+=xs;return s1<s2;}string PrintMinNumber(vector<int>& numbers) {if (numbers.size() == 0) {return "";}sort(numbers.begin(), numbers.end(), comp);\string result;for(int i=0;i<numbers.size();i++){result+=to_string(numbers[i]);}return result;}
};
链表相关
题目4:输入两个链表,找出它们的第一个公共结点。
图示如下。
题目解析: 先统计出每个链表的节点的个数,然后计算出两个链表的节点的个数的差值,然后让链表的节点多的那个链表先让它的指针往后走差值步,然后让两个链表同时向后进行遍历,当两个链表遍历到相同的节点时,就证明这个节点就是两个链表的第一个公共节点。
编码如下。
/*
struct ListNode {int val;struct ListNode *next;ListNode(int x) :val(x), next(NULL) {}
};*/
class Solution {public:static int GetLength(ListNode* head) {int count = 0;while (head) {count++;head = head->next;}return count;}ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {if (pHead1 == nullptr || pHead2 == nullptr) {return nullptr;}//计算每个链表的长度int length1 = GetLength(pHead1);int length2 = GetLength(pHead2);int step = abs(length1 - length2);if (length1 > length2) {while (step) {pHead1 = pHead1->next;step--;}} else {while (step) {pHead2 = pHead2->next;step--;}}while (pHead1 && pHead2) {if (pHead1 == pHead2) {return pHead1;}pHead1 = pHead1->next;pHead2 = pHead2->next;}return nullptr;}
};
以上便是本期的所有内容。
本期内容到此结束^_^