算法学习——引导(数组)

第一题 搜索螺旋排序数组

在这里插入图片描述
二分查找时,右指针应该为数组大小减一
二分查找只适用于有序数列
注意考虑边界0 、 1 等特殊值

class Solution {
public:
    int search(vector<int>& nums, int target) {
        //暴力枚举 复杂度O(n)
        //if(!nums.size())
        //    return -1;
        // for(int i = 0; i < nums.size(); i++)
        // {
        //     if(target == nums[i])
        //         return i;
        // }
        // return -1;

    //不能使用二分查找,二分查找只适合有序的
    //     int left = 0;
    //     int right = nums.size() - 1;
    //     int mid;
    //     while(left <= right)
    //         mid = (left + right) / 2;
    //         if(target > nums[mid])
    //         {
    //             left = mid + 1;
    //         }
    //         else if(target < nums[mid])
    //         {
    //             right = mid - 1;
    //         }
    //         else
    //         {
    //             return mid;
    //         }
    //     }
    //     return -1;
    
        //改进版本的二分查找,考虑到有一部分是有序的
        int n = nums.size();
        if (!n)//判断为空的时候
        {
            return -1;
        }
        if (n == 1)//一个元素不用二分
        {
            return (target == nums[0])? 0 : -1;
        }
        int l = 0;
        int r = n - 1;

        // while (l <= r)
        // {
        //     int mid = (l + r) / 2;
        //     if(target == nums[mid])//如果为中间值返回
        //         return mid;
        //     if(nums[0] < nums[mid])//判断左侧是否为序区间
        //     {
        //         if(target < nums[mid])//判断中间值是否在左侧有序区间
        //         {
        //             r = mid - 1;//如在左侧有序区间,在左侧使用二分查找
        //         }
        //         else//如不在左侧
        //         {
        //             l = mid + 1;
        //         }
        //     }
        //     else//如左侧不为有序区间,则右侧为有序区间
        //     {
        //         if(target > nums[mid])//判断中间值是否在右侧有序区间
        //         {
        //             l = mid + 1;//如在右侧有序区间,在右侧使用二分查找
        //         }
        //         else//如不在右侧有序区间
        //         {
        //             r = mid - 1;
        //         }
        //     }
            
        // }
         while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target) return mid;
            if (nums[0] <= nums[mid])//如果左边是有序的,
            {
                if (nums[0] <= target && target < nums[mid]) //左边有序并且目标值在左边
                {
                    r = mid - 1;//使用二分查找
                } 
                else //如果右边无序,且目标值在右边
                {
                    l = mid + 1;//遍历右侧
                }
            } 
            else //左边无序则右边一定有序
            {
                if (nums[mid] < target && target <= nums[n - 1])//右边有序,且目标值在右边
                {
                    l = mid + 1;//右边二分查找
                } 
                else //左边无序,且目标值在左边
                {
                    r = mid - 1;//遍历左侧                }
            }
        }

        return -1;
    }
};

第二题 搜索旋转排序数组Ⅱ

在这里插入图片描述

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int n = nums.size();
        if(!n)
        { 
            return false;
        }

        // //暴力
        // for(int i = 0; i < nums.size(); i++)
        // {
        //     if(target == nums[i])
        //     {
        //         return true;
        //     }
        // }
        // return false;

        //二分
        int l = 0;
        int r = n - 1;
        int mid;
        while(l <= r)
        {
            mid = (l + r) / 2;
            if(target == nums[mid])
            {
                return true;
            }
            if(nums[mid] == nums[l] && nums[mid] == nums[r])//当序列允许重复的情况下,无法判断哪一边是有序的
            {
                l++;
                r--;
            }
            else if(nums[l] <= nums[mid])//此处可随l的值更新,因为nums[l] == nums[mid] 已经判断过,也可以使用nums[0],但速度会变慢
            {
                 if(nums[l] <= target && target < nums[mid])
                 {
                     r = mid - 1;
                 }
                 else
                 {
                     l = mid + 1;
                 }
            }
            else
            {
                if(nums[mid] < target && target <= nums[r])//此处可随r的值更新,因为nums[r] == nums[mid] 已经判断过,也可以使用nums[n - 1],但速度会变慢
                {
                    l = mid + 1;
                }
                else
                {
                    r = mid - 1;
                }
            }

        }
        return false;

    }
};

