剑指offer之4/6

1.数组中出现次数超过一半的数字
在遍历数组的时候保存两个值,一个数数组中的数字,一个是次数
当遍历到下一个数字时,如果与前面的数组相当,则加1,否则减1。
我们要找的数字出现次数比其他所有数字出现的次数和还多。我们要找的数字肯定是最后一次把次数设置为1的数字。
个人理解 用重复的数字去抵消不同的数字

2.最小的K个数
用快排里的partition函数求解

3.数据流中的中位数
使用堆,分成一个最大堆和一个最小堆,如果数据size是偶数,那么中位数是最大堆的最后一位,如果是奇数,中位数就是最大堆的堆顶和最小堆的堆顶的和的1/2。
需要注意的问题:新进来一个数字,如果数据size是偶数,将其插入到最小堆中(中位数后面的一堆数)中,这里有一个问题,新来的数据如果比较小,小于最大堆(中位数前面的一堆数)时,需要把新来的数插入最大堆,然后把原最大堆的堆顶插入到最小堆中。这样可以保证最小堆里的数都比最大堆大。

4.连续子数组的最大和

int FindGreatestSumOfSubArray(vector<int> array) 
    {
       int sum=0;
       int maxsum=0x80000000;///最小的负数
       for(int i=0;i<array.size();i++)
       {
           sum=sum+array[i];
           if(sum<array[i]) //如果sum还没有新进来的这个数大,那前面就不要了,sum=当前的数
               sum=array[i];
           if(maxsum<sum) //记录最大的maxsum
               maxsum=sum;
       }
        return maxsum;
    }

4.1-n整数1出现的次数
主要思路:设定整数点(如1、10、100等等)作为位置点i(对应n的各位、十位、百位等等),分别对每个数位上有多少包含1的点进行分析
//根据设定的整数位置,对n进行分割,分为两部分,高位n/i,低位n%i
以百位为例
//当i表示百位,且百位对应的数>=2,如n=31456,i=100,则a=314,b=56,此时百位为1的次数有a/10+1=32(最高两位0~31),每一次都包含100个连续的点,即共有(a/10+1)100个点的百位为1
//当i表示百位,且百位对应的数为1,如n=31156,i=100,则a=311,b=56,此时百位对应的就是1,则共有a%10(最高两位0-30)次是包含100个连续点,当最高两位为31(即a=311),本次只对应局部点00~56,共b+1次,所有点加起来共有(a/10
100)+(b+1),这些点百位对应为1
//当i表示百位,且百位对应的数为0,如n=31056,i=100,则a=310,b=56,此时百位为1的次数有a/10*100=31(最高两位0~30)

5.数字序列中某一位的数字
个位数10个,十位数90个,百位数900个。。。。
可以根据输入的数,跳过一些数,缩小查找的范围
找1001位,个位跳过,十位跳过,百位总共3*900=2700位,2700<1001,说明1001位一定在百位数字范围内,1001-10-90=881,,881=270 *3+1。说明这个数位于370这个数字的中间那个数,也就是7。

6.把数组排成最小的数
思路:把数组转化成字符串(大数)
实际就是按照字符串大小排序,然后拼成最小的数

7.把数字翻译成字符串
从右往左看这个字符串(这样可以避免重复计算),从最后一个字符不断向前判断翻译可能性。根据公式f(i)=f(i+1)+f(i+2) 函数f(i)表示以第i位为开始,所有翻译的可能性。

8.礼物的最大价值
典型动态规划
f(i,j)=max(f(i-1,j),f(i,j-1))+g(i,j) 当前的最大值等于左边和上边之间的最大值+当前值

9.最长不含重复字符的子字符串
动态规划法
第一种情况,进来一个字符,跟前面的字符没有重复,f(i)=f(i-1)+1
第二种情况,和前面的字符有重复,距离上一个一样的字符距离为d,如果d<=f(i-1),说明上一个一样的字符在上一个最长不重复字符串的内部,此时f(i)=d。
第三种情况,和前面字符有重复,d>f(i-1),说明上一个一样的字符不再上一个最长不重复字符串的内部,不影响现在的最长不重复字符串计算,f(i)=f(i-1)+1

