剑指offer 刷题5 数组

目录

数组中只出现一次的数字 3/9

数字在排序数组中出现的次数3/9

数组中的逆序对

把数组排成最小的数


数组中只出现一次的数字 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;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值