赞
踩

解题思路:让两个指针分别指向两个链表,当遍历到最末尾的结点后,指针指向另一个链表的头结点。例如:A 和 B 开始的时候分别指向 4 和 5。两个指针分别遍历链表,当 A 遍历到末尾结点的 5 时,下一步让其指向头结点为 5 的结点。而 B,让其指向头结点为 4 的结点。这样,A 和 B 指针在指向公共结点的时候都走了相同的距离,所以它们相遇的时候该结点就是公共结点。
- /**
- * Definition for singly-linked list.
- * struct ListNode {
- * int val;
- * ListNode *next;
- * ListNode(int x) : val(x), next(NULL) {}
- * };
- */
- class Solution {
- public:
- ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
- ListNode *node1 = headA;
- ListNode *node2 = headB;
-
- while (node1 != node2) {
- if(node1 != NULL)
- node1 = node1->next;
- else
- node1 = headB;
- if(node2 != NULL)
- node2 = node2->next;
- else
- node2 = headA;
- }
- return node1;
- }
- };


解题思路:由题可知,数组是排好序的,所以可以先用二分查找找出 target 所在的下标,然后根据下标向前向后查找即可。
- class Solution {
- public:
- int search(vector<int>& nums, int target) {
- int count = 0, mid, low = 0, high = nums.size()-1;
-
- while(low <= high)
- {
- mid = low + (high - low)/2;
- if(nums[mid] == target){
- count = searchcount(nums, target, mid);
- break;
- }
- else if(nums[mid] < target)
- low = mid + 1;
- else
- high = mid - 1;
- }
- return count;
- }
- int searchcount(vector<int>& nums, int target, int index)
- {
- int count = 0, left = index - 1, right = index + 1;
- while( left >= 0 ) {
- if(nums[left--] == target)
- count++;
- else
- break;
- }
- while( right < nums.size() ) {
- if(nums[right++] == target)
- count++;
- else
- break;
- }
- return count + 1;
- }
- };


解题思路:是有序数组,第一个想到的就是二分查找。
- class Solution {
- public:
- int missingNumber(vector<int>& nums) {
- int left = 0, right = nums.size();
- while(left < right){
- int mid = left + (right - left)/2;
- if(mid == nums[mid]){
- left = mid+1;
- }else{
- right = mid;
- }
- }
- return left;
- }
- };

解题思路:二叉搜索树中序遍历可以得到递增有序序列。将二叉树中序非递归遍历反向操作,每次先右子树入栈即可获得递减有序序列。这样就能很快找到第 k 大的结点。
- /**
- * Definition for a binary tree node.
- * struct TreeNode {
- * int val;
- * TreeNode *left;
- * TreeNode *right;
- * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
- * };
- */
- class Solution {
- public:
- int kthLargest(TreeNode* root, int k) {
- stack<TreeNode*> sta;
- TreeNode* p = root;
- int count = 0, result;
- while(!sta.empty() || p != NULL)
- {
- while(p)
- {
- sta.push(p);
- p = p->right;
- }
- p = sta.top();
- if(++count == k)
- result = p->val;
- sta.pop();
- p = p->left;
- }
- return result;
- }
- };


解题思路:利用递归计算二叉树的深度。
- /**
- * Definition for a binary tree node.
- * struct TreeNode {
- * int val;
- * TreeNode *left;
- * TreeNode *right;
- * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
- * };
- */
- class Solution {
- public:
- int maxDepth(TreeNode* root) {
- if(root == NULL)
- return 0;
- int i = maxDepth(root->left);
- int j = maxDepth(root->right);
-
- return i > j ? i+1: j+1;
- }
- };