int AAA(const std::string &str)
{
   int curLength=0;
   int maxLength=0;
   int position[]={-1} ///用于保存每个字符出现的位置
   for(int i=0;i<str.length();i++)
   {
        int pre_pos=position[str[i]-'a'] ///这个字符之前的位置
        if(pre_pos>0&&i-pre_pos>curLength) ///第一种和第三种情况
          curLength++;
        else
        {
            if(maxLength<curLength)
                maxLength=curLength; ///把最大长度保留住
            curLength=i-pre_pos; ///第二种情况
        }
        if(curLength>maxLength)
            maxLength=curLength;
   }
   return maxLength;
}

10.丑数(只包含2.3.5因子的数)
思路:一个丑数乘以2、3、5结果还是一个丑数。
按从小到大的顺序把每个丑数放入一个数组。每新进来一个丑数,都是之前数组中丑数乘2、3或5的结果,找出第一个比之前数组最大的数大的丑数,插入数组。
设置界限,以判断第一个新进来的丑数到底是乘几得到的。
2排好序的丑数数组中的数,在界限之前,乘积小于当前最大丑数,界限之后,乘积大于当前最大丑数。
3
排好序的丑数数组中的数,在界限之前,乘积小于当前最大丑数,界限之后,乘积大于当前最大丑数.
5*排好序的丑数数组中的数,在界限之前,乘积小于当前最大丑数,界限之后,乘积大于当前最大丑数

11
(1) 第一个只出现一次的字符
思路:利用希哈表记录每一个字符出现的字符出现的次数,找出第一个只出现一次的字符。
(2)字符流中第一个只出现一次的字符
还是使用希哈表,字符流中每进入一个字符,就更新希哈表。

12.数组中的逆序对
思路:先把数组分割成子数字,两个相连的子数组之间统计逆序对,统计完之后在进行合并,排序,循环这个过程,完成任务

int InversePairs(vector<int> data) 
    {
        int n=data.size();
        if(n<=0)
            return 0;
        vector<int> copy;
        for(int i=0;i<n;i++)
            copy.push_back(data[i]);// copy的作用是记录排好序的子数组
        long long count=InversePairsCore(data,copy,0,n-1);
        return count%1000000007;
    }
    long long InversePairsCore(vector<int> &data,vector<int> &copy,int start,int end)
    {
        if(start==end)
        {
           copy[start]=data[end];
          return 0; 
        }
        int mid=(end-start)>>1;
        long long left=InversePairsCore(copy,data,start,start+mid);//这里把输入第一位变成了copy
        long long right=InversePairsCore(copy,data,start+mid+1,end); //注意
        int i=start+mid;///第一个指针
        int j=end;//第二个指针
        int indexcopy=end;///指向辅助数组
        long long count=0;
        while(i>=start&&j>=start+mid+1)//排好序的左右数组,再合并排序,每一次的copy,都会变成下一次合并的data!!!
        {
            if(data[i]>data[j])
             {
                  copy[indexcopy--]=data[i--];
                  count=count+j-start-mid;          //count=count+j-(start+length+1)+1;
                }
             else
                {
                  copy[indexcopy--]=data[j--];
                }          
        }
       for(;i>=start;i--)  //把剩下的放入copy数组
           copy[indexcopy--]=data[i];
       for(;j>=start+mid+1;j--)
           copy[indexcopy--]=data[j]; 
       return left+right+count; ///左右+最后一次左右合并
    }

13.两个链表的第一个公共结点
思路一:从链表尾部向前比较,最后一个相同结点,就是第一个公共结点。
思路二:先遍历两个链表得到他们的长度,找出较长的链表,让较长的链表先走到与短链表对其的位置,然后在开始对应比较。

猜你喜欢

转载自blog.csdn.net/weixin_39373577/article/details/90349791