求职Leetcode题目(4)

1.电话号码组合 

 这道题是典型的回溯方法,直接看图和代码理解就好了,没什么好说的。

 

class Solution {
	//一个映射表,第二个位置是"abc“,第三个位置是"def"。。。
	//这里也可以用map,用数组可以更节省点内存
	String[] letter_map = {" ","*","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
	public List<String> letterCombinations(String digits) {
		//注意边界条件
		if(digits==null || digits.length()==0) {
			return new ArrayList<>();
		}
		iterStr(digits, new StringBuilder(), 0);
		return res;
	}
	//最终输出结果的list
	List<String> res = new ArrayList<>();
	
	//递归函数
	void iterStr(String str, StringBuilder letter, int index) {
		//递归的终止条件,注意这里的终止条件看上去跟动态演示图有些不同,主要是做了点优化
		//动态图中是每次截取字符串的一部分,"234",变成"23",再变成"3",最后变成"",这样性能不佳
		//而用index记录每次遍历到字符串的位置,这样性能更好
		if(index == str.length()) {
			res.add(letter.toString());
			return;
		}
		//获取index位置的字符,假设输入的字符是"234"
		//第一次递归时index为0所以c=2,第二次index为1所以c=3,第三次c=4
		//subString每次都会生成新的字符串,而index则是取当前的一个字符,所以效率更高一点
		char c = str.charAt(index);
		//map_string的下表是从0开始一直到9, c-'0'就可以取到相对的数组下标位置
		//比如c=2时候,2-'0',获取下标为2,letter_map[2]就是"abc"
		int pos = c - '0';
		String map_string = letter_map[pos];
		//遍历字符串,比如第一次得到的是2,页就是遍历"abc"
		for(int i=0;i<map_string.length();i++) {
			//调用下一层递归,用文字很难描述,请配合动态图理解
            letter.append(map_string.charAt(i));
            //如果是String类型做拼接效率会比较低
			//iterStr(str, letter+map_string.charAt(i), index+1);
            iterStr(str, letter, index+1);
            letter.deleteCharAt(letter.length()-1);
		}
	}
}

 2.k个一组反转链表

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode hair = new ListNode(0); // 创建一个哑节点(hair),其next指向头节点head
        hair.next = head; // 哑节点的next指向链表的头节点
        ListNode pre = hair; // pre作为前一个节点的指针,初始指向哑节点

        while (head != null) { // 循环直到链表尾部
            ListNode tail = pre; // tail作为当前k个节点组的尾部节点的前一个节点
            // 查看剩余部分长度是否大于等于 k
            for (int i = 0; i < k; ++i) {
                tail = tail.next; // 移动tail指针,查找尾部节点
                if (tail == null) { // 如果剩余节点不足k个,则停止反转
                    return hair.next; // 返回反转后的链表头节点
                }
            }
            ListNode nex = tail.next; // nex指向当前尾部节点的下一个节点,即下一组的头节点
            ListNode[] reverse = myReverse(head, tail); // 调用myReverse函数反转当前k个节点
            head = reverse[0]; // 反转后,head指向新的头部节点
            tail = reverse[1]; // tail指向新的尾部节点

            // 把子链表重新接回原链表
            pre.next = head; // 将反转后的子链表接到前一个节点后面
            tail.next = nex; // 将新尾部节点的next指向下一组的头节点
            pre = tail; // 更新pre为当前尾部节点,为下一次循环做准备
            head = tail.next; // 更新head为下一个待反转的子链表的头节点
        }
        return hair.next; // 返回反转后的链表头节点
    }

    public ListNode[] myReverse(ListNode head, ListNode tail) {
        ListNode prev = tail.next; // prev指向尾部节点的下一个节点,即反转后的新尾部
        ListNode p = head; // p作为当前遍历节点
        while (prev != tail) { // 当prev未到达尾部节点时,继续反转
            ListNode nex = p.next; // 保存当前节点的下一个节点
            p.next = prev; // 将当前节点的next指向prev,实现反转
            prev = p; // 更新prev为当前节点,向前移动
            p = nex; // 更新p为下一个待反转的节点
        }
        return new ListNode[]{tail, head}; // 返回包含新尾部和新头部节点的数组
    }
}

3.交错字符串 

解题思路: 

可定义 boolean[][] dp ,dp[i][j] 代表 s1 前 i 个字符与 s2 前 j 个字符拼接成 s3 的 i+j 字符,也就是存在目标路径能够到达 i ,j ;
状态方程:

边界 1:dp[0][0] = true;
边界 2:if i=0 : dp[0]dp[j] = s2[0-j) equals s3[0,j) 遇到 false 后面可以直接省略
边界 3:if j=0 : dp[i]dp[0] = s1[0-i) equals s3[0,i) 遇到 false 后面可以直接省略

其他情况,到达(i,j)可能由(i-1,j)点向下一步,选择 s1[i-1] 到达;也可能由 (i,j-1) 点向右一步,选择 s2[j-1] 到达;
dp[i,j] = (dp[i-1][j] &&s3[i+j-1] == s1[i-1]) || (dp[i][j-1] && s3[i+j-1] == s2[j-1]) 

class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        int m = s1.length(), n = s2.length();
        if (s3.length() != m + n) return false;
        // 动态规划,dp[i,j]表示s1前i字符能与s2前j字符组成s3前i+j个字符;
        boolean[][] dp = new boolean[m+1][n+1];
        dp[0][0] = true;
        for (int i = 1; i <= m && s1.charAt(i-1) == s3.charAt(i-1); i++) dp[i][0] = true; // 不相符直接终止
        for (int j = 1; j <= n && s2.charAt(j-1) == s3.charAt(j-1); j++) dp[0][j] = true; // 不相符直接终止
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                dp[i][j] = (dp[i - 1][j] && s3.charAt(i + j - 1) == s1.charAt(i - 1))
                    || (dp[i][j - 1] && s3.charAt(i + j - 1) == s2.charAt(j - 1));
            }
        }
        return dp[m][n];
    }
}

  • 15
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值