又来更新剑指offer上的题目思路啦。

11、【二进制中1的个数】

题目:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

eg:NumberOf1(1)=1
NumberOf1(2)=0
NumberOf1(3)=2
NumberOf1(4)=1
NumberOf1(5)=2
NumberOf1(6)=2
NumberOf1(7)=3

思路:每次都将数字n的最后一位1反转成0,不断反转到这个数字变成0,然后我们统计反转了多少次,这样不就可以成功得到这个数字有多少位了吗?

难点:如何反转一个数字的最后一位1,解决方案如下:

n = n & (n - 1);

代码:

     int  NumberOf1(int n) {int number = 0;while (n != 0){n = n & (n - 1);++number; }return number;}

思路2:可不可以尝试不去改变这个数字呢?答案也可以的。假设是int类型,如果我循环32次,判断每个位是否为1,这样不也可以得到这个数字有多少位为1吗?诚然,这样也是存在着缺点的。

缺点:比思路1的时间复杂度略高,不过也是常数级别的,因为数字的位数是有限制的,两者可能系数上存在着差别吧!~~~还有一个缺点,就是这样的代码移植性较差~~

存在坑:

(temp & n) != 0

这里的括号不能去掉,因为&的优先级比 != 低。。。。

代码:

     int  NumberOf1(int n) {int number = 0;int temp = 1;for (int i = 0; i < 32; ++i){if ((temp & n) != 0){++number;}temp = temp<<1;}return number;}

12、【数值的整数次方】

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

例子:2^10

思路1:2*2*2*2*2*2*2*2*2*2,循环N次即可。时间复杂度O(n)没啥好说的,跳过。

思路2: 分治想法,假设是求a^n

  如果n为偶数,那么转化成求x=a^(n/2),然后返回x*x即可。

  如果n为奇数,那么转化成求x=a^(n-1/2),然后返回x*x*a即可。

  时间复杂度是O(lgn)。。。怎么证明?

应用算法导论中的主方法即可。

这里,有T(n) = T(n/2) + 1,即 b = 2, a = 1,f(n)=1。因此满足条件2,因此时间复杂度为O(nlogb(a))=O(nlog2(1)*lgn)=O(1*lgn)=O(lgn)。

哈哈,是不是瞬间感觉写难了。。。。

13、【调整数组顺序使奇数位于偶数前面】

题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

思路:两个数组,一个按顺序保存奇数,另一个按顺序保存偶数。。。时间复杂度O(N),空间复杂度O(N)。

思路2:创建一个数组,循环2次,第一次只扫描奇数,丢进数组中。第二次只扫描偶数,丢进数组。。十分简单。。。

