1.排序【排序是稳定的:两个相同的数,排序后相对位置不变:快排不稳定;归并稳定】
快排:
如何调整范围
经典二分
递归结束条件;条件满足时,进行处理;递归左边,递归右边
递归函数包含什么:结束条件【l==r】,对情况的处理,递归函数的调用。
分界点划分可以是l,r,(l+r)/2,但是如果是选l,比如是1,2排序,i最后结束循环是1,然后最后递归也是sort(q,0,0)
其实有太多问题:>=和>的选择,要不要判断i<j,递归使用i,i-1还是j,j+1【前一个超时了】,先这样吧,想不通。。。。。?????
GOT IT!
按照l<r递归的原则, q[i]</>x的原则【记住】
swap之前记得判断一下i和j,i有可能超过j,就不应该swap了
模版:
void sort(vector<int>&nums,int l,int r){//感觉情况有点多啊
if(l>=r){
return;
}
int i = l-1;
int j = r+1;
int x = nums[(l+r)/2];
while(i<j){
do{
i++;
}while(nums[i]<x);//不是<=,如果=x应该让他放在中间
do{
j--;
} while(nums[j]>x);//不是<=
if(i<j) swap(nums[i],nums[j]);//必须i<j
}//i>=j j,i
sort(nums,l,i-1);
sort(nums,i,r);
}
#include<iostream>
#include<algorithm>
using namespace std;
void sort1(vector<int>&nums,int l,int r){
if(l==r){
return;
}
int mid = nums[(l+r+1) >> 1];
int i = l-1;
int j = r+1;
while(i<j){
do{i++;}while(nums[i]<mid);
do{j--;}while(nums[j]>mid);
if(i<j){
swap(nums[i],nums[j]);
}
}
// sort(l,mid-1);
// sort(mid,r);
sort1(nums,l,i-1);
sort1(nums,i,r);
}
int main(){
int n;
cin>>n;
vector<int>nums(n);//初始化错误
for(int i = 0;i<n;i++){
cin>>nums[i];
}
sort1(nums,0,n-1);
for(int i = 0;i<n;i++){
cout<<nums[i]<<" ";
}
// int l = 0;
// int r = n-1;
// int mid;
// while(l<r){//中间点,小于,大于,交换,左边小于等于,右边大于等于
// mid = l+r >> 1;
// sort(nums,l,mid);
// sort(nums,mid+1,r);
// }
}
关于i,j先-1,在do-while:
while(q[i]<x) i++ ;
因为这里是先判断后 + 1,当q[i] == x == q[j],且 i < j 时,q[i] 与 q[j] 交换之后,其实本质时并没有变化的,但是此时你的while循环会不断重复上述操作,导致你的 i 不会继续+ 1,同理你的 j 也不会 - 1,这段while语句永远在死循环,TLE时必然的。
而while (q[ ++i ]<x) ;可以保证 i 会继续向后遍历,j 同理 。
冒泡排序:
每一次把max数放到最后一位
void bubble_sort(int q[], int n) {
bool flag = true;
while (flag) {
flag = false;
for (int i = 1; i < n; i++) {
if (q[i] > q[i + 1]) {
flag = true;
int t = q[i];
q[i] = q[i + 1];
q[i + 1] = t;
}
}
}
}
归并排序
注意:之后合并还是依赖于nums,所以要把tmp复制回去nums!!
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
void merge_sort(vector<int>&nums,int l,int r,vector<int>&tmp){//分,治,和
if(l == r){
return;
}
int mid = (l+r)>>1;
merge_sort(nums,l,mid,tmp);
merge_sort(nums,mid+1,r,tmp);
//和
int i = l;int j = mid+1;int k = l;
while(i<=mid && j<=r){
if(nums[i]>nums[j]){
tmp[k++] = nums[j++];
}else{
tmp[k++] = nums[i++];
}
}
while(i<=mid){//余下的
tmp[k++] = nums[i++];
}
while(j<=r){//余下的
tmp[k++] = nums[j++];
}//排序操作仍然依赖nums
for(int i = l;i<=r;i++){
nums[i] = tmp[i];
}
}
int main(){
int n;
cin>>n;
vector<int>nums(n);
vector<int>tmp(n);
for(int i = 0;i<n;i++){
cin>>nums[i];
}
merge_sort(nums,0,n-1,tmp);
for(int i = 0;i<n;i++){
cout<<tmp[i]<<" ";
}
}
2、搜索插入位置【实际1h+】
刷题论 04|二分查找,你根本不需要背那么多模板_哔哩哔哩_bilibili
迷茫了5/7
---------------------------------------------
需要注意最好(l+r)/2用l+((r-l)>>2)代替,防止溢出
最优思路:刚开始没想到可以只是先找target,找不到位置就是l【自己举例子】
int findIndex1(vector<int>& nums, int l, int r, int target) {
if (l > r) {//[l,r]大于和大于等于【1,3,5,6,target= 7,l>=r返回的是3而非4】,如果用【1,r】理论看的话,l是可以等于r的,没必要返回循环
return l;
}
int m = l + ((r - l) >>2);
if (nums[m] == target) {
return m;
} else if (nums[m] < target) {
return findIndex1(nums, m + 1, r, target);
} else {
return findIndex1(nums, l, m - 1, target);
}
}
原来的想法是在每次二分查找中如果找不到target,直接找到 一侧小一侧大的那个位置,然后就不知道循环结束条件是什么了,确实也不需要了,不过自己考虑时问题挺多
考虑成了m-1小于target而m+1大于target【应该是m】
0前面插入和nums.size()-1后面插入的逻辑没考虑清楚
int findIndex(vector<int>& nums, int l, int r, int target) {
// if (l > r) {
// 当 l > r 时,说明 target 应插入到 l 的位置
// return l;
}
//
int m = l + (r - l) / 2;
if (nums[m] == target) {
return m; // 直接找到目标
}
// 检查是否满足插入条件:nums[m-1] < target < nums[m]
if (m > 0 && nums[m-1] < target && nums[m] > target) {
return m; // 插入到 m 的位置
}
// 处理边界情况(如插入到数组最左或最右)
if (m == 0 && nums[m] > target) return 0;
if (m == nums.size() - 1 && nums[m] < target) return m + 1;
// 递归搜索左半部分或右半部分
if (nums[m] < target) {
return findIndex(nums, m + 1, r, target);
} else {
return findIndex(nums, l, m - 1, target);
}
}
模版:简单二分,数组中不包含重复元素, 思考:定义是[l,r]闭区间,跳出循环条件:l > r,l / r = mid +-1;
5/9重做版:套y总模版【10min】
问题:1.l = mid+1可能导致越界,例子[1,3] target = 2;方法就是先去判断一下有没有越界。
预防措施:未知
!!!!“搜索数的范围”之所以没写是因为刚开始他写的是(l+r)>>1,不会越界;
所以还是有(l+r+1)>>1时,还是先判断一下l是否会越界,再判断nums[l]和target的关系
2.关于如果没找到怎么办,刚开始没找到规律,就试了一个数;
但是发现有几个例子不对;
其实可以直接写if,如果大于怎么样小于怎么样,不用猜测
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l = 0;
int r = nums.size()-1;
int mid;
while(l<r){
mid = (l+r+1) >> 1;
if(nums[mid] == target){
return mid;
}else if(nums[mid] > target){
r = mid-1;
}else{
l = mid + 1;//越界了
}
}
if(l>=nums.size()){
return l;
}
if(nums[l] == target){
return l;
}
// else{
// return l+1;
// }//鲁莽了,还是可以先比较一下大小的
else if(nums[l]>target){
return l;
}else{
return l+1;
}
}
};
3.搜索二维矩阵【12min】
问题:1.“==”写成了“=”
2.k++没写
//得到m*n矩阵的m/n
vector<vector<int>>matrix;
m = matrix.size();
n = matrix[0].size();
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
//非严格递增顺序排列
int m = matrix.size();
int n = matrix[0].size();
// cout<<"m"<<m<<"n"<<n<<endl;
vector<int>nums(m*n);
int k = 0;
for(int i = 0;i<m;i++){
for(int j= 0;j<n;j++){
nums[k] = matrix[i][j];
k++;//?没写
}
}
int l = 0;
int r = nums.size()-1;
int mid;
// int mid = (l+r+1) >> 1;//[[1]]
while(l<r){
mid = (l+r+1) >> 1;
if(nums[mid]<target){
l = mid+1;
}else if(nums[mid]>target){
r = mid-1;
}else{
return true;
}
}
if(l == nums.size()){//[[1]]只是因为==写成了=
// cout<<nums.size();
return false;
}
if(nums[l] == target){
return true;
}else{
return false;
}
}
};
4.数的范围【5/9】
一个很严重的问题,变量定义到后面就忘记什么是什么了,比如vec数组后面就变成了q[i],target变成了q.......
理解!!!!!【开心,狂喜,兴奋,感觉自己强的可怕】在这个题里面,先做的其实是找到第一个符合target的数,==target时,前面可能还有数,所以==taget时,应该找l,mid,和>target归为一类;
同理,后面要找的是最后一个符合target的数,==target时,后面还可能有数,所以==taget时,应该找mid,r,和<target归为一类;
注意对应模版里的mid是l+r+1 >> 1还是l+r >> 1
最后循环的结果是l==r,要不找到了要不没找到
p.s.本来想找到第一个target之后遍历找一下第一个不是target的数, 超时了。。。
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int n,q;
cin>>n>>q;
vector<int>vec(n);
for(int i = 0;i<n;i++){
cin>>vec[i];
}
int k;
for(int i = 0;i<q;i++){
cin>>k;//如何处理==的情况
int l = 0;
int r = n-1;
int mid;
while(l<r){//l<r
mid = (l+r) >>1;
if(vec[mid] >= k){//>=or> 1,3,3,3,,3
r = mid;
}else{
l = mid+1;
}
}
if(vec[l]!=k){
cout<<"-1 -1"<<endl;//找的是第一个满足x的值
}else{
cout<<l<<" ";//while结束时l==r
// while(vec[l]==k){
// l++;
// }//有问题,这样就都加1了
// l--;
// cout<<l<<endl;
//O(n)
r = n-1;
while(l<r){
int mid = l+r+1 >> 1;
if(vec[mid]<=k) l = mid;
else{//
r =mid-1;
}
}
cout<<l<<endl;
}
}
}
同leetcode,问题就是nums[mid] == target时,到底归于哪一类
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int l = 0;
int r = nums.size()-1;
int mid;
if(nums.size() == 0){
return{-1,-1};
}
vector<int>vec(2);
while(l<r){
mid = (l+r) >> 1;//开始位置
if(nums[mid]>=target){
r = mid;//mid-1-->mid??
}else{
l = mid+1;
}
}
//l越界??
if(nums[l] == target){
vec[0] = l;
r= nums.size()-1;
while(l<r){
mid = (l+r+1) >> 1;
if(nums[mid]>target){
r = mid-1;//mid-1-->mid??
}else{
l = mid;
}
}
vec[1] = l;
}else{
vec[0] = -1;
vec[1] = -1;
}
return vec;
}
};
注: 把nums[mid]==target单独拎出来也通过了
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int l = 0;
int r = nums.size()-1;
int mid;
if(nums.size() == 0){
return{-1,-1};
}
vector<int>vec(2);
while(l<r){
mid = (l+r) >> 1;//开始位置
if(nums[mid]>target){
r = mid-1;//mid-1-->mid??
}else if(nums[mid]<target){
l = mid+1;
}else{
r = mid;
}
}
//l越界??
if(nums[l] == target){
vec[0] = l;
r= nums.size()-1;
while(l<r){
mid = (l+r+1) >> 1;
if(nums[mid]>target){
r = mid-1;//mid-1-->mid??
}else if(nums[mid]<target){
l = mid+1;
}else{
l = mid;
}
}
vec[1] = l;
}else{
vec[0] = -1;
vec[1] = -1;
}
return vec;
}
};
5.搜索旋转排序数组5/11【1.5h+】
原来的想法是找到peak【数组中max的地方】,然后重置数组(改成升序的数组),之后二分
but,重置数组时间复杂度是O(n)
问题:关于找peak,比如【4,5,6,7,8,0,1,2】我要找的是8这个位置
现在不太能理解:用nums[mid]和nums[mid+1]比较的这种if(check)函数?????
class Solution {
public:
int find(vector<int>&nums){
//复原数组需要O(n)空间 hhhhhhhhhh
// if(l == r){
// return 0;
// }
// int i;int j;
// int mid = (l+r+1)>>1;
// if(mid == nums.size()-1){
// return 0;
// }
// if(nums[mid]>nums[mid+1]){
// return mid;
// }else{
// i = find(nums,l,mid-1,target);
// j = find(nums,mid+1,r,target);
// //怀疑返回的值不太对
// }
// if(i!=0 || j!=0){
// return max(i,j);
// }else{
// return 0;
// }
int l = 0;int r = nums.size()-1;
int max = nums[r];
int mid;
while(l<r){
mid = (l+r+1)>>1;
if(nums[mid]>max){//>=or>e.g.[3,1]
l = mid;
}else{
r = mid-1;
}
}
// if()
return l;
}
int search(vector<int>& nums, int target) {
//[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
//旋转后 的数组 nums 和一个整数 target
//先复原?也不太对诶
int peak = find(nums);
cout<<peak<<endl;
int l;int r;
if(peak == nums.size()-1){
l = 0;
r = nums.size()-1;
}else if(nums[0]<=target && nums[peak]>=target){
l = 0;r = peak;
}else{
l = peak+ 1;
r = nums.size()-1;
}
int mid;
while(l<r){
mid = (l+r+1)>>1;
if(nums[mid] > target){
r = mid-1;
}else if(nums[mid]<target){
l = mid+1;
}else{
return mid;
}
}
if(l==nums.size()){
return -1;
}
if(nums[l] == target){
return l;
}else{
return -1;
}
}
};
上面这个peak判断如果==nums.size( )-1其实有问题
emmmm
如果他是完全升序的数组,其实会收敛于0
但如果判断peak==0来看是不是完全升序的数组也有问题
比如【3,1】
其实peak输出是0,但不是完全升序的数组
所以关键在于nums[peak]和nums[nums.size()-1]的大小比较
改为:
class Solution {
public:
int find(vector<int>&nums){
//复原数组需要O(n)空间 hhhhhhhhhh
// if(l == r){
// return 0;
// }
// int i;int j;
// int mid = (l+r+1)>>1;
// if(mid == nums.size()-1){
// return 0;
// }
// if(nums[mid]>nums[mid+1]){
// return mid;
// }else{
// i = find(nums,l,mid-1,target);
// j = find(nums,mid+1,r,target);
// //怀疑返回的值不太对
// }
// if(i!=0 || j!=0){
// return max(i,j);
// }else{
// return 0;
// }
int l = 0;int r = nums.size()-1;
int max = nums[r];
int mid;
while(l<r){
mid = (l+r+1)>>1;
if(nums[mid]>max){//>=or>e.g.[3,1]
l = mid;
}else{
r = mid-1;
}
}
// if()
return l;
}
int search(vector<int>& nums, int target) {
//[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
//旋转后 的数组 nums 和一个整数 target
//先复原?也不太对诶
int peak = find(nums);
cout<<peak<<endl;
int l;int r;
if(nums[peak] < nums[nums.size()-1]){
l = 0;
r = nums.size()-1;
}else if(nums[0]<=target && nums[peak]>=target){
l = 0;r = peak;
}else{
l = peak+ 1;
r = nums.size()-1;
}
int mid;
while(l<r){
mid = (l+r+1)>>1;
if(nums[mid] > target){
r = mid-1;
}else if(nums[mid]<target){
l = mid+1;
}else{
return mid;
}
}
if(l==nums.size()){
return -1;
}
if(nums[l] == target){
return l;
}else{
return -1;
}
}
};
法二:nums[mid]和nums[mid+1],如果不是峰值就左右搜索:
p.s.体会如果要搜两边,用递归函数怎么写
class Solution {
public:
int findPeakRecursive(vector<int>& nums, int l, int r) {
// 终止条件:区间无效或只剩一个元素
if (l >= r) return -1;
int mid = l + (r - l) / 2;
// 关键检查:mid+1是否越界?
if (mid + 1 > nums.size() - 1) {
return -1; // 越界说明无法比较,放弃此分支
}
if (nums[mid] > nums[mid + 1]) {
return mid; // 找到峰值
} else {
// 先搜索右侧
int right_peak = findPeakRecursive(nums, mid + 1, r);
if (right_peak != -1) return right_peak;
// 右侧未找到,搜索左侧
int left_peak = findPeakRecursive(nums, l, mid);
return left_peak;
}
}
int find(vector<int>& nums) {
if (nums.empty()) return -1;
int peak = findPeakRecursive(nums, 0, nums.size() - 1);
// 处理完全升序数组(如 [1,2,3,4])
if (peak == -1) {
return nums.size() - 1; // 最后一个元素是峰值
}
return peak;
}
// int l = 0;int r = nums.size()-1;
// int max = nums[r];
// int mid;
// while(l<r){
// mid = (l+r+1)>>1;
// if(nums[mid]>max){//>=or>e.g.[3,1]
// l = mid;
// }else{
// r = mid-1;
// }
// }
// // if()
// return l;
// }
int search(vector<int>& nums, int target) {
//[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
//旋转后 的数组 nums 和一个整数 target
//先复原?也不太对诶
int peak = find(nums);
cout<<peak<<endl;
int l;int r;
if(peak == nums.size()-1){
l = 0;
r = nums.size()-1;
}else if(nums[0]<=target && nums[peak]>=target){
l = 0;r = peak;
}else{
l = peak+ 1;
r = nums.size()-1;
}
int mid;
while(l<r){
mid = (l+r+1)>>1;
if(nums[mid] > target){
r = mid-1;
}else if(nums[mid]<target){
l = mid+1;
}else{
return mid;
}
}
if(l==nums.size()){
return -1;
}
if(nums[l] == target){
return l;
}else{
return -1;
}
}
};
6.寻找旋转排序数组中的min5/12【18min】
153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)
昨天好像有很多迷惑的点,emmmm,忘记了。。。。
感觉:就像在碰运气,按着测试用例改代码
有几个问题
1.涉及(l+r+1)>>1还是要先判断if(nums.size()==1),要不然会越界
2.关于这个思路找峰值点,完全升序peak收敛于0,但不能用peak==0判断是否是完全升序的数组e.g.【3,1】,他这个峰值刚好是0,应该用nums[0]和max比较
class Solution {
public:
int findMin(vector<int>& nums) {
if(nums.size()==1){
return nums[0];
}
//找到峰值点的下一个数
int l = 0;int r = nums.size()-1;int mid;
int max = nums[r];
while(l<r){
mid = (l+r+1)>>1;//[1]
if(nums[mid]<= max){
r = mid - 1;
}else{
l = mid;//如果mid就是那个peak
}
}
cout<<l;
if(nums[0]<max){
return nums[0];
}else{
return nums[l+1];
}
}
};
7.寻找两个有序数组的中位数【没思路】5/12、5/13【总约为3H】
边界情况
突然感觉很久很久之前貌似做过
思路【看leetcode题解视频】
边界情况:
b/d不存在,即划分的线在数组末尾
迷茫了,困惑了。。。。。【54:39】
有一个问题就是对于check中==的处理,一定是某种条件的逆否吗????
==>5/13如果return的逻辑太复杂,可以不写在while里,这样的话就是while(){if(check){left = mid+1}.....}这种,一般都是一个取反的区间,最后收敛在l==r那里【y总的模版】
看着测试用例打补丁,打不动了。。。。
混乱版本:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
// 两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
//合并需要O(m+n)
//一个很严重的问题,我想的是a和c是作为分界线前面的元素下标,但是给的a的范围是0,nums1.size()-1,应该是-1,nums.size()-1
if(nums1.size()>nums2.size()){
swap(nums1,nums2);
}
int m = nums1.size();
int n = nums2.size();
cout<<m+n;
int a;int b;int c;int d;
int half = (m+n+1)/2;
int l = -1;int r = nums1.size()-1;
int mid;
if(n ==1){//如果a=-1,且c==0,则c-1越界
return nums2[0];
}
while(l<r){
mid = (l+r+1)>>1;
a = mid;
b = mid+1;
c= half-a-2;//下标和元素个数差了1
d = c+1;
// if(b!=nums1.size()-1){
if( (a!=-1 && nums1[a]<=nums2[d]) || (a==-1 && nums1[b]>=nums2[c]) ){
if((m+n)%2 == 0){//偶数
return (max(a == -1 ? nums1[c-1] : nums1[a],nums2[c]) + min(nums1[b],nums2[d]))/2.0;//??
}else{
return max(a == -1 ? nums1[c-1] : nums1[a],nums2[c]);
}
// r = mid;
}else if((a!=-1 && nums1[a] > nums2[d]) || (a==-1 && nums1[b]<nums2[c])){
l = mid-1;
}else{
r = mid;//mid??
}
// }else{
// if(nums1[a]<=nums2[d]){
// return max(nums1[a],nums2[c]);
// }
// else{
// r = mid-1;
// }
// }
}
return -1;
}
};
如果return的逻辑太复杂,可以不写在while里
妙点:
1.确保nums1更短,减少情况讨论
2.mid找的时候定义为分界线后面的那个元素下标,表示前面的元素个数,又因为(l+r+1)>>1,mid不会等于0【如果等于0,l==r==0,此时不满足while循环】,不需要关注mid-1是否越界
3.abcd是否会越界变成了越界就赋值max、min
最后直接判断奇数偶数,直接找max/min
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if(nums1.size()>nums2.size()){//确保nums2更长
swap(nums1,nums2);
}
int m = nums1.size();
int n = nums2.size();
int half = (m+n+1)/2;
int l = 0;int r = nums1.size();
int mid;
int c;
while(l<r){//nums1不会出现
mid = (l+r+1)>>1;//mid表示前面的元素个数,在分界线后
c = half-mid;
if(nums1[mid-1]>nums2[c]){
r = mid-1;
}else{
l = mid;
}
}
mid = (l+r+1)>>1; // 确保mid被正确初始化
int a = mid-1;
int b = mid;
int c2 = half-b; // 修改变量名避免冲突
int d = c2;
int l1 = (a == -1 ? -1e9 : nums1[a]); // 修改条件为a == -1
int l2 = (c2 == 0 ? -1e9 : nums2[c2-1]); // 修改索引和条件
int r1 = (b == m ? 1e9 : nums1[b]);
int r2 = (d == n ? 1e9 : nums2[d]);
if((m+n)%2==0){//偶数
return(max(l1,l2)+min(r1,r2))/2.0;
}else{
return max(l1,l2);
}
}
};
p.s.另一种