剑指offer第六章

1.数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在数组中出现了4次,所以输出4
分析:思路1:直官思路:直接顺序扫描,O(n)
 class Solution {
public:
int GetNumberOfK(vector<int> data ,int k)
{
int count=;
for(int i=;i<data.size();i++)
{
if(data[i]==k)
count++;
}
return count; }
};
思路2:因为是排序数组,所以想到二分查找,关键在于如何用二分查找找到第一个和最后一个k
找第一个k:先拿数组中间的数和k做比较,中间数字比k大,那么k只能出现在前半段,中间比k小,那么k只能出现在后半段,如果相等,先判断是不是第一个k如果中间数字的前一个不是k,则找到第一个k。如果前一个也是k,也就是说第一个k肯定在数组前半段,下一轮继续在前半段查找。
找最后一个k:同上
 class Solution {
/*二分查找 找到第一个K 和 最后一个K 二者位置相减*/
public:
int GetNumberOfK(vector<int> data ,int k) {
if(data.empty())
return ;
int number = ;
int first = GetFirstIndex(data,k,,data.size()-);
int last = GetLastIndex(data,k,,data.size()-);
if(first>- && last>-)
number = last - first +;
return number; }
int GetFirstIndex(vector<int> &data,int k,int start,int end){
if(start > end)
return -;
int mid = start+(end-start)/;
if(data[mid] == k){
if((mid == start) || (data[mid-] != k))
return mid;
else
end = mid-;
}
else{
if(data[mid] > k)
end = mid - ;
else
start = mid + ;
}
return GetFirstIndex(data,k,start,end);
}
int GetLastIndex(vector<int> &data,int k,int start,int end){
if(start > end)
return -;
int mid = start+(end-start)/;
if(data[mid]==k){
if((mid == end) || (data[mid+] != k))
return mid;
else
start = mid +;
}
else{
if(data[mid]>k)
end = mid-;
else
start = mid+;
}
return GetLastIndex(data,k,start,end);
}
};

2.二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
分析:递归
 /*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
if(pRoot==NULL)
return ;
int nLeft=TreeDepth(pRoot->left);
int nRight=TreeDepth(pRoot->right);
int depth=;
if(nLeft>nRight)
depth=nLeft+;
else
depth=nRight+;
return depth;
}
};

3.平衡二叉树

输入一颗二叉树,判断该树是不是平衡二叉树。
如果某二叉树中任意结点的左右子树的深度相差不超过1,那么他就是一颗平衡二叉树
分析:容易想到的思路:在遍历树的每个结点的时候,调用函数TreeDepth得到他的左右子树的深度,如果每个结点的左右子树的深度相差不超过1,则就是平衡二叉树
 class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot)
{
if(pRoot==NULL)
return true;
int left=TreeDepth(pRoot->left);
int right=TreeDepth(pRoot->right);
int diff=left-right;
if(diff>||diff<-)
return false;
return IsBalanced_Solution(pRoot->left)&&IsBalanced_Solution(pRoot->right);
}
int TreeDepth(TreeNode* pRoot)
{
if(pRoot==NULL)
return ;
int nLeft=TreeDepth(pRoot->left);
int nRight=TreeDepth(pRoot->right);
int depth=;
if(nLeft>nRight)
depth=nLeft+;
else
depth=nRight+;
return depth;
}
};

4.数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都只出现了两次,找出这两个数字
要求:时间复杂度是O(n),空间复杂度O(1)
分析:首先:位运算中异或的性质:两个相同数字异或=0,一个数和0异或还是它本身。当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。
 两个相同数字异或=0,一个数和0异或还是它本身。
当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。
依照这个思路,我们来看两个数(我们假设是AB)出现一次的数组。我们首先还是先异或,剩下的数字肯定是A、B异或的结果,这个结果的二进制中的1,表现的是A和B的不同的位。我们就取第一个1所在的位数,记为第n位,接着把原数组分成两组,分组标准是第n位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。
 class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2)
{
int length=data.size();
if(length<)
return;
if(length==)
{
num1[] = data[];
num2[] = data[];
return;
}
int bitResult = ;
for(int i = ; i < length; ++i)
{
bitResult ^= data[i];
}
int index = findFirst1(bitResult);
*num1=*num2=;
for(int i = ; i < length; ++i)
{
if(isBit1(data[i], index))
{
*num1 ^= data[i];
}
else
{
*num2 ^= data[i];
}
}
}
int findFirst1(int num)
{
int index=;
while(((num&)==)&&(index<*sizeof(int)))
{
num=num>>;
++index;
}
return index;
}
bool isBit1(int num,int index)
{
num=num>>index;
return (num&);
}
};

5.和为s的两个数字

输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
分析:先在数组中选择两个数字,如果他们的和等于S,则找到;如果小于S,选择较小的数字后面的数字;如果大于S,选择较大数字前面的数字。因为数组已经是有序的。
 class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum)
{
int length=array.size();
vector<int> res;
int ahead=length-;
int behind=;
while(ahead>behind)
{
long long curSum=array[ahead]+array[behind];
if(curSum==sum)
{
res.push_back(array[behind]);
res.push_back(array[ahead]);
break;
}
else if(curSum>sum)
{
ahead--;
}
else
{
behind++;
}
}
return res;
}
};

6.和为s的连续正数序列

输入一个正数S,打印出所有和为S的连续正数序列(至少含有两个数)
分析:首先考虑用两个数small和big分别表示序列的最小值和最大值。首先把small初始化为1,big初始化为2。如果从small到big的序列和大于S,则去掉序列中较小的值,也就是增加small的值;如果小于S,增大big,让序列包含更多的数字,一直增加到small为(1+S)/2为止
 class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum)
{
vector<vector<int> > res;
if(sum<)
return res;
int small=;
int big=;
int middle=(+sum)/;
int curSum=small+big;
while(small<middle&&big<sum)
{
while(curSum > sum)
{
curSum -= small;
small++;
}
if(curSum==sum)
{
InsertRes(small,big,res);
}
big++;
curSum+=big;
}
return res;
}
void InsertRes(int small,int big,vector<vector<int> > &res)
{
vector<int> temp;
for(int i = small;i<=big;i++)
temp.push_back(i);
res.push_back(temp);
}
};

7.反转单词序列

输入一个英文句子,反转句子中单词的顺序,但单词内字符的顺序不变,标点符号和普通字母一样处理
分析:第一步翻转句子中所有字符;第二步再翻转每个单词中字符的顺序
 class Solution {
public:
string ReverseSentence(string str)
{
ReverseWord(str,,str.size()-);//先整体翻转
int start=,end=;
int i=;
int length=str.size();
while(i<length)
{
while(i<length&&str[i]==' ')//空格跳过
{
i++;
}
start=end=i;
while(i<length&&str[i]!=' ')
{
i++;
end++;
}
ReverseWord(str,start,end-);
}
return str;
}
void ReverseWord(string &str, int start, int end)
{
while(start < end)
{
swap(str[start++], str[end--]);
}
}
};

8.左旋转字符串

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它
分析:三次翻转,先翻转前n个部分,再翻转后一部分,再翻转整体
 class Solution {
public:
string LeftRotateString(string str, int n)
{
int length = str.size();
if(length<=)
return str;
if(length>&& n>&&n<length)
{
int firstStart = ;
int firstEnd = n-;
int secondStart = n;
int secondEnd = length-;
Reverse(str,firstStart,firstEnd);
Reverse(str,secondStart,secondEnd);
Reverse(str,firstStart,secondEnd);
}
return str;
}
void Reverse(string &str, int start, int end)
{
while(start < end)
{
swap(str[start++], str[end--]);
}
}
};

9.扑克牌的顺子

从扑克牌中随机抽取5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以为任意数字。
分析:首先把数组排序,在统计数组中0的个数,最后统计排序之后的数组中相邻数字之间的空缺总数。如果空缺总数小于或者等于0的个数,那么这个数组就是连续的;反之则不连续
注意:如果非零数字重复出现,则不连续
 class Solution {
public:
bool IsContinuous( vector<int> numbers ) {
if(numbers.size()!=)
return false;
sort(numbers.begin(),numbers.end());
int i=;
while(numbers[i]==)
i++;
if(numbers[]-numbers[i]>)
return false;
for(int j=i;j<;j++){
if(numbers[j]==numbers[j+])
return false;
}
return true;
}
};

10.圆圈中最后剩下的数字

0,1,...,n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。求这个圆圈里剩下的最后一个数字。
分析:两种解法:
解法一:经典解法,用环形链表模拟圆圈
 class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
if(n<||m<)
return -;
int i=;
list<int> numbers;
for(i=;i<n;++i)
numbers.push_back(i);
list<int>::iterator current=numbers.begin();//开始点
while(numbers.size()>)
{
for(int i=;i<m;++i)
{
current++;//指针后移
if(current==numbers.end())
current=numbers.begin();
}
list<int>::iterator next=++current;
if(next==numbers.end())
next=numbers.begin();
--current;
numbers.erase(current);
current=next;
}
return *(current);
}
};
解法二:创新解法:找到数学规律
                        递推公式    f[1]=0; 
                                            f[i]=(f[i-1]+m)%i;  (i>1) 
 class Solution {
public:
int LastRemaining_Solution(unsigned int n, unsigned int m)
{
if(n==)
return -;
if(n==)
return ;
else
return (LastRemaining_Solution(n-,m)+m)%n;
}
};

11.求1+2+3+...+n

求1+2+3+...+n要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句
分析:解题思路:
1.需利用逻辑与的短路特性实现递归终止。
2.当n==0时,sum&&((sum+=Sum_Solution(n-1)))只执行前面的判断,为false,然后直接返回0;
3.当n>0时,执行sum+=Sum_Solution(n-1),实现递归计算Sum_Solution(n)。
 class Solution {
public:
int Sum_Solution(int n)
{
int sum = n;
sum&&((sum+=Sum_Solution(n-)));
return sum;
}
};

12.不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
分析:用位运算代替二进制加法
第一步不考虑进位对每一位相加==》采用异或
接着考虑第二步进位,可以想象为是两个数先做位与运算,然后再向左移动一位
第三步把前两个步骤结果相加。第三步相加的过程依然是重复前两步,直到不产生进位为止
 class Solution {
public:
int Add(int num1, int num2)
{
int sum,carry;
do{
sum=num1^num2;
carry=(num1&num2)<<;
num1=sum;
num2=carry;
}while(num2!=);
return num1;
}
};

剑指offer第六章的更多相关文章

  1. 剑指offer第七章&第八章

    剑指offer第七章&第八章 1.把字符串转换成整数 将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数. 数值为0或者字符串不是一个合法的数值则返回0 输入描述: 输入一个字符串 ...

  2. 剑指offer第五章

    剑指offer第五章 1.数组中出现次数超过一半的数 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字. 例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组 ...

  3. 剑指offer第四章

    剑指offer第四章 1.二叉树的镜像 二叉树的镜像:输入一个二叉树,输出它的镜像 分析:求树的镜像过程其实就是在遍历树的同时,交换非叶结点的左右子结点. 求镜像的过程:先前序遍历这棵树的每个结点,如 ...

  4. 剑指offer第三章

    剑指offer第三章 1.数值的整数次方 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方. class Solution { public ...

  5. 《剑指Offer》第二章(一)题3-8

    为春招实习做准备,记录一下<剑指Offer>里面的面试题 第二章 面试题3:数组之中的重复数字. 这个题吧,虽然不难,但是不知道为什么就是看了很久,可能很久没有做算法题了.最后面一句话说的 ...

  6. 《剑指Offer》第二章(一)题 9 -12

    第二章 面试题9:用两个栈实现队列 题目:如面试题,给你两个栈, 实现队列的先进先出,即在队列头删除一个元素以及在队列的尾部添加一个元素 思路:这个题的分析感觉很巧妙,从一个具体的例子入手,找出其中的 ...

  7. 剑指offer—第三章高质量代码(o(1)时间删除链表节点)

    题目:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点,链表节点与函数的定义如下:struct ListNode{int m_nValue;ListNode* m_pValue ...

  8. 剑指offer—第三章高质量的代码(按顺序打印从1到n位十进制数)

    题目:输入一个数字n,按照顺序打印出1到最大n位十进制数,比如输入3,则打印出1,2,3直到最大的3位数999为止. 本题陷阱:没有考虑到大数的问题. 本题解题思路:将要打印的数字,看成字符串,不足位 ...

  9. 剑指offer—第三章高质量代码(数值的整数次方)

    高质量的代码:容错处理能力,规范性,完整性.尽量展示代码的可扩展型和可维护性. 容错处理能力:特别的输入和处理,异常,资源回收. 规范性:清晰的书写,清晰的布局,合理的命名. 完整性:功能测试,边界测 ...

随机推荐

  1. brew 与 nvm

    brew  与 nvm 是两个管理软件工具 今天更新了brew结果brew下安装的软件都找不着了.得重新安装,据说brew已经不再更新了.应该是通过github的吧. 结果得重装node与npm,这两 ...

  2. C# SQLite写入和读取DateTime类型

    很简单 1.不要相信网上大部分人说的话,比如存到int里 (ps:版本差距知道吗?) 2.nuget包下载最新版的sqlite 3.SQLite支持DateTime类型(图形化工具不会给提示无视它), ...

  3. 设计模式--备忘录模式C++实现

    备忘录模式C++实现 1定义Memento pattern 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可以将该对象恢复到原先的状态 2类图 3实现 clas ...

  4. 虚拟机VMware搭建代码环境

    安装git yum install git -y 安装nvm curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.29.0/ins ...

  5. zabbix selinux audit2allow 问题

    he following example demonstrates using audit2allow to create a policy module: A denial and the asso ...

  6. angularJS----filter

    angularJS过滤器 过滤器(filter)正如其名,作用就是接收一个输入(隐式的接收数据源),通过某个规则进行处理,然后返回处理后的结果.主要用在数据的格式化上,例如获取一个数组中的子集,对数组 ...

  7. jps、jstack、jmap、jhat、jstat、hprof使用详解

    https://my.oschina.net/feichexia/blog/196575#comment-list A. jps(Java Virtual Machine Process Status ...

  8. 重温ASP.NET WebAPI(二)进阶

    重温ASP.NET WebAPI(二)进阶   介绍 本文为个人对WebApi的回顾无参考价值. 本文内容: Rest和UnitOfWork 创建WebAPi的流程 IOC-Unity的使用 MEF ...

  9. 2: 使用Prism初始化程序(纯汉语版)

    本篇内容讲解了Prism应用程序启动和运行都发生了什么.一个Pris应用程序在程序启动期间需要注册和配置——这被叫做引导应用程序.Prism引导过程包括创建和配置一个模块目录,创建一个例如Unity的 ...

  10. 淘宝TDDL配置以及使用

    此章节具体介绍一下淘宝TDDL具体配置和使用 1. Spring配置文件配置:================spring-mybatis.xml 中配置============= <bean ...