第三题 寻找排序数组中的最小值

在这里插入图片描述

class Solution {
public:
    int findMin(vector<int>& nums) {
        int n = nums.size();
        if(!n)
        {
            cerr << "error: nums.size() == 0.\n";
            return 0;
        }
        暴力
        // int min = 50001;
        // for(int i = 0; i < nums.size(); i++)
        // {
        //     if(nums[i] < min)
        //     {
        //         min = nums[i];
        //     }
        // }
        // return min;

        先排序,在输出,O(nlogn)
        // sort(nums.begin(), nums.end());
        // return nums[0];


        //两个for,速度快一半吧 O(n/2)
        // int l = 0;
        // int r = n - 1;
        // int mid = 0;
        // int min = 50001;
        // mid = (l + r) / 2;
        // if(nums[0] <= nums[mid])
        // {
        //     min = nums[0];
        //     for(; mid <= r; mid++)
        //     {
        //         if(nums[mid] < min)
        //         {
        //             min = nums[mid];
        //         }
        //     }
        // }
        // else
        // {
        //     min = nums[r];
        //     for(; l <= mid; l++)
        //     {
        //         if(nums[mid] < min)
        //         {
        //             min = nums[l];
        //         }
        //     }
        // }
        // return min;

        //二分法
        //补充:nums[mid] > nums[right]最极端的情况是位于最大值处,加一即为最小值,因而,left == mid + 1;
        //补充:nums[mid] < nums[right]最极端的情况是mid处即为最小值,因而,right == mid;
        //补充:如果left 和 right 都 === mid 当序列为基数个元素时循环将无法结束
        //下面的解释引用自leetcode评论
        int left = 0;
        int right = nums.size() - 1;
        int mid = 0;
        //疑问:为什么while的条件是low<high,而不是low<=high呢
        // 解答:low<high,假如最后循环到{*,10,1,*}的这种情况时,nums[low]=10,nums[high]=1,nums[mid]=10,low=mid+1,
        //直接可以跳出循环了,所以low<high,此时low指向的就是最小值的下标;
        // 如果low<=high的话,low=high,还会再不必要的循环一次,此时最后一次循环的时候会发生low==high==mid,
        //则nums[mid]==nums[high],则会走一次else语句,则low=mid+1,此时low指向的是最小值的下一个下标,
        // 则需要return[low-1]
        while(left < right)
        {
            mid = (left + right) / 2;
            if(nums[mid] < nums[right])
            {
                //如果中间值小于最大值,则最大值减小
                //疑问:为什么 high = mid;而不是 high = mid-1;
                //解答:{4,5,1,2,3},如果high=mid-1,则丢失了最小值1
                right = mid;
            }
            else
            {
                //如果中间值大于最大值,则最小值变大
                //疑问:为什么 low = mid+1;而不是 low = mid;
                //解答:{4,5,6,1,2,3},nums[mid]=6,low=mid+1,刚好nums[low]=1
                //继续疑问:上边的解释太牵强了,难道没有可能low=mid+1,正好错过了最小值
                //继续解答:不会错过!!! 如果nums[mid]是最小值的话,则其一定小于nums[high],走if,就不会走else了
                left = mid + 1;
            }
        }
        
        return nums[left];
    }
};

第四题 斐波那契数

在这里插入图片描述
滚动数组思想:“简要来说就是通过观察dp方程来判断需要使用哪些数据,可以抛弃哪些数据,一旦找到关系,就可以用新的数据不断覆盖旧的数据量来减少空间的使用。”感谢——Andy01_

