11. 旋转数组的最小值
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
思路:由于旋转数组的特性,尾元素肯定是小于首元素的(当不考虑重复值时)。所以,每次找中间值,如果中间值大于首元素,则中元素为前面那段,相反,则为后面那段。截止条件为s,e相差1。
其次,考虑旋转0个的情况,返回的是第一个。在其次,考虑有重复的情况。如{1,0,1,1,1},{1,1,1,0,1}都可以看成{0,1,1,1,1}的旋转,故此时使用循环依次查找。
代码:
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.size() == 0) //注意vector的长度并不是array.length
return 0;
int length = rotateArray.size();
int s = 0, e = length - 1;
int mid = 0, min;
while(rotateArray[s] >= rotateArray[e])//旋转数组的特性,尾元素肯定是小于首元素的(当不考虑重复值时)
{
if(e - s == 1) return rotateArray[e];
mid = (s + e)/2;
if(rotateArray[s] == rotateArray[mid] && rotateArray[e] == rotateArray[mid] )//当三个下标的值相等时,只得顺序查找
{
min = rotateArray[s];
for(int i = s; i <= e; i++)
{
if(rotateArray[i] < min)
min = rotateArray[i];
}
return min;
}
if(rotateArray[mid] <= rotateArray[e]) //如果中间值大于首元素,则中元素为前面那段,相反,则为后面那段
e = mid;
else if(rotateArray[mid] >= rotateArray[s])
s = mid;
}
return rotateArray[mid];
}
};
12. 二进制中1 的个数
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
思路:
way1: 首先对负数做一点点特殊操作,可以将最高位的符号位1变成0,也就是n & 0x7FFFFFFF(0x7FFFFFFF 的二进制表示就是除了首位是 0,其余都是1,也就可以表示最大的int整型数。),这样就把负数转化成正数了,唯一差别就是最高位由1变成0,因为少了一个1,所以count加1。之后再按照while循环里处理正数的方法来操作就可以啦!
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
if( n < 0)
{
n = n & 0x7FFFFFFF;
count ++;
}
while(n != 0)
{
if(n % 2 == 1)
count ++;
n = n / 2;
}
return count;
}
};
**way2:**除了使用除2操作,把整数右移一位和把整数除以2在数学上是等价的,
在计算机中,除法的效率比移位运算要低得多,在实际变成中应尽可能的利用以为运算代替乘除法。同理,取余操作的效率也是明显低于和1做与运算的,这两个操作都能打到同样的效果,但是与运算的效率更高一些。
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
if( n < 0)
{
n = n & 0x7FFFFFFF;
count ++;
}
while(n != 0)
{
if(n & 1 == 1)
count ++;
n = n >> 1;
}
return count;
}
};
**way3:**把一个整数减去1在与它本身做与运算,就会把该整数最右边一个1变成0。那么一个整数的二进制有多少个1,就可以进行多少次这样的操作就可以了。
举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000。
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
while( n != 0)
{
count ++;
n = n & (n-1);
}
return count;
};
13. 求数值中的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0
思路:方法1,直接求,注意任意数的0次方等于1,负数的次方等于它的倒数(时间复杂度O(n))。方法2,利用平方的性质,an=a(n/2) * a(n/2)或an=a^(n-1/2) * a^(n-1/2)*a(分别为偶数、奇数的情况)(时间复杂度O(logn))
代码一:
class Solution {
public:
double Power(double base, int exponent) {
double result = 1;
if(exponent > 0)
{
while(exponent --)
{
result *= base;
}
}
else if ( exponent < 0)
{
exponent = -exponent;
while(exponent --)
{
result *= base;
}
result = 1.0 / result; //幂次方为负数时,其结果为其倒数
}
return result;
};
代码2(利用性质):
class Solution {
public:
double Power(double base, int exponent) {
if (exponent == 0) return 1;
int n = exponent > 0 ? exponent : -exponent;
double result = Power(base, n >> 1);
result *= result;
if(n & 1) result *= base;
if(exponent < 0) result = 1.0 /result; //幂次方为负数时,其结果为其倒数
return result;
}
};
14. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
思路:(1)新开数组直接模拟,或者用插入排序等方法
代码1:
class Solution {
public:
void reOrderArray(vector<int> &array) {
vector<int> arr;
for(int i = 0; i < array.size(); i++)
if((array[i] & 1) == 1)
arr.push_back(array[i]);
for(int i = 0; i < array.size(); i++)
if((array[i] & 1) == 0)
arr.push_back(array[i]);
for(int i = 0; i < arr.size(); i++)
array[i] = arr [i];
}
};
(2)首先,设置前指针指向第一个数,并且只向后移动;
然后,.设置第二个指针指向最后一个数,并且只向前移动;
最后,在两个指针相遇之前,第一个指针总是位于第二个指针的前面。如果第一个指针指向的数字是偶数,并且第二个指针指向的数字是奇数,我们就交换这两个数字。
测试不能通过!!!原来快排是不稳定的,而此题目要求相对位置不变!!!
其实,要想保证原有次序,则只能顺次移动或相邻交换。那么依然设置两个指针:
首先,begin从左向右遍历,找到第一个偶数;
然后,from从begin+1开始向后找,直到找到第一个奇数;
接着,将[begin,…,from-1]的元素整体后移一位;
最后将找到的奇数放入begin位置,然后begin++。
代码2:
class Solution {
public:
void reOrderArray(vector<int> &array) {
int begin = 0, end, tmp;
while(begin < array.size())
{
//找到第一个偶数
while(begin < array.size() && (array[begin] & 1) == 1)
begin ++;
end = begin + 1;
//找到第一个奇数
while(end < array.size() && (array[end] & 1) == 0)
end ++;
//将[begin,…,end-1]的元素整体后移一位
if(end < array.size())
{
tmp = array[end];
for(int i = end - 1; i >= begin; i--)
array[i+1] = array[i];
array[begin] = tmp;
}
else
break;
}
}
};
15. 链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
思路:方法1,遍历两次,第一次计算总长度,第二次找length-k+1个即可。(略)
方法2,遍历一次,用两个指针,第一个指针指向第i个,第二个指针指向i+k-1个即可,当第二个指针指向最后一个时,第一个指针指向的是倒数第k个。注意考虑空指针,k为0的情况(k为无符号,无符号-1很大的)
代码:
/*
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 || k == 0) return nullptr;
ListNode *p1 = pListHead, *p2 = pListHead;
for(int i = 0; i < k-1; i++)
if(p1->next != nullptr)
p1 = p1->next;
//考虑列表的长度小于k值
else
return nullptr;
while(p1->next != nullptr)
{
p1 = p1->next;
p2 = p2->next;
}
return p2;
}
};
16. 反转链表
输入一个链表,反转链表后,输出新链表的表头。
思路:遍历时,新链表newhead指向当前遍历结点,newhead->next指向上一个遍历的结点。为了防止断链,需要加临时变量存储当前的next。
代码:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead == nullptr) return nullptr;
ListNode *p = nullptr, *pNode = pHead, *r = nullptr;
while(pNode != nullptr)
{
ListNode *s = pNode->next;
p = pNode;
p->next = r;
r = pNode;
pNode = s;
// pNode = pNode->next; //不能省去第一个s赋值直接将pNode->next赋值给pNode。
}
return p;
}
};
17. 合并两个排序链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
代码1(递归):
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == nullptr) return pHead2;
if(pHead2 == nullptr) return pHead1;
ListNode *p = nullptr;
if(pHead1->val <= pHead2->val)
{
p = pHead1;
p->next = Merge(pHead1->next, pHead2);
}
else
{
p = pHead2;
p->next = Merge(pHead1, pHead2->next);
}
return p;
}
};
代码2(非递归):
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == nullptr) return pHead2;
if(pHead2 == nullptr) return pHead1;
ListNode *p = nullptr, *r = nullptr;
while(pHead1 != nullptr && pHead2 != nullptr)
{
if(pHead1->val <= pHead2->val)
{
if(p == nullptr) p = r = pHead1;
else{
r->next = pHead1;//有疑问???
r = r->next;
}
pHead1 = pHead1->next;
}
else
{
if(p == nullptr) p = r = pHead2;
else{
r->next = pHead2;
r = r->next;
}
pHead2 = pHead2->next;
}
}
if(pHead1 != nullptr) r->next = pHead1;
if(pHead2 != nullptr) r->next = pHead2;
return p;
}
};
18. 树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
具体实现如下图所示:
/*
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;
if(pRoot1->val == pRoot2->val && check(pRoot1, pRoot2))
return true;
return HasSubtree(pRoot1->left, pRoot2) || HasSubtree(pRoot1->right, pRoot2);//如果根结点不匹配,则判断是否与左右子树匹配
}
bool check(TreeNode *pRoot1, TreeNode *pRoot2)
{
if(pRoot2 == nullptr) return true;// 说明root2遍历完了,返回true
if(pRoot1 == nullptr) return false;
if(pRoot1->val == pRoot2->val) return check(pRoot1->left, pRoot2->left) && check(pRoot1->right, pRoot2->right);
else return false;
}
};
19. 二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:
二叉树的镜像定义:源二叉树
8
/
6 10
/ \ /
5 7 9 11
镜像二叉树
8
/
10 6
/ \ /
11 9 7 5
代码:
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
void Mirror(TreeNode *pRoot) {
if(pRoot == nullptr) return;
Mirror(pRoot->left);
Mirror(pRoot->right);
TreeNode *tmp;
tmp = pRoot->right;
pRoot->right = pRoot->left;
pRoot->left = tmp;
}
};
20. 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
思路:
把矩阵看成由若干个顺时针方向的圈组成,循环打印矩阵中的每个圈,每次循环打印一个圈。打印一圈通常分为四步,第一步从左到右打印一行;第二步从上到下打印一列;第三步从右到左打印一行;第四步从下到上打印一列。设置四个变量left,right,top,below,用于表示圈的方位,每一步根据起始坐标和终止坐标循环打印。
注意:最后一圈有可能不需要四步,有可能只有一行,只有一列,只有一个数字,因此我们要仔细分析打印每一步的前提条件:
打印第一步,第一步总是需要的。
打印第二步的前提条件是(top<below)
打印第三步的前提条件是(top<below && left<right)
打印第四步的前提条件是(top+1<below&&left<right)
`
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> res;
if(matrix.empty()) return res;
int row = matrix.size();
int col = matrix[0].size();
int top = 0, below = row - 1;
int left = 0, right = col -1;
while(left <= right && top <= below)
{
for(int i=left; i<=right; ++i)
res.push_back(matrix[top][i]);
if(top < below)
for(int i=top+1; i<=below; ++i)
res.push_back(matrix[i][right]);
if(top < below && left < right)
for(int i=right-1; i>=left; --i)
res.push_back(matrix[below][i]);
if(top + 1 < below && left <right)
for(int i=below-1; i>=top + 1; --i)
res.push_back(matrix[i][left]);
++ left; -- right; ++ top; --below;
}
return res;
}
};
来源:CSDN
作者:Tefuir 06
链接:https://blog.csdn.net/qq_43003252/article/details/104133852