眼瞅着研二就快结束了,论文还没创新点,实习也没找,秋招快开始了,于是乎临时抱佛脚,想着刷一刷剑指offer热热手来着,献上解题步骤和代码,供自己参阅,也给诸君一个参考,希望对大家有所帮助。
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
if(array.size()==0)
{
return false;
}
int width_1 = array.size();
int width_first = width_1-1;
int width_2 = array[0].size();
int width_second = 0;
while(width_first>-1 && width_second < width_2)
{
if(target==array[width_first][width_second])
{
return true;
}
else{
if(target<array[width_first][width_second]){
width_first--;
}
else {
width_second++;
}
}
}
return false;
}
};
本题为大家做的最多的题,主要抓住数列从左到右,从上到下递增,注意语法,一般不太会错。好久没做了,看错题的我傻傻的写反了,僵硬
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串We%20Are%20Happy。
class Solution {
public:
void replaceSpace(char *str,int length) {
if (length==0||(!str)){
return; }
int place_count = 0;
int length_self = 0;
for (int i=0;str[i]!='\0';i++)
{
length_self++;
if(str[i]==' ')
{
place_count ++;
}
}
char *str_1 = str+length_self;
char *str_2 = str+length_self+2*place_count;
while(str_1<str_2)
{
if(*str_1 == ' ')
{
*str_2 -- = '0';
*str_2 -- = '2';
*str_2 -- = '%';
}
else{
*str_2-- = *str_1;
}
str_1--;
}
}
};
本题考察的重点是指针的概念:
以上题为例:
char *str; //为传入的参数,该参数str内是一个char类型变量的地址,
//即str = char型变量的实际地址值
char *str_1 = str+length_self; //此处将以str为标准地址,length_self为
//偏移地址的地址值在str_1内保存
*str_1 == ' '; //此处为str_1指针指向的char型变量用法
*str_2-- = *str_1; //此处用法展示了指针在进行所指的变量操作后,
//再进行++或--,仍是对其中保存的地址进行操作
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> ArrayList;
while(head)
{
ArrayList.push_back(head->val);
head=head->next;
}
//方法1:reverse
reverse(ArrayList.begin(),ArrayList.end());
return ArrayList;
//方法2:
return vector<new>(ArrayList.rbegin(),ArrayList.rend());
}
};
这个依次迭代就好,链表长度不可知,所以考察重点在怎么把vector中内容翻转,我在这里使用了algorithm库中的reverse函数,也可以使用vector自身的迭代器进行处理.
重点在方法2,可以使用vector.rbegin(),vector.rend()这两个封装好的指针,直接在return中生成新的vector作为函数的返回值
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
TreeNode* node = rreConstructBinaryTree(0,pre.size()-1,0,vin.size()-1,pre,vin);
return node;
}
TreeNode* rreConstructBinaryTree(int a1,int a2,int b1,int b2,vector<int> pre,vector<int> vin)
{
TreeNode* node;
node->val = pre[a1];
if(a1==a2){
return node;
}
auto iter = find(vin.begin(),vin.end(),pre[a1]);
int tem_index =distance(vin.begin(),iter);
node->left = rreConstructBinaryTree(a1+1,a1+tem_index-b1,b1,tem_index-1,pre,vin);
node->right = rreConstructBinaryTree(a1+tem_index-b1+1,a2,tem_index+1,b2,pre,vin);
return node;
}
};
尝试用python解决,发现几行代码就可以实现:
class Solution:
# 返回构造的TreeNode根节点
def reConstructBinaryTree(self, pre, tin):
if not pre or not tin:
return None
root = TreeNode(pre.pop(0))
index_root = tin.index(root.val)
root.left = self.reConstructBinaryTree(pre,tin[:index_root])
root.right = self.reConstructBinaryTree(pre,tin[index_root+1:])
return root
# write code here
em,不死心,继续尝试使用C++用同样的思路解决问题,最终两天后搞定了处理方法:
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
if(vin.size()==0||pre.size()==0)
return NULL;
TreeNode* node = new TreeNode(pre[0]);
//方法1:
//int index_iter = 0;
//for(int i=0;i
//{
// if(pre[0]==vin[i])
// {
// index_iter = i;
// break;
// }
//}
//方法2:使用find找到指针,然后和初始地址相减得到index
auto iter = find(vin.begin(),vin.end(),pre[0]);
int index_iter = distance(vin.begin(),iter);
vector<int>pre_left,pre_right,vin_left,vin_right;
for(int i = 0;i<index_iter;i++)
{
pre_left.push_back(pre[i+1]);
vin_left.push_back(vin[i]);
}
for(int i=index_iter+1;i<vin.size();i++)
{
pre_right.push_back(pre[i]);
vin_right.push_back(vin[i]);
}
node->left = reConstructBinaryTree(pre_left,vin_left);
node->right = reConstructBinaryTree(pre_right,vin_right);
return node;
}
};
如题
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if(stack2.empty())
{
while(!stack1.empty())
{
stack2.push(stack1.top());
stack1.pop();
}
}
int temp=stack2.top();
stack2.pop();
return temp;
}
private:
stack<int> stack1;
stack<int> stack2;
};
主要考察stack的用法
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
//方法1:比较所有元素 39ms+1004K
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.empty()) return 0;
int flag = rotateArray[0];
for(int i=1;i<rotateArray.size();i++)
{
flag = min(flag,rotateArray[i]);
}
return flag;
}
};
//方法2:直接找前一个元素大于后一个元素的位置就可以 34ms+992K
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.empty()) return 0;
for(int i=0;i<rotateArray.size()-1;i++)
{
if(rotateArray[i]>rotateArray[i+1])
{
return rotateArray[i+1];
}
}
return rotateArray[0];
}
};
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
//4ms,596K
class Solution {
public:
int Fibonacci(int n) {
if(n==0) return 0;
if(n==1) return 1;
int pre = 0;
int form = 1;
for(int i=0;i<n-1;i++)
{
int temp = pre;
pre = form;
form = temp+pre;
}
return form;
}
};
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
class Solution {
public:
int jumpFloor(int number) {
if(number == 1) return 1;
if(number == 2) return 2;
if(number == 3) return 3;
return jumpFloor(number-1)+jumpFloor(number-2);
}
};
熟悉递归用法即可
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
class Solution {
public:
int jumpFloorII(int number) {
if(number == 1) return 1;
if(number == 2) return 2;
int num=0;
for(int i=1;i<number;i++)
{
num += jumpFloorII(i);
}
return num+1;
}
};
还是要注意递归用法
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
class Solution {
public:
int rectCover(int number) {
if(number <= 0) return 0;
int num_1 = 1;
if(number == 1) return 1;
int num_2 = 2;
if(number == 2) return 2;
int num_3;
for(int i = 3;i<=number;i++)
{
num_3 = num_1+num_2;
num_1 = num_2;
num_2 = num_3;
}
return num_3;
}
};
仍然是斐波那契数列,观察后得以发现,可以递归,也可以直接写循环
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
class Solution {
public:
int NumberOf1(int n) {
int flag = 0;
int trig = 1;
while(trig)
{
if(n&trig){
flag++;
}
trig = trig<<1;
}
return flag;
}
};
本想用整数除法解决,先把负数转换为正数,完成求补码的过程,但是一直有小一半的测试集无法通过,后来又想了想,本身负数在计算机中用补码保存,所以应该按照按位操作的原则进行处理。所以应该定义一个int flag = 1;然后逐位和原数相与,每次结果为1时判断有一个1存在,计位数加1。
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
class Solution {
public:
double Power(double base, int exponent) {
return pow(base,exponent);
}
};
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
class Solution {
public:
void reOrderArray(vector<int> &array) {
int flag = INT_MIN;
for(int i=0;i<array.size();i++)
{
if(array[i]%2==0){
flag = i;
break;
}
}
for(int i = flag+1;i < array.size();i++)
{
if(array[i]%2==1){
array.insert(array.begin()+flag,array[i]);
array.erase(array.begin()+i+1);
flag++;
}
}
}
};
我采用了找到从左向右的第一个偶数,然后将其后的每一个奇数依次insert插入在该偶数处,原数使用.erase()删除,进而实现该功能
输入一个链表,输出该链表中倒数第k个结点。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
ListNode* head = pListHead;
int len = 0;
while(head)
{
head = head->next;
len++;
}
if(len<k) return NULL;
while(len>=len-k-1)
{
pListHead=pListHead->next;
len--;
}
return pListHead;
}
};
我选择先定义一个链表指针,然后将改指针迭代到链表尾部求得链表长len,再用len和k处理得到链表结果。
输入一个链表,反转链表后,输出新链表的表头。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(!pHead) return NULL;
ListNode* node_pre = pHead;
ListNode* node_next = pHead->next;
node_pre->next = NULL;
while(node_next){
ListNode* temp = node_next->next;
node_next->next = node_pre;
node_pre = node_next;
node_next = temp;
}
return node_pre;
}
};
注意保存前后两个节点,第一次头结点指向NULL,其后,依次在循环中:1.将后节点的下一个节点保存;2.将后节点指向前节点;3.将前节点改为后节点,后节点改为原链表的下一个节点,完成编程(提醒自己,写代码多动脑子,每次做题做错都是因为不动脑子= =)
怎么说呢?还需要加深对指针赋值的理解
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
//非递归处理方法
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(!pHead1 && !pHead2) return NULL;
ListNode* head = new ListNode(0);
ListNode* node = head;
while(pHead1!=NULL && pHead2!=NULL){
if (pHead1->val>pHead2->val){
node->next = pHead2;
node = node->next;
pHead2 = pHead2->next;
}
else{
node->next = pHead1;
node = node->next;
pHead1 = pHead1->next;
}
}
if(pHead1==NULL){
node->next = pHead2;
}
if(pHead2==NULL){
node->next = pHead1;
}
return head->next;
}
};
首先是使用非递归的方法,任意声明一个节点,然后再声明一个变量保存空节点,将该节点的next判断后依次指向下一个节点,最终得到结果。
//递归处理方法
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(!pHead1&&!pHead2){
return NULL;
}
if(!pHead1){
return pHead2;
}
if(!pHead2){
return pHead1;
}
if(pHead1->val>pHead2->val){
ListNode* node = new ListNode(pHead2->val);
node->next = Merge(pHead1,pHead2->next);
return node;
}
else{
ListNode* node = new ListNode(pHead1->val);
node->next = Merge(pHead1->next,pHead2);
return node;
}
}
};
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
//递归解决方法
/*
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* pRoot1, TreeNode* pRoot2)
{
if(!pRoot1||!pRoot2) return false;
return IsSame(pRoot1,pRoot2)||HasSubtree(pRoot1->left,pRoot2)||HasSubtree(pRoot1->right,pRoot2);
}
bool IsSame(TreeNode* a,TreeNode* b){
if(!b) return true; //这里只用!b,是因为子树只要B是A的一部分就行,即根部分或者子树部分两方向都可以有缺失
if(!a) return false;
if(a->val!=b->val){
return false;
}
else{
return IsSame(a->right,b->right)&&IsSame(a->left,b->left);
}
}
};
还是注意设定跳出递归的条件,准确理解题目要求,加注释处须注意,包含B结构意味着只要将B是A的某个子树的一部分即可(不要求和A从某个节点开始直到最终节点完全相等)
操作给定的二叉树,将其变换为源二叉树的镜像。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
void Mirror(TreeNode *pRoot) {
if(!pRoot) return ;
TreeNode* temp = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = temp;
Mirror(pRoot->left);
Mirror(pRoot->right);
}
};
超简单的一道题,确实需要递归,但是直接递归调用主函数就行了,找准停止条件,为空节点即可,想复杂了,好久没做出来···代码功底还是差,加油,多刷题
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> out_mat;
int a=0;
int m = matrix[0].size();
int b=0;
int n = matrix.size();
int count=0;
while(m>a&&n>b){
for(int i=a;i<m;i++){
out_mat.push_back(matrix[b][i]);
}
b++;
if(m>a){
for(int i=b;i<n;i++){
out_mat.push_back(matrix[i][m-1]);
}
}
m--;
if(n>b){
for(int i=m-1;i>=a;i--){
out_mat.push_back(matrix[n-1][i]);
}
}
n--;
if(m>a){
for(int i= n-1;i>=b;i--){
out_mat.push_back(matrix[i][a]);
}
}
a++;
}
return out_mat;
}
};
开始的时候,受之前一个用python螺旋读取图片像素点的方法诱导,我选择使用方向参数direct依次按方向判断处理矩阵,后来发现想的太复杂了,只要有四个参数,每次执行四次,分别对不同的参数处理一次,在后三次处理时加入判断该for循环能否执行的条件即可,该次运行结束后,while循环条件会自动终止该迭代,返回结果即可
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
class Solution {
public:
stack<int> stemp;
stack<int> smin;
void push(int value) {
stemp.push(value);
if(smin.empty()){
smin.push(value);
}
else{
if(smin.top()>value){
smin.push(value);
}
}
}
void pop() {
if(smin.top()==stemp.top()){
smin.pop();
}
stemp.pop();
}
int top() {
return stemp.top();
}
int min() {
return smin.top();
}
};
主要考察栈的特点,需要根据栈的特点进行编程,每次入栈是,判断最小栈顶元素是否大于当前压入元素,决定是否入栈,每次弹出原堆栈时,判断最小栈的栈顶元素是否被弹出,故当前最小值为最小值栈顶元素
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
//使用辅助栈,建立两个指针依次上挪,看第二个指针能否被挪到第二个vector尾部
//可以则说明顺序合理,不可以则不行
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
stack<int> temp;
int p1=0;
int p2=0;
while(p2 < popV.size()){
if(pushV[p1]==popV[p2]){
p1++;p2++;
}
else if(!temp.empty()&&temp.top()==popV[p2]){
p2++;
temp.pop();
}
else if(p1<pushV.size()){
temp.push(pushV[p1]);
p1++;
}
else{
return false;
}
}
return true;
}
};
巧妙使用指针处理问题,也曾经单纯从排序方面考虑过,但是结果最多只有66.7%的测试案例可以通过
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
/*
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> temp;
if(!root) return temp;
queue<TreeNode*> line;
line.push(root);
TreeNode* p;
while(!line.empty()){
temp.push_back(line.front()->val);
if(line.front()->left){
line.push(line.front()->left);
}
if(line.front()->right){
line.push(line.front()->right);
}
line.pop();
}
return temp;
}
};
简单版本,记得leetcode上有用二维矩阵保存的题目,和本题区别在于中间插入NULL节点,每次遇到空节点则push_back当时的一维容器进二维容器里,迭代至队列为空。
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
//使用两个额外的标记作为子树头尾索引的标记
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
if(sequence.size()==0){
return false;
}
return isBST(sequence,0,sequence.size()-1);
}
bool isBST(vector<int> sequence,int pstart,int pend){
if(pstart>pend){
return true;
} //此处判断前面所有元素是否只为左子树或右子树元素,index=pstart时,传入处的flag-1就小于0了,即pstart>pend
int temp = sequence[pend];
int flag = pstart;
while(sequence[flag]<temp&&flag<pend){
flag++;
}
for(int i=flag;i<pend;i++){
if (sequence[i]<temp){
return false;
}
}
return isBST(sequence,pstart,flag-1)&&isBST(sequence,flag,pend-1);
}
};
见代码注释和代码逻辑
输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
//也是看了结果才写的,把题目想复杂了
/*
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>> final_buff;
vector<int> temp;
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
if(!root){
return final_buff;
}
temp.push_back(root->val);
if(expectNumber-root->val==0&&!root->left&&!root->right){
final_buff.push_back(temp);
}
FindPath(root->left,expectNumber-root->val);
FindPath(root->right,expectNumber-root->val);
if(temp.size()!=0){ //在左子树调用该程序后,会在返回前删除左子树加入的元素,避免在兄弟右子树调用时的值影响
temp.pop_back();
}
return final_buff;
}
};
主要是额外创建一个二维容器和一个一维容器,在递归过程中及时将符合条件的一维容器及时放进二维容器中,然后程序结尾处及时清理要使用两次的值,代码段中有注释。
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)