【剑指offer】数组的几个题

1.统计一个数字在排序数组中出现的次数

https://www.nowcoder.com/practice/70610bf967994b22bb1c26f9ae901fa2?tpId=13&tqId=11190&tPage=2&rp=2&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking

三种方法实现:

//方法一 O(n)遍历 没想到O(n)的解法也可以通过测试

    int GetNumberOfK(vector<int> data ,int k) 
    {
        int num = 0;
        for(int i = 0;i < data.size();i++)
        {
            if(data[i] == k)
            {
                num++;
            }
        }
        return num;
    }


    
    //方法二:有序数组 当然二分查找 找第一个k位置 第二个k位置 相减得出结果

int findk1(vector<int> &data ,int k,int first,int last)
{
    int mid;
    while(first <= last)
    {
        mid = (first + last) >> 1;
        if(data[mid] < k) 
        {
            first = mid + 1;//这一句 如果不+1... 末位可能永远
        }
        else if(data[mid] > k) 
        {
            last = mid - 1;//忘了-1 导致没找到的时候 无限循环 退不出去
        }
        else if(mid -1 >= first && data[mid -1] == k)
        {
            last = mid -1;
        }
        else
        {    
            return mid;
        }
    }
    return -1;
}
int findk2(vector<int> &data ,int k,int first,int last)
{
    int mid;
    while(first <= last)
    {
        mid = (first + last) >> 1;
        
        if(data[mid] < k) 
        {
            first = mid + 1;//这一句 如果不+1... 末位可能永远
        }
        else if(data[mid] > k) 
        {
            last = mid-1;
        }
        else if(mid +1 <= last && data[mid+1] == k)
        {
            first = mid +1;
        }
        else 
        {    
            return mid;
        }
    }
    return -1;
}

 int GetNumberOfK(vector<int> data ,int k) 
{
    if(data.empty()) return 0;
     
    int dsize = data.size();
    int firstk = findk1(data,k,0,dsize-1);
    int lastk = findk2(data,k,0,dsize-1);
     
    if(firstk != -1 && lastk != -1)
    {
      return (lastk - firstk +1);
    }
    return 0;
        
}

方法二的二分查找,是找一个最左边一个最右边;

方法三就是常规的二分查找;
    //方法三:二分查找到一个k的位置  左右分别++ -- 计数 but超出时间限制 想不通为啥O(n)遍历都可以,这个就超出时间

   int GetNumberOfK(vector<int> data ,int k) 
    {
        int dsize = data.size();
        if(dsize <= 0) return 0;
        
        int first = 0;      //first下标
        int last = dsize-1; //last下标
        
        int mid = HalfSearch(data,first,last,k);        //mid下标
        if(mid == -1) return 0;
        
        int num = 1;
        for(int i = mid+1; i < dsize;i++)//向右遍历 i++
        {
            if(data[i] == k) num++;
            else break;
        }
        for(int i = mid -1;i >= 0;i--) //向左遍历 i--
        {
            if(data[i] == k) num++;
            else break;
        }
        
        return num;
    }
    
    int HalfSearch(vector<int> &data ,int first,int last,int k)
    {
        int mid;
        while(first <= last)
        {
            mid = (first + last) >> 1;
            if(data[mid] == k) return mid;
            if(data[mid] < k) 
            {
                first = mid + 1;//这一句 如果不+1... 末位可能永远
            }
           if(data[mid] > k) 
           {
               last = mid -1 ;//这一句 如果不-1...
           }
        }
        return -1;
    }

总结:这道题,就是一个二分查找,当然讨论区还是看到了大神的神级别思路,让我钦佩不已。

二分查找,+1 和-1的问题让我真的搞了好久......惭愧惭愧,一直报错超出时间限制,我还很奇怪在我本地测试实例就没有问题,后来一测试查找不存在的数据就无限循环了,额真的是........
   

2.一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

方法一::map计数法: 将vector中的每个数据当做map的key值,挨个插入,key第一次插入对应的value=1,key值第二次插入对应的value为2;统计个数完毕,最后找到value==1的,即为数组中只有一个

代码:

void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) 
    {
        //先判断不合理条件
        int datasize = data.size();
        if(datasize < 2) return;
        
        map<int,int> count;
        for(int i = 0;i < datasize;i++)
        {
            int key = data[i];
            map<int,int>::iterator it = count.begin();
            it = count.find(key);
            if(it == count.end())
            {
                count.insert(pair<int, int>(key,1));
            }
            else //if(it != count.end())
            {
                it->second = 2;
            }
        }
        
        map<int,int>::iterator it = count.begin();
        bool flag = true;
        for(it;it != count.end();it++)
        {
            if(flag && it->second == 1)
            {
                *num1 = it->first;
                flag = false;
                continue;
            }
            else if(!flag && it->second == 1)
            {
                *num2 = it->first;
                break;
            }
        }  
    }

方法1.5:方法一对染能做出来,但是确实太笨重了,挨个遍历计数,再挨个遍历找计数为1的。

讨论区看到一个用set的,也是统计个数,不过方法比我巧妙。set中插入vector中数据,如果插入过再次插入时就直接删除该数据,那么最后set中剩下的两个数字就是最后的结果,因为出现两次的数字直接被删掉了。

而且代码简练,就几行。感觉我写的map表技术法太.....Low了......

代码:

链接:https://www.nowcoder.com/questionTerminal/e02fdb54d7524710a7d664d082bb7811?f=discussion
来源:牛客网

void FindNumsAppearOnce(vector<int> data,int* num1,int *num2)
 {
        set<int> save;
        set<int>::iterator iter;
        for (int i = 0; i < data.size(); i++){
            if (save.find(data[i]) == save.end())
                save.insert(data[i]);
            else{
                iter = save.find(data[i]);
                save.erase(iter);
            }
        }
        iter = save.begin();
        *num1 = *iter;
        *num2 = *(++iter);
    }

 

