leetCode进阶算法题+解析(二十一)

单词拆分

题目:给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
示例 2:
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
注意你可以重复使用字典中的单词。
示例 3:
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

思路:这道题看似简单,越想越难。首先这个可能性很多。我觉得用递归不错。首先最主要的是把list换成set。利用set的contains判断是否有可拆分的串。然后把给定的字符串抛出去可拆分的串剩下的递归,如过能拆到字符串为null说明是true。注意下从头开始可拆分的串就不是一定的,把所有可能的分支都要走一遍。目前思路就这样,暂时决定暴力法解题。我去代码实现下看看。

class Solution {
    HashSet ar = new HashSet<String>();
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> set = new HashSet<String>(wordDict);
        return dfs(s,set);
    }
    public boolean dfs(String s,Set<String> set){
        //当字符串长度为0或者剩余的字符串直接出现在字典则直接返回true
        if(s.length()==0 || set.contains(s)) return true;
        for(int i = 1; i<s.length();i++){
            String s1 = s.substring(0,i);
            if(set.contains(s1) && dfs(s.substring(i),set)){
                return true;
            }
        }
        return false;
    }
}

首先暴力法写出来了,其次提交没通过,因为超时。然后三十几个测试案例我过了29个,我决定代码逻辑是没问题。但是这么暴力有点问题。
然后这个性能的话,只能照着这个思路调优啦。其实之前在做的时候我是有点思路的,我看了超时的测试案例,是都是aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab
这样一个串,每个a都走一遍,想想都可怕。。。所以我要看看这块要怎么调整。
这个题我放弃了,改了n次,次次超时,后来看了题解,差不多的代码,百分之八十相同的思路。人家过了,我超时了,脑壳痛。
我直接上人家的代码把:

public class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        return word_Break(s, new HashSet(wordDict), 0, new Boolean[s.length()]);
    }
    public boolean word_Break(String s, Set<String> wordDict, int start, Boolean[] memo) {
        if (start == s.length()) {
            return true;
        }
        if (memo[start] != null) {
            return memo[start];
        }
        for (int end = start + 1; end <= s.length(); end++) {
            if (wordDict.contains(s.substring(start, end)) && word_Break(s, wordDict, end, memo)) {
                return memo[start] = true;
            }
        }
        return memo[start] = false;
    }
}

是我刚刚思路的进化版,他加了个boolean来记忆到某个位置是不是已经可以做中断位。如果是改为true。不是则是false。
这里要注意一点,他用的Boolean。默认值是null,true和false都是后赋值的,所以这里不等于null则返回值本身是比我用boolean好的多的操作。
然后剩下的逻辑就很简单了,因为我最开始用数字截取的办法,所以是不能这么做的,只能定一个开始的下标start常量了。
然后我还看了下这个题有人用dp做出来了,我再去研究研究动规怎么写。
我直接贴代码(我是参考题解背着默写的,哈):

public class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> wordDictSet=new HashSet(wordDict);
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

首先这是很精巧的思路,也是用boolean数组辅助判断的。再次重申这句话,动态规划就是记录中间过程的递归。
然后是双层循环,外层循环的第i个元素之前是不是可以被分词。
首先照着代码理逻辑:
这里设置dp[0]是true。当这个要拆分的串是空串肯定可以被拆分。
然后往下递归,dp[下标]是为了表示到某下标为止,之前的是可以正好被拆分的。
比如 canam ;如果可以拆成ca.na,c,an,a无论怎么拆,但是有一点是固定的,就是在第二个a的之前都是可以正常拆分的,我们只要知道在这之后的是不是可以正常拆分。
然后因为截取字符串包前不包后,所以虽然是判断到了substring(j,i),但是我们得到的结果是i前面那个元素的结果。
这里的boolean下标和实际字符串的下标是多了1的。
然后这个题就简单了啊。只要确定到了某位置,这个为止是可以正好拆分的就设置前一个boolean值true。最后我们只要判断最后一个字符的+1的布尔值是正是负就行了。
反正动规其实就是这么个特点,乍一眼看很懵,但是只要思路理清楚了,就很简单了。这个题就到这里了。下一题了。

环形链表2

题目:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。说明:不允许修改给定的链表。

