1.面试题三 数组中重复的数字
题目一
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1
法一、利用hashmap,bitset尤其适用于这种形式
/*(O(n),O(n))
* 每个数字以O(1)的时间来判断哈希表
* 需要一个大小为O(n)的哈希表
*/
class Solution {
public:
/**
* @param numbers int整型vector
* @return int整型
*/
int duplicate(vector<int>& numbers) {
bitset<10000> hashmap;
for(int i = 0; i < numbers.size(); i++){
if(hashmap[numbers[i]]){
return numbers[i];
}
hashmap[numbers[i]] = 1;
}
return -1;
}
};
法二、
/**替换法(O(n),O(1))
*数组存放原则:numbers[i] = i
遍历数组所有元素,交换不符合数组存放原则的元素:
例如[2,3,1,0,2]
遍历0位元素2:(交换0位元素2和2位元素1)->[1,3,2,0,2]
遍历0位元素1:(交换0位元素1和1位元素3)->[3,1,2,0,2]
遍历0位元素3:(交换0位元素3和3位元素0)->[0,1,2,3,2]
依次遍历0、1、2、3位置元素,都符合存放原则numbers[i] = i,不做任何操作
遍历末位元素2,此时末位元素2和2位元素2相等,出现了两次,即数字2位重复元素
*/
int duplicate(vector<int>& numbers) {
for(int i = 0; i < numbers.size(); i++)
{
if(numbers[i] == i){
continue;
}else{
if(numbers[i] == numbers[numbers[i]]){
return numbers[i];
}else{
/**
*int temp = numbers[i];
*numbers[i] = numbers[numbers[i]];
*numbers[temp] = temp;
*注意这里交换时
*第三行不能写为 numbers[numbers[i]] = temp
*因为numbers[i]的值已经发生改变
**/
int temp = numbers[numbers[i]];
numbers[numbers[i]] = numbers[i];
numbers[i] = temp;
i--;
}
}
}
return -1;
}
题目二
bool count(vector<int>& numbers, int start, int end){
int length = numbers.size();
int count = 0;
for(int i = 0; i < length; i++){
if((numbers[i] >= start) && (numbers[i] <= end)){
count++;
}
}
if(count > (end - start + 1)){
return 1;
}else{
return 0;
}
}
int duplicate(vector<int>& numbers) {
int length = numbers.size();
int start = 1;
int end = length - 1;
while(start < end){
int middle = start + (end - start)/2;
if(count(numbers, start, middle)){
end = middle;
}else{
start = middle + 1;
}
}
if(count(numbers, start, end)){
return start;
}else{
return -1;
}
}
2.面试题四 JZ4 二维数组中的查找
描述
在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
给定 target = 7,返回 true。
给定 target = 3,返回 false。
数据范围:矩阵的长宽满足 0 \le n,m \le 5000≤n,m≤500
进阶:空间复杂度 O(1)O(1) ,时间复杂度 O(n+m)O(n+m)
法一、
bool Find(int target, vector<vector<int>> array)
{
int rows = array.size();
int cols = array[0].size();
if (cols == 0)
{
return false;
}
if (target > array[rows - 1][cols - 1])
{
return false;
}
int hang = 0;
int lie = 0;
for (int i = 0; i < max(rows, cols); i++)
{
if (rows == cols)
{
if (array[i][i] >= target)
{
hang = i;
lie = i;
break;
}
}
else
{
if (i < min(rows, cols))
{
if (array[i][i] >= target)
{
hang = i;
lie = i;
break;
}
}
else
{
if (rows > cols)
{
if (array[i][cols - 1] >= target)
{
hang = i;
lie = cols - 1;
break;
}
}
else
{
if (array[rows - 1][i] >= target)
{
hang = rows - 1;
lie = i;
break;
}
}
}
}
}
for (int j = lie; j < cols; j++)
{
for (int i = 0; i <= hang; i++)
{
if (array[i][j] == target)
{
return true;
}
}
}
for (int j = hang; j < rows; j++)
{
for (int i = 0; i <= lie; i++)
{
if (array[j][i] == target)
{
return true;
}
}
}
return false;
}
法二、
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
bool found = false;
int rows = array.size();
int cols = array[0].size();
int row = 0;
int col = cols - 1;
while(row<rows && col>=0){
if(array[row][col] == target){
return true;
}else if(array[row][col] > target){
col--;
}else{
row++;
}
}
return found;
}
};
3. JZ5 替换空格
题目一
法一
string replaceSpace(string s) {
// write code here
int length = s.size();
for(int i = 0; i < s.size(); i++){
if(s[i] == ' '){
s[i] = '%';
s.insert(i+1,"20");
}
}
return s;
}
//c++可以直接对字符串进行遍历
string replaceSpace(string s) {
// write code here
string ans="";
// 直接对字符串进行遍历
for(auto x:s){
// 对空白字符进行替换
if(x==' '){
ans+="%20";
// 否则不进行处理
}else{
ans+=x;
}
}
return ans;
}
法二
/*
* 双指针从后向前遍历
* 若以s[]的形式越界访问或者修改数组,超出s的长度范围
* 不会被输出,所以实现要resize一下。
*/
string replaceSpace(string s)
{
// write code here
int i = 0;
int length = 0;
int needlength = 0;
char a[3] = {'%', '2', '0'};
while (s[i] != '\0')
{
if (s[i] == ' ')
{
length++;
needlength += 3;
}
else
{
length++;
needlength++;
}
i++;
}
cout << length << " " << needlength << endl;
s.resize(needlength+1);
while (length >= 0)
{
//cout << s[length] << " ";
if (s[length] == ' ')
{
for (int i = 0; i < 3; i++)
{
s[needlength --] = a[2 - i];
}
}
else
{
s[needlength] = s[length];
needlength--;
}
length--;
}
return s;
}
题目二
合并两个有序的数组
描述
给出一个有序的整数数组 A 和有序的整数数组 B ,请将数组 B 合并到数组 A 中,变成一个有序的升序数组
注意:
- 保证 A 数组有足够的空间存放 B 数组的元素, A 和 B 中初始的元素数目分别为 m 和 n,A的数组空间大小为 m+n
- 不要返回合并的数组,将数组 B 的数据合并到 A 里面就好了
- A 数组在[0,m-1]的范围也是有序的
法一、
void merge(int A[], int m, int B[], int n) {
int k = m+n-1;
m--;
n--;
while(min(m,n) >= 0){
if(A[m] > B[n]){
A[k] = A[m];
m--;
}else{
A[k] = B[n];
n--;
}
k--;
}
if(m < 0){
for(int i = 0; i <= n; i++){
A[i] = B[i];
}
}
}
//细节操作
void merge(int A[], int m, int B[], int n) {
int i=m-1,j=n-1,p=m+n-1;//i,j指针指向原末尾,p指针指向合并后的A末尾
while(~i&&~j) {// ~i <==> i>=0,因为-1取反过后返回的是0,位运算一般情况比其他运算符速度要快
A[p--]=A[i]>B[j]?A[i--]:B[j--];//合并A、B数组, 比较i,j指针指向的元素,将大的那个丢进去
}
while(~j){//如果B数组还有剩余,则直接全部移到A数组中
A[p--]=B[j--];
}
/*此处如果出现A数组有剩余的情况,理论上应该执行while(i>=0)A[p--]=A[i--]
但是如果A有剩余必然有p==i(出现剩余的情况,只有可能一个出现剩余,不可能出现AB数组都剩余的情况)
(如果出现AB都剩余,就跟第一个for循环矛盾了)
既然对于指针p==i,那么此时这个while循环就会显得很多余
*/
}
面试题六 JZ6 从尾到头打印链表
法一
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> v;
if(head == nullptr){
return v;
}
cout<< head->val;
printdigui(v, head);
return v;
}
void printdigui(vector<int>& v, ListNode* head){
if((head -> next) == nullptr){
v.push_back(head->val);
}else{
printdigui(v, head->next);
v.push_back(head->val);
}
}
};
//对比别人的题解,相当的干练
class Solution {
public:
vector<int> ans;
void dfs(ListNode* now){
// 递归的出口为当前的指针为空的情况
if(!now){
return;
}
// 向后面进行递归
dfs(now->next);
// 递归之后收集权值
ans.push_back(now->val);
}
vector<int> printListFromTailToHead(ListNode* head) {
dfs(head);
return ans;
}
};
法二
//利用std提供的栈实现
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> v;
std::stack<ListNode*> nodes;
ListNode* nodetemp = head;
while(nodetemp != nullptr){
nodes.push(nodetemp);
nodetemp = nodetemp->next;
}
while(!nodes.empty()){
nodetemp = nodes.top();
v.push_back(nodetemp->val);
nodes.pop();
}
return v;
}
};
面试题七 JZ7 重建二叉树
法一
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
if(pre.size() == 0){
return nullptr;
}
TreeNode* it = new TreeNode(pre[0]);
int i = 0;
while(pre[0] != vin[i]){
i++;
}
vector<int> pre1,vin1,pre2,vin2;
for(int j = 1; j <= i; j++){
pre1.push_back(pre[j]);
}
for(int j = 0; j < i; j++){
vin1.push_back(vin[j]);
}
for(int j = i+1; j < pre.size(); j++){
pre2.push_back(pre[j]);
}
for(int j = i+1; j < vin.size(); j++){
vin2.push_back(vin[j]);
}
it->left = reConstructBinaryTree(pre1, vin1);
it->right = reConstructBinaryTree(pre2, vin2);
return it;
/*对于vector的处理可以更巧妙
*int leftSize = posi - vin.begin();
*int rightSize = vin.end() - posi - 1;
node->left = reConstructBinaryTree(vector(pre.begin() + 1, pre.begin() + 1 + leftSize),
vector(vin.begin(), vin.begin() + leftSize));
node->right = reConstructBinaryTree(vector(pre.begin() + 1 + leftSize, pre.end()),
vector(vin.begin() + leftSize + 1, vin.end()))
*/
}
面试题8 JZ8 二叉树的下一个结点
法一
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
//先得到中序遍历,再去寻找下一个值
//时间复杂度也是线性的,第一步:最坏为O(N), N为整棵树结点的个数。第二步:O(N), //第三步:最坏为O(N),所以整的时间复杂度:3*O(N)
class Solution {
public:
vector<TreeLinkNode*> v;
void tree2mid(TreeLinkNode* pNode){
if(pNode == nullptr){
return; //void 不能返回NULL,也不能不写return
}
tree2mid(pNode->left);
v.push_back(pNode);
tree2mid(pNode->right);
}
TreeLinkNode* GetNext(TreeLinkNode* pNode) {
if(pNode == nullptr){
return nullptr;
}
TreeLinkNode* root = pNode;
TreeLinkNode* tmp = pNode;
while(tmp){
root = tmp;
tmp = tmp -> next;
}
tree2mid(root);
for(int i = 0; i < v.size(); i++)
{
if (v[i] == pNode && i + 1 != v.size()) {
return v[i+1];
}
}
return nullptr;
}
};
法二
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode) {
if(pNode == nullptr){
return nullptr;
}
TreeLinkNode* tmp1 = pNode->right;
if(tmp1 == nullptr){
TreeLinkNode* tmp = pNode;
while(tmp != tmp->next->left && tmp->next!=nullptr){
tmp = tmp->next;
}
return tmp->next;
}
while(tmp1 -> left != nullptr){
tmp1 = tmp1->left;
}
return tmp1;
}
};
面试题九 JZ9 用两个栈实现队列
法一
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if(!stack2.empty()){
int i = stack2.top();
stack2.pop();
return i;
}
while(!stack1.empty()){
stack2.push(stack1.top());
stack1.pop();
}
int i = stack2.top();
stack2.pop();
return i;
}
private:
stack<int> stack1;
stack<int> stack2;
};
相关题目
用两个队列构建一个栈
class Solution
{
public:
void push(int node) {
if(!queue1.empty()){
queue1.push(node);
}else{
queue2.push(node);
}
}
int pop() {
if(queue2.empty()){
while(queue1.size() != 1){
int i = queue1.front();
queue1.pop();
queue2.push(i);
}
int i = queue1.front();
queue1.pop();
return i;
}else{
while (queue2.size() != 1)
{
int i = queue2.front();
queue2.pop();
queue1.push(i);
}
int i = queue2.front();
queue2.pop();
return i;
}
}
private:
queue<int> queue1;
queue<int> queue2;
};
/*
思路:由于队列和栈进出相反. 后进的需要先出, 每次需要把队尾的元素出队,必然要把除了队尾的元素之外的元素,先找地方(d2)保存起来. 下一次出队的时候,同样道理,.
进队时,两个队列d1,d2的状态必然是 : 一个为空 , 另一个装元素. 将需要进队的元素直接插入非空那个队列队尾.
出队时, 将 非空队列的 除去最后一个元素外的所以元素 都放到另一个空队列中,在将最后一个出队...整个过程就是倒腾过来 , 倒腾过去.
但是要知道 , 两个队列是交替做临时队列的 , 而不是说每次都用d2做临时存储,然后出队完还要把d2中的东西还给d1,这是不需要的! 因为下次你用d1做存储区不就行了么..不用还回去!!!这就是优化点!
*/
面试题十 JZ10 斐波那契数列
法一
class Solution {
public:
int Fibonacci(int n) {
if(n <= 1){
return 1;
}
int tmp1 = 1;
int tmp2 = 1;
for(int i = 0; i < n-2; i++){
int temp = tmp2;
tmp2 = tmp1 + tmp2;
tmp1 = temp;
}
return tmp2;
}
};
//递归的程序,函数不断进出栈,时间复杂度过高
法二
//避免重复计算,空间换时间
class Solution {
public:
int Fibonacci(int n) {
if(n <= 1){
return 1;
}
int tmp1 = 1;
int tmp2 = 1;
for(int i = 0; i < n-2; i++){
int temp = tmp2;
tmp2 = tmp1 + tmp2;
tmp1 = temp;
}
return tmp2;
}
};
面试题十一 JZ11 旋转数组的最小数字
描述
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1 \le n \le 100001≤n≤10000,数组中任意元素的值: 0 \le val \le 100000≤val≤10000
要求:空间复杂度:O(1)O(1) ,时间复杂度:O(logn)O(logn)
法一
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.size() == 0){
return 0;
}
int i = 0;
int temp = rotateArray[0];
while(temp <= rotateArray[i] && i <= rotateArray.size()-1 ){
i++;
}
if(i > rotateArray.size()-1){
return temp;
}else{
return rotateArray[i];
}
}
};
法二
//二分查找的应用
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int p1 = 0;
int p2 = rotateArray.size() - 1;
while(p1 != p2){
int mid = p1 + ((p2-p1) >> 1);
if(rotateArray[mid] > rotateArray[p2]){
p1 = mid + 1;
}else if(rotateArray[mid] < rotateArray[p2]){
p2 = mid;
}else{
p2--;
}
}
return rotateArray[p1];
}
};
面试题十二 JZ12 矩阵中的路径
深度优先遍历 回溯
法一
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param matrix char字符型vector<vector<>>
* @param word string字符串
* @return bool布尔型
*/
int vis[205][205] ,n,m;
const int dxy[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
string word1;
bool hasPath(vector<vector<char> >& matrix, string word) {
n = matrix.size(), m = matrix[0].size();
memset(vis, 0, sizeof(vis));
// write code here
word1 = word;
if(matrix.size() == 0 || matrix[0].size() == 0){
return false;
}
if(word.size() == 0){
return true;
}
string::iterator it = word1.begin()+1;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(matrix[i][j] == word[0]){
if(find(matrix, it, i, j)){
return true;
}
vis[i][j] = false;
}
}
}
return false;
}
bool dfs(vector<vector<char>>& matrix, string::iterator a,int x, int y){
if(a == word1.end()){
return true;
} // 找到一条合法路径
vis[x][y] = true; // 标记当前位置
for(int i = 0; i < 4; i++){
int dx = x + dxy[i][0], dy = y + dxy[i][1];
if(dx >= 0 && dx < n && dy >= 0 && dy < m && !vis[dx][dy] && matrix[dx][dy] == *a){
if(dfs(matrix,a+1, dx, dy)) return true;
vis[dx][dy] = false; // 回溯
}
}
return false;
}
bool find(vector<vector<char>>& matrix, string::iterator a, int row, int col){
if(a == word1.end()){
return true;
}
vis[row][col] = true;
for(int i = 0; i < 4; i++){
int dx = row + dxy[i][0];
int dy = col + dxy[i][1];
if(dx >= 0 && dx < n && dy >= 0 && dy < m && !vis[dx][dy] && matrix[dx][dy] == *a){
if(find(matrix,a+1,dx,dy)){ //这个地方写成++a就会出问题,还没搞清楚
return true;
}
vis[dx][dy] = false;
}
}
return false;
}
};
对比一下别人答案找找差距
class Solution {
private:
static constexpr int maxn = 205; // 根据题目给出的信息,矩阵最大为200 * 200
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param matrix char字符型vector<vector<>>
* @param word string字符串
* @return bool布尔型
*/
int vis[maxn][maxn], n, m; // n为矩阵行数, m为矩阵列数
const int dxy[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 向四个方向移动的偏移量
bool hasPath(vector<vector<char>>& matrix, string word) {
n = matrix.size(), m = matrix[0].size();
memset(vis, 0, sizeof(vis)); // 初始化
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(matrix[i][j] == word[0]){ // 找到入口点
if(dfs(matrix, i, j, 1, word)){
return true;
}
vis[i][j] = false; // 记得恢复状态
}
}
}
return false;
}
bool dfs(vector<vector<char>>& matrix, int x, int y, int p, string& word){
if(p == word.size()) return true; // 找到一条合法路径
vis[x][y] = true; // 标记当前位置
for(int i = 0; i < 4; i++){
int dx = x + dxy[i][0], dy = y + dxy[i][1];
if(dx >= 0 && dx < n && dy >= 0 && dy < m && !vis[dx][dy] && matrix[dx][dy] == word[p]){
if(dfs(matrix, dx, dy, p + 1, word)) return true;
vis[dx][dy] = false; // 回溯
}
}
return false;
}
};
JZ13 机器人的运动范围
法一
深度优先的遍历 dfs
class Solution {
public:
int vis[100][100],m,n;
int nums;
int mv[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};
int movingCount(int threshold, int rows, int cols) {
m = rows;
n = cols;
memset(vis, 0, sizeof(vis));
find(threshold, 0, 0);
return nums;
}
void find(int threshold, int row, int col){
if(vis[row][col] == 0){
if((row/10+row%10 + col/10+col%10) <= threshold){
vis[row][col] = 1;
nums++;
for(int i = 0; i < 4; i++){
int dx = row + mv[i][0];
int dy = col + mv[i][1];
if(dx >= 0 && dy >= 0 && dx < m && dy < n && vis[dx][dy] == 0){
find(threshold, dx, dy);
}
}
}else{
vis[row][col] = -1;
}
}else{
return;
}
return;
}
};
法二
广度优先 bfs (通常配合队列完成)
class Solution {
public:
int vis[100][100],m,n;
int mv[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};
int nums = 1;
int movingCount(int threshold, int rows, int cols) {
vector<pair<int, int>> v;
vector<pair<int, int>> v_temp;
v.push_back(make_pair(0,0));
memset(vis, 0, sizeof(vis));
vis[0][0] = 1;
while(!v.empty()){
for(int i = 0; i < v.size(); i++){
int x = v[i].first;
int y = v[i].second;
for(int j = 0; j < 4; j++){
int dx = x + mv[j][0];
int dy = y + mv[j][1];
if(dx >= 0 && dy >= 0 && dx < rows && dy < cols && dx/10 + dx%10 + dy/10 + dy%10 <= threshold && vis[dx][dy] == 0){
vis[dx][dy] = 1;
nums++;
v_temp.push_back(make_pair(dx, dy));
}else if(dx >= 0 && dy >= 0 && dx < rows && dy < cols && dx/10 + dx%10 + dy/10 + dy%10 > threshold && vis[dx][dy] == 0){
vis[dx][dy] = -1;
}else{
continue;
}
}
}
v.clear();
v.assign(v_temp.begin(), v_temp.end());
v_temp.clear();
}
return nums;
}
};
//用队列改进后的代码
class Solution {
public:
int vis[100][100],m,n;
int mv[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};
int nums = 1;
int movingCount(int threshold, int rows, int cols) {
queue<pair<int, int>> q;
q.push(make_pair(0,0));
memset(vis, 0, sizeof(vis));
vis[0][0] = 1;
while(!q.empty()){
int x = q.front().first;
int y = q.front().second;
q.pop();
for(int j = 0; j < 4; j++){
int dx = x + mv[j][0];
int dy = y + mv[j][1];
if(dx >= 0 && dy >= 0 && dx < rows && dy < cols && dx/10 + dx%10 + dy/10 + dy%10 <= threshold && vis[dx][dy] == 0){
vis[dx][dy] = 1;
nums++;
q.push(make_pair(dx, dy));
}else if(dx >= 0 && dy >= 0 && dx < rows && dy < cols && dx/10 + dx%10 + dy/10 + dy%10 > threshold && vis[dx][dy] == 0){
vis[dx][dy] = -1;
}else{
continue;
}
}
}
return nums;
}
};
JZ14 剪绳子
动态规划
class Solution {
public:
int f[60];
int cutRope(int number) {
if (number == 2) {
return 1;
}
else if (number == 3) {
return 2;
}
//n为2,3的时候特殊处理,因为这时候不分的是最大,3之后不分的就都比不分的大了,因为
//最少得分一刀
memset(f, 0, sizeof(f));
f[0] = 1;
int i = 1;
while(i <= number){
f[i] = i;
for(int j = 1; j <= i/2; j++){
if(f[j]*f[i-j] > f[i]){
f[i] = f[j]*f[i-j];
}
}
i++;
}
return f[number];
}
};
JZ15 二进制中1的个数
位运算 (非常不熟悉) 位运算比乘除快得多
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
unsigned int flag = 1;
while(n){
if(n & 1){
count++;
}
n = n >> 1;
}
return count;
}
};
//这种情况在n为负数的情况下,右移n会补1,会造成死循环
//所以下面采用左移一个flag的方式,避免了n的移动
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
unsigned int flag = 1;
while(n){
if(n & 1){
count++;
}
n = n >> 1;
}
return count;
}
};
//上面方式循环次数等于位数,下面的方法有几个1就循环几次
//利用 n 和 n-1 按位与后 消除最右边一个1的原理
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
while(n){
++count;
n = (n-1) & n;
}
return count;
}
};
JZ16 数值的整数次方
二分的递归 不要傻傻的递归
class Solution {
public:
double Power(double base, int exponent) {
if(exponent == 0){
return 1;
}
if(exponent == 1){
return base;
}
double result;
//一定要注意,不要习惯性的设置为int类型
result = Power(base, abs(exponent) >> 1);
result *= result;
if(exponent & 1 == 1){
result *= base;
}
if(exponent < 0){
double a = 1;
return a/result;
}
return result;
}
};
JZ17 打印从1到最大的n位数
对于这种看起来简单的问题,更应该小心
题目一
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型 最大位数
* @return int整型vector
*/
vector<int> printNumbers(int n) {
// write code here
int numbers = 1;
int i = 0;
while(i++ < n){
numbers *= 10;
}
vector<int> v;
for(int i = 1; i < numbers; i++){
v.push_back(i);
}
return v;
}
};
//若要考虑大数情况,常用方法是用字符串或者数组代表大数
````c++
bool Increment(char* number){
bool isOverflow = false; //判断是否超过
int nTakeOver = 0; //进位判断
int nLength = strlen(number);
for(int i = nLength - 1; i >= 0; --i){ //从最后一位开始
int nSum = number[i] - '0' + nTakeOver; //记录当前位的数值(包括低位进位)
if(i == nLength - 1){ //最后一位直接++
nSum ++;
}
if(nSum >= 10){ //若需要进位
if(i == 0){ //为第1位时,溢出
isOverflow = true;
}else{ //进位,nSum归零,进位标志设为1
nSum -= 10;
nTakeOver = 1;
number[i] = '0' + nSum;
}
}else{ //不需要进位
number[i] = '0' + nSum;
break;
}
}
return isOverflow ;
}
//打印数字,前面为0时不能打印出来
void PrintNumber(const char* number){
int n = strlen(number);
bool isnotzero = false;
for(int i = 0; i < n; i++){
if(number[i] != '0'){
isnotzero = true;
}
if(isnotzero){
cout << number[i];
}
}
cout << endl;
}
void PrintToMaxOfDigits(int n){
if(n <= 0){
return;
}
char* number = new char[n+1];
memset(number, '0', n);
number[n] = '\0'; //右斜零
while(!Increment(number)){
PrintNumber(number);
}
delete[] number; //一定记得释放空间
}
扩展题目二
实现大数的加法,给定两个字符串形式的非负整数 num1和num2 ,计算它们的和。
string bigsum(string s,string m){
int takeover = 0;
int nlength = max(s.size(),m.size());
string output;
int i = s.size()-1;
int j = m.size()-1;
//此处的条件判断非常巧妙
while(i>=0 || j>=0 || takeover){
int num1 = i >= 0 ? s[i] - '0' : 0;
int num2 = j >= 0 ? m[j] - '0' : 0;
int sum = num1 + num2 + takeover;
takeover = sum /10;
sum = sum %10;
output.push_back(sum + '0');
i--;j--;
}
reverse(output.begin(),output.end());
return output;
}
//若要考虑负数呢?
JZ18 删除链表的节点
法一
遍历前一个节点
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @param val int整型
* @return ListNode类
*/
ListNode* deleteNode(ListNode* head, int val) {
// write code here
ListNode* temp = new ListNode(-1); //切记要用new的形式给指针赋值,否则会导致错误
temp->next = head;
ListNode* p = temp;
//避免了当相同节点为头节点时,返回头节点导致的错误
while(temp->next != nullptr && temp != nullptr){
if(temp->next->val == val){
temp->next = temp->next->next;
break;
}
temp = temp->next;
}
// if(head->val == val){
// return head->next;
// }
return p->next;
}
};
法二
把下一个节点的内容复制到需要删除的节点上
class Solution {
public
ListNode* deleteNode(ListNode* head, int val) {
// write code here
ListNode* temp = head;
while(temp->val != val && temp != nullptr){
temp = temp->next;
}
if(temp == nullptr){
return head;
}
temp -> val = temp->next->val;
temp -> next = temp->next->next;
return head;
}
};
扩展题目 JZ76 删除链表中重复的结点
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
//保存重复元素出现的位置
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead) {
int a[1000];
memset(a,-1,sizeof(a));
ListNode* temp = pHead;
int num = 0;
vector<int> v;
while(temp != nullptr){
if(a[temp->val] != -1){
v.push_back(a[temp->val]);
v.push_back(num);
num++;
temp = temp -> next;
continue;
}
a[temp->val] = num;
num++;
temp = temp -> next;
}
ListNode* temp1 = new ListNode(-1);
ListNode* temp2 = temp1;
temp1->next = pHead;
int n = 0;
while(temp1->next != nullptr && temp1 != nullptr){
if(count(v.begin(), v.end(), n)){
temp1->next = temp1->next->next;
n++;
continue;
}
n++;
temp1 = temp1->next;
}
return temp2->next;
}
};
//双指针遍历
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead) {
ListNode* p1 = new ListNode(-1);
ListNode* p2 = new ListNode(-1);
p1->next = pHead;
ListNode* p3 = p1;
int a = 0;
while(p1->next != nullptr){
p2 = p1->next;
while(p2->next != nullptr){
if(p1->next->val == p2->next->val){
p2->next = p2->next->next;
a = 1;
}
else{
p2 = p2->next;
}
}
if(a == 1){
p1->next = p1->next->next;
a = 0;
}else{
p1 = p1->next;
}
}
return p3->next;
}
};
//递归解法
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
// 递归出口:当「输入节点为空」或者「不存在下一节点」,直接返回
if (pHead == null || pHead.next == null) return pHead;
if (pHead.val != pHead.next.val) {
// 若「当前节点」与「下一节点」值不同,则当前节点可以被保留
pHead.next = deleteDuplication(pHead.next);
return pHead;
} else {
// 若「当前节点」与「下一节点」相同,需要跳过「值相同的连续一段」
ListNode tmp = pHead;
while (tmp != null && tmp.val == pHead.val) tmp = tmp.next;
return deleteDuplication(tmp);
}
}
}
JZ19 正则表达式匹配
重点在于判断匹配的递归性质的掌握
https://blog.nowcoder.net/n/1aaa31fb6db943bbb92e9ca3f6bfb982
//递归的解法
class Solution {
public:
bool match(string str, string pattern) {
if(pattern.empty()) return str.empty();
if(pattern.size() >= 2 && pattern[1] == '*') {
if(!str.empty() && (str[0] == pattern[0] || pattern[0] == '.')){
return match(str.substr(1,str.length()), pattern) ||
match(str, pattern.substr(2,pattern.length()));
}else{
return match(str, pattern.substr(2,pattern.length()));
}
}
if(!str.empty() && (str[0] == pattern[0] || pattern[0] == '.')){
return match(str.substr(1,str.length()), pattern.substr(1,pattern.length()));
}
return false;
}
};
//动态规划
class Solution {
public:
bool match(string str, string pattern) {
bool dp[20][30];
memset(dp,false,sizeof(dp));
int m = str.length();
int n = pattern.length();
dp[0][0] = true;
for(int i=1;i<=n;i++){
dp[i][0]=false;//非空文本串与空模式串一定不匹配
}
for(int i = 0; i <= m; i++){
for(int j = 0; j <= n; j++){
if (j <= n-1 && pattern[j] == '*') continue;
if(pattern[j-1] == '*'){
dp[i][j] = (j>=2 && dp[i][j-2]) || ((i >= 1 && dp[i-1][j])
&& (str[i-1] == pattern[j-2] || pattern[j-2] == '.'));
}
if (i - 1 >= 0 && pattern[j-1] != '*') {
dp[i][j] = dp[i - 1][j - 1] && (str[i-1] == pattern[j-1] || pattern[j-1] == '.');
}
}
}
return dp[m][n];
}
};
JZ20 表示数值的字符串
https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/