C++中的二分法和双指针法及常见题目汇总

Posted cucucudeblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++中的二分法和双指针法及常见题目汇总相关的知识,希望对你有一定的参考价值。

1. 二分法

  二分查找也属于顺序表查找范围,二分查找也叫做折半查找,二分查找的时间效率为(logN)

  二分查找的基本思想是:在有序表中,取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功,如果给定值小于中间值,则查找数组的前半段,否则查找数组的后半段。

  二分查找只适用于有序数组或者链表

二分法常见题目汇总

 一、剑指offer

  1. 面试题4:二维数组中的查找 
  2. 面试题11:旋转数组中的最小数字
  3. 面试题53(a):在排序数组中查找数字

 二、leecode

  1. 统计有序矩阵中的负数  
  2. 寻找比目标字母大的最小字母

1. 面试题4:二维数组中的查找

  • 题目描述:

技术图片

  •  问题分析,由于每一行从左到右递增,每一列从上到下递增,因此我们可以考虑左下角元素,从左到右递增,从下到上递减  

  若需要查找的元素target等于左下角元素,则查找到,若target小于左下角元素,向上移动,若target大于左下角元素,则向右移动

  • 代码参考
 1 class Solution {
 2 public:
 3     bool Find(int target, vector<vector<int> > array) {
 4         if(array.empty())
 5             return 0;
 6         int rows=array.size()-1;
 7         int cols=0;
 8         while(rows>=0&&cols<array[0].size())
 9         {
10             if(target>array[rows][cols])
11                 ++cols;
12             else if(target<array[rows][cols])
13                 --rows;
14             else
15                 return true;
16         }
17         return false;
18     }
19 };

2. 面试题11:旋转数组中的最小数字

  •  题目描述

  技术图片

 

  •  问题分析

  首先要搞定出旋转数组的定义,其是将一个非递减排序的数组的一个旋转,因此,旋转数组是由两个递增的子数组组成,并且第一个子数组中的元素都比第二个子数组中元素大。因此,旋转数组的最小数字一定是第二个递增子数组的头元素。因此寻找旋转数组的最小数字的方法如下:

  首先设置三个指针,first指向数组头,mid指向数组中间,last指向数组尾

  如果mid>first,则mid在第一个递增的子数组中,最小值在数组的后半部分

  如果mid<last,则mid在第二个递增的子数组中,最小值在数组的前半部分

  • 参考代码
 1 class Solution {
 2 public:
 3     int minNumberInRotateArray(vector<int> rotateArray) {
 4         int first=0;
 5         int last=rotateArray.size()-1;
 6         int mid=first;
 7         while(rotateArray[first]>=rotateArray[last])
 8         {
 9             if(last-first==1)
10             {
11                 mid=last;
12                 break;
13             }
14             else
15             {
16                 int mid=(first+last)/2;
17                 if(rotateArray[first]<=rotateArray[mid])
18                     first=mid;
19                 else if(rotateArray[last]>=rotateArray[mid])
20                     last=mid;
21             }
22         }
23         return rotateArray[mid];
24     }
25 };

3. 面试题53 :在排序数组中查找数字

