目录
数组中只出现一次的数字 3/9
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
参考:https://www.cnblogs.com/silentteller/p/12056878.html
参考:https://www.cnblogs.com/zhudingtop/p/11387283.html
借题发挥发现自己操作符和 操作运算符方面很差,补了一下下。
看了半天没看懂私聊大佬,大佬给我回信我才看懂
——————开始解题——————
设数组{1,5,6,1}, 只出现一次数字为5(0101)和6(0110)
1 相同数字异或为0,不同数字异或为1,因此把整个数组的元素异或的结果为目标数字的异或结果。详细版:5和6的异或结果,即(0011)
2 根据异或结果,找到目标数字在哪一位上不同。异或同则为0,不同为1,哪一位上是1,说明哪一位上数字不同。详细版:接着将上述结果(0011)与1进行按位与运算,即(0011 & 0001),最右边第一位结果为1,所以压根没进while循环,意味着最右边第一位上,这两个数字是不同的
3接着通过最右边一位将这两个数字区分开,将最右一位是1的分到一个数组,将最右是0的分到一个数组,分别异或,最后分别得到的就是只出现一次的两个数字。详细版:判断数组每一个元素与d(0001)的与运算结果是1还是0,分开两个数组,然后做异或。
【异或:相同为0】
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
if(data.size() == 0)
return ;
int flag = data[0];
for(int i = 1; i < data.size(); i++){
flag ^= data[i];
}
int d = 1;
while((d & flag) == 0){
d = d <<1;
}
*num1 = 0;
*num2 = 0;
for(int i = 0; i < data.size(); i++){
if((d&data[i]) != 0)//【易错】我之前没加(d&data[i])的括号,优先级出现错误
*num1 ^= data[i];
else
*num2 ^= data[i];
}
}
};
数字在排序数组中出现的次数3/9
参考;https://blog.csdn.net/a136522541/article/details/96426609
思路:数组有序,因此重复的数字是连续的,只要求出第一次和最后一次出现的下标,然后相减+1,就得到了数字重复的次数
二分法查找target的变形,要在找到目标值时候判断前后是否有相同数字,时间复杂度O(NlogN)
【有序数组】【二分查找】【递归】【重复次数】
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
if(data.size() == 0){
return 0;
}
//数组有序,因此第一次出现在左边,最后一次出现在右边
int l = FindFirst(data, k, 0, data.size()-1);
int r = FindEnd(data, k, 0, data.size()-1);
if(r != -1 && l != -1)//如果r l不等于-1 则出现次数可得
return r-l+1;
else
return 0;
}
int FindFirst(vector<int> &data ,int k, int left, int right){
//找出目标数字k第一次出现的下标,【&会更快】
if(left > right)
return -1;//出口A l>r输入位置非法,并且没找到目标数字
int mid = left + (right - left) / 2;//中间元素
if(data[mid] == k){//如果目标元素 = mid
if((mid > 0 && data[mid-1] != k) || mid == 0)//检查mid的位置是否合法,1 mid=0,说明mid在数组开始地方;2 mid>0&&mid前一个元素不等于目标数字
return mid;//出口B
else
right = mid - 1;//【重要】如果mid>0且mid前一个元素也等于目标数字,right前移【易错】
}
else if(data[mid] < k){//mid值小于目标数字,说明目标数字在mid右侧,left右移
left = mid + 1;
}
else{//mid值大于目标数字,说明目标数字在mid左侧, right左移
right = mid -1;
}
return FindFirst(data, k, left, right);//递归
}
int FindEnd(vector<int> &data ,int k, int left, int right){
//找出目标数字最后一次出现的下标
if(left > right)
return -1;//出口A,非法输入,说明一直没找到目标数字
int mid = left + (right - left) / 2;
if(data[mid] == k){//mid = 目标数字
if((mid < data.size()-1 && data[mid+1] != k) || mid == data.size()-1)
//检测mid位置是否合法,如果mid到达数组末尾,合法,如果mid不在末尾&&下一个元素不等于目标数字,位置合法
return mid;
else
left = mid + 1;//存在下一个元素也等于目标值,left右移
}
else if(data[mid] < k){//如果mid小于目标值,目标值在mid右侧,left右移
left = mid + 1;
}
else{//如果mid大于目标值,目标值在mid左侧
right = mid -1;
}
return FindEnd(data, k, left, right);//递归
}
};
数组中的逆序对
参考:https://blog.csdn.net/lym940928/article/details/91354887(归并排序的拓展应用,讲的太好了)
参考:https://www.cnblogs.com/silentteller/p/11991571.html
浙江大学数据结构:https://www.bilibili.com/video/av55114968?p=112
浙大数据结构的归并排序是从前往后进行比较,本题是求逆序对,求左前面比后面大的数,所以从后往前比较会更合适。
归并排序是递归方法,先将数组分成左右两段。再将左右两段继续分成小左、小右两段,一直这样分割,知道子数组只剩一个元素的时候停止,进行归并。(递归)
设数组左left,中间mid,右边right。保存归并结果的临时数组temp大小为原数组大小,他的指针为index.逆序对个数cnt=0
左半段指针LeftEnd指向左半段的最后一个元素,初始化为mid;
右半段指针RightEnd指向右半段最后一个元素,初始化为right;
将data[LeftEnd]和data[RightEnd]进行比较大小:data[LeftEnd]大,则逆序对个数=右边剩下的元素个数(j - mid),将 data[LeftEnd]存进临时数组temp,指针index--,LeftEnd--;
data[RightEnd]大,data[RightEnd]存进临时数组temp,指针index--,RightEnd--;
如果左半边有剩余,将左边复制给temp
如果右半边有剩余,将右边复制给temp
归并结束,将temp里的元素,复制到原数组left开始的地方到left+数组大小的地方。
【归并排序】【递归】
class Solution {
public:
int InversePairs(vector<int> data) {
return mergesort(data, 0, data.size()-1)%1000000007;
}
long long mymerge(vector<int>& vec, int left, int mid, int right){
vector<int> temp(right-left+1,0);//存放结果的临时数组
int index = right-left;//临时数组最后一位的下标:数组个数-1
long long countnum = 0;//逆序对计数
int i = mid;//指向左边数组的最后一个
int j = right;//指向右边数组的最后一个
while(i >= left && j >= mid+1){//判断左右两个数组内都有元素
if(vec[i] > vec[j]){//如果左边最后一个大于右边最后一个
countnum += (j-mid);//逆序对个数 = 右边数组剩余元素个数
temp[index--] = vec[i--];//将左边最后一个复制进临时数组,并且左半边数组和临时数组指针都向左-1
}
else{//如果左边最后一个小于等于右边最后一个,
temp[index--] = vec[j--];//将右边最后一个复制到临时数组,并且右边数组和临时数组指针向左-1
}
}
while(i >= left)//如果左半边数组还剩下
temp[index--] = vec[i--];//把它复制到临时数组
while(j >= mid+1)//如果右边数组有剩余
temp[index--] = vec[j--];//将他复制到临时数组
for(int i = 0; i < temp.size(); ++i)//将临时数组复制到原数组从left开始的位置
vec[i+left] = temp[i];
return countnum;//返回逆序对计数
}
long long mergesort(vector<int> &vec, int left, int right){//递归
if(left >= right)//输入非法返回0
return 0;
int mid = (left + right) / 2;
long long leftCount = mergesort(vec, left, mid);//左半边的归并
long long rightCount = mergesort(vec, mid+1, right);//右半边的归并
long long res = mymerge(vec, left, mid, right);//左右半边的归并合并
return res + leftCount + rightCount;//返回计数总和
}
};
把数组排成最小的数
参考:https://www.cnblogs.com/silentteller/p/12055543.html
参考:https://blog.csdn.net/dawn_after_dark/article/details/81256350
参考:https://blog.csdn.net/diyinqian/article/details/72904404
【sort的cmp定义】【cmp必须static】【auto取得是numbers里面的内容即int】
class Solution {
public:
string PrintMinNumber(vector<int> numbers) {
if(numbers.size() == 0)//如果数组为空,返回空字符串
return "";
sort(numbers.begin(), numbers.end(), cmp);//按照大小进行排序,这个比较方式cmp是自己规定的
string res = "";//res初始化为空字符
for(auto i:numbers)//【易错】
res += to_string(i);//将数组元素进行拼接
/*for(int i = 0; i < numbers.size(); i++){
res += to_string(numbers[i]);
}*/
return res;//返回拼接后的字符
}
static bool cmp(int a, int b){//【易错:cmp一定是static,不然会报错】ab<ba 返回真,ba<ab 返回假
string strA = to_string(a) + to_string(b);
string strB = to_string(b) + to_string(a);
return strA < strB;
}
};