LeetCode刷题の排序和查找动态规划复杂度贪心

查找

704.二分法查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.size()==0){
            return -1;
        }
        int lower=0,upper=nums.size()-1;
        while(lower<=upper){
            //差值为1的时候,mid优先等于lower
            //差值为0的时候,还会继续循环
            int mid=lower+(upper-lower)/2;
            if(nums[mid]>target){
                //差值为1的情况下,mid比target大,那么减一
                //差值为0的情况下,mid比target大,减一则结束 
                //注意
               upper=mid-1;
            }
            if(nums[mid]<target){
                //差值为1的情况下,mid比target小,加一/
                //差值为0的情况下,mid比target小,加一则结束
                //注意
                lower=mid+1;
            }
            if(nums[mid]==target){
                return mid;
            }
        }
        return -1;
    }
};

148. 排序链表

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
方法:归并排序 nlogn时间复杂度
bottom-up merge sort maybe a good idea.Assuming that there is a linked list, -1 → 5 → 3 → 4 → 0, we firstly have to count the number of all nodes in this list, marked as len.
Then cut step nodes from the list as l1 and another step nodes from the list as l2, here step = 1. Merge l1 and l2 into a sorted list, as l1 and l2 are already sorted, we just have ‘sew’ them together. Then go on the rest pieces until there is an empty list between l1 and l2.Increasing step from 1 to 2 ^ i, where 2 ^ i < len. We got a sorted list finally, just as the following picture showing us.
在这里插入图片描述
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *cut(ListNode *head, int size){
        ListNode *temp=head;
        for(int i=0;i<size-1&& temp ;i++){
            temp=temp->next;
        }
        ListNode *next=nullptr;
        if(temp){next=temp->next;temp->next=nullptr;}
        return next;
    }
    ListNode *merge(ListNode *l1, ListNode *l2){
        ListNode* dummyHead =new ListNode(0);
        ListNode *p=dummyHead;
        while(l1&&l2){
            if(l1->val<=l2->val){
                p->next=l1;
                p=l1;
                l1=l1->next;
            }
            else{
                p->next=l2;
                p=l2;
                l2=l2->next;
            }
        }
        p->next=l1?l1:l2;
        return dummyHead->next;
    }
    ListNode* sortList(ListNode* head) {
        ListNode *temp=head;
        int len=0;
        while(temp){len++;temp=temp->next;}
        ListNode *dummyHead =new ListNode(0);
        dummyHead->next=head;
        for(int step=1;step<len;step<<=1){
            ListNode *tail=dummyHead;
            ListNode *cur=dummyHead->next;
            while(cur){
                ListNode *left=cur;
                ListNode *right=cut(left,step);
                cur=cut(right,step);
                tail->next=merge(left,right);
                while(tail->next){
                    tail=tail->next;
                }
            }
        }
        return dummyHead->next;
    }
};

动态规划

139. 单词拆分

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
在这里插入图片描述
解释:dp[i]=1说明字符串的前i-j位可以用wordDict表示。

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        vector<char> dp(s.size(), -1);//存储已经判断过的字符串
        return wordBreak(s, wordDict, dp, 0);
    }

    bool wordBreak(string& s, vector<string>& wordDict, vector<char>& dp, int pos) {
        if (pos == s.size()) return true;
        if (-1 != dp[pos]) return dp[pos];                    // 记忆化搜索加速
        for (int i = 0; i < wordDict.size(); ++i) {
            int j = pos;
            int k = 0;
            while (k < wordDict[i].size() && j < s.size()) {
                if (wordDict[i][k] != s[j]) break;
                k++;
                j++;
            }
            if (k != wordDict[i].size()) continue;
            if (wordBreak(s, wordDict, dp, j)) {
                dp[pos] = 1;
                return true;
            }
        }
        dp[pos] = 0;
        return false;
    }
};

135. 分发糖果

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
在这里插入图片描述

class Solution {
public:
    int candy(vector<int>& ratings) {
        vector<int>candies(ratings.size(),1);
        for(int i=1;i<ratings.size();i++){
            //必须与前一个相比
            if(ratings[i]>ratings[i-1]){
                candies[i]=candies[i-1]+1;
            }
        }
        //必须从倒数开始,见下图反例
        for(int i=ratings.size()-2;i>=0;i--){
            if(ratings[i]>ratings[i+1]){
                candies[i]=max(candies[i],candies[i+1]+1);
            }
        }

        int count=0;
        for(auto x:candies){
            count+=x;
        }
        return count;
    }
};

在这里插入图片描述

89. 格雷编码

格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。格雷编码序列必须以 0 开头。
思路:
由n位推导n+1位结果时,n+1位结果包含n位结果,同时包含n位结果中在高位再增加一个位1所形成的令一半结果,但是这一半结果需要与前一半结果镜像排列
在这里插入图片描述

class Solution {
public:
    vector<int> grayCode(int n) {
        vector<int>res={0};
        for(int i=1;i<=n;i++){
            int epoch=1<<(i-1);//i - 1位结果前增加一位1
            for(int j=res.size()-1;j>=0;j--){ // 镜像排列
                res.push_back(epoch+res[j]);
            }
        }
        return res;
    }
};

复杂度

136. 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        unordered_map<int,int> ma;
        for(auto &x: nums){
            ma[x]+=1;
        }
        for(auto x:nums){
            if(ma[x]==1){
                return x;
            }
        }
        return 0;
    }
};

