解题思路:让两个指针分别指向两个链表,当遍历到最末尾的结点后,指针指向另一个链表的头结点。例如: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;elsenode1 = headB;if(node2 != NULL)node2 = node2->next;elsenode2 = 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;elsehigh = 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-na[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;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] + " ";elseresult += 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 whilereturn *(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;    // 只有一个非空则返回该指针,两个都为空则返回空指针}
};

剑指 offer 编程题 C++ 版总结(下)相关推荐

  1. 剑指 offer 编程题 C++ 版总结(中)

    解题思路:从根结点开始向下进行对比,(1) 若两边的结点为空,返回 true.(2) 一个结点为空,而另一个结点不为空,此时二叉树不是对称的,返回 false.(3) 两边结点都不为空,但是结点的值不 ...

  2. 剑指 offer 编程题 C++ 版总结(上)

    解题思路:(1) 最简单的方法是从后往前遍历(需要两层循环),但是这样时间复杂度太高了,力扣上不能全过.(2) 由题可知,数值范围为0-n-1,所以可以创建一个辅助数组,让原始序列通过取余操作映射到辅 ...

  3. 剑指offer编程题(JAVA实现)——第35题:数组中的逆序对

    github https://github.com/JasonZhangCauc/JZOffer 剑指offer编程题(JAVA实现)--第35题:数组中的逆序对 题目描述 在数组中的两个数字,如果前 ...

  4. 剑指offer编程题(JAVA实现)——第38题:二叉树的深度

    github https://github.com/JasonZhangCauc/JZOffer 剑指offer编程题(JAVA实现)--第38题:二叉树的深度 题目描述 输入一棵二叉树,求该树的深度 ...

  5. 三天刷完《剑指OFFER编程题》--Java版本实现(第三天)

    正在更新中......... 剑指offer --Python版本的实现: 剑指offer(1/3)第一大部分 剑指offer(2/3)第二大部分 剑指offer(3/3)第三大部分 -------- ...

  6. 剑指Offer——编程题的Java实现(更新完毕……)

    目录 二维数组中的查找 替换空格 从尾到头打印链表 重建二叉树 用两个栈实现队列 用两个队列实现一个栈 旋转数组的最小数字 斐波那契数列 跳台阶 变态跳台阶 矩形覆盖 二进制中1的个数 数值的整数次方 ...

  7. 编程 跳台阶_Java版剑指offer编程题第8题--跳台阶

    跟learnjiawa一起每天一道算法编程题,既可以增强对常用API的熟悉能力,也能增强自己的编程能力和解决问题的能力.算法和数据结构,是基础中的基础,更是笔试的重中之重. 不积硅步,无以至千里: 不 ...

  8. 一只青蛙跳向三个台阶_Java版剑指offer编程题第9题--变态跳台阶

    跟learnjiawa一起每天一道算法编程题,既可以增强对常用API的熟悉能力,也能增强自己的编程能力和解决问题的能力.算法和数据结构,是基础中的基础,更是笔试的重中之重. 不积硅步,无以至千里: 不 ...

  9. 剑指Offer——编程题的Java实现

    声明:我写这个的意图是我在看书的过程中,就我掌握的内容做一个笔记,没有抄袭的意图.再次说明一下,我找工作的过程中并不顺利,没有像那些牛人们拿到一大把的Offer,我只是希望通过我自己的努力,不断提高自 ...

最新文章

  1. JavaBean规范
  2. 基于机器视觉的缺陷检测的原理与方法
  3. 内核随记(三)--同步(2)【转】
  4. 学习资源之4:Linux
  5. 八十四、搜索框动画效果实现,React-Redux 进行应用数据的管理
  6. createbitmap导致的内存泄漏如何处理_C++ 如何避免内存泄漏,一篇就够
  7. 2021牛客暑期多校训练营4 D-Rebuild Tree(prufer序列+树形dp)
  8. 发布web应用程序是出现unsafe code
  9. Linux命令速查手册出炉!
  10. VuePress 1.0.0 发布,Vue 轻量级静态网站生成器
  11. sh/bash/csh/Tcsh/ksh/pdksh等shell本质区别
  12. [故事]Making machines with brains
  13. c语言实验赵欣版答案,赵欣-计算机科学与技术学院
  14. Java vbnullstring_VB中Null、Empty、Nothing及vbNullString的区别
  15. zbbz插件使用教程_zbbz加载成功用不了_坐标标注插件zbbz【CAD教学】
  16. 机器学习数据预处理之字符串转数字
  17. python贪吃蛇_python实现贪吃蛇
  18. Unity: 打飞碟Adapter版
  19. 【模拟电路】电源效率与散热
  20. LBS与空间索引原理

热门文章

  1. 【Boost】以boost::function和boost:bind取代虚函数
  2. HTTP/3 来了,你了解它么?
  3. Flink 状态一致性:端到端状态一致性的保证
  4. 操作系统 :银行家算法的实现(C++)
  5. C++ STL : 模拟实现STL中的string类
  6. 字符函数和内存函数模拟实现
  7. WebRTC的现状和未来:专访W3C WebRTC Chair Bernard Aboba(下)
  8. URL2Video:把网页自动创建为短视频
  9. 【大会】延迟还能再低点吗?不能,但也能
  10. FFmpeg优化 苏宁PP体育视频剪切效率提升技巧