算法,第一周

/**
 * 题目
 * 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,
 * 并返回他们的数组下标。
 *
 * 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
 *
 * 示例:
 *
 * 给定 nums = [2, 7, 11, 15], target = 9
 *
 * 因为 nums[0] + nums[1] = 2 + 7 = 9
 * 所以返回 [0, 1]
 */

/**
 * 我的解题思路主要是对数组进行循环尝试,比较烂
 * 该题的思路是怎么在不循环当前数组的情况下,获取所需数是否存在,以及位置,我当时也考虑了能不能一次循环解决,
 * 但是因为各种原因没有尝试,当引以为戒,不能急于求成
 * 时间复杂度o(n2)
 */
public class 两数之和 {
    class Solution {
        public int[] twoSum(int[] nums, int target) {
            for(int k=0;k<nums.length;k++)
                for (int j = 0; j < nums.length; j++)
                    if (j!=k&& nums[j]+nums[k]==target)
                        return new int[]{k, j};
            return null;
        }
    }
}

/**
 * 第一名的解题思路主要是利用测试用例所包含的数字不大的特点,时间复杂度o(n)
 * 先将当前数和目标数相减,得到所需数,同时取反,获得所需数的去除符号位的补码值
 * 再去寻找对应补码是否存在记录,若不存在,将当前数的去除符号位的补码值位进行记录
 * 但这样是有bug的,若给定的数与列表中的减数大于2048,会导致精度丢失问题,例如
 * {4096,1},1这样的测试用例就会通过该测试,得到结果{0,1},因为4096失去精度得到0
 * 那直接把数组设置为int型的最大长度呢?也同样不行,会导致数组过大,力扣会报超过内存限制的错误,
 * 本地会报Requested array size exceeds VM limit错误
 * 所以在一般情况下,尤其是数字较小的情况下,这个算法是最优解,但是极限情况下并不适用,
 * 但依然具有很高参考价值
 */
class 第一名 {
    public int[] twoSum(int[] nums, int target) {
        int max = 2047;
        int[] map = new int[2048];
        for (int i = 0; i < nums.length; i++) {
            int dif = map[(target - nums[i]) &max];
            if (dif != 0) {
                return new int[]{dif - 1, i};
            }
            map[(nums[i]) & max] = i + 1;
        }
        return null;
    }
}

/**
 * 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,
 * 并且它们的每个节点只能存储 一位 数字。
 *
 * 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
 *
 * 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
 * 示例:
 *
 * 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
 * 输出:7 -> 0 -> 8
 * 原因:342 + 465 = 807
 */

/**
 * 这道题我的思路是对前一节点进行求和,设置标志位解决进位问题,时间复杂度为o(n),
 * 第一名的思路和我差不多,但是细节处理的不好,导致比第一名有接近一倍的性能差距,
 * 直接看第一名的代码吧
 */
public class 两数相加 {
    ListNode listNode=new ListNode(0);
    ListNode l=new ListNode(0);
    boolean isCarry=false;
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode listNode=this.listNode;
        while (true) {
            l1=(l1==null?l:l1);
            l2=(l2==null?l:l2);
            int i = isCarry ? 1 : 0;
            if ((i += (l1.val + l2.val)) >= 10) {
                listNode.val = (i - 10);
                isCarry = true;
            } else {
                listNode.val = i;
                isCarry=false;
            }
            l1 = l1.next;
            l2 = l2.next;
            if(l1==null&&l2==null) {
                if (!isCarry)
                    break;
                listNode.next = new ListNode(1);
                break;
            }
            listNode.next = new ListNode(0);
            listNode = listNode.next;
        }
        return this.listNode;
    }

    public class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
        }
    }
    class 第一名 {
        /**
         * 第一名利用了链表的性质,直接把传入的一条链表当成主链进行添加,大大节省了类的创建时间
         * 是解决链表问题的好办法,作为值得借鉴,同时直接使用变量int作为标志位,可以减少一次计算过程
         * 这些细节以后还是要多注意一下
         */
        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            int c = 0;
            ListNode pre = null;
            ListNode res = l1;
            while(l1 != null || l2 != null) {
                if(l1 == null) {
                    pre.next = l2;
                    l1 = l2;
                    l2 = null;
                }
                l1.val += (l2 == null) ? c : (l2.val + c);
                c = 0;
                if(l1.val > 9) {
                    l1.val %= 10;
                    c = 1;
                }
                pre = l1;
                l1 = l1.next;
                if(l2 != null) l2 = l2.next;
            }
            if(c != 0) {
                ListNode hi = new ListNode(1);
                pre.next = hi;
            }
            return res;
        }
    }
}
/**
 * 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
 *
 * 示例 1:
 *
 * 输入: "abcabcbb"
 * 输出: 3
 * 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
 * 示例 2:
 *
 * 输入: "bbbbb"
 * 输出: 1
 * 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
 * 示例 3:
 *
 * 输入: "pwwkew"
 * 输出: 3
 * 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
 *      请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
 */

/**
 * 我的思路是,因为子串是藏在原始字符串中的一串字符串,而无重复字串无非就是找,
 * 藏在原始字符串中的一串无重复字符串,那么我们就可以用两个数组下标标记这个无重复字符串,
 * 那么什么是无重复字串呢?也就是说字符串是不能有任何重复字符的,
 * 我们可以用循环来查找我们标记的字符串是否与新加入的字符串重合,如果重合就将我们的标记初始值向后移动,
 * 标记新的字符串,并把两个重复字符串中间的字符长度与max取最大,官方和我的思路差不多,
 * 但是官方利用了字符串的特性,直接获取重复位置,进而标记重复字符串,
 * 看来以后一旦碰到这种需要求之前位置的算法,首先就要考虑能不能直接获取之前的位置,
 * 而不是通过循环去找,时间复杂度o(n2)
 *
 */
public class 无重复字符的最长子串 {
    public int lengthOfLongestSubstring(String s) {
        int start = 0;
        char[] chars = s.toCharArray();
        int max = chars.length > 0 ? 1 : 0;
        for (int i = 1; i < chars.length; i++)
            for (int j = start; j < i; j++)
                if (chars[i] == chars[j]) {
                    max = Math.max(max, (i - start));
                    start = j + 1;
                    break;
                }
        return Math.max(max, (chars.length - start));
    }

    /**
     * 官方主要是考虑到字符本身只有128位(不包括中文),完全可以利用这个特性,
     * 我们只需要考虑将之前出现的字符位置放入到这128位数组中,如果读取到了之前的字符,
     * 就将标记初始值向后移动,同时修改128位字符存放的位置,以达到记录的效果,
     * 而我还需要繁琐的寻找重复字符,显然效率低下,时间复杂度o(n)
     */
    public class 官方题解 {
        public int lengthOfLongestSubstring(String s) {
            int n = s.length(), ans = 0;
            int[] index = new int[128];
            for (int j = 0, i = 0; j < n; j++) {
                i = Math.max(index[s.charAt(j)], i);
                ans = Math.max(ans, j - i + 1);
                index[s.charAt(j)] = j + 1;
            }
            return ans;
        }
    }
}

第4见我写的博客详解,这里就不多阐述了,因为该题较为麻烦,耗时多天,本周到此为止,开始第二周

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值