剑指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. Unity教程之-UGUI一个优化效率小技巧

    无意间发现了一个小技巧.如下图所示,可以发现UGUI的Image组件的RaycastTarget勾选以后会消耗一些效率,为了节省效率就不要勾选它了,不仅Image组件Text组件也有这样的问题. 一般 ...

  2. VC中release和debug版本的区别

    DEBUG版本 包含调试信息,编译器生成的代码方便调试 RELEASE版本 算是发布版本,RELEASE下不能调试,生成的文件更小,编译器生成的程序速度更快但是Debug下生成的exe文件在单独运行时 ...

  3. 基于JQuery 的消息提示框效果代码

    提示框效果 一下是封装到 Jquery.L.Message.js 中的JS文件内容 var returnurl = ''; var messagebox_timer; $.fn.messagebox ...

  4. zabbix selinux audit2allow 问题

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

  5. 普通用户启动redis

    重庆231 Redis 服务器 redis用户启动 复制 /etc/init.d/redis 启动脚本到 /redisdata/redis3.2下面,修改内容 [root@localhost ~]# ...

  6. NPOI:初次操作(新建Excel)

    1. 由于在某些电脑上没有安装office,或者有权限限制,使用COM组件进行读写Excel的话会出现问题, 为此,NPOI是一个很好的选择,NPOI可以在上述环境中满足Office的操作需求,并且功 ...

  7. flume 启动,停止,重启脚本

    #!/bin/bash #echo "begin start flume..." #flume的安装根目录(根据自己情况,修改为自己的安装目录) path=/sysware/apa ...

  8. 《Effective C++》第8章 定制new和delete-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  9. 经典问题:查询有多少段区间和等于k值

    题目连接 题意:在大小为1e5以内的数组求存在多少个区间和的值等于k的次方 这种题很经常见,总是想着用两个for循环解决,但是一定会超时. 题解:算出前缀和,使用map去查找mp[sum[i+1]-t ...

  10. Linux系统下第三方软件安装实例

    在第三方软件安装的时候,首先要有自己的rpm安装包!然后要有可以使用的yum源(前面有关于yum源的介绍) 一.安装wps步骤如下: 1)首先进入安装包所在目录,并查看有没有要安装的软件 2)然后,对 ...