// //空间复杂度为O(n)
// class Solution {
// public:
//     int fib(int n) {
//         int f[1000] = {0};
//         if(n < 2)
//             return n;
//         f[0] = 0;
//         f[1] = 1;
//         for(int i = 2; i <= n; i++)
//         {
//             f[i] = f[i - 1] + f[i - 2];
//         }
//         return f[n];

//     }
// };

//用滚动数组思想降低空间复杂度
//分析上述的数组中只需要三个位置来存储数据
class Solution {
public:
    int fib(int n) {
      if(n < 2)
      {
          return n;
      }

      int a[3];
      a[0] = 0;
      a[1] = 1;

      for(int i = 2; i <= n; i++)
      {
          a[2] = a[1] + a[0];//a[3] = a[2] + a[1];需要把 a[2] 和 a[1] 保留, a[0] 是空闲
          a[0] = a[1];
          a[1] = a[2];
      }
      return a[2];
    }
};




第五题 第N个泰波那契数

在这里插入图片描述

class Solution {
public:
    int tribonacci(int n) {
        
        if(n < 2)
        {
            return n;
        }
        if(n == 2)
        {
            return 1;
        }

        int a[4] = {0};
        a[0] = 0;
        a[1] = 1;
        a[2] = 1;

        for(int i = 3; i <= n; i++)
        {
            a[3] = a[0] + a[1] + a[2];//a[4] = a[3] + a[2] + a[1]; 数组滚动
            a[0] = a[1];
            a[1] = a[2];
            a[2] = a[3];
        }
        return a[3];
    }
};


第六题 拿硬币 贪心?不太懂贪心是什么

在这里插入图片描述

class Solution {
public:
    int minCount(vector<int>& coins) {
        //暴力
        // int ans = 0;
        // for(int i = 0; i < coins.size(); i++)
        // {
        //     if(coins[i] % 2 == 0)
        //     {
        //         ans += coins[i] / 2;
        //     }
        //     else
        //     {
        //         ans += coins[i] / 2 + 1;
        //     }
        // }
        // return ans;

        //贪心?
        int ans = 0;
        for(int i = 0; i < coins.size(); i++)
        {
            ans += (coins[i] + 1 ) / 2;//coins[i] + 1 来处理 % 取余为1的情况
        }
        return ans;
    }
};

第七题 山脉数组的峰顶索引

在这里插入图片描述

class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr) {
    // 枚举
    //    int mi = -1;
    //    for(int i = 1; i < arr.size() - 1; i++)
    //    {
    //        if(arr[i] > arr[i + 1])
    //        {
    //            mi = i;
    //            break;
    //        }
    //    } 
    //    return mi;
	
        if(arr.size() < 3)
            return -1;
         //二分法
        int left = 1;//边界注意别溢出
        int right = arr.size() - 2;
        int mid = 0;
        int ans = 0;
        while(left <= right)
        {
            mid = (left + right) / 2;
            if(arr[mid] > arr[mid - 1] && arr[mid] > arr[mid + 1])
            {
                return mid;
            }
            if( arr[mid] > arr[mid + 1])
            {
                ans = mid;
                right = mid - 1;
            }
            else
            {
                left = mid + 1;
            }
        }
        return left;
    }
   
};

第八题 差的绝对值为K的数对数

在这里插入图片描述

class Solution {
public:
    int countKDifference(vector<int>& nums, int k) {
        // int ans = 0;
        // //暴力
        // for(int i = 0; i < nums.size(); i++)
        // {
        //     for(int j = i + 1; j < nums.size(); j++)
        //     {
        //         if(abs(nums[j] - nums[i]) == k) 
        //         {
        //             ans++;
        //         }
        //     }
        // }
        // return ans;
		
		//哈希表,没太看懂
        int res = 0, n = nums.size();
        unordered_map<int int> cnt;
        for(int j = 0; j < n; ++j)
        {
            res += cnt.count(nums[j] - k);
            res +=cnt.count[nums[j] + k];
            ++cnt[nums[j]];
        }

    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值