目录
- 前言
- 正文
- 1. 最小的K个数
- 2.整数中出现1的个数
- 3.数字在升序数组中出现的次数
- 4.包含min函数的栈
- 5.数组中只出现一次的两个数字(20210519)
- 6.和为S的两个数字(20210519)
- 7.左旋转字符串(20210519)
- 8. 孩子们的游戏
- 9. 求1+2+...n?不能用啥加法和if语句
- 10. 字符流中第一个不重复的字符
- 11. 二叉树的下一个节点
- 12. 从上往下打印二叉树
- 13. 顺时针打印矩阵
- 14. 树的子结构
- 15. 二叉搜索树的后序遍历序列
- 16. 括号匹配深度
- 17. 删除链表中重复的节点
- 18. 对称的二叉树
- 19. 复杂链表的复制
- 20. 二叉搜索树与双向链表
- 21.连续子数组的最大和
- 22.把数组排数字最小的树
- 23.和为S的连续正数序列
- 24. 丑数
- 25. 数组中的逆序对
- 26. 正则表达式的匹配
- 27. 表示数值的字符串
- 28. 按之字形顺序打印二叉树
- 29.序列化二叉树
- 30. 滑动窗口的最大值
- 31. 矩阵中的路径
- 32. 机器人的运动范围
- 33.旋转数组的最小数字
- 34.斐波那契数列
- 35. 跳台阶
- 36.跳台阶的扩展问题
- 37. 矩形覆盖
- 38. 二进制中一的个数
- 34.数值的整数次方
- 35.调整数组顺序使奇数位于偶数前面
- 36. 链表中倒数最后k个结点
- 37. 翻转链表
- 38. 合并两个有序的数组
- 39.树的子结构
- 40.二叉树的镜像
- 41. 顺时针打印矩阵
- 参考
前言
正文
1. 最小的K个数
code
:
全排列
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> res;
if(k>input.size()||input.empty()||k<=0)
return res;
sort(input.begin(),input.end());//粗暴的排序
for(int r = 0;r<k;r++)
res.push_back(input[r]);
return res;
}
};
Partition方法
class Solution {
public:
int Partition(vector<int> &input,int begin,int end)
{
int low = begin;
int high = end;
int pivot = input[low];
while(low<high)//这里其实一个排序过程
{
while(low<high&&pivot<=input[high])//不断的拿标志位和右边高的地方进行比对
high--;
input[low] = input[high];//若发现有元素是比标志位还要小的,就进行替换
while(low<high&&pivot>=input[low])//现在开始比较左边那些比较小的
low++;
input[high] = input[low];//
}
input[low] = pivot;
return low;//low的地方就在中间的位置,此时,low的左边都是小于该数的,右边的都是大于该数的
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> output;
if(k>input.size()||input.empty()||k<=0)
return output;
int start= 0;
int end = input.size()-1;
int index = Partition(input, start, end);
while(index!=(k-1))
{
if(index>k-1)
{
end = index-1;
index = Partition(input,start,end);
}
else
{
start = index +1;
index = Partition(input,start, end);
}
}
// sort(input.begin(),input.end());
for(int r = 0;r<k;r++)
{
output.push_back(input[r]);
}
return output;
}
};
冒泡排序
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> output;
if(k>input.size()||input.empty()||k<=0)
return output;
int temp ;
for(int i =0;i<input.size()-1;i++)
{
for(int j = 0;j<input.size()-i-1;j++)
{
if(input[j]>input[j+1])//这个判断条件就是判断最小的四个元素
{
temp = input[j];
input[j] = input[j+1];
input[j+1] = temp;
}
}
}
// sort(input.begin(),input.end());
for(int r = 0;r<k;r++)
{
output.push_back(input[r]);
}
return output;
}
};
大顶堆的方法
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
//建立一个容量为k的大根堆的优先队列,遍历一遍元素,若
vector<int> ret;
if (k==0 || k > input.size())
return ret;
priority_queue<int,vector<int>> pq;//这个大顶堆的声明很重要
//priority_queue<int,vector<int>,greater<int>> qp;//这个代表的是小顶堆
for(const int val:input)
{
if(pq.size()<k)//前K个随便入,等到其他就得慢慢来了
{
pq.push(val);
}
else{
if(val<pq.top())//若值比堆顶还要小,那么就入堆
{
pq.pop();
pq.push(val);
}
}
}
while(!pq.empty())//然后再把这个堆中的内容倒出来
{
ret.push_back(pq.top());
pq.pop();
}
return ret;
}
};
2.整数中出现1的个数
题目
解法一:暴力解法,笔试可用,面试gg
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n) {
//如果是笔试这样写,估计没啥问题,但如果是面试这样写,估计直接gg
string allStr;
int count = 0;
for(int i = 1;i<=n;i++)
{
string str = to_string(i);;
allStr.append(str);
}
for(int j = 0;j<allStr.length();j++)
{
if(allStr.at(j)==('1'))
count++;
}
return count;
}
};
3.数字在升序数组中出现的次数
题目
my code
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
//首先就是暴力解法:从头到尾遍历一遍,将次数进行统一,但这肯定不是这道题的初衷
int a[100] = {0};
int flag ;
int count = 0;
for(int i = 0;i<data.size();i++)
{
a[data[i]]++;
if(data[i]==k)
{
count = a[data[i]];
}
if(data[i]>k)
break;
}
return count;
}
};
二分查找解法
根据有序的这个条件,进行查找,查找到后,往前找一找,往后找一找
code
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
//第一种解法就是:暴力解法:从头到尾遍历一遍,将次数进行统一,但这肯定不是这道题的初衷
//第二种根据有序的这个条件就是二分查找
if(data.size()==0||k<data[0]||k>data[data.size()-1])
return 0;
int left =0;
int right = data.size()-1;
int count = 0;
int found = 0;
int mid = -1;
while(left<right)
{
mid = (left+right)/2;
if(data[mid]>k)
right = mid-1;
else if(data[mid]<k)
left = mid+1;
else
{
count++;//首先,这个是找到的第一个,所以进行count++;
found = mid;//然后把小标记录下来
break;
}
}
int prev = mid-1;
int foll = mid+1;
//找到那个数值,然后往前找,往后找
while(prev>=left)
{
if(data[prev]==data[mid])
{
count++;//首先,就是数量累加
prev--;//然后这个往前找的指针往最开始走
}
else
{
break;
}
}
while(foll<=right)
{
if(data[foll]==k)
{
count++;
foll++;
}
else
{
break;
}
}
return count++;
}
};
4.包含min函数的栈
题目
code
class Solution {
public:
stack<int> normal,minval;
//弄一个辅助栈来专门存储正栈中当前最小的元素
void push(int value) {
normal.push(value);
if(minval.empty())
minval.push(value);
else
{
if(value<=minval.top())
minval.push(value);
else
minval.push(minval.top());
}
}
void pop() {
normal.pop();
minval.pop();
}
int top() {
return normal.top();
}
int min() {
return minval.top();
}
};
5.数组中只出现一次的两个数字(20210519)
题目
code
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param array int整型vector
* @return int整型vector
*/
vector<int> FindNumsAppearOnce(vector<int>& array) {
//我的想法基本又是暴力解法。循环遍历一遍就可以了。这种暴力解法出现问题了,时间因那个改是溢出了。
int res = 0;
for(int i =0;i<array.size();i++)
{
res^=array[i];
}//这样求出来的值是c=a^b的值
int compare = 1;
while((compare&res)==0)//判断c的哪一位是1,可以根据这一个将整个数组分成两个部分1=1^0
{
compare<<=1;
}
int a = 0;
int b = 0;
for(int j=0;j<array.size();j++)
{
if((compare&array[j])==0)//这个筛选出那一位都是0的
{
a^=array[j];//将这一组中的所有值都进行异或,那么剩下的那一个就是只出现一次的数值
}
else//相反这一个就可以筛选出那一位都是1的
{
b^= array[j];//而这个也是相同的道理,只要不断的异或,剩下的就是只出现一次的数
}
}
vector<int> output;//接下来,根据条件进行加入该数组即可。
if(a<b)
{
output.push_back(a);
output.push_back(b);
}
else
{
output.push_back(b);
output.push_back(a);
}
return output;
}
};
题解:这个用暴力解法当然是可以的,但是,时间复杂度就太高了,所以,我们采用异或的这个解法,就可以完美的剔除出这两个数字,从而得到这两个数字的异或。 然后,再根据条件,对这个数字进行拆分。
方法二:本质与方法一一致
解题思路
利用异或运算,相同为0不同为1的性质,先将整个数组异或,得到的值 是只出现一次的两个数异或的结果,一定有一位不是0,依次从低位开始与1按位与 确定哪一位为1,将得到的结果 再依次与数组中元素执行按位与,为0的分一组,为1的分一组,这样可以将数组的元素分为两组,这两组元素满足两个条件,出现两次的数字都在一组,只出现一次的数字各在其中一组。两组元素各自依次异或,就是这两个值。
code
class Solution {
public int[] singleNumbers(int[] nums) {
int ret = nums[0];
for(int i = 1;i<nums.length;i++){//这样与一遍就得到两个不同的数与的结果
ret^=nums[i];
}
int h = 1;
while((ret&h)==0){//判断是哪一位
h<<=1;
}
int [] res = new int[2];
for(int a:nums){
if((a&h)==0){//将所有数再与一遍。在该位置上。
res[0]^=a;
}else{
res[1]^=a;
}
}
return res;
}
}
6.和为S的两个数字(20210519)
题目
code
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> output;
if(array.size()<2)
return output;
int left = 0;
int right = array.size()-1;
int product =0;
while(left<right)
{
if((array[left]+array[right])<sum)
left++;
if((array[left]+array[right])>sum)
right--;
if((array[left]+array[right])==sum)
{
output.push_back(array[left]);//第一组就是符合条件的一组
output.push_back(array[right]);
left++;
right--;
break;
}
}
return output;
}
};
题解
这题第一个主要重要的想法就是两个指针的同时移动,一个就是当你找到第一个两个数的时候,这两个数就已经是满足条件的两个数了。所以,无需再继续往下找了。
7.左旋转字符串(20210519)
题目
方法一:直接用另一个字符串来存储要转换位置的这个字符串
code
class Solution {
public:
string LeftRotateString(string str, int n) {
//这题的想法之一就是弄一个数组来存就可以了
string s = "";
for(int i=n;i<str.length();i++)
{
s+=str[i];
}
for(int i =0;i<n;i++)
{
s+=str[i];
}
return s;
}
};
8. 孩子们的游戏
题目
code
// struct ListNode{
// int val;
// struct ListNode *next;
// }
class Solution {
public:
int LastRemaining_Solution(int n, int m) {
//这个原来就叫做约瑟夫环
//我想到的想法是把所有的孩子都用链表串联起来,把最后一个元素接再第一个元素。
//然后用不断的next就可以了,只要next的个数符合这个m的话,最后一定是会找到仅剩的那个数的
if(n<=0)
return -1;
ListNode *head = new ListNode(0);
ListNode *node = head;
for(int i= 1;i<n;i++)
{
node->next = new ListNode(i);
node = node->next;
}
node->next = head;//把最后一个节点指向第一个节点
int k = 0;
while(node->next !=node)//当其不自己指向自己的时候
{
if(++k==m)
{
node->next = node->next->next;
k=0;
}
else
{
node = node->next;
}
}
return node->val;
}
};
code
class Solution {
public:
int LastRemaining_Solution(int n, int m) {
if(n<=0)
return -1;
int index =0;
for(int i =2;i<=m;++i)
{
index = (index+m)%i;
}
return index;
}
};
9. 求1+2+…n?不能用啥加法和if语句
题目
code
class Solution {
public:
int Sum_Solution(int n) {
bool x = n>1&&(n+=Sum_Solution(n-1));
return n;
}
};
10. 字符流中第一个不重复的字符
题目
code
class Solution
{
public:
//Insert one char from stringstream
map<char,int> map;
queue<char> que;
//关于这题没啥想法,应该主要还是对数据结构的不熟悉
void Insert(char ch) {
if(map.find(ch)==map.end())//查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器)。
{
que.push(ch);
}
++map[ch];//不管是不是第一次,都要进行计数
}
//return the first appearence once char in current stringstream
char FirstAppearingOnce() {
while(!que.empty())
{
char ch = que.front();
if(map[ch]==1)
{
return ch;
}
else
{
que.pop();
}
}
return '#';
}
};
思路:确实一开始看到的时候,没啥特别的想法,应该还是自己对数据结构的熟练度还是不够。这个map的用法还是要学会。map.find map[ch]++ 。
11. 二叉树的下一个节点
题目
方法一:这是一种比较聪明的方法。其实倒也不是很聪明,就是你对这个例子的大局观是要有的,不要仅仅局限于这棵树有多大。这就会导致你在写代码的时候,存在很大的局限性。
code
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode) {
//这个想法到时有想到,但是我的局限性太大了,我只是按照某个特定的树去想。应该要往更宽更高的地方想。
//并且还学到,在这里,做next->next的时候,是会报堆栈错误的,因为你无法确定这个next->next这个是否存在
if(!pNode)//本节点都为空了
{
return pNode;
}
if(pNode->right)//如果这个节点有右孩子的话
{
pNode = pNode->right;
while(pNode->left)//知道找到这个右孩子的最左下角
pNode = pNode->left;
return pNode;//然后返回就可以了
}
//如果是最后的根节点的话,那么一般要找这个节点的父节点的最父节点,也就是根节点
while(pNode->next)
{
TreeLinkNode *root = pNode->next;
if(root->left ==pNode)
{
return root;
}
pNode = pNode->next;
}
//最后一个节点
return nullptr;
}
};
方法二:暴力解法
code
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
class Solution {
public:
void pre_order(TreeLinkNode *root,vector<TreeLinkNode*> &v)
{
if(!root)//这是一个习惯,当有这种节点进来的时候一定要进行判断
return;
pre_order(root->left,v);//用递归就可以实现中序遍历了,只要将那个节点在适时的位置进行输出,或存储就可以了
v.push_back(root);
pre_order(root->right,v);
}
TreeLinkNode* GetNext(TreeLinkNode* pNode) {
//方法二,就是暴力解法了,根据这个节点,弄出中序遍历的整个序列,从而得到那个节点的下一个节点
TreeLinkNode *root = nullptr;
TreeLinkNode *tmp = pNode;
//第一步
while(tmp)
{
root = tmp;
tmp = tmp->next;
}
//第二步
vector<TreeLinkNode*> v;
pre_order(root,v);
//第三步
int n = v.size();
for(int i = 0;i<n;i++)
{
if(v[i]==pNode&&i+1!=n)
return v[i+1];
}
return nullptr;
}
};
题解:你要进行各种遍历,一个非常重要的就是找到根节点,这样才可以调用各种递归获取这棵树的各个节点。接下来,就是根据那个vector去进行遍历就可以了。
12. 从上往下打印二叉树
题目
code
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root) {
vector<int> array;
if(root==NULL)
return array;
queue<TreeNode*> que;
que.push(root);
TreeNode* value;
while(!que.empty())
{
value = que.front();
array.push_back(value->val);
que.pop();
if(value->left)
que.push(value->left);
if(value->right)
que.push(value->right);
}
return array;
}
};
13. 顺时针打印矩阵
题目
code
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> array;
if(matrix.empty())
return array;
int rows = matrix.size();
int cols = matrix[0].size();
int left = 0;
int right = cols-1;
int top = 0;
int bottom = rows-1;
while(left<=right&&top<=bottom)
{
//从左到右
for(int i = left;i<=right;i++)
{
array.push_back(matrix[top][i]);
}
//从上到下
for(int i = top+1;i<=bottom;++i)
{
array.push_back(matrix[i][right]);
}
if(top!=bottom)
{
for(int i = right-1;i>=left;--i)
{
array.push_back(matrix[bottom][i]);
}
}
//从下到上
if(left!=right)
{
for(int i = bottom-1;i>top;--i)
{
array.push_back(matrix[i][left]);
}
}
left++,top++,right--,bottom--;
}
return array;
}
};
题解:这题难倒是不难,主要还是要抓准规律。做一个循环不断的往里面走就可以了。但要注意的是,在角落的那个点,也就是i==j的那个点,我们是拿不到的。
14. 树的子结构
题目
未AC的解法,就是最后的判断还是有问题,有机会再搞一下。
code
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<int> Inorder(TreeNode *root)
{
vector<int> arr;
if(root==NULL)
return arr;
if(root->left)
Inorder(root->left);
arr.push_back(root->val);
if(root->right)
Inorder(root->right);
return arr;
}
bool HasSubtree(TreeNode* root1, TreeNode* root2) {
if(root1==NULL||root2==NULL)
{
return false;
}
vector<int> arr1 = Inorder(root1);
vector<int> arr2 = Inorder(root2);
bool flag = true;
for(int i = 0;i<arr1.size();i++)
{
int k = i;
for(int j = 0;j<arr2.size();j++)
{
if(arr1[k]!=arr2[j])
{
flag = false;
break;
}
else
{
k++;
}
}
}
if(flag)
return true;
return false;
}
};
解法二:这是可以AC的解法
code
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
bool HasSubtree(TreeNode* root1, TreeNode* root2) {
if(root1==NULL||root2==NULL)
return false;
bool result = false;
if(root1->val==root2->val)
result = HadSubuTreeHelper(root1,root2);
if(!result)
result = HasSubtree(root1->left,root2);
if(!result)
result = HasSubtree(root1->right,root2);
return result;
}
bool HadSubuTreeHelper(TreeNode* root1,TreeNode* root2)
{
if(root2==NULL)
return true;
if(root1==NULL)
return false;
if(root1->val!=root2->val)
return false;
return HadSubuTreeHelper(root1->left,root2->left)&&HadSubuTreeHelper(root1->right,root2->right);
}
};
15. 二叉搜索树的后序遍历序列
题目
code
class Solution {
public:
bool VerifySquenceOfBST(vector<int> seq) {
if(seq.empty()||seq.size()==0)
return false;
if(seq.size()==1)
return true;
return Judge(seq,0,seq.size()-1);
}
bool Judge(vector<int> sce,int start,int end)
{
if(start>=end)
return true;
int j;
int middle;
for(int i =0;i<end;i++)
{
if(sce[i]>=sce[end])
{
j = i;
middle = j;
break;
}
}
while(j<end)
{
if(sce[j]<sce[end])
return false;
j++;
}
return Judge(sce,start,middle-1)&&Judge(sce,middle+1,end-1);
}
};
思路
这个的思路其实还是比较简单的,就是不断的缩小这棵树的大小,然后,不断的把middle+1到end这段的距离不断的缩短。最好达到平衡。
16. 括号匹配深度
题目
code
//我关于这道题目的想法是用栈进行匹配,看右括号最长可以匹配多少个
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
string s;
cin>>s;
int depth=0;//存储最大深度
int tot = 0;//存储当前深度
for(int i= 0;i<s.length();i++)//确实是直接判断就可以了,看连续的(有多少个
{
if(s[i]=='(')
tot++;
else
tot--;
depth=max(depth,tot);
}
cout<<depth;
return 0;
}
17. 删除链表中重复的节点
题目
code
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* head) {
if(head==nullptr)//首先,若头结点为空的话,直接返回这个头
return head;
ListNode *pre,*cur,*next;//肯定要有三个指针,重点是,这个是个有序的链表
pre = nullptr;
cur = head;
next = cur->next;//基本的初始化
while(next)
{
if(cur->val!=next->val)//若都不相等,就如影随形,因为cur和Next若相等时一定相邻的
{
pre = cur;
cur = next;
next = next->next;
}
else
{
while(next&&cur->val==next->val)//直到next的值和cur的值不相等了
{
next = next->next;
}
ListNode *tmp;
while(cur!=next)//接下来就是删除cur(包括cur)到next之前的所有节点
{
tmp = cur->next;//先保存下要删除的这个节点的下一个节点,把cur往右边移
delete cur;
cur = tmp;
}
if(pre==NULL)//如果是删除最头部的几个节点,到时直接将cur 的节点作为头结点就可以了
head= cur;
else
{
pre->next= cur;//否则的话,pre就要直接连到cur了
}
if(next)
next = next->next;
}
}
return head;
}
};
思路:整体的话,需要抓住的是该链表是有序的这个条件,不然,还真不是很好做。所以读题很重要。这题,如果是题意的话,基本上都是可以如影随性的。
18. 对称的二叉树
题目
code
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
bool isSame(TreeNode* root1,TreeNode* root2)
{
if(!root1&&!root2)
return true;
if(!root1||!root2)
return false;
return root1->val==root2->val&&isSame(root1->left,root2->right)&&isSame(root1->right,root2->left);
}
bool isSymmetrical(TreeNode* pRoot) {
return isSame(pRoot,pRoot);
}
};
思路:最开始,我居然是先用二叉树的镜像,弄完后,还存了一下数据。发现是有问题的,就是当所有节点的数据都是相同的情况下,这两种树是无法分辨出来的。所以,第二种方法,我是打算直接去比较两棵树的节点的,反正也是出现一定问题,看了解决的答案之后,才发现,直接比较树的左右就好了。根本没必要那么搞。直接右子树和左子树比较,左子树和右子树进行比较就可以了。
19. 复杂链表的复制
题目
code
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* head) {
//确实第一遍读起来是没啥思路,但还是犯了没仔细思考一下采用什么数据结构可以解决这个问题的毛病
//使用的数据结构不就是那几种,堆栈,hashmap,数组,链表
if(head==NULL)
return NULL;
RandomListNode *newHead = new RandomListNode(head->label);//新建一个新的节点
RandomListNode *newP = newHead;
RandomListNode *oldP = head;
unordered_map<RandomListNode*,RandomListNode*> record;
while(oldP)//这样就把所有的旧的节点和新的节点做了一个hash对应
{
record[oldP] = new RandomListNode(oldP->label);
oldP = oldP->next;
}
oldP = head;
while(oldP)
{
record[oldP]->next=record[oldP->next];
record[oldP]->random = record[oldP->random];
oldP = oldP->next;
}
return record[head];
}
};
思路
整个思路其实是比较简单的,用一个map表来存储整个节点,并将这个节点和新建的节点做一个映射。最后才复现就可以了
20. 二叉搜索树与双向链表
题目
code
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
TreeNode *pre = NULL,*head = NULL;
void middle_order(TreeNode* root)
{
vector<int> out;
if(root==NULL)
return;
middle_order(root->left);
if(pre!=NULL)
pre->right=root;
else
head = root;
root->left = pre;
pre = root;
middle_order(root->right);
}
TreeNode* Convert(TreeNode* root) {
if(root==NULL)
return NULL;
middle_order(root);
return head;
}
};
21.连续子数组的最大和
题目
code
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
//确实是有技巧的,只需要遍历一遍数组就可以了
if(array.size()==0)
return 0;
int res = INT_MIN,s = 0;
for(auto x:array)
{
if(s<0)
s=0;
s+=x;
res = max(res,s);
}
return res;
}
};
思路
这种方法还是有一丝偷懒的意味,有着一丝巧力,但是如果要求的是拥有最大和的连续子数组,用这种方法就不行了。其实好像也不是不行,只要我在获得最大值的时候,记住当前的i,就可以有一个结尾的地方了。然后,前面的从第一个开始加,如果加起来是正,那就可以如果,总的加起来是负且小于当前的最大值,直接把前面的若干个值都给去掉了。
22.把数组排数字最小的树
题目
code
class Solution {
public:
static bool cmp(int a,int b)
{
string as = to_string(a);
string bs = to_string(b);
return as+bs<bs+as;
}
string PrintMinNumber(vector<int> numbers) {
//这里采用的是将数组中元素进行组合比对的方式,从而得到所有元素的值
sort(numbers.begin(),numbers.end(),cmp);
string res;
for(auto x:numbers)
{
res+=to_string(x);
}
return res;
}
};
思路
其实要注意要返回的值是字符串的方式,我们只要有一个合理的比较方式就可以了。就可以将数组中的元素排列成一个合理的方式进行排列。
23.和为S的连续正数序列
题目
暴力求解法一
code
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
//暴力求解法
vector<vector<int>> vec;
vector<int> vec2;
if(sum==0)
return vec;
int s = 0;
for(int i = 1;i<sum;i++)
{
for(int j = i;j<sum;j++)
{
s+=j;
if(s>sum)
{
vec2.clear();
s= 0;
break;
}
else if(s<=sum)
{
vec2.push_back(j);
}
if(s==sum)
{
vec.push_back(vec2);
s = 0;
break;
}
}
}
return vec;
}
};
方法二:区间调整法
code
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int>> ret;
int l = 1,r = 1;
int tmp = 0;
while(l<=sum/2)
{
if(tmp<sum)//若当前的区间和小于sum,扩大右边界
{
tmp+=r;
++r;
}
else if(tmp>sum)//发现整个和大于左边界,缩小左边界
{
tmp-=l;
++l;
}
else//否则就是值相等的时候
{
vector<int> ans;
for(int k = l;k<r;k++)//计算一下整个区间的和
{
ans.push_back(k);
}
ret.push_back(ans);
tmp-=l;//减掉最左那个值
++l;//增加左边界
}
}
return ret;
}
};
24. 丑数
题目
code
class Solution {
public:
int GetUglyNumber_Solution(int index) {
//穷举法的话会超时,使用选择前三个丑数*2最小的那一个,做为新的丑数
if(index<=0)//开始的几个数据要进行排除
return 0;
int p2 = 0,p3 = 0,p5 = 0;
int dp[index];//弄一个普通数组就可以存着index这个丑数了
dp[0] = 1;
for(int i = 1;i<index;i++)
{
int n2 = dp[p2]*2;
int n3 = dp[p3]*3;
int n5 = dp[p5]*5;
dp[i] = min(n2,min(n3,n5));//因为第i个丑数一定是这三个数中最小的那个数
if(dp[i]==n2)//所以,只要将对应的那个丑数指针再++就可以了。
++p2;
if(dp[i]==n3)
++p3;
if(dp[i]==n5)
++p5;
}
return dp[index-1];//注意返回的是第index-1个数
}
};
思路:这道题只看文字解题的话,很困难。但其实思路很简单。就是首先,你将三个指针都指向第一个丑数。第二个丑数,其实就是这三个指针分别乘以2,3,5后之中最小的值。我给你举个例子。
25. 数组中的逆序对
题目
超时的暴力方法
code
class Solution {
public:
//这种方法显示是超时的,不能使用的
int InversePairs(vector<int> data) {
if(data.size()==0)
return 0;
int sum = 0;
for(int i = 0;i<data.size();i++)
{
for(int j = i+1;j<data.size();j++)
{
if(data[i]>data[j])
sum++;
sum%=1000000007;
}
}
return sum;
}
};
归并排序算法
code
class Solution {
public:
const int kmod = 1000000007;
int InversePairs(vector<int> data) {
int ret = 0;
//在最外层开辟数组
vector<int> tmp(data.size());
merge_sort(data,tmp,0,data.size()-1,ret);
return ret;
}
//往下传,分区间后,分完后,要往回收
void merge_sort(vector<int> &arr,vector<int> &tmp,int l,int r,int &ret)
{
if(l>=r)
return;
int mid = l+((r-l)>>1);//去取中间的位置
merge_sort(arr,tmp,l,mid,ret);//不断的分成小的区间
merge_sort(arr,tmp,mid+1,r,ret);
merge(arr,tmp,l,mid,r,ret);//每一个区间分完后,最终都是有序的,然后,进行排序
}
void merge(vector<int> &arr,vector<int> &tmp,int l,int mid,int r,int &ret)
{
int i = l,j = mid+1,k = 0;
while(i<=mid&&j<=r)
{
if(arr[i]>arr[j])//只要这个i是大于后面那个j的话,那个这个i后面的那些书也
{
tmp[k++] = arr[j++];
ret +=(mid-i+1);
ret%=kmod;
}
else{
tmp[k++] = arr[i++];
}
}
while(i<=mid)
tmp[k++] = arr[i++];
while(j<=r)
tmp[k++] = arr[j++];
for(k = 0,i=l;i<=r;++i,++k)
{
arr[i] = tmp[k];
}
}
};
26. 正则表达式的匹配
题目
递归回溯法
code
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @param pattern string字符串
* @return bool布尔型
*/
bool match(string s, string p) {
// write code here
if(p.empty())
return s.empty();//若p为空,s也有空,则匹配成功
//查看首元素是否相同
bool first_match = (!s.empty()&&(s[0]==p[0]||p[0]=='.'));
//如果下一个字符是*,就要特殊处理
if(p.size()>=2&&p[1]=='*')
{
//出现这种情况,那么就有两种情况
//第一种,直接把p去掉最前面的两个字符
//第二种,去掉s的第一个字符,然后继续与p等待,这样可以解决*可以代替若干个字符的这种情况
return (match(s,p.substr(2))||(first_match&&match(s.substr(1),p)));
}
else
{
return (first_match&&match(s.substr(1),p.substr(1)));
}
}
};
思路
这种解法 的重点,是要熟知在遇到*时所应该采取的方法。
27. 表示数值的字符串
题目
code
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @return bool布尔型
*/
bool isNumeric(string str) {
// write code here
bool sign = false;
bool num = false;
bool dec = false;
bool point = false;
bool exp = false;
for(int i = 0;i<str.size();i++)
{
if(str[i]=='-'||str[i]=='+')
{
if(sign)//若判断前面已经有这种类似的标志位了,则直接return false即可
return false;
if(i==0||str[i-1]=='e'||str[i-1]=='E')//若-号的前面是e或是E,则也是可以的
sign = true;
else
return false;
}
else if(str[i]=='.')
{
if(exp||point)
return false;
else
point = true;
}
else if(str[i]=='e'||str[i]=='E')
{
if(exp||(!num&&!dec))
return false;
else
{
sign = false;
num = false;
dec = false;
exp = true;
}
}
else if(str[i]>='0'&&str[i]<='9')
{
if(point)
dec = true;
else
num = true;
}
else
return false;
}
return num||dec;
}
};
思路
这道题如果是这种解决方法的话,那就没有太大算的必要性了。因为确实存在太多容易忽略的小细节。而且,也不考察任何的技巧。
28. 按之字形顺序打印二叉树
题目
code
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* root) {
vector<vector<int>> arr_out;
vector<int> arr_small;
if(root==nullptr)
return arr_out;
stack<TreeNode*> st1;
stack<TreeNode*> st2;
st1.push(root);
while(!st1.empty()||!st2.empty())
{
while(!st1.empty())
{
TreeNode* temp1 = st1.top();
arr_small.push_back(temp1->val);
if(temp1->left)
st2.push(temp1->left);
if(temp1->right)
st2.push(temp1->right);
st1.pop();
}
if(arr_small.size())
{
arr_out.push_back(arr_small);
arr_small.clear();
}
while(!st2.empty())
{
TreeNode* temp2 = st2.top();
arr_small.push_back(temp2->val);
if(temp2->right)
st1.push(temp2->right);
if(temp2->left)
st1.push(temp2->left);
st2.pop();
}
if(arr_small.size())
{
arr_out.push_back(arr_small);
arr_small.clear();
}
}
return arr_out;
}
};
思路
这道题按理说可以采用两个栈或是一个栈一个队列的方式进行计算,这样才能较好的解决 反过来的这个问题。
29.序列化二叉树
题目
code
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
string preOrder(TreeNode *root)
{
if(root==nullptr)
return "#";
string ret;
ret += to_string(root->val)+'!';
ret += preOrder(root->left);
ret += preOrder(root->right);
return ret;
}
char retStr[1000]={};
char* Serialize(TreeNode *root) {
//首先,序列化可以使用前序,或是层序遍历都行,这里使用前序
string temp = preOrder(root);
for(int i = 0;i<temp.length();i++)
{
retStr[i] = temp[i];
}
return retStr;
}
int stringlen = 0;
int pos = 0;
TreeNode* buildTree(char *str)
{
if(str[pos]=='#')
{
pos++;
return NULL;
}
int val = 0;
while(str[pos]!='!')//在遇到!之前的那个数字,将其存储下来。
{
val = 10*val+str[pos]-'0';//这里的val,我之前多声明了一次,导致出错,这个错误不应该犯
pos++;
}
pos++;
TreeNode *root2 = new TreeNode(val);
if(pos<stringlen)
root2->left = buildTree(str);
if(pos<stringlen)
root2->right = buildTree(str);
return root2;
}
TreeNode* Deserialize(char *str) {
stringlen = strlen(str);
TreeNode* root = buildTree(str);
return root;
}
};
思路
这道题目的思路倒是不难,但因为我对建树的操作不是很熟,导致没能做出来,其实,我是要new出一个节点,将值填入。然后,之后,就可以递归建树了,以后应该也可以这样。
30. 滑动窗口的最大值
题目
方法一:暴力解法
code
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
vector<int> arr;
if(num.size()==0||size<1||num.size()<size)
return arr;
int max = 0;
for(int i = 0;i<=num.size()-size;i++)
{
for(int j = i+size-1;j<num.size();j++)
{
for(int k= i;k<=j;k++)
{
if(num[k]>max)
{
max = num[k];
}
}
arr.push_back(max);
max = 0;
break;
}
}
return arr;
}
};
方法二:
code
//滑动窗口的最大值
//设定双端队列 deque 实现非严格递减的单调队列,队首就是当前滑窗内的最大元素
//1 - 当出滑窗的元素恰好是单调队列的队头元素,一起出栈
//2 - 让所有小于新加入元素的单调队列元素出队,新元素入队
//3 - 形成滑窗后,取队首元素加入结果 res
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
vector<int> res;
int n = num.size();
if(num.size()<size||num.size()<1||size<1)
return res;
int low = 1-size;//因为要保证当high走到size的时候,才会有第一个元素入数组
int high = 0;
deque<int> dp;
while(high<n)
{
if(low>=1&&num[low-1]==dp[0])//当目前的最大元素是low-1.已经出了滑窗的话,就要出队列了
{
dp.pop_front();
}
while(!dp.empty()&&num[high]>dp[0])//判断是否大于dp[0]
{
dp.pop_front();
}
while(!dp.empty()&&num[high]>dp[dp.size()-1])//判断是否大于dp的最后一个元素
{
dp.pop_back();
}
dp.push_back(num[high]);//直接加入high元素
if(low>=0)//dp[0]每次都是最大值,所以,直接加入,但注意,要low端滑到0的时候,才可以加入该数组
res.push_back(dp[0]);
low++;
high++;
}
return res;
}
};
31. 矩阵中的路径
题目
code
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param matrix char字符型vector<vector<>>
* @param word string字符串
* @return bool布尔型
*/
bool hasPath(vector<vector<char> >& matrix, string word) {
//我来写的话,估计程序比较复杂,并且要耗费很多时间
int rows = matrix.size();
int cols = matrix[0].size();
if(rows==1&&cols==1)
{
if(matrix[0][0]==word[0])
return true;
else
return false;
}
for(int i = 0;i<rows;i++)
{
for(int j =0;j<cols;j++)
{
if(dfs(matrix,rows,cols,word,0,i,j))
return true;
}
}
return false;
}
bool dfs(vector<vector<char> >& matrix,int rows,int cols,string word,int u,int x,int y)
{
if(word[u]=='\0')//这个的意思就是查到字符串的最后了
return true;
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
for(int i =0;i<4;i++)
{
int a = x+dx[i],b=y+dy[i];
if(a>=0&&a<rows&&b>=0&&b<cols&&matrix[a][b]==word[u])
{
char t = matrix[a][b];
matrix[a][b]='*';
if(dfs(matrix,rows,cols,word,u+1,a,b))//只有特定匹配的值才会返回true,其他三路都会返回false
return true;
matrix[a][b] = t;//这种情况就是即使这个点是相等的,但这条路不对,你就要进行回溯
}
}
return false;
}
};
32. 机器人的运动范围
**题目 **
code
class Solution {
public:
int dfs(int threshold, int i, int j,int rows, int cols,vector<vector<bool>>&used)
{
if(i<0||j<0||i>=rows||j>=cols||(i/10+i%10+j/10+j%10)>threshold||used[i][j])
return 0;
used[i][j]=true;
//是统计多条路径加起来可以到达的格子数,实际是可以回退。
return 1+dfs(threshold,i+1,j,rows,cols,used)+dfs(threshold,i-1,j,rows,cols,used)+dfs(threshold,i,j+1,rows,cols,used)+dfs(threshold,i,j-1,rows,cols,used);
}
int movingCount(int threshold, int rows, int cols) {
vector<vector<bool>> used(rows,vector<bool>(cols,false));
return dfs(threshold,0,0,rows,cols,used);
}
};
思路
这道题目根前面那道题还是很像的,就是开始的动态数组的初始化不是很熟,导致耽误了很长的时间。
33.旋转数组的最小数字
题目
暴力解法一
code
class Solution {
public:
int minNumberInRotateArray(vector<int> arr) {
if(arr.size()==0)
return 0;
int min = arr[0];
for(int i =1;i<arr.size();i++)
{
if(arr[i]<min)
{
min = arr[i];
}
}
return min;
}
};
二分解法二
code
class Solution {
public:
int minNumberInRotateArray(vector<int> arr) {
if(arr.size()==0)
return 0;
int first=0,last=arr.size()-1;
while(first<last)
{
if(arr[first]<arr[last])
return arr[first];//证明应该就还是非递减序列,直接返回即可。
int mid = first+(last-first)/2;
if(arr[mid]>arr[last])
{
first = mid+1;
}
else if(arr[mid]<arr[last])//仔细分析一下整个数组的规律可得
{
last = mid;
}
else
{
--last;
}
}
return arr[first];
}
};
思路
整个二分法其实肯定就是提升整个查询到那个最小值的速度了。你首先跟端点进行比较,然后,确定那个最小值有可能在当前这个index的左边还是右边,进而移动right和left的范围。思路倒也不是很难。但是要注意的情况是这种1 ,1 ,1 ,0,1这种情况,这种就只能逐渐的移动指针了。
34.斐波那契数列
题目
递归解法一
code
int Fibonacci(int n) {
if (n==0 || n==1) return n;
return Fibonacci(n-1) + Fibonacci(n-2);
}
动态规划解法
code
class Solution {
public:
int Fibonacci(int n) {
//动态递归的解法
vector<int> dp(n+1,0);
dp[1] =1;
for(int i=2;i<=n;++i)
{
//从下往上,不断的求到dp[n]。其实就是不断的把之前的值进行存储,然后进行使用,这就是动态递归
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
思路
这题关于递归的思路当然是很清晰的,就是有时候忘记了,0的时候是0,而1的时候是1。而比较需要掌握的是关于动态规划的方法。其实就是不断的利用前面计算的值去计算后面的值。
35. 跳台阶
题目
解法一:递归解法
code
class Solution {
public:
int jumpFloor(int n) {
//整个的逆向思维就是下台阶
if(n<=1)
return 1;
return jumpFloor(n-1)+jumpFloor(n-2);
}
};
解法二:动态规划解法
code
class Solution {
public:
int jumpFloor(int n) {
//整个的逆向思维就是下台阶
vector<int> dp(n+1,0);
dp[0]=1;
dp[1]=1;
for(int i = 2;i<=n;i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
思路
主要难点还是这个思路的转化。青蛙跳台阶,可以逆向思维成,青蛙下台阶,就可以得出这样的一个式子:f(n)=f(n-1)+f(n-2);
36.跳台阶的扩展问题
题目
code
class Solution {
public:
int jumpFloorII(int n) {
if (n==0 || n==1) return 1;
vector<int> f(n+1, 0);
f[0] = f[1] = 1;
for (int i=2; i<=n; ++i) {
for (int j=0; j<i; ++j) {
f[i] += f[j];
}
}
return f[n];
}
};
思路
这道题目的思路倒还是比较明确的,反正就是把之前的f(n)=f(n-1)+f((n-2)改成f(n)=f(n-1)+…f(1)+f(0).其实就是这样的一个思路而已。
37. 矩形覆盖
题目
方法一:递归算法
code
class Solution {
public:
int rectCover(int n) {
vector<int> dp(n+1,0);
dp[0]=0;
dp[1]=1;
dp[2]=2;
for(int i =3;i<=n;i++)
{
dp[i] = dp[i-1]+dp[i-2];
}
return dp[n];
}
};
方法二:动态规划的算法
code
class Solution {
public:
int rectCover(int n) {
vector<int> dp(n+1,0);
dp[0]=0;
dp[1]=1;
dp[2]=2;
for(int i =3;i<=n;i++)
{
dp[i] = dp[i-1]+dp[i-2];
}
return dp[n];
}
};
思路
关于这道题目,主要就是关于dp[2]的值要提前进行定义,并且,使用动态规划的算法进行计算。
38. 二进制中一的个数
题目
解法一:
code
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
while(n)
{
count++;
n = n&(n-1);
}
return count;
}
};
思路
这种属于递归解法,但这种解法真是属于很聪明的解法,真的值得记住。
34.数值的整数次方
题目
code
class Solution {
public:
double Power(double base, int exponent) {
if(exponent<0)
{
base = 1/base;
exponent = -exponent;
}
double sum=1.0;
for(int i = 0;i<exponent;i++)
{
sum *=base;
}
return sum;
}
};
35.调整数组顺序使奇数位于偶数前面
题目
解法一:要开辟额外空间去存储
code
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param array int整型vector
* @return int整型vector
*/
vector<int> reOrderArray(vector<int>& array) {
vector<int> arr(array.size());
int k = 0;
for(int i = 0;i<array.size();i++)
{
if(array[i]%2==1)
{
arr[k++]= array[i];
}
}
for(int i = 0;i<array.size();i++)
{
if(array[i]%2==0)
{
arr[k++]= array[i];
}
}
return arr;
}
};
思路
这个思路当然是较为通俗易懂的,但是确实要开辟另一个空间,有可能题目会做限制。
方法二:用一个while循环,就可以解决这个问题
code
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param array int整型vector
* @return int整型vector
*/
vector<int> reOrderArray(vector<int>& array) {
if(array.size()==0)
return array;
vector<int> num(array.size());
int head = 0;
int tail = array.size()-1;
int index_head = head;
int index_tail=tail;
while(head<array.size()&&tail>=0)
{
if(array[head]%2==1)
{
num[index_head] = array[head];
index_head++;
}
head++;
if(array[tail]%2==0)
{
num[index_tail]=array[tail];
index_tail--;
}
tail--;
}
return num;
}
};
36. 链表中倒数最后k个结点
题目
解法
code
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead ListNode类
* @param k int整型
* @return ListNode类
*/
ListNode* FindKthToTail(ListNode* head, int k) {
if(head==NULL)
return NULL;
ListNode* node1 = head;
ListNode* node2 = head;
while(k--)
{
if(node1==NULL)
return NULL;
node1 = node1->next;
}
while(node2&&node1)
{
node1 = node1->next;
node2 = node2->next;
}
return node2;
}
};
思路
这种解法还是比较简单的,就是简单的双指针解法。
37. 翻转链表
题目
code
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* head) {
if(head==nullptr)
return nullptr;
ListNode* pre = NULL;
ListNode* node = head;
ListNode* next = NULL;
ListNode* reverseHead = NULL;
while(node)
{
next = node->next;
if(next==NULL)
reverseHead = node;
node->next = pre;
pre = node;
node = next;
}
return reverseHead;
}
};
思路
确实我觉得多锻炼代码真的是有用的,现在这道题目,我用了不到5分钟,基本就手撕出来了。感觉很简单,但之前,可能花费的时间就很长,所以说,天道酬勤。加油。这道题就是要把节点的指向稍微改一下,然后,做一下赋值就很快。
38. 合并两个有序的数组
题目
解法一:递归迭代法
code
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* head1, ListNode* head2) {
ListNode *head = new ListNode(-1);
ListNode *cur = head;
while(head1&&head2)
{
if(head1->val<=head2->val)
{
cur->next = head1;
head1 = head1->next;
}
else
{
cur->next = head2;//就简单把这里的head2当做是一个节点就可以了
head2 = head2->next;
}
cur = cur->next;
}
cur->next= head1?head1:head2;
return head->next;//第一个新建的节点不算,从next开始算起
}
};
解法二:递归法
code
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* head1, ListNode* head2) {
//递归方法
if(!head1)
return head2;
if(!head2)
return head1;
if(head1->val<=head2->val)
{
head1->next = Merge(head1->next,head2);//若head1->val<=head2->val。则代表去连接head1的这个节点
return head1;
}
else
{
head2->next = Merge(head1,head2->next);//若head->val>head2->val,则代表去连接head2的这个节点
return head2;
}
}
};
39.树的子结构
题目
code
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
bool HasSubtree(TreeNode* root1, TreeNode* root2) {
if(root1==NULL||root2==NULL)
return false;
bool result = false;
if(root1->val==root2->val)
result = HadSubuTreeHelper(root1,root2);
if(!result)
result = HasSubtree(root1->left,root2);
if(!result)
result = HasSubtree(root1->right,root2);
return result;
}
bool HadSubuTreeHelper(TreeNode* root1,TreeNode* root2)
{
if(root2==NULL)
return true;
if(root1==NULL)
return false;
if(root1->val!=root2->val)
return false;
return HadSubuTreeHelper(root1->left,root2->left)&&HadSubuTreeHelper(root1->right,root2->right);
}
};
40.二叉树的镜像
题目
code
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
TreeNode* Mirror(TreeNode* root) {
if(root==nullptr)
return nullptr;
Mirror(root->left);
Mirror(root->right);
TreeNode* temp = root->left;
root->left = root->right;
root->right = temp;
return root;
}
};
思路
这道题目说难也不难,就是自己也有一定的思路,但最后,在几分钟内没写出来,证明还是对递归的整个思路不熟,有机会还是要抽个时间攻克下这部分的内容。加油。
41. 顺时针打印矩阵
题目
code
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
//做一个循环,不断的往内圈走
vector<int> arr;
if(matrix.empty())
return arr;
int rows = matrix.size();
int cols = matrix[0].size();
int left = 0;
int right = cols-1;
int top = 0;
int bottom = rows-1;
while(left<=right&&top<=bottom)
{
//从左到右
for(int i = left;i<=right;i++)
{
arr.push_back(matrix[top][i]);
}
//从上到下
for(int j = top+1;j<=bottom;j++)
{
arr.push_back(matrix[j][right]);
}
//接下来。从右从左走。判断是不是出现只有一行的情况了
if(top!=bottom)//这两个部分很重要,如果前面已经top==bottom,说明只有一行了,就没必要
{
for(int i = right-1;i>=left;--i)
{
arr.push_back(matrix[bottom][i]);
}
}
//从下面往上走。判断是不是只有一行了
if(left!=right)//如果只剩一列了,则没必要再往下走了
{
for(int j = bottom-1;j>top;--j)
{
arr.push_back(matrix[j][left]);
}
}
left++,right--,top++,bottom--;
}
return arr;
}
};
思路
整个思路都还是较为简单的,但是有几个部分要注意。就是在循环时,对终止条件的判断,在发现只剩最后一行的时候,只要走一遍,从左到右的数据遍历就可以了,没必要再从右往左重复走一遍。同理,若只剩一列了,则只需要从上往下走一遍就可以了,没必要从下往上再走一遍。
42. 包含min函数的栈
题目
code
class Solution {
public:
stack<int> st1;
stack<int> st2;
void push(int value) {
if(st1.empty())
{
st1.push(value);
st2.push(value);
}
else
{
st1.push(value);
if(value<=st2.top())
{
st2.push(value);
}
else
{
st2.push(st2.top());
}
}
}
void pop() {
st1.pop();
st2.pop();
}
int top() {
return st1.top();
}
int min() {
return st2.top();
}
};
解析
这道题最开始做的时候,比较难的地方应该是在理解题意。
43. 栈的压入、弹出序列
题目
code
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
if(pushV.size()!=popV.size())
return false;
stack<int> st;
int k = 0;
for(int i = 0;i<pushV.size();i++)
{
st.push(pushV[i]);
while(!st.empty()&&st.top()==popV[k])//这里要判断一下栈是否为空,别盲目出栈
{
st.pop();
k++;
}
}
return st.empty();
}
};
44. 从上往下打印二叉树
题目
code
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root) {
vector<int> arr;
if(root==nullptr)
return arr;
queue<TreeNode*> que;
que.push(root);
while(!que.empty())
{
TreeNode* node = que.front();
arr.push_back(node->val);
que.pop();
if(node->left)
que.push(node->left);
if(node->right)
que.push(node->right);
}
return arr;
}
};
45. 二叉搜索树的后序遍历序列
题目
code
class Solution {
public:
bool VerifySquenceOfBST(vector<int> seq) {
if(seq.size()==0)
return false;
return isSeq(seq,0,seq.size()-1);
}
bool isSeq(vector<int> seq,int left,int right)
{
if(left>=right)
return true;
int index = right;
while(index>left&&seq[index-1]>seq[right])
{
--index;
}
for(int i = index-1;i>=left;--i)
{
if(seq[i]>seq[right])
return false;
}
return isSeq(seq,left,index-1)&&isSeq(seq,index,right-1);
}
};
46.二叉树中和为某一值的路径
题目
code
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
vector<vector<int>> ret;
vector<int> path;
if(root==nullptr)
return ret;
dfs(root,expectNumber,path,ret);
return ret;
}
void dfs(TreeNode *root,int sum,vector<int> &path,vector<vector<int>> &ret)
{
path.push_back(root->val);
if(root->val==sum&&!root->left&&!root->right)
{
ret.push_back(path);
}
if(root->left)
dfs(root->left,sum-root->val,path,ret);
if(root->right)
dfs(root->right,sum-root->val,path,ret);
path.pop_back();
}
};
思路
这道题目的核心还是得抓住遍历的思想和递归的思想。才能做出来。