方法二:(看讨论区的做法)

1、异或思想,一个数与自己异或为0,一个数与0异或为自己

2、由于其它数字两两相同,所以所有数异或则得到这两个不同数的异或结果。取这个结果的第一个1作为标志位

3、这个标志的1,必须是:这两个数在该位一个为0,一个为1

4、这样可以将数组分为两组,一组在该标志位为1,一组在该标志位为0,这两个不同数字分别在这两组内

5、将两组内的数分别异或,得到两个结果则为这两个不同的数

代码:

void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) 
     {
        int datasize = data.size();
        if(datasize < 2) return;
         
         int num=0;
         for(int i=0;i < datasize;i++)
         {
            num^=data[i];//所有数异或,结果为不同的两个数字的异或
         }
        int count=0;//标志位,记录num中的第一个1出现的位置
        for(;count < datasize;count++)
        {
            if((num&(1<<count))!=0)
            {
                break;
            }
        }
        *num1=0;
        *num2=0;
        for(int i=0;i < datasize;i++){
            if((data[i]&(1<<count))==0)
            {//标志位为0的为一组,异或后必得到一个数字(这里注意==的优先级高于&,需在前面加())
                *num1^=data[i];
            }else
            {
                *num2^=data[i];//标志位为1的为一组
            }
        }   
    }

3.顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.

vector<int> printMatrix(vector<vector<int> > matrix) 
{
    vector<int> vec;
    int rownum = matrix.size();
    if(rownum == 0) return vec;
    int colnum = matrix[0].size();
    if(colnum == 0) return vec;

        
    int rfirst = 0;//行的最小值下标
    int rlast = rownum - 1; //行的最大值下标
        
    int cfirst = 0;         //列的最小值下标
    int clast = colnum -1 ; //列的最大值下标
        
    while(clast >= cfirst  && rfirst <= rlast)
    {
        //向右: 
        //行:最小行         
        //列:从从cfirst -> clast 
        //走完以后 最小行要+1
        for(int i = cfirst;i <= clast && clast >= cfirst  && rfirst <= rlast;i++)
        {
            vec.push_back(matrix[rfirst][i]);
        }
        rfirst++;
            
        //向下:  
        //行:rfirst -> rlast
        //列:最大列  
        //走完以后,最大列 -1
        for(int i = rfirst;i <= rlast&& clast >= cfirst  && rfirst <= rlast;i++)
        {
            vec.push_back(matrix[i][clast]);
        }
        clast--;
            
        //向左: 
        //行:最大行   
        //列:最大 -> 最小 
        //走完以后,最大行 -1
        for(int i = clast;i >= cfirst&& clast >= cfirst  && rfirst <= rlast;i--)
        {
            vec.push_back( matrix[rlast][i] );
        }
        rlast--;
            
        //向上
        //行:最大 -> 最小
        //列:最小
        //走完以后,最小列+1
        for(int i = rlast; i >= rfirst&& clast >= cfirst  && rfirst <= rlast;i--)
        {
            vec.push_back(matrix[i][cfirst]);
        }
        cfirst++;
    }
    
    return vec;
}

4.求数组数组中最小的k个数:

代码:

    //方法一:sort排序后 找前k个 时间复杂度O(n*lgn)
    /*
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) 
    {
        vector<int> vec;
        int size = input.size();
        if(k > size || k < 0 || size == 0) 
            return vec;
        
        sort(input.begin(),input.end());
        for(int i = 0;i < k;i++)
        {
            vec.push_back(input[i]);
        }
        return vec;
    }
    */
    
    //方法二:
    //冒泡k次 把最小的放到最后,在从vec中删除末位  时间复杂度O(k*n)
    /*
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) 
    {
        vector<int> vec;
        int size = input.size();
        if(k > size || k < 0 || size == 0)  return vec;
        
        for(int i = 0;i < k;i++)
        {
            for(int j = 0;j < input.size()-1;j++)
            {
                if(input[j] < input[j+1])
                {
                    int tmp = input[j];
                    input[j] = input[j+1];
                    input[j+1] = tmp;
                }
            }//冒泡出一个最小的
            vec.push_back(input[input.size()-1]);
            input.pop_back();
        }
        return vec;
    }
    */
    //方法三:放入set集合,空间复杂度O(n),时间复杂度O(n*logn)
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) 
    {
        vector<int> vec;
        int size = input.size();
        if(k > size || k < 0 || size == 0)  return vec;
        
        set<int> s;
        for(int i = 0;i < size;i++)
        {
            s.insert(input[i]);
        }
        set<int>::iterator it = s.begin();
        for(int i = 0;i < k;i++)
        {
            vec.push_back(*it);
            it++;
        }
        return vec;
    }

目前我认为的最优解是:堆排序,建立小根堆,时间复杂度:O(nlgk)

代码参考牛客网,这里贴出链接:

链接:https://www.nowcoder.com/questionTerminal/6a296eb82cf844ca8539b57c23e6e9bf?f=discussion
来源:牛客网

class Solution {
private:
     void heapSort(vector<int> &input, int root, int end){
        for(int j = end -1; j >= root; j --){
            int parent = (j + root -1)/2;
            if(input[parent] > input[j]){
                int temp = input[j];
                input[j] = input[parent];
                input[parent] = temp;
            }
        }  
     }
     
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> result ;
        if(k > input.size()) return result;
        for(int i = 0; i < k ; i ++){
            heapSort(input,i,input.size());
            result.push_back(input[i]);
        }
        return result;
    }
};

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值