11. 二进制中1的个数
二进制中1的个数_牛客题霸_牛客网 (nowcoder.com)
法一:
使用库函数bitset。
class Solution {
public:
int NumberOf1(int n) {
return bitset<32>(n).count();
}
};
法二:
手动位运算。对于n=1100,n-1=1011。n &= n-1后,n=1000。这个过程相当于从二进制数中消去了一个1。
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
while(n){
count++;
n &= (n-1);
}
return count;
}
};
12. 数值的整数次方
数值的整数次方_牛客题霸_牛客网 (nowcoder.com)
LCR 134. Pow(x, n) - 力扣(LeetCode)
法一:
常规解法,区分正负即可。
class Solution {
public:
double Power(double base, int exponent) {
if(exponent == 0) return 1.0;
if(base == 0) return 0.0;
bool flag = false;
if(exponent < 0){
flag = true;
exponent *= -1;
}
double ans = 1.0;
for(int i = 0;i<exponent;i++){
ans*= base;
}
if(flag) ans = 1/ans;
return ans;
}
};
法二:
快速幂:把幂次按照二进制拆开,分别计算。例如,计算一个数的10次方相当于计算一个数的1010(二进制)次方,可以看作按照x的2次方一次递增。
class Solution {
public:
double Power(double base, int exponent) {
if(exponent == 0) return 1;
if(base == 0) return 0;
// exp一定要用long。假设exponent=INT_MIN,其对应的正数大于INT_MAX,故不能用int
long exp = exponent;
if(exponent < 0){
exp = exponent *(-1.0);
}
double ans = 1.0;
while(exp != 0){
if((exp & 1) == 1){
ans *= base;
}
base *= base;
exp >>= 1;
}
return exponent < 0 ? 1 /ans : ans;
}
};
13. 调整数组顺序使奇数位于偶数前面
调整数组顺序使奇数位于偶数前面_牛客题霸_牛客网 (nowcoder.com)
法一:
使用辅助数组
class Solution {
public:
void reOrderArray(vector<int> &array) {
vector<int> vec;
for(auto& i : array)
if(i & 1) array.push_back(i);
for(auto& i : array)
if(!(i & 1)) array.push_back(i);
copy(vec.begin(),vec.end(),array.begin());
}
};
法二:
in-place算法。
用i
记录当前需要插入的奇数的位置,用j
遍历数组。如果遇到奇数,就将其插入到i
所在的位置(i
到j-1
之间所有的数据往后挪一位)。
class Solution {
public:
void reOrderArray(vector<int> &array) {
int i = 0;
for(int j = 0;j<array.size();j++){
if(array[j] & 1){
int temp = array[j];
for(int k = j-1;k>=i;--k){
array[k+1] = array[k];
}
array[i++] = temp;
}
}
}
};
法三:
使用STL函数****stable_partition()
****对指定区域内的数据进行分组,重新排列指定区域内存储的数据,使其分为两组,第一组位符合筛选条件的数据,另一组为不符合筛选条件的数据。
函数原型:
template< class BidirIt, class UnaryPredicate >
BidirIt stable_partition( BidirIt first, BidirIt last, UnaryPredicate p );
第三个参数可以传入一个仿函数,函数指针,lambda表达式。
#include <algorithm>
class Solution {
public:
void reOrderArray(vector<int> &array) {
stable_partition(array.begin(),array.end(),[](int x){return x & 1;});
}
};
14. 链表中倒数第k个结点
链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)
双指针
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(!pListHead || k <= 0) return nullptr;
auto slow = pListHead,fast = pListHead;
int n = 0;
while(k--){
if(fast)
fast = fast->next;
else
return nullptr;
}
while(fast){
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
15. 反转链表
法一:
双指针。以1,2,3链表为例,用三个指针,
- cur指针用来遍历,指向
head
。 - pre指向cur的前一个节点,故一开始其必须初始化为
nullptr
。 - 由于在遍历过程中需要将
cur->next
指向pre
。故还需要一个temp指针用来保存cur->next
实现遍历。
class Solution {
public:
ListNode* ReverseList(ListNode* head) {
if(head == nullptr) return nullptr;
ListNode* dummyNode = new ListNode(0);
ListNode* cur = head;
ListNode* pre = nullptr;
ListNode* temp = nullptr;
while(cur){
temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
};
法二:
递归。一次反转整个链表很困难,但反转第一个元素和第二个元素比较容易。
- 故将原问题可以减小为反转前两个元素。依次往后递归。
- 在思考时,假定
ReverseList(head->next)
代表已经反转好的后续元素。只需要改变链表前两个元素的指针指向并考虑边界条件即可。 - 边界条件:
head == nullptr
代表链表本身为空,head->next == nullptr
代表递归出口
class Solution {
public:
ListNode* ReverseList(ListNode* head) {
if(head == nullptr) return nullptr;
if(head->next == nullptr) return head;
ListNode* ans = ReverseList(head->next);
head->next->next = head;
head->next = nullptr;
return ans;
}
};
16. 合并两个有序链表
合并两个排序的链表_牛客题霸_牛客网 (nowcoder.com)
经典题目。起一个虚拟头节点,然后一边遍历,一边将小的插在都节点后面,直到两个链表有一个为空。最后将不为空的链表插入到结果的后面即可。
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
if(pHead1 == nullptr) return pHead2;
if(pHead2 == nullptr) return pHead1;
ListNode* dummyNode = new ListNode(0);
ListNode* cur = dummyNode;
while(pHead1 && pHead2){
if(pHead1->val < pHead2->val){
cur->next = pHead1;
pHead1 = pHead1->next;
}else{
cur->next = pHead2;
pHead2 = pHead2->next;
}
cur = cur->next;
}
if(pHead1) cur->next = pHead1;
if(pHead2) cur->next = pHead2;
return dummyNode->next;
}
};
17. 树的子结构
树的问题归根结底的递归问题。
根据题意,判断B是不是A的子结构。由于题意表明空树不是任意一个树的子结构,故一开始就将空树进行处理(直接return false
)。
之后判断B是否是A的子结构。若B是,则A的任意一个节点都有可能是B的根节点。故需要先序遍历A的每个节点判断以A中的节点node为根节点的子树是否包含树B。(isSameTree
)在该函数中,
- 终止条件:
- B为空,表示,B已匹配完成,返回true
- A为空,表示已经越过A的叶节点,即匹配失败,返回false
- A和B的值不同:表明匹配失败,返回false
- 返回值:到了最后返回的时候表明A和B的根节点相同,故需要判断其左右子树是否相等。故返回值为
isSameTree(pRoot1->left,pRoot2->left) && isSameTree(pRoot1->right,pRoot2->right);
class Solution {
public:
bool isSameTree(TreeNode* pRoot1,TreeNode* pRoot2){
if(pRoot2 == nullptr) return true;
if(pRoot1 == nullptr || pRoot1->val != pRoot2->val) return false;
return isSameTree(pRoot1->left,pRoot2->left) && isSameTree(pRoot1->right,pRoot2->right);
}
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2) {
if(pRoot1 == nullptr || pRoot2 == nullptr) return false;
return isSameTree(pRoot1,pRoot2) || HasSubtree( pRoot1->left, pRoot2) || HasSubtree(pRoot1->right,pRoot2);
}
};
18. 二叉树的镜像
二叉树的镜像_牛客题霸_牛客网 (nowcoder.com)
只要能遍历一遍所有节点,然后交换每个节点的左右孩子即可。故三种遍历及其迭代法改造以及层序遍历都可以。
法一:
理解了递归,就理解了树。这里用先序遍历,先交换节点的左右子节点,之后分别递归处理左右子树。
class Solution {
public:
TreeNode* Mirror(TreeNode* pRoot) {
if(pRoot == nullptr) return nullptr;
TreeNode* temp = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = temp;
Mirror(pRoot->left);
Mirror(pRoot->right);
return pRoot;
}
};
法二:
先序迭代法
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();
swap(cur->left,cur->right);
if(cur->left) st.push(cur->left);
if(cur->right) st.push(cur->right);
}
return pRoot;
}
};
法三:
中序遍历。采用递归实现的中序遍历,部分节点的左右孩子会反转两次。故与传统中序写法不太一致。
class Solution {
public:
TreeNode* Mirror(TreeNode* pRoot) {
if(pRoot == nullptr) return nullptr;
Mirror(pRoot->left); // 左
swap(pRoot->left,pRoot->right); //中
Mirror(pRoot->left); //“右”,因为左右节点已经交换了,所赐此时的left为原来的right
return pRoot;
}
};
19. 顺时针打印矩阵
顺时针打印矩阵_牛客题霸_牛客网 (nowcoder.com)
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> ans;
if(matrix.empty()) return ans;
int rl = 0, rh = matrix.size()-1;
int cl = 0, ch = matrix[0].size()-1;
while(1){
for(int i = cl;i<=ch;i++) ans.push_back(matrix[rl][i]);
if(++rl > rh) break;
for(int i = rl;i<=rh;i++) ans.push_back(matrix[i][ch]);
if(--ch < cl) break;
for(int i = ch;i >= cl ;i--) ans.push_back(matrix[rh][i]);
if(--rh < rl) break;
for(int i = rh;i >= rl;i--) ans.push_back(matrix[i][cl]);
if(++cl > ch) break;
}
return ans;
}
};
20. 包含min函数的栈
包含min函数的栈_牛客题霸_牛客网 (nowcoder.com)
法一:
用一个栈,每次存完以后额外存一下最小值。
class Solution {
public:
void push(int value) {
if(st.empty()){
st.push(value);
st.push(value);
}else{
int mi = st.top();
mi = value < mi ? value : mi;
st.push(value);
st.push(mi);
}
}
void pop() {
if(st.empty()) return ;
st.pop();
st.pop();
}
int top() {
if(st.empty()) return -1;
int temp = st.top();
st.pop();
int ans = st.top();
st.push(temp);
return ans;
}
int min() {
if(st.empty()) return -1;
return st.top();
}
private:
stack<int> st;
};
法二
也可以用两个栈,一个存数据,另一个存最小值。跟上面原理一样。
class Solution {
public:
void push(int value) {
if(st.empty()){
st.push(value);
minSt.push(value);
}else{
int mi = minSt.top();
mi = value < mi ? value : mi;
minSt.push(mi);
st.push(value);
}
}
void pop() {
if(st.empty()) return ;
st.pop();
minSt.pop();
}
int top() {
if(st.empty()) return -1;
return st.top();
}
int min() {
if(st.empty()) return -1;
return minSt.top();
}
private:
stack<int> st;
stack<int> minSt;
};