上面几套题其实思路都是一样的,掌握了规律后其实很容易得到想要的结果,所以今天我就多写写,争取尽快写完这上面的试题。
二进制中1的个数
题目描述:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
题目分析:这道题本身并不算难,但是必须搞清楚负数补码的关系后才好继续,这方面也是我的弱点,所以我打算在这里复习一下负数补码的关系:
正数:正数的补码是其二进制表示,与原码相同。如+9的补码是00001001,和自身的二进制表示相同。
负数:求负整数的补码,将其原码除符号位外的所有位取反后加1。如-5的补码是这样求的。正数5的码(00000101)———所有位取反(11111010)————加一后操作(11111010)
同一个数字在不同的补码表示形式中是不同的。比如-15的补码,在8位二进制中是11110001,然而在16位二进制补码表示中,就是1111111111110001。以下都使用8位2进制来表示。在牛客网上应该默认是32位,4个字节,因此求补码的时候需要注意与的数的大小。
已知一个数的补码,求原码的操作其实就是对该补码再求补码。
因此该题目的具体做法如下,先判断正负,再移位操作,而对某个位取反,在计算机中其实就是异或操作:
class Solution {
public:
int NumberOf1(int n) {
if (n == 0) {
return 0;
}
int count(0);
if (n < 0) { //如果是负数,求补码
n = 0x7fffffff & n; //除标志位,取反
count++;
}
while (n != 0) {
count += n & 1;
n = n >> 1; //移位操作后求具体的值
}
return count;
}
};
数值的整数次方
题目描述:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0。这个题表面上看起来比较简单。但是如果将负数考虑进来的话就需要额外操心了。如果base为负的话,那么当exponet为偶数时,需要去掉负号。而如果exponent为负数的话,那么相当于求原来数的倒数了。
class Solution {
public:
double Power(double base, int exponent) {
if (base == 0)return 0; //特殊条件1
if (exponent == 0)return 1; //特殊条件2
int exponentflag = 0;
int baseflag = 0;
if (exponent < 0) { //如果幂为负,先将幂转为正,再将相应标志位置一
exponentflag = 1;
exponent = -exponent;
}
if (base < 0) { //如果底为负,先将底转为正,再将相应标志位置一
baseflag = 1;
base = -base;
}
double output=1;
for (int i = 0; i < exponent; i++) {
output *= base;
}
if (exponentflag == 1) { //幂为负,取倒数
output = 1 / output;
}
if (baseflag == 1) { //底为负,且幂的绝对值为奇数,取反
if (exponent % 2 == 1) {
output = -output;
}
}
return output;
}
};
调整数组顺序使奇数位于偶数前面
题目描述:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
题目分析:这个题第一眼看上去并不难,最简单的思路就是设计两个数组,一个存奇数,一个存偶数,然后最终将奇数先放回原来的坑中,再将偶数放回之前的坑中。这样的做法时间复杂度使2n,空间复杂度是n。
class Solution {
public:
void reOrderArray(vector<int> &array) {
vector<int>hellokitty=array;
array.clear();
for (int i = 0; i < hellokitty.size(); i++)
{
if (hellokitty[i] % 2 == 1) { array.push_back(hellokitty[i]); }
}
for (int i = 0; i < hellokitty.size(); i++)
{
if (hellokitty[i] % 2 == 0) { array.push_back(hellokitty[i]); }
}
}
};
但是如果不适用额外的空间的话,有没有什么比较好的解法呢?考虑到不能改变先后顺序,可以采用冒泡排序的思想,如果前偶后奇,则交换两者的位置,这样的话两次嵌套循环后就能达到理想的效果。
//这里懒得写了,直接拷贝别人的即可,时间复杂度略高,n^2
class Solution {
public:
void reOrderArray(vector<int> &array) {
int i, j;
int temp = 0;
for(i=0; i<array.size(); ++i){
for(j=0; j<array.size(); ++j){
if(((array[j]&1) == 0)&&((array[j+1]&1) == 1)){
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}
};
链表中倒数第K个节点
题目描述:输入一个链表,输出该链表中倒数第k个结点。
题目分析:这道题的解法很多,比如说将整个链表压入栈中,然后依次弹出k个即可,这样的话时间复杂度为n+k,空间复杂度为n略大。故考虑用两个指针的方法,这种方法非常巧妙。指针A从头部出发,而另一个指针B从第K个节点出发,当指针B到达末尾时,指针A所指向的位置就是所求节点。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead==nullptr)return nullptr;
if(k==0)return nullptr;
ListNode* p1=pListHead; //慢指针
ListNode* p2 = pListHead; //快指针
for (int i = 1; i < k; i++) {
p2 = p2->next;
if (p2 == nullptr)return nullptr;
}
while (p2->next != nullptr) {
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
};