目录
剑指Offer(15)--二进制中1的个数
题目:
请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。
#include<iostream>
using namespace std;
/*
方法一:
首先把n和1做与运算,判断n的最低位是不是1.接着把1左移一位,再和
n做与运算,判断n的次低位是不是1.....,直到最后将1左移32位之后
变为0停住
*/
int NumberOf1(int n)
{
int count = 0;
unsigned int k = 1;
while (k)
{
if (n&k)
count++;
k = k << 1;
}
return count;
}
/*
方法二:
把一个整数减去1,再和原整数做与运算,旧把该整数最右边的1变成0。
那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的
操作。
*/
int NumberOf1(int n)
{
int count = 0;
while (n)
{
++n;//只要n不为0,说明至少有一个1
n = (n - 1)&n;
}
return count;
}
剑指Offer(41)--数据流中的中位数
题目:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
数据流是指一直会有数据进行加入
#include<iostream>
#include<vector>
#include<algorithm>
#include<xfunctional>
using namespace std;
template<class T>
/*
维护一个大顶堆和一个小顶堆,且小顶堆中的最小元素都大于大顶堆
中的最大元素,且两个堆的数量相差最大为1,则中位数为小顶堆的
最小元素,也就是堆头;或者是大顶堆和小顶堆堆头的平均值。
两个堆中数据的数目之差不超过1,当大堆中的元素和小堆中的元素
相等时,加入小堆,否则加入大堆。能够保证大堆小堆相差数目为1
*/
class DynamicArray
{
public:
void Insert(int num)
{
//偶数时的情况,应该往min里面插
if (((min.size() + max.size()) & 1) == 0)
{
if (max.size()>0&&num<max[0])
{
max.push_back(num);
//当已建堆的容器范围内有新的元素插入末尾后,
//应当调用push_heap将该元素插入堆中。
push_heap(max.begin(), max.end(), less<T>());
num = max[0];
//将堆顶(所给范围的最前面)元素移动到所给范围的最后,
//并且将新的最大值置于所给范围的最前面
pop_heap(max.begin(), max.end(), less<T>());
max.pop_back();
}
min.push_back(num);
push_heap(min.begin(), min.end(), greater<T>());
}
//奇数时的情况,往大根堆里面插
else
{
if (min.size() > 0 && min[0]<num)
{
min.push_back(num);
push_heap(min.begin(), min.end(), greater<T>());
num = min[0];
pop_heap(min.begin(), min.end(), greater<T>());
min.pop_back();
}
max.push_back(num);
push_heap(max.begin(), max.end(), less<T>());
}
}
T GetMedian()
{
int size = min.size() + max.size();
if (size == 0)
throw exception("No numbers are avalible");
T median = 0;
if ((size & 1) == 1)
median = min[0];
else
median = (min[0] + max[0]) / 2;
return median;
}
private:
vector<T> min;
vector<T> max;
};
剑指Offer(49)--丑数
题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。求按从小到大的顺序的第1500个丑数。例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做第一个丑数。
#include<iostream>
#include<algorithm>
using namespace std;
//方法一:挨个判断每个整数是不是丑数
bool IsUgly(int number);
int GetUglyNumber(int index)
{
if (index <= 0)
return -1;
int number = 0;//对从1开始的每个数进行判断
int count = 0;//第几个丑数
while (count < index)
{
++number;
if (IsUgly(number))
++count;
}
return number;
}
bool IsUgly(int number)
{
while (number % 2 == 0)
number /= 2;
while (number % 3 == 0)
number /= 3;
while (number % 5 == 0)
number /= 5;
return (number == 1) ? true : false;
}
/*
方法二:以空间换取时间,创建数组保存已经找到的丑数
根据丑数的定义,丑数应该是另一个丑数乘以2、3、5的
结果,创建一个数组,里面是排好序的丑数
*/
int Min(int value1, int value2, int value3);
int GetUglyNumber2(int index)
{
if (index <= 0)
return -1;
int *arr = new int[index];//创建丑数的集合,保存所有已找到的丑数
arr[0] = 1;
int next = 1;//下一个丑数的下标
int *p2 = arr;
int *p3 = arr;
int *p5 = arr;
while (next < index)
{
int min = Min(*p2*2, *p3*3, *p5*5);
arr[next] = min;
//只要小于,说明*p2*2的结果已经在数组中了
while (*p2 * 2 <= min)
++p2;
while (*p3 * 3 <= min)
++p2;
while (*p5 * 5 <= min)
++p5;
++next;
}
int result = arr[next - 1];
delete[] arr;
return result;
}
int Min(int value1, int value2, int value3)
{
int value = min(value1, value2);
return min(value, value3);
}
剑指Offer(64)--求1+2+....+n
题目:
求1+2+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字即条件判断语句(A?B:C)。
#include<iostream>
#include<vector>
using namespace std;
class Count{
public:
Count()
{
++N;
Sum += N;
}
static void Reset() { N = 0; Sum = 0; }
static unsigned int GetSum() { return Sum; }
private:
static unsigned int N;
static unsigned int Sum;
};
unsigned int Count::N = 0;
unsigned int Count::Sum = 0;
unsigned int ResultSum(unsigned int n)
{
Count::Reset();
Count *p = new Count[n];
delete[]p;
p = nullptr;
return Count::GetSum();
}
剑指Offer(65)--不用加减乘除做加法
题目:写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
思路:三部曲
第一步:不考虑进位对每一位相加。和异或的结果是一样的,相同为0,不同为1。
第二步:进位,只有1加1的时候才产生进位,所以可以想象成两个数先做与运算,然后左移一位。
第三步:把前面两者的结果相加。
之后进行重复操作,直到不产生进位为止。
#include<iostream>
using namespace std;
int Add(int num1, int num2)
{
//sum是只相加不产生进位,carry是进位
int sum, carry;
do
{
//相同为0,不同为1,起到了相加的效果
sum = num1^num2;
//只有相同位置都为1才需要有进位
carry = (num1&num2) << 1;
num1 = sum;
num2 = carry;
} while (num2 != 0);
return num1;
}
衍生问题:二进制做乘法
其实就是乘数是几,就进行几次加法运算。
/*
* a: 被乘数
* b: 乘数
*/
int multiply(int a, int b){
// 取绝对值
int multiplicand = a < 0 ? Add(~a, 1) : a;
int multiplier = b < 0 ? Add(~b , 1) : b;// 如果为负则取反加一得其补码,即正数
// 计算绝对值的乘积
int product = 0;
int count = 0;
while(count < multiplier) {
product = Add(product, multiplicand);
count = Add(count, 1);// 这里可别用count++,都说了这里是位运算实现加法
}
// 确定乘积的符号
if((a ^ b) < 0) {// 只考虑最高位,如果a,b异号,则异或后最高位为1;如果同号,则异或后最高位为0;
product = Add(~product, 1); //如果是异号,取反加一就是相应的负数
}
return product;
}
衍生问题:不使用新的变量,交换两个数的值
#include<iostream>
using namespace std;
int main(void)
{
//基于加减法
int a, b;
a = a + b;
b = a - b;
a = a - b;
//基于异或运算
a = a^b;
b = a^b;
a = a^b;
return 0;
}
跳跃游戏II
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
思路:贪心算法
//时间复杂度(n),空间复杂度O(1)
int jump(vector<int>& nums)
{
const int length = nums.size();
if (length == 1)
return 0;
int step = 0;//最小步数
//[left,right]是当前能覆盖的区间
int left = 0;
int right = 0;
while (left <= right)
{
++step;
int oldRigth = right;
for (int i = left; i <= oldRigth; ++i)
{
int newRight = i + nums[i];
if (newRight >= length - 1)
return step;
if (newRight > right)
right = newRight;
}
left = oldRigth + 1;
}
return -1;
}