解题思路:利用递归计算二叉树的深度。
- /**
- * Definition for a binary tree node.
- * struct TreeNode {
- * int val;
- * TreeNode *left;
- * TreeNode *right;
- * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
- * };
- */
- class Solution {
- public:
- bool isBalanced(TreeNode* root) {
- return !root ? true : abs(depth(root->left) - depth(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
- }
- int depth(TreeNode* cur) { //计算二叉树的最大深度
- return !cur ? 0 : max(depth(cur->left), depth(cur->right)) + 1;
- }
- };


解题思路:(1) 用哈希表。(2) 题目中强调其他数字出现两次,如果只有一个数字的情况下,我们可是使用异或(相同为0,不同为1)得出要找的数字。但是题中要找的数数字有两个,所以这个时候还是需要对整个序列求异或。这样最终得到的是要找的两个数字的异或的结果。此时,我们可以根据得到的值,将原本序列分成两个序列(每个序列除了一个单独数字外,其余的数字都是成对出现的),然后再对这两个序列进行异或操作就能找出两个只出现一次的数字。
- class Solution {
- public:
- vector<int> singleNumbers(vector<int>& nums) {
- unordered_map<int, int> res;
- vector<int> number;
- for (int i = 0; i < nums.size(); i++) {
- res[nums[i]]++;
- }
- for (auto c : nums) {
- if (res[c] == 1) {
- number.emplace_back(c);
- }
- }
- return number;
- }
- };
-
-
- class Solution {
- public:
- vector<int> singleNumbers(vector<int>& nums) {
- //总共分为两个步骤:(1) 整个序列求异或 (2) 根据异或值划分序列后再求异或
- //第一步:求所有的数异或的结果
- int k = 0;
- for (int num : nums) {
- k ^= num;
- }
- //获得k中最低位的1,k里面两数不同,故肯定有一位1是不同的,可以以此区分两个序列
- int mask = 1;
- while ((k & mask) == 0) {
- mask <<= 1;
- }
- int a = 0;
- int b = 0;
- //第二步:根据 mask 划分序列后再求异或
- for (int num : nums) {
- if ((num & mask) == 0) {
- a ^= num; // 找到那位不为1的元素, 其余数为偶数,会自动消去
- } else {
- b ^= num; // 找到那位为1的元素
- }
- }
- return {a, b};
- }
- };


解题思路:(1) 用哈希表。(2) 使用位运算。得注意的是:如果某个数字出现 3 次,那么这个 3 个数字的和肯定能被 3 整除,则其对应二进制位的每一位的和也能被 3 整除。统计数组中每个数字的二进制中每一位的和,判断该和是否能被 3 整除。若可以,则只出现一次的数字的二进制数中那一位为 0,否则为 1。
- class Solution {
- public:
- int singleNumber(vector<int>& nums) {
- unordered_map<int, int> mp;
- for(int n : nums) mp[n] ++;
- int ans;
- for(auto pr : mp){
- if(pr.second == 1){
- ans = pr.first;
- break;
- }
- }
- return ans;
- }
- };
-
-
- class Solution {
- public:
- int singleNumber(vector<int>& nums) {
- int ans = 0;
- for(int i = 0; i < 32; ++i){
- int cnt = 0;
- for(int n : nums){
- // n & 1 << i 的值大于0即为真
- if(n & (1 << i)) cnt++;
- }
- // 构造只出现一次的那个数字,采用异或的方法生成二进制中的每一位
- if(cnt % 3 == 1) ans ^= (1 << i);
- }
- return ans;
- }
- };


解题思路:题中所给的序列是递增排序序列,所以可以使用双指针 low 和 high 解决该问题。若 low 和 high 的两个元素相加为 target 此时记录相应的值返回即可。若两者的和大于 target,令 high--。反之令 low++。
- class Solution {
- public:
- vector<int> twoSum(vector<int>& nums, int target) {
- int size = nums.size();
- vector<int> asd(2, 0);
- int low = 0, high = size-1;
- while(low < high)
- {
- if(nums[low] + nums[high] == target){
- asd[0] = nums[low];
- asd[1] = nums[high];
- }
- if(nums[low] + nums[high] > target){
- high--;
- }
- else{
- low++;
- }
- }
- return asd;
- }
- };


解题思路:该题所求的是所有和为 target 的连续正整数序列的组合。这里可以使用暴力枚举解决该题。首先需要确定边界,limit 为 target / 2 向下取整。内层循环完成累加操作,若累加和等于 target,则记录相应的数字序列。循环终止的条件是累加和大于 target。
- class Solution {
- public:
- vector<vector<int>> findContinuousSequence(int target) {
- vector<vector<int>> vec;
- vector<int> res;
- int sum = 0, limit = (target - 1) / 2; //(target - 1)/2 等效于 target / 2 下取整
- for (int i = 1; i <= limit; ++i) {
- for (int j = i;; ++j) {
- sum += j; //完成累加操作
- if (sum > target) {
- sum = 0;
- break;
- }
- else if (sum == target) { //记录数字序列
- res.clear();
- for (int k = i; k <= j; ++k) res.emplace_back(k);
- vec.emplace_back(res);
- sum = 0;
- break;
- }
- }
- }
- return vec;
- }
- };

下面这个方法是对上述代码的改进。每次操作之后的序列和操作之前的序列相比大部分数字都是一样的,只是增加或减少了一个数字,因此我们可以在前一个序列的和的基础上求操作之后的序列的和。这样可以减少很多不必要的运算,从而提高代码效率。
- class Solution {
- public:
- vector<vector<int>> findContinuousSequence(int target) {
- vector<vector<int>> result;
- vector<int> asd;
- if(target < 3)
- return result;
- int low = 1, high = 2;
- int middle = (target+1)/2;
- int sum = low + high;
- while(low < middle)
- {
- if(sum == target)
- {
- Add(result, asd, low, high);
- }
-
- while(sum > target && low < middle)
- {
- sum -= low;
- low++;
- if(sum == target)
- {
- Add(result, asd, low, high);
- }
- }
-
- high++;
- sum += high;
- }
- return result;
- }
- void Add(vector<vector<int>>& result, vector<int>& asd, int low, int high)
- {
- for(int i = low; i <= high ; i++)
- asd.push_back(i);
- result.push_back(asd);
- asd.clear();
- }
- };

若上面的题目不要求组合内的序列是连续的,那该怎么解决?
- #include <iostream>
- #include <list>
- #include <cmath>
- using namespace std;
-
- int *a, *b, N, n;
- int total = 0;
- void find(int sum, int n, int b[])
- {
- //递归出口
- if(sum <= 0 || n <= 0)
- return;
-
- //输出找到的结果
- if(sum == b[n-1]) //表示找到了一个值
- {
- a[n-1] = 1;
- for(int i = 0; i < N; i++)
- {
- if(a[i] == 1)
- cout << b[i] <<" ";
- }
- cout <<endl;
- total++;
- }
-
- a[n-1] = 1;
- find(sum-b[n-1], n-1, b); //如果放入n,则从剩余n-1个数中填满sum-n
- a[n-1] = 0;
- find(sum, n-1, b); //如果不放入n,从n-1个数中填满sum
- }
-
- int main()
- {
- int sum;
- cout<<"Please Input n&sum:"<<endl;
- cin>>N>>sum;
- a = new int[N];
- b = new int[N];
- for(int i = 0; i < N; i++)
- cin >> b[i];
- n = N;
- find(sum, n, b);
- cout<<"Total:"<<total<<endl;
- //cout << Number <<endl;
- delete []a;
- delete []b;
- return 0;
- }


解题思路:(1) 该题是利用空格进行划分,所以可以使用双指针从后往前将字符串进行分割。 (2) 利用 strtok 函数对字符串进行分割,利用 vector 存储分割后的单词,接着完成一个 reverse 操作。最后根据题意,添加相应的空格即可。
- class Solution {
- public:
- string reverseWords(string s) {
- string str;
- if(s.empty()) //输入字符为空,返回空
- return str;
- cout << s.size() <<endl;
- int i{0}, j{0}; //i,j用来表示单词开始和结束的位置
- j = s.size() - 1;
- for(j; j >= 0; --j)
- {
- if(s[j] != ' ') //遇到不是空格的
- {
- i = j;
- while(i >=0 && s[i] != ' ') //从j开始向左寻找单词,i>=0防止越界访问
- --i;
- for(int k = i + 1; k <= j; ++k) //单词序列加入字符串中
- str.push_back(s[k]);
- str.push_back(' '); //加入一个空格
- j = i; //改变j的位置
- }
- }
- if(str.size() > 0)
- str.pop_back(); //移除末尾空格
- return str;
- }
- };
-
-
- class Solution {
- public:
- string reverseWords(string s) {
- vector<string> asd;
- int size = s.size();
- char* p = new char[size+1];
- strcpy(p, s.c_str());
- char* token = strtok(p, " ");
- while(token != NULL){
- asd.push_back(token);
- token = strtok(NULL, " ");
- }
- reverse(asd.begin(), asd.end());
- string result;
- for(int i = 0; i < asd.size(); i++){
- if(i != asd.size()-1)
- result += asd[i] + " ";
- else
- result += asd[i];
- }
- delete []p;
- return result;
- }
- };


- class Solution {
- public:
- string reverseLeftWords(string s, int n) {
- reverse(s.begin(), s.begin() + n);
- reverse(s.begin() + n, s.end());
- reverse(s.begin(), s.end());
- return s;
- }
- };

解题思路:直接根据题意做题,用变量记录划窗中的最大值和其下标。当划窗移动的时候,先判断最值下标是否在区间内。如果在,只用对比新添加的元素和最值的大小即可。如果不在,就需要重新寻找划窗中的最大值了。
- class Solution {
- public:
- vector<int> maxSlidingWindow(vector<int>& nums, int k) {
- vector<int>a;
- if(nums.size() == 0){
- return a;
- }
- int n = nums.size() - k + 1; // 滑动窗口滚动的次数
- int t = -1; // 当前滑窗最大值的下标
- int max; // 当前滑窗最大值
-
- // 遍历,找每个滑窗的最大值
- for(int i = 0;i < n ;i++){
- // 上个滑窗的最大值还在当前滑窗内,如果新加入的元素比最大值还大,更新最大值和其下标
- if(t >= i){
- if(nums[i+k-1] >= max){
- t = i+k-1;
- max = nums[i+k-1];
- }
- }
- // 不在的时候重新遍历当前滑窗,这里效率就低
- else{
- max = nums[i];
- for(int j = i + 1;j < k + i;j++){
- if(nums[j] >= max){
- t = j;
- max = nums[j];
- }
- }
- }
- // 添加最大值
- a.push_back(max);
- }
- return a;
- }
- };


解题思路:解决该题需要用到两个数据结构,queue 和 deque,其中 queue 用于存储数据,deque 用于记录序列的最大值。
push_back():当添加一个新的数据的时候,如果 deque 不为空的情况下且其中的值小于添加的值,那么就将 deque 中的数据 pop_back(),然后将数据同时添加到 deque 和 queue 中。
pop_front():正常 pop 队列元素即可。需要添加的代码是,如果 queue 和 deque 队首元素相等,则两个都要 pop 元素。
max_value():deque 为空时 return -1 ,不为空时 return deque 的队首元素。
- class MaxQueue {
- queue<int> q;
- deque<int> d;
- public:
- MaxQueue() {
- }
-
- int max_value() {
- if (d.empty())
- return -1;
- return d.front();
- }
-
- void push_back(int value) {
- while (!d.empty() && d.back() < value) {
- d.pop_back();
- }
- d.push_back(value);
- q.push(value);
- }
-
- int pop_front() {
- if (q.empty())
- return -1;
- int ans = q.front();
- if (ans == d.front()) {
- d.pop_front();
- }
- q.pop();
- return ans;
- }
- };


解题思路:有一串连续的数字(无重复),这串数字中最大值为 m,最小值为 n,这串数字一共包含 m-n+1 个数字。同样,如果我们能够知道 5 张扑克牌中的最大值 maxValue 和最小值 minValue,那我们就知道,要使它为顺子需要 maxValue - minValue + 1 张牌。
(1) 在查找 maxValue 和 minValue 过程中,跳过大小王。
(2) 如果 maxValue - minValue + 1 > 5,说明题目给的 5 张牌不足以构成顺子,即使里面有大小王,也不够用来填补使它构成顺子,所以返回 false。
(3) 如果 maxValue - minValue + 1 <= 5,说明 5 张牌足以构成顺子(里面的大小王能填补在合适位置),返回 true。
- class Solution {
- public:
- bool isStraight(vector<int>& nums) {
- bool m[15];
- memset(m, 0, sizeof(m));
- int minValue = 14, maxValue = 0;
- for (int item : nums) {
- if (item == 0) {
- continue;
- }
- if (m[item]) {
- return false;
- }
- m[item] = true;
- minValue = min(minValue, item);
- maxValue = max(maxValue, item);
- }
- return maxValue - minValue + 1 <= 5;
- }
- };


解题思路:这个问题实际上是约瑟夫问题。下面这个例子是N=8 m=3的例子。我们定义F(n,m)表示最后剩下那个人的索引号,因此我们只关系最后剩下来这个人的索引号的变化情况即可。

现在我们知道了G的索引号的变化过程,那么我们反推一下从N = 7 到N = 8 的过程。我们先把被删除的C补充回来,然后右移m个人,发现溢出了,再把溢出的补充在最前面,经过这个操作就恢复了N = 8 的排列了!

因此我们可以推出递推公式 f(8,3) = [ f(7, 3) + 3 ] % 8。进行推广泛化,即 f(n,m) = [ f(n−1,m) + m ]%n 。
- 法一:递推公式
- class Solution {
- public:
- int lastRemaining(int n, int m) {
- int pos = 0; // 最终留下的初始位置
- for(int i = 2; i <= n; i++){
- pos = (pos + m) % i; // 每次循环右移
- }
- return pos;
- }
- };
-
- 法二:用链表
- int lastRemaining(int n, int m)
- { // n个数字,每次删除第m个数字
- if(n < 1 || m < 1)
- return -1;
-
- int i = 0;
- list<int> a;
- for(i = 0; i < n; i++)
- a.push_back(i);
-
- list<int>::iterator current = a.begin();
- list<int>::iterator next;
-
- while(a.size() > 1){
- for(int i = 1; i < m; i++)
- {
- current++;
- if(current == a.end())
- current = a.begin();
- }
- next = ++current;
- if(next == a.end())
- next = a.begin();
-
- --current;
- a.erase(current);
- current = next;
- }//end while
-
- return *(current);
- }


解题思路:最简单的办法是使用两层循环解决该问题,即外层循环从 0 开始,内层循环使用一个变量记录循环过程中最大的利润。这样算法复杂度就是 O(n^2)。其实在遍历过程中记录当前元素之前的最小值,那么就可以优化掉一次循环。
- class Solution {
- public:
- int maxProfit(vector<int>& prices) {
- if(prices.size() == 0)
- return 0;
- int minPrice = prices[0];
- int maxGap = 0;
- for(int i = 0; i < prices.size(); i++){
- minPrice = min(minPrice, prices[i]); // 更新股票最小值
- maxGap = max(maxGap, prices[i] - minPrice); // 更新利润最大值
- }
- return maxGap;
- }
- };

解题思路:根据题意做题即可。
- class Solution {
- public:
- int strToInt(string str) {
- int i = 0, flag = 1;
- long res = 0; //默认flag = 1,正数
- while (str[i] == ' ') i++;
- if (str[i] == '-') flag = -1;
- if (str[i] == '-' || str[i] == '+') i ++;
- for (; i < str.size() && (str[i] >= '0' && str[i] <= '9'); i++) {
- res = res * 10 + (str[i] - '0');
- if (res >= INT_MAX && flag == 1) return INT_MAX;
- if (res > INT_MAX && flag == -1) return INT_MIN;
- }
- return flag * res;
- }
- };


解题思路:二叉搜索树,其中序遍历有序。所以,当左右子树的值都大于根的值。则移动至根的右子树。当左右子树的值都小于根的值,则移动至根的左子树。若此时,一个结点的值比根大,另一个结点的值比根小,则 这个根就是两者最近祖先结点。
- /**
- * Definition for a binary tree node.
- * struct TreeNode {
- * int val;
- * TreeNode *left;
- * TreeNode *right;
- * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
- * };
- */
- class Solution {
- public:
- TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
- if(root == NULL)return NULL;
-
- if(p->val > root->val && q->val > root->val)
- return lowestCommonAncestor(root->right, p, q);
- else if(p->val < root->val && q->val < root->val)
- return lowestCommonAncestor(root->left, p, q);
- else return root;
- }
- };


解题思路:递归查询两个节点 p 和 q,如果某个节点等于节点 p 或节点 q,则返回该节点的值给父节点。如果当前节点的左右子树分别包括 p 和 q 节点,那么这个节点必然是所求的解。如果当前节点有一个子树的返回值为 p 或 q 节点,则返回该值。(告诉父节点有一个节点存在其子树中)如果当前节点的两个子树返回值都为空,则返回空指针。
- /**
- * Definition for a binary tree node.
- * struct TreeNode {
- * int val;
- * TreeNode *left;
- * TreeNode *right;
- * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
- * };
- */
- class Solution {
- public:
- TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
- if(root == NULL)return NULL;
- if(root == p||root == q)return root;
-
- TreeNode* left = lowestCommonAncestor(root->left, p, q);
- TreeNode* right = lowestCommonAncestor(root->right, p, q);
-
- if(left && right) return root;
- return left ? left : right; // 只有一个非空则返回该指针,两个都为空则返回空指针
- }
- };

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。