(a) 统计数字在排序数组中出现的次数

  • 题目描述

      技术图片

  • 问题分析

  为了统计数字在排序数组中出现的次数,我们可利用二分法,找到第一个出现的k,再找到最后一个出现的k,即可以实现统计数字在排序数组中出现的次数

  为了找到第一次出现的k,三个指针,一个指针指向数组头,一个指针指向数组尾,一个指针指向中间。如果中间指针指向的数字等于要查找的k,则判断中间指针的前一个数字是否是k,如果不是k,则找到第一个出现的k,如果是k,则第一个出现的k 在数组的前半部分。如果中间指针指向的数字小于k,则k在数组的后半部分,如果中间指针指向的数字大于k,则k在数组的前半部分  

  为了找到最后一次出现的k,方法是类似的,不同之处在于,如果中间指针等于k,则判断中间指针的后一个数字是否为k,如果不是k,则找到最后一个k出现的位置,否则,最后一个k出现的位置在数组的后半部分

  • 参考代码
 1 class Solution {
 2 public:
 3     //找到第一个出现的k,如果mid==k,则判断mid-1是否为k,如果为k,则第一个出现的k在数组的前半部分
 4     //如果不为k,则找到第一个出现的K的位置
 5     //如果mid<k,则k在数组的后半部分
 6     //如果mid>k,则k在数组的前半部分
 7     int getFirstk(vector<int>&data,int k,int begin,int end)
 8     {
 9         int mid=(begin+end)/2;
10         if(begin>end)
11             return -1;
12         else
13         {
14             mid=(begin+end)/2;
15             if(data[mid]==k)
16             {
17                 if(mid==0||(mid>0&&data[mid-1]!=k))
18                     return mid;
19                 else 
20                     end=mid-1;
21             }
22             else if(data[mid]>k)
23                 end=mid-1;
24             else
25                begin=mid+1;
26             return getFirstk(data,k,begin,end);
27         }
28     }
29     //找到最后一个k出现的位置
30     int getLastk(vector<int>&data,int k,int begin,int end)
31     {
32         int mid;
33         int len=data.size();
34         if(begin>end)
35             return -1;
36         else
37         {
38             mid=(begin+end)/2;
39             if(data[mid]==k)
40             {
41                 if((data[mid+1]!=k&&mid<len-1)||mid==len-1)
42                     return mid;
43                 else 
44                     begin=mid+1;
45             }
46             else if(data[mid]>k)
47                 end=mid-1;
48             else
49                begin=mid+1;
50             return getLastk(data,k,begin,end);
51         }
52     }
53     int GetNumberOfK(vector<int> data ,int k) {
54         if(data.empty())
55             return 0;
56         int first=0;
57         int last=data.size()-1;
58         int firstk=getFirstk(data,k,first,last);
59         int lastk=getLastk(data,k,first,last);
60         int number=0;
61         if(firstk>-1&&lastk>-1)
62             number=lastk-firstk+1;
63         return number;
64     }
65 };

 二、leecode刷题

1. 统计有序矩阵中的负数

  • 题目描述

  技术图片

 

  •  问题分析

  这道题和前面二维数组的查找是有类似的地方的,二维矩阵从左到右非递增,从上到下非递增,对于左下角元素,从左到右递减,从下到上递增,从左下角元素开始进行扫描,如果当前元素为正,则当前元素以上的元素都比其大,因此都为正,因此向右查找;如果当前元素为负,则从当前元素开始的每一个元素都为负,统计负数的个数并且向上一排寻找

  • 代码参考
 1 class Solution {
 2 public:
 3     int countNegatives(vector<vector<int>>& grid) {
 4         if(grid.empty())
 5             return 0;
 6         int row=grid.size()-1;
 7         int col=0;
 8         int number=0;
 9         int rows=grid.size();
10         int cols=grid[0].size();
11         while(row>=0&&col<grid[0].size())
12         {
13             if(grid[row][col]>=0)
14             {
15                 ++col;
16                 continue;
17             }
18             else
19             {
20                 number+=cols-col;
21                 --row;
22             }
23             
24         }
25         return number;
26     }
27 };

2. 寻找比目标字母大的最小字母

  • 题目描述

        技术图片

 

  •  题目分析

  要寻找比目标字母大的最小字母,采用二分法。

  1. 对下标作二分,找到第一个大于target值的下标

  2. target可能比letters中的所有元素都小,也可能比letters中的所有元素都大,因此第一个大于target值的下标取值范围为[0,letters.size()]

  3. 当left==right时退出,因此循环条件为while(left<right)

  4. 当letters[mid]>target,mid可能是结果,因此在数组的前半部分寻找

  5. 当letters[mid]<=target,mid不可能是结果,直接舍弃,因此在数组的后半部分寻找

  6. 当循环退出时,left==right,若target大于letters中的所有元素,left=letters.size(),此时返回letters[0]

  • 参考代码
 1 class Solution {
 2 public:
 3     char nextGreatestLetter(vector<char>& letters, char target) {
 4         int left=0;
 5         int right=letters.size();
 6         while(left<right)
 7         {
 8             int mid=(left+right)/2;
 9             //如果letters[mid]>target,mid是可能结果,在数组的前半部分
10             if(letters[mid]>target)
11                 right=mid;
12             //如果letters[mid]<=target,mid不可能是结果,舍去,在数组后半部分
13             else 
14                 left=mid+1;
15         }
16         //如果target大于数组中的所有元素时,left==letters.size(),返回letters[0]
17         if(left==letters.size())
18             return letters[0];
19         else
20             return letters[left];
21     }
22 };

 

 

2. 双指针法

 

以上是关于C++中的二分法和双指针法及常见题目汇总的主要内容,如果未能解决你的问题,请参考以下文章

双指针法及题目

双指针法及题目

443. 压缩字符串『简单』

2021/5/20 刷题笔记相交链表以及双指针法

leetcode 11.乘最多水的容器(双指针法)

leetcode 11.乘最多水的容器(双指针法)