题目描述:输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示
利用递增,右上角为分割点
class Solution {
public:
int NumberOf1(int n) {
//bitset运用 将n转化为 32位二进制数表示 不足32位的前面补齐即可 count()二进制数中1的个数
return bitset<32>(n).count();
}
};
class Solution {
public:
int NumberOf1(int n) {
//右移运算
int result = 0;
for(int i=0; i<32; i++)
{
if((n & 1)!=0) result++;
n = n>>1;
//if((n & (1 << i)) != 0) result++;//左移
}
return result;
}
};
思路:n&(n−1)n&(n-1)n&(n−1),会将n的二进制中最低位由1变成0
如果一个整数不为0,那么这个整数至少有一位是1。如果把这个数减1,那么原来处在整数最右边的1就会变为0,如果最右边的1后面还有0的话,原来在1后面的所有的0都会变成1。其余所有位将不会受到影响。
举个例子: 一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011。也就是说减1的结果是把最右边的一个1开始的所有位都取反了。
这时,如果再把原来的整数和减去1之后的结果做与运算,来整数最右边的1之后的所有位都会变成0,如1100&1011=1000。也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0。那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
class Solution {
public:
int NumberOf1(int n) {
int result = 0;
//与操作
while(n)
{
n = n & (n-1);
result++;
}
return result;
}
};
题目:实现函数 double Power(double base, int exponent),求base的exponent次方。
给定一个double类型的浮点数base和int类型的整数exponent,保证base和exponent不同时为0。不得使用库函数,同时不需要考虑大数问题,也不用考虑小数点后面0的位数
方法都是看题解的
class Solution {
public:
double Power(double base, int exponent) {
if(base == 0.0) return 0;
if(exponent == 0) return 1;
//数学计算
bool flag = false;
if(exponent < 0)
{
flag = true;
exponent *= -1;
}
double result = base;
for(int i=1; i<exponent; i++)
{
result *= base;
}
if(flag) return 1.0 / result;
return result;
}
};
以下内容搬运力扣大佬的题解,很清晰,作者是jyd,来源力扣,链接
也是在模拟幂运算的过程,如果是偶数次方幂运算,底数扩大2倍,幂数就要缩小2倍;如果是奇数次方幂运算,取出一个底数,就变成了偶数次方的幂运算了,过程一样。所以才会有幂数对2取余数( 判断二进制数最右一位值)的操作了。
注意点:
long exp = exponent;
这一步原因如下:这里是因为计算机内int的范围问题。在普通计算机内,负数都是补码表示的。int32的范围是[-2147483648, 2147483648],但int是无法正常表示-2147483648的补码,计算机里-2147483648的补码和2147483648的补码一样,这是因为溢出了,表示不了,而在long类型下可以正常表示-2147483648。
if(exponent < 0) exp = exponent * (-1.0);
这一步原因如下:当exponent == INT_MIN
时,对应的相反数是大于INT_MAX的,所以要用一个大于 INT_MAX的类型来保存。把此时的exponent转正数时, exponent*(-1)的结果依然是int类型(隐藏类型),然后将结果赋值给 exp。但用来保存结果值的不应该是个int型,因此用double型的-1(-1.0) ,这样就可以将相乘的结果值保存为一个 double类型了,然后再进行赋值。
class Solution {
public:
double Power(double base, int exponent) {
if(base == 0.0) return 0;
if(exponent == 0) return 1;
//快速幂运算
long exp = exponent;
if(exponent < 0)
{
exp = exponent * (-1.0);
}
double result = 1.0;
while (exp != 0)
{
//exp对2取余数 等价于 判断二进制数最右一位值是0还是1,即exp & 1
if((exp & 1) == 1) result *= base;//奇数次方时,可以再往上乘一个
base *= base;//叠加
//exp对2向下取整 等价于 右移一位,即exp >> 1
exp = exp >> 1;//减少乘次数
}
return exponent < 0 ? 1.0/result : result;
}
};
题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
对应力扣922题,写过,第一时间反应过来的是暴力解法
一个数组,先保存奇数,再保存偶数。时间/空间都是 O ( n ) O(n) O(n)
class Solution {
public:
void reOrderArray(vector<int> &array) {
vector<int> result(array.size(), 0);
int count = 0;
for(int i = 0; i<result.size(); i++)
{
if((array[i] % 2) != 0) result[count++] = array[i];
}
for(int j = 0; j<array.size(); j++)
{
if((array[j] % 2) == 0) result[count++] = array[j];
}
array.assign(result.begin(), result.end());
}
};
class Solution {
public:
void reOrderArray(vector<int> &array) {
//暴力解法-优化
int len = array.size();
int oddindex = 0, evenindex = 0;//奇偶数的个数
vector<int> temp(len / 2 + 1, 0);
for(int i=0; i<array.size(); i++)
{
if((array[i] & 1) == 1) array[oddindex++] = array[i];//奇数直接存在原数组
else temp[evenindex++] = array[i];//偶数存在辅助数组
}
for(int j=0; j<evenindex; j++)
{
array[j + oddindex] = temp[j];
}
}
};
不使用辅助数组,在原数组修改,但是时间复杂度是 O ( n 2 ) O(n^2) O(n2),空间是 O ( 1 ) O(1) O(1),空间换时间
如果两个相邻的数是前偶后奇,就交换,双层循环是从两头向中间逼近,外循环从前,内循环从外
class Solution {
public:
void reOrderArray(vector<int> &array) {
//原地解法
for(int i=0; i<array.size(); i++)//外循环向中间逼近
{
for(int j=array.size()-1; j>i; j--)//内循环向中间逼近
{
if((array[j-1] & 1) == 0 && (array[j] & 1) == 1)//前面是偶数,后面是奇数
{
swap(array[j-1], array[j]);
}
}
}
}
};
题目:输入一个链表,输出该链表中倒数第k个结点。
求总结点数len,然后正序遍历len-k,找到倒数第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) {
int len = 0;
ListNode* dummyhead = pListHead;
while (dummyhead)
{
dummyhead = dummyhead->next;
len++;
}
len = len - k;
if(len < 0) return nullptr;
dummyhead = pListHead;
while(len--)
{
dummyhead = dummyhead->next;
}
return dummyhead;
}
};
pListHead相当于是快指针,先走k步,然后慢指针和快指针同时开始走,要注意慢指针走到倒数第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* slownode = pListHead;
//pListHead相当于是快指针,先走k步
while (k) //k 一直走到 0 即可
{
k--;
//在其中判断是否出现k 大于链表总长度的情况 比如 【1,2,3,4,5】 6这样的情况,如果出现这样的情况,返回即可
if(!pListHead) return nullptr;
pListHead = pListHead->next;
}
while (pListHead) //先走的不能为空
{
slownode = slownode->next;
pListHead = pListHead->next;
}
//if(slownode->next) slownode->next = nullptr;
return slownode;
}
};
题目描述:完成队列的Push和Pop操作。 队列中的元素为int类型给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
用一个指针暂存下一个要反转的结点,前指针指向后面的指针进行反转。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @return ListNode类
*/
ListNode* ReverseList(ListNode* head) {
if (head == nullptr || head->next == nullptr) return head;
//头插法 前后指针
ListNode* pre = nullptr;
ListNode* cur = head;
ListNode* temp = nullptr;
while(cur)
{
temp = cur->next;//保存cur下一个结点,下一个要反转的结点
cur->next = pre;//翻转
pre = cur;//更新
cur = temp;//更新
}
return pre;
}
};
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @return ListNode类
*/
ListNode* ReverseList(ListNode* head) {
if (head == nullptr || head->next == nullptr) return head;
// 头插法
ListNode* pre = new ListNode(0);
ListNode* cur = head->next;
ListNode* temp = nullptr;
//head -> 1 -> 2 -> 3 -> null
pre->next = head;//pre新链表的虚拟头结点 pre->head
head->next = nullptr;//新链表是以null开始的,相当于pre -> null,即pre->next新链表是真正表头
while (cur) {
temp = cur;//保存当前结点 后面要让pre更新 temp=1
cur = cur->next;//cur为下一个反转结点 cur=2
//此时pre=null temp=1, cur=2
temp->next = pre->next;//相当于1 -> null
pre->next = temp;//pre=1
//此时1 -> null
}
return pre->next;
}
};
题目描述:输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。要求空间复杂度 O(1),时间复杂度 O(n)。
双指针+虚拟表头,注意新链表指针后移
空间复杂度 O(1),时间复杂度 O(n+m)
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead1 ListNode类
* @param pHead2 ListNode类
* @return ListNode类
*/
ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
if(pHead1 == nullptr) return pHead2;
if(pHead2 == nullptr) return pHead1;
ListNode* dummyhead = new ListNode(0);
ListNode* cur = dummyhead;
while(pHead1 && pHead2)
{
if(pHead1->val > pHead2->val) swap(pHead1, pHead2);//选结点值较小的
cur->next = pHead1;//新链表连接结点
//结点更新
pHead1 = pHead1->next;//pHead1的结点用掉了 要更新
cur = cur->next;//指针后移,否则从新链表头结点直接连下一个结点
}
//是否遍历完 奇数长度的链表还剩下一个结点没有连接
cur->next = pHead1 ? pHead1 : pHead2;
return dummyhead->next;//去掉新链表的表头
}
};
时间、空间都是O(n+m),扩展思路
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead1 ListNode类
* @param pHead2 ListNode类
* @return ListNode类
*/
ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
//递归法 不符合要求
//递归结束条件
if(pHead1 == nullptr || pHead2 == nullptr)
{
return pHead1 ? pHead1 : pHead2;//返回不为空的链表
}
//选择结点值较小的作为新链表结点
if(pHead1->val <= pHead2->val)
{
//pHead1结点值较小 连接下一个(两个链表中结点值小的)新结点
//问题由1->3->5 2->4->6的合并 变成了3->5 2->4->6 的合并
pHead1->next = Merge(pHead1->next, pHead2);//注意传入的链表
return pHead1;
}
else
{
pHead2->next = Merge(pHead2->next, pHead1);
return pHead2;
}
}
};
class Solution {
public:
int minNumberInRotateArray(vector<int>& nums) {
if(nums.size() == 0) return 0;
//二分法 左闭右开 最值在两头
int left = 0, right = nums.size()-1;
while(left+1 < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < nums[right]) right = mid;//说明右边有序,那就向左边走
else if(nums[mid] == nums[right]) right = right-1;
else left = mid;
}
return min(nums[left], nums[right]);
}
};
题目描述: 输入两棵二叉树A,B,判断B是不是A的子结构。(我们约定空树不是任意一个树的子结构)。假如给定A为{8,8,7,9,2,#,#,#,#,4,7},B为{8,9,2},2个树的结构如下,可以看出B是A的子结构。
这道题和力扣的 572. 另一个树的子树类似,力扣认为空树也是子树结构,上次写的这道题的记录,对称二叉树的相关题
/*
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 == nullptr || pRoot2 == nullptr) return false;
//三种情况 p2是p1的左子树、p2是p1的右子树、p2从根节点就是p1的子树 有一个成立即可 或条件
//前序遍历 另一个函数判断子树结构
return isSametree(pRoot1, pRoot2) || HasSubtree(pRoot1->left, pRoot2) || HasSubtree(pRoot1->right, pRoot2);
}
bool isSametree(TreeNode* A, TreeNode* B)
{
//判断B是否为A子树结构 逐个结点判断
//如果B是空结点,递归结束;A是空结点,或者 A、B结点值不相等 不合要求;依次左、右结点比较
if(B == nullptr) return true;
if(A == nullptr) return false;
if(A->val == B->val) return isSametree(A->left, B->left) && isSametree(A->right, B->right);
else return false;
}
};
题目描述: 操作给定的二叉树,将其变换为源二叉树的镜像。
数据范围:二叉树的节点数 0≤n≤10000 , 二叉树每个节点的值 0≤val≤10000
要求: 空间复杂度 O(n) 。本题也有原地操作,即空间复杂度 O(1)的解法,时间复杂度 O(n)。
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
TreeNode* Mirror(TreeNode* pRoot) {
//递归
if(pRoot == nullptr) return nullptr; //结束
swap(pRoot->left, pRoot->right);
Mirror(pRoot->left);
Mirror(pRoot->right);
return pRoot;
}
};
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
TreeNode* Mirror(TreeNode* pRoot) {
if(pRoot == nullptr) return nullptr;
//队列迭代
queue<TreeNode*> que;
que.push(pRoot);
while(!que.empty())
{
TreeNode* cur = que.front();//中
que.pop();
if(cur)
{
//存当前节点的左、右子节点 然后交换
que.push(cur->left);
que.push(cur->right);
swap(cur->left, cur->right);
}
}
return pRoot;
}
};
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
TreeNode* Mirror(TreeNode* pRoot) {
if(pRoot == nullptr) return nullptr;
//栈迭代
stack<TreeNode*> st;
st.push(pRoot);
while(!st.empty())
{
TreeNode* cur = st.top();
st.pop();
if(cur)
{
st.push(cur->left);
st.push(cur->right);
swap(cur->left, cur->right);
}
}
return pRoot;
}
};
题目描述:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵:
四个方向模拟,注意越界判断
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> result;
if(matrix.empty()) return result;
int rl = 0, rh = matrix.size() - 1;//上下边界 行 记录待打印的矩阵上下边缘
int cl = 0, ch = matrix[0].size() - 1;//左右边界 列 记录待打印的矩阵左右边缘
while(1)
{
for(int i=cl; i<=ch; i++) result.push_back(matrix[rl][i]);//固定上边界 行 从左往右
if(rl++ > rh) break;//操作行 防止越界
for(int i=rl; i<=rh; i++) result.push_back(matrix[i][ch]);//固定右边界 列 从上往下
if(ch-- < cl) break;
for(int i=ch; i>=cl; i--) result.push_back(matrix[rh][i]);//固定下边界 行 从右往左
if(rh-- < rl) break;
for(int i=rh; i>=rl; i--) result.push_back(matrix[i][cl]);//固定左边界 列 从下往上
if(cl++ > ch) break;
}
return result;
}
};
while循环条件改了
#include
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
if(matrix.size() == 0 || matrix[0].size() == 0) return vector<int>();
vector<int> result;
int top = 0, bottom = matrix.size()-1;
int left = 0, right = matrix[0].size()-1;
while(top <= bottom && left <= right)
{
for(int i=left; i<=right; ++i) result.push_back(matrix[top][i]);//从左往右
if(++top > bottom) break;//逐行向下 防止越界
for(int i=top; i<=bottom; ++i) result.push_back(matrix[i][right]);//从上往下
if(--right < left) break;
for(int i=right; i>=left; --i) result.push_back(matrix[bottom][i]);//从右往左
if(--bottom < top) break;
for(int i=bottom; i>=top; --i) result.push_back(matrix[i][left]);//从下往上
if(++left > right) break;
}
return result;
}
};
题目描述:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))
此栈包含的方法有:
push(value):将value压入栈中
pop():弹出栈顶元素
top():获取栈顶元素
min():获取栈中最小元素
class Solution {
public:
void push(int value) {
if(st.empty() && minst.empty())
{
st.push(value);
minst.push(value);
}
else
{
//st肯定是要入栈的 是否入minst要看值的大小
st.push(value);
if(value <= minst.top()) minst.push(value);
else minst.push(minst.top());//保证minst存的是最小值且与st结构保持一致
}
}
void pop() {
st.pop();
minst.top();
}
int top() {
return st.top();
}
int min() {
return minst.top();
}
private:
stack<int> st;
stack<int> minst;
};
注意一个栈时,最小值在下面
class Solution {
//一个栈
stack<int> st;
int minNum = INT_MAX;
public:
void push(int value) {
minNum = std::min(value, minNum);//std::min不是要实现的min函数
st.push(minNum);//最小值在下面
st.push(value);
}
void pop() {
st.pop();//弹出当前值
st.pop();//弹出当前最小值
//获取新的最小值 要先把新的最小值 上面的值取出
if(!st.empty())
{
int temp = st.top();
st.pop();
minNum = st.top();
st.push(temp);
}
}
int top() {
return st.top();
}
int min() {
return minNum;
}
};
std::bitset 类型表示一个固定长度的位序列,每个位都只能是 0 或 1。这个固定长度在创建对象时指定,并且不能在运行时更改。类似于整数类型,std::bitset 支持多种操作,包括位运算、位查询和位设置。
#include
std::bitset<N> bitset1; // 创建一个长度为 N 的 bitset,所有位都被初始化为 0
std::bitset<N> bitset2(value); // 使用二进制整数 value 初始化一个长度为 N 的 bitset
std::bitset<N> bitset3(string); // 使用二进制字符串 string 初始化一个长度为 N 的 bitset
std::bitset<N> bitset4(bitset); // 使用另一个 bitset 初始化一个长度为 N 的 bitset
bitset<8> bitset2(12); //长度为8,二进制保存,前面用0补充
string s = "100101";
bitset<10> bitset3(s); //长度为10,前面用0补充
char s2[] = "10101";
bitset<13> bitset4(s2); //长度为13,前面用0补充
cout << bitset1 << endl; //0000
cout << bitset2 << endl; //00001100
cout << bitset3 << endl; //0000100101
cout << bitset4 << endl; //0000000010101
bitset<8> foo ("10011011");
cout << foo.count() << endl; //5 (count函数用来求bitset中1的位数,foo中共有5个1
cout << foo.size() << endl; //8 (size函数用来求bitset的大小,一共有8位
cout << foo.test(0) << endl; //true (test函数用来查下标处的元素是0还是1,并返回false或true,此处foo[0]为1,返回true
cout << foo.test(2) << endl; //false (同理,foo[2]为0,返回false
cout << foo.any() << endl; //true (any函数检查bitset中是否有1
cout << foo.none() << endl; //false (none函数检查bitset中是否没有1
cout << foo.all() << endl; //false (all函数检查bitset中是全部为1
//&、|、^、~ 等,使其支持类似于整数类型的位运算操作
std::bitset<4> bitset1("1010");
std::bitset<4> bitset2("0110");
std::bitset<4> bitset3 = bitset1 & bitset2; // 按位与运算
std::bitset<4> bitset4 = bitset1 | bitset2; // 按位或运算
std::bitset<4> bitset5 = bitset1 ^ bitset2; // 按位异或运算
std::bitset<4> bitset6 = ~bitset
//左移、右移运算符进行位移操作
std::bitset<4> bitset1("0101");
std::bitset<4> bitset2 = bitset1 << 2; // 左移 2 位,结果为 "010100"
std::bitset<4> bitset3 = bitset1 >> 1; // 右移 1 位,结果为 "0010"
//转换成二进制字符串
std::bitset<4> bitset1("1010");
std::string str = bitset1.to_string(); // "1010"