class Solution {
private:
static bool cmp(int a,int b)//定义比较规则,例如a=2,b=21,两者都转为字符串,a+b就为221,b+a就为212,则b+a应该在前面,a+b在后面
{
string before="";//存储a+b
string after="";//存储b+a
before+=to_string(a);
before+=to_string(b);
after+=to_string(b);
after+=to_string(a);
return before numbers) {
string result="";
if(numbers.empty())
return result;
//对numbers中的元素按照cmp定义的规则排序,排完序之后从头组合成字符串就是结果
sort(numbers.begin(),numbers.end(),cmp);
for(int i=0;i
33、丑数
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
我们拿到这个题,第一就是想到先实现一个判断丑数的方法,然后逐个数进行判断,找到第index个丑数即可,但是这种方法效率很低,因为是逐个判断每个数,因此要考虑另一种算法,也就是定义一个数组存放排序的丑数,一个数是丑数,那么它*2、3、5的结果都是丑数,此时这个数组中最大的丑数为M,要生成下一个丑数,就是这个数组中某一个丑数2、3、5的结果,若将每一个丑数都2,可能有小于M的一些丑数,因为数组中已经存在这些丑数(因为数组是排序的),就忽略,也会有大于M的一些丑数,在这里面第一个大于M的数的下标记为M2,同时关于3和5的情况也和上述一样,得到M3和M5,最后求M2、M3、M5代表的元素最小的即可。
class Solution {
public:
int GetUglyNumber_Solution(int index) {
if(index<7)//1---6都是丑数
return index;
vector arr(index);//存放排序的丑数,一共有index个
arr[0]=1;//第一个丑数是1
int m2=0,m3=0,m5=0;
for(int i=1;i
34、第一个只出现一次的字符
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
这里使用哈希的思想,统计每个字符出现的次数并且使用一个容器按照对应位置存储,然后遍历这个容器,哪个次数为1就返回下标即可。
class Solution {
public:
int FirstNotRepeatingChar(string str) {
if(str.empty())
return -1;
unordered_map m;
for(int i=0;i
35、数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
这个题拿到可能第一时间想到的是遍历数组,当前元素与后面的每一个元素相比,最后得到逆序对即可,但是这种方法它的时间复杂度为O(n^2),为了提高效率,这里我们采用归并排序的思想,以空间换时间,也就是比如7 5 4 6,我们将其划分为7 5 和4 6,然后进行:
(1)7 5求出逆序对(这里采用的是递归的方式求出这个子数组的逆序对)7>5,有一对;
(2)4 6求出逆序对(也是采用的递归的方式求出这个子数组的逆序对)6>4,有一对;
(3)7 5和4 6分别排序,为5 7和4 6;
(4)设两个指针分别指向两个子数组分别的最大值,i指向7,j指向6;
(5)比较i和j所指向的值,若array[i]>array[j],因为j指向的是当前子数组的最大值,因此这个子数组有几个元素,就与array[i]有几对逆序对(当前有两个元素4 6,逆序对加2,则此时逆序对一共是2+2=4),7>6比较完之后,将i指向的值放入辅助数组中,i向前走一步到5,此时辅助数组中有一个元素7;
(6)判断i和j指向的值,array[i] (7)判断array[i]和array[j],5>4,由于第二个子数组中只有一个元素,逆序对+1,即为4+1=5对,将5放入辅助数组,第一个子数组遍历完毕,第二个只剩下4,将4放入辅助数组,结束。
class Solution {
public:
int InversePairs(vector data) {
if(data.size()<=1)
return 0;//如果少于等于1个元素,直接返回0
int* ret=new int[data.size()];
//初始化该数组,该数组作为存放临时排序的结果,最后要将排序的结果复制到原数组中
for(unsigned int i=0;i& data,int*& ret,int start,int end)
{
if(start==end)
{
ret[start]=data[start];
return 0;
}
//将数组拆分成两个子数组,分别求内部的逆序对数
int length=(end-start)/2;
//分别计算左边子数组和右边子数组
int leftcount=MergeSort(data,ret,start,start+length)%1000000007;
int rightcount=MergeSort(data,ret,start+length+1,end)%1000000007;
//进行逆序计算
int i=start+length;//左边数组的最后一个下标
int j=end;//右边数组的下标
int index=end;//辅助数组下标,从最后一个算
int count=0;
while(i>=start && j>=start+length+1)//因为两个子数组是向前遍历的,因此只要i不越界start,j不越界start+length+1
{
if(data[i]>data[j])
{
ret[index--]=data[i--];
//统计长度
count+=j-start-length;
if(count>=1000000007)//数值过大求余
count%=1000000007;
}
else
{
ret[index--]=data[j--];
}
}
for(;i>=start;--i)
{
ret[index--]=data[i];
}
for(;j>=start+length+1;--j)
{
ret[index--]=data[j];
}
//排序
for(int i=start; i<=end; i++) {
data[i] = ret[i];
}
//返回最终的结果
return (count+leftcount+rightcount)%1000000007;
}
};
36、两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
如果有公共结点,说明后面就是使用公共的尾部,先计算两个链表的长度,哪个链表长,就从哪个链表开始走他们相减的大小,然后两者一起走,直到遇到相同的结点返回即可。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
private:
int GetLength(ListNode* head)
{
if(head==nullptr)
return 0;
ListNode* pCur=head;
int sum=0;
while(pCur)
{
sum++;
pCur=pCur->next;
}
return sum;
}
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
int len1=GetLength(pHead1);
int len2=GetLength(pHead2);
if(len1>len2)
{
for(int i=0;inext;
}
}
else
{
for(int i=0;inext;
}
}
while(pHead1!=nullptr)
{
if(pHead1==pHead2)
return pHead1;
pHead1=pHead1->next;
pHead2=pHead2->next;
}
return nullptr;
}
};
37、数字在排序数组中出现的次数
统计一个数字在排序数组中出现的次数。
首先我们知道他是排序数组,因此可以想到二分查找,如果一个元素重复了,一定是连续出现的(因为是排序数组),因此要想要知道某个数字出现的次数,要知道他第一次出现的下标和最后一次出现的下标,那么可以分别写得到第一次出现的k和最后一次出现的k的函数,例如要知道k第一次出现的下标,我们定义start和end,每次求出mid,如果此时mid所指的值不是k,那就要按照二分查找的方法来查找k,即如果arr[mid]大于k,就将end走到mid-1,如果arr[mid]end结束,返回-1;
当然获得最后一次出现的k的下标也是这种思想;
我们主函数中实现获得第一次和最后一次出现k的下标(在不是-1的情况下),然后最后一次-第一次+1就是k出现的次数。
class Solution {
private:
int GetFirstK(vector data,int k,int start,int end)
{
int length=data.size();
int mid = (start+end)>>1;
while(start <= end)
{
if(data[mid]>k)
{
end=mid-1;
}
//如果中间不是k
else if(data[mid]= 0 && data[mid-1] == k)
end = mid-1;
else
return mid;
mid=(start+end)>>1;
}
return -1;
}
int GetLastK(vector data,int k,int start,int end)
{
int length = data.size();
int mid = (start + end) >> 1;
while(start <= end)
{
if(data[mid] > k)
{
end = mid-1;
}
else if(data[mid] < k){
start = mid+1;
}
else if(mid+1 < length && data[mid+1] == k){
start = mid+1;
}
else{
return mid;
}
mid = (start + end) >> 1;
}
return -1;
}
public:
int GetNumberOfK(vector data ,int k) {
int len=data.size();
if(len==0)
return 0;
int count=0;
int first=GetFirstK(data,k, 0,data.size()-1);
int last=GetLastK(data,k, 0,data.size()-1);
if(first!=-1 && last!=-1)
return last-first+1;
return 0;
}
};
38、二叉树的深度
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
如果根结点为空,返回0(这也是递归终止条件),否则就去左子树计算高度和去右子树计算高度(递归),最后返回较高子树的高度+1即可。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
if(pRoot==nullptr)
return 0;
int leftDepth=TreeDepth(pRoot->left);
int rightDepth=TreeDepth(pRoot->right);
return leftDepth>rightDepth ? leftDepth+1 : rightDepth+1;
}
};
39、平衡二叉树
判断一课树是否是平衡二叉树,我们第一时间想到的方法是分别计算出左右子树的高度,然后看其高度差的绝对值是否<=1,然后递归当左右子树都平衡时才算平衡,但是这种方法我们会重复遍历同一个结点,因为计算高度时会遍历响相应的结点,而判断平衡时又会遍历这些节点,因此我们要想另一种更加简便的算法
我们可以使用后序遍历的顺序来遍历,这样不会重复遍历同一个结点,也就是边判断平衡边保存当前的二叉树的高度,不用重复遍历,在遍历完某结点的左右孩子之后,根据高度来判断平衡;
实现代码:
class Solution {
private:
bool _IsBalanced_Solution(TreeNode* pRoot,int* pDepth)//保存当前代表的二叉树的高度
{
if(pRoot==nullptr)
{
*pDepth=0;
return true;
}
int leftDepth;
int rightDepth;//左子树和右子树的高度
if(_IsBalanced_Solution(pRoot->left,&leftDepth) && _IsBalanced_Solution(pRoot->right,&rightDepth))//左右子树都平衡
//遍历过某结点的左右节点之后,根据高度判断是否平衡
{
int diff=leftDepth-rightDepth;
if(diff<=1 && diff>=-1)
{
*pDepth=leftDepth>rightDepth? leftDepth+1:rightDepth+1;
return true;
}
}
return false;
}
public:
bool IsBalanced_Solution(TreeNode* pRoot) {
int depth=0;
return _IsBalanced_Solution(pRoot,&depth);
}
};
40、数组中只出现一次的数字
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
遇到这个问题,我们可以先想一个简单的情景,就是一个整形数组中除了一个数字之外,其他的数字都出现了两次,写程序找出这一个只出现一次的数字,这样的问题,不难解决,就是任何一个数字异或自己都是0,0异或任何一个数等于那个数,因此我们可以依次异或数组中的每个数,最后的结果就是要找的那个数,那么相同的道理,我们也可以使用这种思想来解决这个问题,就是将数组分为两个子数组,然后将要找的两个数字分别放在一个子数组中,那么问题就在于如何让这两个数字分别放在两个子数组中,这里我们也可以采用异或的方法,即:
我们也是依次将数组中的每个数异或,最终得到的结果就是要找的两个数异或的结果(因为其他数都出现了两次,都异或掉了),那么我们就可以根据这个结果的二进制的某一位是否是1来使两个数分别在一个子数组中(因为某一位是1表示这两个数的那一位一定一个是0,一个是1,因此可以区分),可以将那一位二进制位是1的数放在第一个子数组,将那一位二进制位是0的数放在第二个子数组(出现两次的数一定在一个子数组中,因为他们那一位二进制位一定相同,要么都是1,要么都是0),然后两个子数组分别再依次异或就可以得到这两个数。
实现代码:
class Solution {
private:
unsigned int FindFirstBit1(int num)//找num的哪一二进制位是1,用index表示
{
unsigned int index=0;
while(((num&1)==0) && (index < 8*sizeof(int)))//找哪一位是1,并且index不能超过32位
{
index++;
num = num>>1;
}
return index;
}
private:
bool Isbit1(int num,unsigned int index)//判断num的index位是否是1
{
num = num>>index;
return (num&1);
}
public:
void FindNumsAppearOnce(vector data,int* num1,int *num2) {
if(data.size()==0 || data.size() <2)
return;
int ret=0;//用来进行异或,并且保存异或的结果
//第一次数组依次异或
for(int i=0;i