思路3:类似冒泡算法,前偶后奇数就交换。

    void reOrderArray(vector<int> &arr) {//双指针int n = arr.size();for(int i = 0; i < n; ++i){for(int j = 0; j < n-i-1; ++j){if(arr[j]%2 == 0 && arr[j+1]%2 == 1) swap(arr[j],arr[j+1]);}}}

思路4:两个指针咯,一个指针1从头到尾,另外一个指针2从尾到头,如果指针1发现偶数,那么停下来,如果指针2发现奇数,那么也停下来。再交换两个指针指向的数就Ok了。

   问题:这样是真的可以吗?答案是不可以的哦~哈哈,因为这样违反了题目要求——要求奇数和偶数顺序不能改变。。。嗯,是的,如果是让顺序可以随意的话,那么这就是很nice的解。

14、【链表中倒数第k个结点】

题目:输入一个链表,输出该链表中倒数第k个结点。

思路1:遍历得到链表长度n,然后再遍历一次,给返回第n-k+1个元素。这里遍历了两次,时间复杂度为0(n),空间复杂度为O(1)。

思路2:思路1是否存在优化的地方呢?是存在的,我们可以考虑考虑能不能不遍历两次,这时候我们想能不能用空间换时间。

    好比,将链表转化成数组保存下来,那么我们第二次遍历链表操作可以换成获取数组中的n-k+1个元素操作了,只需要遍历一次链表就可以了。但是这种方法的空间复杂度为O(N)。

思路3:两个指针,也就是传说中的快慢指针。一个指针先走k步,然后慢指针才开始跑。快指针到头了,那么落后了k步的慢指针不就是倒数第k个元素了吗?

代码:

    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {if(NULL == pListHead) return NULL;ListNode* pfront = pListHead;ListNode* pend = pListHead;for (int i = 0; i < k; i++){if(pfront != NULL){pfront = pfront->next;}else{return NULL;//超出了}}for(;pfront != NULL;pfront = pfront->next){pend = pend->next;}return pend;}

15、【反转链表】

输入一个链表,反转链表后,输出链表的所有元素。

A->B->C->D->EA->B->C->D<-E (D->NULL)A->B->C<-D<-E (C->NULL)A->B<-C<-D<-E (B->NULL)A<-B<-C<-D<-E (A->NULL)

代码

    ListNode* ReverseList(ListNode* pHead){if(pHead == NULL||pHead->next == NULL) return pHead;//得到链表尾部ListNode* tail = ReverseList(pHead->next);pHead->next->next = pHead;pHead->next = NULL;return tail;}

16、【合并两个排序的链表】

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

额,这题啥也不用说了吧,,,不就是归并排序的合并过程么。。。。直接贴代码吧。。。

    ListNode* Merge(ListNode* pHead1, ListNode* pHead2){ListNode* res = NULL;ListNode* cur = NULL;ListNode* add = NULL;if(pHead1 == NULL) return pHead2;if(pHead2 == NULL) return pHead1;while(pHead1 != NULL &&pHead2 != NULL)//两个迭代器{add = pHead1->val >= pHead2->val ?pHead2:pHead1;if(res == NULL)  res = cur = add;第一次才执行 在于选择一条链出来else{cur->next = add;cur = cur->next;}if(add == pHead1) pHead1=pHead1->next;else pHead2 = pHead2->next;}if(pHead1 != NULL) cur->next = pHead1;if(pHead2 != NULL) cur->next = pHead2;return res;}

17、【树的子结构】

题目:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

其实这里主要是靠对问题的拆分能力。

  子函数CompTree:判断两棵树是否相等。

  判断是否为子树:假设当前节点为根,那么是否为子结构?

代码:

class Solution {
private:bool CompTree(TreeNode* pRoot1, TreeNode* pRoot2)//判断2是否为1的子树。。。。{if(pRoot2 == NULL) return true;if(pRoot1 == NULL) return false;if(pRoot1->val == pRoot2->val) return CompTree(pRoot1->left,pRoot2->left)&&CompTree(pRoot1->right,pRoot2->right);return false;}
public:bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2){if(pRoot1 == NULL ||pRoot2 == NULL ) return false;return CompTree(pRoot1,pRoot2) || HasSubtree(pRoot1->left,pRoot2) || HasSubtree(pRoot1->right, pRoot2);}
};

18、二叉树的镜像

如果这时候,我们用自然语言去描述,可以这样认为怎么将一颗二叉树转化成源二叉树的镜像:交换两棵树的左子树指针、右子树指针。

假设树的结构如下:

struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};

我们轻易得到第一个版本可以AC的代码。使用递归即可。

零、先序遍历递归版本

class Solution {
public:void Mirror(TreeNode *pRoot) {if(pRoot != NULL){TreeNode *tmp = pRoot->left;pRoot->left = pRoot->right;pRoot->right = tmp;if (pRoot->left != NULL) {Mirror(pRoot->left);}if (pRoot->right != NULL) {Mirror(pRoot->right);}}}
};

但是这个代码是不是最优的呢?很显然,不是,因为其用到递归,可以用非递归实现吗?答案是可以的。

这时候,我们仔细观看一下上面的代码,发现,如果我们将交换左右子树指针的代码转换成print树节点node值。那么上面的代码不就是先序遍历树的代码吗?

因此,我们可以深思,本质上,这条题的解答,就是遍历树,然后将每一个节点的左右孩子交换。

那么我们又想到了树的遍历方式,不难得到以下几种解答:

一、基于队列的广度优先遍历

class Solution {
public:void Mirror(TreeNode *pRoot) {//if(pRoot != NULL){//基于队列广度优先遍历的的版本1queue<TreeNode*> que;que.push(pRoot);while(!que.empty()){TreeNode* pt = que.front();que.pop();if(pt->left != NULL || pt->right != NULL ){TreeNode* temp = pt->left;pt->left = pt->right;pt->right = temp;}if(pt->left != NULL){que.push(pt->left);}if(pt->right != NULL){que.push(pt->right);}}}}
};

二、后续遍历的递归版本

class Solution {
public:void Mirror(TreeNode *pRoot) {//if(pRoot != NULL){//后序遍历整个二叉树,交换里面的两个指针,Mirror(pRoot->left);Mirror(pRoot->right);TreeNode* temp = pRoot->left;pRoot->left = pRoot->right;pRoot->right = temp;*/}}
};

三、先续遍历版本改出来的非递归版本

class Solution {
public:void Mirror(TreeNode *pRoot) {//if(pRoot != NULL){stack<TreeNode*> stk;stk.push(pRoot);while(!stk.empty()){TreeNode* pt = stk.top();stk.pop();if(pt->left != NULL){stk.push(pt->left);}if(pt->right != NULL){stk.push(pt->right);}if(pt->left != NULL || pt->right != NULL ){TreeNode* temp = pt->left;pt->left = pt->right;pt->right = temp;}}}}
};

四、基于后续遍历版本改出来的非递归版本

class Solution {
public:void Mirror(TreeNode *pRoot) {//if(pRoot != NULL){stack<TreeNode*> stk;stk.push(pRoot);while(!stk.empty()){TreeNode* pt = stk.top();stk.pop();if(pt->left != NULL || pt->right != NULL ){TreeNode* temp = pt->left;pt->left = pt->right;pt->right = temp;}if(pt->left != NULL){stk.push(pt->left);}if(pt->right != NULL){stk.push(pt->right);}}}}}
};

神奇的代码,,,已经写完了,其实这里考的是将一个未知问题转化成一个已知问题去解决。

19、逆时针打印矩阵

题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,

例如,如果输入如下矩阵: 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.

还是画图吧,假设要打印的矩阵为:

1   2   3   4
5   6   7   8
9   10  11  12
13  14  15  16

我们首先打印
1   2   3   4
5           8
9           12
13  14  15  16

然后打印
    6   7 10  11

是不是很清晰?我们只要实现一个子函数,完成打印正方形的工作,然后写个循环,不断从外到里去调用那个子函数就ok了。

代码如下:

class Solution {
public:void GetArray(vector<vector<int> > &matrix,vector<int> &res,int ibegin,int iend,int jbegin,int jend){//注意 ibegin == iend时候 循环 1 3执行两次 所以要判断//同理 jbegin == jend也有这个情况int i = 0;//cout<<ibegin<<" "<<iend<<" "<<jbegin<<" "<<jend<<" "<<endl;for(i = jbegin; i <= jend;++i) res.push_back(matrix[ibegin][i]);//左到右for(i = ibegin+1; i <= iend;++i) res.push_back(matrix[i][jend]);//上到下if(ibegin != iend)for(i = jend-1; i >= jbegin;--i) res.push_back(matrix[iend][i]);//右到左if(jbegin != jend)for(i = iend-1; i > ibegin;--i) res.push_back(matrix[i][jbegin]);//下到上}vector<int> printMatrix(vector<vector<int> > matrix) {int row = matrix.size();//行int col = matrix[0].size();//列vector<int>  res;int ibegin = 0;int iend = row-1;//int jbegin = 0;int jend = col-1;for(;ibegin <= iend && jbegin <= jend;++ibegin,--iend,++jbegin,--jend){GetArray(matrix,res,ibegin,iend,jbegin,jend);}return res;}
};

这里考的是对问题的分解能力。

20、包含max函数的栈

题目:定义栈的数据结构,请在该类型中实现一个能够得到栈最大元素的max函数。

假设存在栈 1, 2, 3, 3, 2, 4, 5, 2, 1
那么可以搞一个辅助栈 1, 2, 3, 3, 3, 4, 5, 5, 5

其中辅助栈保存的是栈中的最大元素即可,当弹出时候,两边同时弹出即可。

代码:

class Solution {
public:void push(int value) {mdata.push(value);if(mmax.empty()) mmax.push(value);else{int val = mmax.top();val = val < value ? val:value;mmax.push(val);}}void pop() {mdata.pop();mmax.pop();}int top() {mmax.pop();int val = mdata.top();mdata.pop();return val;}int min() {return mmax.top();}
private:stack<int> mdata;stack<int> mmax;
};

转载于:https://www.cnblogs.com/ccXgc/p/9031974.html

剑指offer解题思路锦集11-20题相关推荐

  1. 【LeetCode】《剑指Offer》第Ⅰ篇⊰⊰⊰ 3 - 11题

    [LeetCode]<剑指Offer>第Ⅰ篇⊰⊰⊰ 3 - 11题 文章目录 [LeetCode]<剑指Offer>第Ⅰ篇⊰⊰⊰ 3 - 11题 03. 数组中重复的数字(ea ...

  2. 剑指offer笔记(七) 第47题至第53题

    剑指offer笔记(七) 第47题至第53题 前言 一.JZ47 礼物的最大价值 二.JZ48 最长不含重复字符的子字符串 三.JZ49 丑数 四.JZ50 第一个只出现一次的字符 五.JZ51 数组 ...

  3. LeetCode和剑指offer题目大合集

    AlgorithmCode 本仓库收集一些算法的答案,目标是整理一套系统的算法参考答案以供其他学习者参考,我也在慢慢的学算法并且在坚持刷题,我会不定期的上传新的题目,希望大家共同努力! https:/ ...

  4. 剑指offer解题记录(JAVA)

    面试题3:数组中重复的数字 题目链接 import java.util.Arrays;/*** P39 面试题3:数组中重复的数字* 在一个长度为n的数组里所有数字都在0~n-1的范围内 数组中某些数 ...

  5. java计算筛子概率_剑指Offer解题报告(Java版)——n个骰子的点数 43

    问题 n个骰子朝上的数之和为s,求s的所有可能以及概率 分析问题 如果是用笨方法,一般人最开始都会想到笨方法,那就是枚举法 举个例子,比如两个骰子,第一个骰子的结果为1,2,3,4,5,6,两个骰子的 ...

  6. java翻转单词顺序split_剑指offer解题报告(Java版)——翻转单词顺序 左旋字符串 42...

    引言 这种翻转的问题会遇到很多,其实就是一个倒序的问题,对于第一个题只是想翻转单词的顺序,而并不想把整个字符串翻转了,如果完全翻转的话,比如I am a student.中所有字符翻转得到.tnedu ...

  7. [剑指offer][JAVA]面试题第[11]题[旋转数组的最小数字][二分法][分治]

    [问题描述][简单] 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如,数组 [3,4,5,1,2] 为 [1,2,3, ...

  8. java 数组中某个数出现的概率_剑指Offer解题报告(Java版)——排序数组中某个数的个数 38...

    分析问题 问题只需要找到排序数组中某个数K的个数,由于已经是排序了,K一定是在一堆的,所以我们只需要找到第一个K的index1,然后找到最后一个K的index2就可以了 而寻找K的过程我们一般通过二分 ...

  9. C++剑指offer:解题报告之DP优化学习记 (二) ——浅论DP斜率优化 (Print Article 【HDU - 3507】 )

    链接:https://share.weiyun.com/5LzbzAc 目录 前言 斜率优化前期准备 1.从状态转移方程出发 2.推理状态转移方程 对结论的进一步推导 干货!综合结论 判断斜率大小的方 ...

最新文章

  1. “金三银四”跳槽需谨慎(送福利礼包)!
  2. iebook 发布到网站 独家秘诀
  3. 汇编和python-python语言属于汇编语言吗?_后端开发
  4. wordpress mysql 安装_wordpress 搭建安装教程 1 安装数据库、SQLyog
  5. C语言函数与接口有什么区别?
  6. springboot整合rabbitmq(搭建)
  7. hdu - 1827 Summer Holiday (强连通)
  8. docker部署python web应用_安装docker并部署web项目
  9. 在Python中使用设置文件的最佳做法是什么? [关闭]
  10. python的枚举函数_enumerate()函数~~返回一个枚举对象
  11. Go基础编程:Socket编程
  12. STM32CubeMX | 30-使用硬件SPI读写FLASH(W25Q64)
  13. HTML5调整图像垂直边距,77.通过vspace和hspace属性可以分别调整图像的垂直边距和水平边距。()()...
  14. VS2010:X64和X86冲突问题
  15. python爬虫系列之下载在线文档Excel(石墨)
  16. 互联网创业公司是否需要技术外包?
  17. 关于Navicat和DBeaver的个人使用中问题
  18. 三种加快计算机启动速度的办法,电脑开机慢怎么解决?四种提速方法,前三种方法不花钱就能提速!...
  19. 风影版OllyDBG 1.10 2016-12-09修改版
  20. Python爬虫响应码为404错误

热门文章

  1. mysql 事务处理
  2. ZeroClipboard 和JqueryUI_dialog 完美组合!
  3. 即时通讯软件设计(一)
  4. 如何更好的格式化Objective-C代码
  5. distinct 只针对一个字段
  6. 一些前端面试题(一)
  7. 最近学习的 Node.js 之 http
  8. 004-ubuntu安装配置SSH服务
  9. vue中key的作用
  10. 《java入门第一季》之面向对象面试题(继承中构造方法的关系)