思路二:
哈希集
若第一次出现,插入哈希集
第二次出现,冲哈希集内删除
最后剩下的就是那个只出现一次的数字

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        unordered_set<int> bobo;
        int ans;
        for(auto i : nums){
            if(bobo.count(i))   bobo.erase(i);
            else    bobo.insert(i);
        }
        for(auto j : bobo)  ans = j;
        return ans;
    }
};

贪心算法

134. 加油站

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明:
如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
思路:贪心算法
该题目是贪心算法的典型案例。关键在于要彻底理解题目的本质,这可以帮助你想到最简单的解法。
如果无法环绕一周,那么gas[i]-cost[i]的差值累加必然小于0,;否则,就是能够环绕一周。
一次遍历,在遍历中一边累加total一边寻找正确的start。
假如total>=0,那么就返回start,否则返回-1。

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int sum=0;
        int total=0;
        int start =0;
        for(int i=0;i<gas.size();i++){
            total+=gas[i]-cost[i];
            if(sum<0){
                sum=gas[i]-cost[i];
                start =i;
            }
            else{
                sum+=gas[i]-cost[i];
            }
        }
        return total>=0?start:-1;
    }
};

在这里插入图片描述

回溯

131. 分割回文串

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
深度优先搜索(也可以称为回溯法),DFS
因为需要输出所有的可行解,所以采用深度优先遍历(回溯法)。
输出最佳解的题目一般可以使用动态规划在这里插入图片描述

class Solution {
public:
    vector<vector<string>> partition(string s) {
        vector<vector<string>> res;
        vector<string> cur;
        dfs(s,cur,res);
        return res;
    }
    bool isPalindrome(string s){
        return s==string(s.rbegin(),s.rend());
    }
    void dfs(string s,vector<string> &cur,vector<vector<string>> &res){
        //判断是否是空串
        if (s==""){
            res.push_back(cur);
            return;
        }
        for (int i = 1; i <= s.length(); ++i) {
            string sub=s.substr(0,i);
            if (isPalindrome(sub)){
                cur.push_back(sub);
                dfs(s.substr(i,s.length()-i),cur,res);
                cur.pop_back();//返回上一分支
            }
        }
    }
};

在这里插入图片描述

127. 单词接龙

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> wordSet(wordList.begin(),wordList.end());
        unordered_map<string,int> pathNum;
        pathNum[beginWord]=1;
        if(!wordSet.count(endWord)) return 0;//endword如果不在字典里就返回0
        //BFS队列
        queue<string>q;
        q.push(beginWord);
        //如果q里面的newoWrd没有出现在wordLit里面q会出现空
        while(!q.empty()){
            string word=q.front();
            q.pop();
            //暴力替换,遍历字典
            for(int i=0;i<word.size();i++){
                string newWord=word;
                for(char ch='a';ch<'z';ch++){
                    newWord[i]=ch;
                    if(wordSet.count(newWord)&&newWord==endWord)return pathNum[word]+1;
                    //如果pathNum里面没有newWord并且wordSet里面有newWord则进入if语句
                    if(wordSet.count(newWord)&&!pathNum.count(newWord)){
                        q.push(newWord);
                        pathNum[newWord]=pathNum[word]+1;
                    }

                }

            }

        }
        return 0;
    }
};

在这里插入图片描述

动态规划

120. 三角形最小路径和

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
方法二 动态规划
自底而上,第i行的最小路径和 = 第i+1的最小路径和 + 第i行路径值

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        vector<vector<int>> dp(triangle);
        for(int i=triangle.size()-2; i>=0; i--) 
            for(int j=0; j<triangle[i].size(); j++) dp[i][j] = min(dp[i+1][j],dp[i+1][j+1]) + dp[i][j];
        return dp[0][0];
    }
};

优化,在原来数组基础上动作

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        for(int i=triangle.size()-2; i>=0; i--) 
            for(int j=0; j<triangle[i].size(); j++) 
                triangle[i][j] = min(triangle[i+1][j],triangle[i+1][j+1]) + triangle[i][j];  
        return triangle[0][0];
    }

参考地址:优秀代码

91. 解码方法

一条包含字母 A-Z 的消息通过以下方式进行了编码:

‘A’ -> 1
‘B’ -> 2

‘Z’ -> 26
给定一个只包含数字的非空字符串,请计算解码方法的总数。
思路:动态规划
这里DP数组长度为s.size()+1,
1.如果s[i]为0则再分为s[i-1]为1或2的问题:
若s[i-1]=‘1’ or ‘2’,则dp[i+1]=dp[i-1],否则return 0
2。如果s[i]!='0’则判断s[i-1]是否等于1或者s[i-1]是否等于2同时s[i]<6:
如果是则dp[i+1]=dp[i]+dp[i-1],否则dp[i+1]=d[p[i]。

class Solution {
public:
    int numDecodings(string s) {
        if(s[0]=='0')return 0;
        vector<int>dp(s.size()+1);
        dp[0]=1;dp[1]=1;
        for(int i=1;i<s.size();i++){
            //1.s[i]为0的情况
            if(s[i]=='0'){
                //s[i - 1]等于1或2的情况
                if(s[i-1]=='1'||s[i-1]=='2'){
                    dp[i+1]=dp[i-1];//由于s[1]指第二个下标,对应为dp[2],所以dp的下标要比s大1,故为dp[i+1]
                }
                else{
                    return 0;
                }
            }
            //2.s[i]不为0的情况
            else{
                //s[i-1]s[i]两位数要小于26的情况
                if(s[i-1]=='1'||(s[i-1]=='2'&&s[i]<='6')){
                    dp[i+1]=dp[i]+dp[i-1];
                }
                //其他情况
                else{
                    dp[i+1]=dp[i];
                }
            }
        }
        return dp[s.size()];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值