示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。
进阶:
你是否可以不用额外空间解决此题?

题目截图

思路:好像做过类似的,我当时好像用的快慢指针还是啥来着?其实这个题用额外空间就很简单啦,每个节点放set,如果出现contains说明环了。并且第一个出现contains的节点就是环的头节点,但是!!现在不让用额外空间,应该还是用双指针。快慢指针只要有重合就说明有环,重点是环入口是什么。。我想想。
好了,琢磨半小时数学总算是理明白逻辑了,这里有一点:慢指针决定不可能跑完一整圈环。因为快指针是慢指针速度两倍,而环最少两个节点,所以节点越多说明快指针追上的越快。不过这个不重要,有这个意识就行了。然后到了画图的环节,这个比较复杂所以我决定手画。

草图

然后我觉得这个就可以明白了。。还有一个更草的解释:
红色线绿色线长度是一样的,虚线部分长度也是一样的,所以a和B不知道多少圈,反正和A的交点肯定会是入口
image.png

然后数学懂了代码就是分分钟的事,我直接贴代码了:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head, slow = head;
        while (true) {
            if (fast == null || fast.next == null) return null;
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) break;
        }
        fast = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return fast;
    }
}

下一题了直接。

重排链表

题目:给定一个单链表 L:L0→L1→…→Ln-1→Ln ,将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:
给定链表 1->2->3->4, 重新排列为 1->4->2->3.
示例 2:
给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.

思路:这个题又不知道怎么混进来的,简单的离谱了啊。我打算都add进list。然后第一个的下一个是最后一个,最后一个变成第二个,第二个的下一个正数第二个,正数第二个就变成第三了,第三个的下一个倒数第二个,以此类推。我先去实现下看有什么坑。
做的过程中觉得list太麻烦了,我用了双端队列。直接贴代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public void reorderList(ListNode head) {
        LinkedList<ListNode> queue = new LinkedList<>();
        ListNode cur = head;
        while (cur != null) {
            queue.addLast(cur);
            cur = cur.next;
        }
        while (!queue.isEmpty()) {
            if (cur == null) {
                cur = queue.pollFirst();
            } else {
                cur.next = queue.pollFirst();
                cur = cur.next;
            }
            cur.next = queue.pollLast();
            cur = cur.next;
        }
        if (cur != null) {
            cur.next = null;
        }
    }
}

性能不咋地但是起码过了, 我也不知道坑在哪里,这个时候就需要去看评论了。


image.png

没话讲,我觉得简单是因为没读懂题目?呵~~~~
笑而不语。
然后这个大大的方法其实也不是很难懂,我就不明白不也是中间接next么?算了吧,我也贴出来大家可以看一下:

    void reorderList(ListNode* head) {
        
        if(head==NULL || head->next == NULL)
            return;
        
        //快慢指针分出两段
        ListNode *slow = head,*fast = head;
        
        while(fast->next && fast->next->next){
            
            slow = slow->next;
            fast = fast->next->next;
        }
        
        
        //后端反转
        ListNode *needReverser = slow->next;
        slow->next = NULL;
        needReverser = reverse(needReverser);
        
        //插入前端缝隙
        ListNode *cur = head;
        while(cur && needReverser){
            ListNode *curSecond = needReverser;
            needReverser = needReverser->next;
            ListNode *nextCur = cur->next;
            curSecond->next = cur->next;
            cur->next = curSecond;
            
            cur = nextCur;
        }
        
    }
    
    ListNode *reverse(ListNode *head){
        
        ListNode *p1 = NULL;
        ListNode *p2 = head;
        ListNode *p3 = p2;
        
        while(p2){
            p3 = p2->next;
            p2->next = p1;
            p1 = p2;
            p2 = p3;            
        }
        
        return p1;
        
        
    }
    

今天的笔记就记到这里,很丧,本来是周四的学习笔记,现在写完凌晨十二点半了。。。主要是今天工作很忙,一点没空做,然后最开始的两道题还都比较麻烦,尤其是第二道题,数学思路就寻思了不到一个小时,哎~~~~如果稍微帮到你了记得点个喜欢点个关注,另外祝大家工作顺顺利利!生活健健康康!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352

推荐阅读更多精彩内容