代码随想录算法训练营第一天| LeetCode 704. 二分查找、LeetCode 27. 移除元素

发布时间 2023-07-26 17:25:54作者: 银河小船儿

704. 二分查找

        题目链接:https://leetcode.cn/problems/binary-search/
      视频链接:https://www.bilibili.com/video/BV1fA4y1o715

       卡哥的题目建议 大家能把 704 掌握就可以,35. 搜索插入位置 和 34. 在排序数组中查找元素的第一个和最后一个位置 ,如果有时间就去看一下,没时间可以先不看,二刷的时候在看。先把 704写熟练,要熟悉 根据 左闭右开,左闭右闭 两种区间规则 写出来的二分法

       做题思路:

      1,先了解什么是二分查找:二分查找也称折半查找(Binary Search)。
     2,再了解折半查找的实现原理:假设我们在有三个数的有序数组中查找一个目标数,这个目标数我们一开始也不知道是有序数组中的哪个数,只能通过把目标数与有序数组的中间数进行比较,进一步缩小范围(见下面的 3)来找到目标数。如果目标数比中间的数大,则在有序数组的后半部分进行查找;如果目标数比中间数小,则在有序数组的前半部分进行比较;如果相等,则找到了比较元素的位置。折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序(升序降序都可,我们默认升序)排列。

    3,确定在哪个范围里比较和查找,一开始大范围是在整个数组,当比较完了,我们就知道在前半部分或者后半部分的小范围里查找,还要更新小范围的边界值,在小范围里比较再细分成更小的范围,如此类推.........,这个范围数学化理解就是区间,区间可以分为左闭右闭,左闭右开,(这两种区间用得多),我自己比较好理解左闭右闭这种方式的。

我没用力扣的环境运行,用的是vs,看了视频后,就差不多能理解代码了,用左闭右闭方式的完整代码如下:

 

 1 #include <iostream>
 2 using namespace std;
 3 const int N = 10;
 4 int a[N]; //声明全局数组
 5 int BinarySearch(int a[], int target)
 6 {
 7     int left = 0, right = sizeof(a) - 1; //左闭右闭区间范围为[left, right],即[0, n-1],left,right,也是数组下标
 8     int middle; //数组的中间数的下标
 9     while (left <= right) //在一个合法的区间里比较和查找,比如[1, 1],这个区间只有1,合法
10     {
11         middle = left + (right - left) / 2; //防止溢出 等同于(left + right)/2
12         if (target < a[middle]) //查找范围在数组的前半部分
13         {
14             right = middle - 1;//因为区间是左闭右闭,所以前半部分的右边界值不包含middle
15         }
16         else if (target > a[middle]) //查找范围在数组的后半部分
17         {
18             left = middle + 1;//因为区间是左闭右闭,所以后半部分的左边界值不包含middle
19         }
20         else if (target == a[middle])//目标数和数组中间数相等
21         {
22             return middle; //直接输出数组中间数的下标,break退出循环
23             break;
24         }    
25     }
26     return -1;
27 }
28 int main()
29 {
30     int n; //数组的元素个数为 n 
31     cout << "请输入数组的元素个数:" << endl;
32     cin >> n;
33     cout << "请输入数组:" << endl;
34     for (int i = 0; i < n; i++)
35     {
36         cin >> a[i]; //循环输入数组中的元素
37     }
38     cout << "请输入要查找的值:" << endl;
39     int target;  //目标数
40     cin >> target;
41     cout << BinarySearch(a, target) << endl;
42     
43     return 0;
44 }

 

       关于代码中的middle = left + (right - left) / 2, ,不理解溢出的话,就死记住吧。

 运行结果如下:

 

 

 

 

 27. 移除元素

       题目链接:https://leetcode.cn/problems/remove-element/

       视频讲解:https://www.bilibili.com/video/BV12A4y1Z7LP

       文章讲解:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html

      卡哥的题目建议  暴力的解法,可以锻炼一下我们的代码实现能力,建议先把暴力写法写一遍。 双指针法 是本题的精髓,今日需要掌握,至于拓展题目可以先不看。 

        有两种方法:暴力解法和双指针法。

        暴力解法思路:用两个for循环,第一个for循环用来遍历数组,第二个for循环用来前移数据覆盖,就跟单链表的删除操作类似。完整代码如下:

 

 1 #include <iostream>
 2 using namespace std;
 3 const int N = 10;
 4 int a[N]; //声明全局数组
 5 int main()
 6 {
 7     int n; //数组的元素个数为 n 
 8     cout << "请输入数组的元素个数:" << endl;
 9     cin >> n;
10     cout << "请输入数组:" << endl;
11     for (int i = 0; i < n; i++)
12     {
13         cin >> a[i]; //循环输入数组中的元素
14     }
15     cout << "请输入要移除元素的值:" << endl;
16     int val;
17     cin >> val;
18     for (int i = 0; i < n; i++)//第一个循环是为了把遍历数组
19     {
20         if (a[i] == val) //判断数组的哪个数和要移除的数相等
21         {
22             for (int j = i; j < n; j++)//如果有一个数相等的话,那就从当前位置开始进行前移覆盖操作
23             {
24                 a[j] = a[j + 1];//数组的后一个数前移覆盖当前位置的数
25             }
26             i--; //覆盖后,数组的有效元素减一,更新i值
27             n--; //数前移移除后,原来数组的有效元素减一,更新n值
28         }    
29     }
30     cout << n << endl; 
31     return 0;
32 }

 

 

运行结果如下:

 

 

     

         双指针法做题思路:也称快慢指针法,通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。这个解法不用知道为啥,为了方便操作就行。

      快指针:寻找新数组的元素 ,新数组就是不含有要移除的元素的数组;

      慢指针:指向更新新数组下标的位置。

        完整代码如下:

 1 #include <iostream>
 2 using namespace std;
 3 const int N = 10;
 4 int a[N]; //声明全局数组
 5 int main()
 6 {
 7     int n; //数组的元素个数为 n 
 8     cout << "请输入数组的元素个数:" << endl;
 9     cin >> n;
10     cout << "请输入数组:" << endl;
11     for (int i = 0; i < n; i++)
12     {
13         cin >> a[i]; //循环输入数组中的元素
14     }
15     cout << "请输入要移除元素的值:" << endl;
16     int val;
17     cin >> val;
18     int slow = 0; //慢指针的变量
19     for (int fast = 0; fast < n; fast++)
20     {
21         if (val != a[fast]) //如果fast位置上的元素和要移除的元素不相等的话,
22         {
23             a[slow] = a[fast]; //就把fast位置上的元素覆盖成slow位置上的元素
24             slow++;//slow虽然是下标,但是它也记录了新数组的个数,自己看视频就会发现
25         }
26     }
27     cout << slow << endl; 
28     return 0;
29 }

 

 运行结果如下: