个人算法总结P1-P34

本文详细介绍了几种编程算法,包括链表表示的两数相加的递归与迭代解法,滑动窗口在找无重复字符最长子串问题中的应用,二分查询法用于寻找两个正序数组中位数的两种策略,以及最长回文子串的中心扩散算法。此外,还涵盖了整数反转和字符串转换整数过程中的溢出处理问题。

P2 两数相加

0、题目

在这里插入图片描述

1、long和int的区别

int只有32位,最大只有2137383647,超过了就会溢出

而long有64位,更适合计算超过10位数的整数

2、链表使用

链表计算时,用一个result来固定链表开头,之后使用一个cur来进行辅助,他可以代表当前指针在哪,可以往后移动

3、递归和迭代的区别

递归是整个函数的循环

迭代是函数中某段代码的循环,例如for

4、代码
(递归法)
class Solution {
        int carry = 0;//记录进位

        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            //定义终止条件,当l1,l2指针都为null时且进位为0 ->null
            if (l1 == null && l2 == null && carry == 0) return null;

            //当有一条链表为null 且 进位为0时,next指针直接指向另外一条链表返回
            if (l1 != null && l2 == null && carry == 0) return l1;
            else if (l1 == null && l2 != null && carry == 0) return l2;

            //sum = 两链表指针位置上的数字加上进位
            int sum = (l1 == null ? 0 : l1.val) + (l2 == null ? 0 : l2.val) + carry;
            //计算进位
            carry = sum / 10;
            //计算链表的value
            int value = sum % 10;
            ListNode node = new ListNode(value);

            //递归算出这个node的next指向
            node.next = addTwoNumbers((l1 == null ? null : l1.next), (l2 == null ? null : l2.next));

            return node;
        }
    }
(迭代法)
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int total;
        // 来储存十位数,加到next里
        int next1 = 0;
        // 结果
        ListNode resoult = new ListNode();
        // 代表指针
        ListNode cur = resoult;
        // 当两者都能继续时
        while (l1 != null && l2 != null) {
            total = l1.val + l2.val + next1;
            cur.next = new ListNode(total % 10);
            next1 = total / 10;
            l1 = l1.next;
            l2 = l2.next;
            cur = cur.next;
        }
        // 只有l1可以继续
        while (l1 != null) {
            total = l1.val + next1;
            cur.next = new ListNode(total % 10);
            next1 = total / 10;
            l1 = l1.next;
            cur = cur.next;
        }
        // 只有l2可以继续
        while (l2 != null) {
            total = l2.val + next1;
            cur.next = new ListNode(total % 10);
            next1 = total / 10;
            l2 = l2.next;
            cur = cur.next;
        }
        // 判断后面是否还有数字
        // 应用在类似99+9中,会多出一个百位
        if (next1 != 0) {
            cur.next = new ListNode(next1);
        }
        // 这里为什么返回的是next
        // 因为如果不用next会多出一个0,resoult只是一个伪头
        return resoult.next;
    }
}

P3 无重复字符的最长子串

0、题目

在这里插入图片描述

1、滑动窗口算法

此算法是一种基于两种指针的算法,两个指针之间形成一个窗口

这个窗口可以是固定大小,也可以是动态变化的

什么情况可以用滑动窗口来解决实际问题呢?

  1. 一般给出的数据结构是数组或者字符串
  2. 求取某个子串或者子序列最长最短等最值问题或者求某个目标值时
  3. 该问题本身可以通过暴力求解
2、这道题目中此算法的使用

利用了set这个数据类型的特性

先使用right进行移动,查找元素时,

(1)如果发现set中没有此元素,把right对应的元素添加到set中,同时让长度len增加

(2)如果发现set中有此元素,说明重复了,移动left,对set中的元素进行逐个删除,直至set中查找不到此元素,同时让长度len减小

(具体查看这个视频)

https://www.bilibili.com/video/BV113411v7Ak/?spm_id_from=333.788&vd_source=d222e7454d13bc863f6b03624d77b447
3、代码
public static int lengthOfLongestSubstring(String s) {
    int right = 0;
    int left = 0;
    int maxLen = 0;
    int len = 0;
    Set set = new HashSet<>();
    while (right != s.length()) {
        if (!set.contains(s.charAt(right))) {
            set.add(s.charAt(right));
            right++;
            len = right - left;
            if (len > maxLen) {
                maxLen = len;
            }
        } else {
            set.remove(s.charAt(left));
            left++;
            len--;
        }
    }
    return maxLen;
}

P4 寻找两个正序数组的中位数

0、题目

在这里插入图片描述

1、为什么使用二分查询法

一般有类似时间复杂度为log的、有序数组,这类字眼,而且同时出现时,就是使用二分查询法

2、二分查询法讲解

使用low、mid、high

动图

3、此题解法1:将所有元素分为两部分

在这里插入图片描述

Partiton的计算:

根据partition1 + partition2 == (len(nums1)+len(nums2)+1)/2

这里需要满足x2 <= y6,x3 >= y5,然后对partition1逐步增加,直到满足x2 <= y6,x3 >= y5

在这里插入图片描述

注:边界条件: 若分割点取到了数组的首或尾时怎么办

​ 根据数组的有序性在数组两侧添加虚拟的-∞+∞.(不需要物理添加,只需要逻辑上添加)

在这里插入图片描述

代码
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    // 必须在较小的数组上尝试partition1的取值,因此对两个数组进行交换
    if (nums1.length > nums2.length) {
        return findMedianSortedArrays(nums2, nums1);
    }

    int len1 = nums1.length;
    int len2 = nums2.length;

    for (int partition1 = 0; partition1 <= len1; partition1++) {
        // partition1在数组nums1上滑动, 根据partition1求出partition2
        int partition2 = (len1 + len2 + 1) / 2 - partition1;

        // 分别求出两个分割位置左右的值
        int maxLeft1 = (partition1 == 0) ? Integer.MIN_VALUE : nums1[partition1 - 1];
        int minRight1 = (partition1 == len1) ? Integer.MAX_VALUE : nums1[partition1];

        int maxLeft2 = (partition2 == 0) ? Integer.MIN_VALUE : nums2[partition2 - 1];
        int minRight2 = (partition2 == len2) ? Integer.MAX_VALUE : nums2[partition2];

        // 若找到满足条件的分割方式,则求解中位数
        if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {
            if ((len1 + len2) % 2 == 0) {
                return ((double) Math.max(maxLeft1, maxLeft2)
                        + Math.min(minRight1, minRight2)) / 2;
            } else {
                return (double) Math.max(maxLeft1, maxLeft2);
            }
        }
    }

    throw new IllegalArgumentException();
}
4、此题解法2:二分查找法

在上一个解法的基础上,改变partition1的求取方式,借助数组的有序性,减少求取partition1的次数

在这里插入图片描述

代码
public double findMedianSortedArrays(int[] nums1, int[] nums2) {

    if (nums1.length > nums2.length) {
        return findMedianSortedArrays(nums2, nums1);
    }

    int len1 = nums1.length;
    int len2 = nums2.length;

    // 分别定义了partition1取值的上下限
    int low = 0;
    int high = nums1.length;

    while (low <= high) {

        int partition1 = (low + high) / 2;
        int partition2 = (len1 + len2 + 1) / 2 - partition1;

        int maxLeft1 = (partition1 == 0) ? Integer.MIN_VALUE : nums1[partition1 - 1];
        int minRight1 = (partition1 == len1) ? Integer.MAX_VALUE : nums1[partition1];

        int maxLeft2 = (partition2 == 0) ? Integer.MIN_VALUE : nums2[partition2 - 1];
        int minRight2 = (partition2 == len2) ? Integer.MAX_VALUE : nums2[partition2];

        if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {
            // 若找到符合条件的分割,则直接返回
            if ((len1 + len2) % 2 == 0) {
                return ((double) Math.max(maxLeft1, maxLeft2)
                        + Math.min(minRight1, minRight2)) / 2;
            } else {
                return (double) Math.max(maxLeft1, maxLeft2);
            }
        } else if (maxLeft1 > minRight2) {
            // partiton1太靠右了,将区间向左倾斜
            high = partition1 - 1;
        } else {
            // partition1太靠左了,将区间向右倾斜
            low = partition1 + 1;
        }
    }

    throw new IllegalArgumentException();
}
5、视频讲解
https://www.bilibili.com/video/BV1hk4y1B74M/?spm_id_from=333.337.search-card.all.click&vd_source=d222e7454d13bc863f6b03624d77b447

P5 最长回文子串

0、题目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ydMdkdHg-1680336290647)(C:\Users\卯末\AppData\Roaming\Typora\typora-user-images\image-20230303114227932.png)]

1、中心扩散算法

根据某个char的左右对等关系来运转,分为l和r,如果l==r,l可以继续向左一位,而r则是向右一位,直到左右不对等,然后开始下一个char

2、代码
public static String longestPalindrome(String s) {
    String result = "";
    String re = "";
    int l = 0;
    int r = 0;

    for (int i = 0; i < s.length(); i++) {
        // 奇数时
        r = i + 1;
        l = i - 1;
        while (r < s.length() && l >= 0 && s.charAt(l) == s.charAt(r)) {
            r++;
            l--;   
        }
        // 这里出来的l和r,分别是我们所需要字符串的左边界-1和有边界+1
        if (result.length() < r - l - 1) {
            result = s.substring(l + 1, r);
        }

        // 偶数时
        r = i + 1;
        l = i;
        while (r < s.length() && l >= 0 && s.charAt(l) == s.charAt(r)) {

            r++;
            l--;
        }
        if (result.length() < r - l - 1) {
            result = s.substring(l + 1, r);
        }
    }


    return result;
}
3、视频
https://www.bilibili.com/video/BV1UZ4y1U7tt/?spm_id_from=333.337.search-card.all.click&vd_source=d222e7454d13bc863f6b03624d77b447

P6 N字形变换

0、题目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sVnMVc9E-1680336290647)(C:\Users\卯末\AppData\Roaming\Typora\typora-user-images\image-20230304144659845.png)]

1、思路

利用一个新的数组,长度为numRows,遍历字符串s,如果i到numRows-1就开始减小,到达0就开始增加

在这里插入图片描述

2、代码
public static String convert(String s, int numRows) {
        int temp = 0; // 0向下,1向上
        int num = 0; // 上次增长位置
        String result = "";
        if (numRows == 1) {
            return s;
        }
        String[] strings = new String[numRows];
        for (int i = 0; i < s.length(); i++) {
            if (strings[num]==null){
                strings[num] = "";
            }

            strings[num] += s.charAt(i);
            if (num == numRows - 1) {
                temp = 1;
            } else if (num == 0) {
                temp = 0;
            }

            if (temp == 0) {
                num++;
            } else if (temp == 1) {
                num--;
            }


        }
        for (String i : strings) {
            if (i == null) {
                return result;
            }
             System.out.println(i);
            result += i;
        }
        return result;
    }
}

P7 整数反转 、P8 字符串转换整数 (atoi)

0、题目

1、int类型溢出问题

查看是否溢出,包括正负数

P7溢出代码
        while (x != 0) {
//            if ((result > 214748364 || result < -214748364)) {
//                return 0;
//            }
            if (((result > (Integer.MAX_VALUE - ge) / 10) && temp == true) 
                || ((result < (Integer.MIN_VALUE - ge) / 10) && temp == false)) {
                return 0;
            }
            ge = x % 10;
            x = x / 10;
            result = result * 10 + ge;
        }
P8溢出代码
while (i < s.length() && ((s.charAt(i) - '0') >= 0 && (s.charAt(i) - '0') <= 9)) {
	// 在加上下一个数之前,对reslut进行溢出验证
    // 用最大值减去此次想要加上的值,再除10,和reslut进行对比,如果大于就溢出了
    if (result > (Integer.MAX_VALUE - (s.charAt(i) - '0'))/10) {
        return temp ? Integer.MAX_VALUE : Integer.MIN_VALUE;
    }
    result = (s.charAt(i) - '0') + result * 10;
    i++;
}

P13 罗马数字转整数

0、题目

在这里插入图片描述

1、算法简述

使用map来存储字符以及他的对应值,只存储基本字符,然后判断当前字符和他之前一位的字符,他们所对应值的大小,之后进行操作

2、代码
/*
    I             1
    V             5
    X             10
    L             50
    C             100
    D             500
    M             1000
*/
public static int romanToInt(String s) {
    int result = 0;
    int last = 0; // 前一位的数字
    Map<String, Integer> map = new HashMap<>();
    map.put("I", 1);
    map.put("V", 5);
    map.put("X", 10);
    map.put("L", 50);
    map.put("C", 100);
    map.put("D", 500);
    map.put("M", 1000);
    for (int i = 0; i < s.length(); i++) {
        Integer integer = map.get(s.charAt(i) + "");
        result += integer;
        if (integer > last) { // 例如IV,4
            result -= last * 2;
        }
        last = integer;
    }
    return result;
}

P15 三数之和、P16 最接近的三数之和

0、题目

在这里插入图片描述

1、三数之和的算法思路

三数之和需要先把数组排序(Arrays.sort(nums)),使用for从0开始遍历,之后创建两个指针,分别是left和

right,分别从开头0和结尾length-1开始算起,把nums[i/left/right]加起来,比较和目标大小,根据大了还是小了

来移动两个指针

2、代码
public static int threeSumClosest(int[] nums, int target) {
    int result = nums[0] + nums[1] + nums[2];
    int temp;
    // 排序
    Arrays.sort(nums);
    for (int i = 0; i < nums.length; i++) {
        int left = i + 1;
        int right = nums.length - 1;
        while (left < right) {
            temp = nums[i] + nums[left] + nums[right];
            if (Math.abs(target - temp) < Math.abs(target - result)) {
                result = temp;
            } else if (temp - target > 0) {
                right--;
            } else if (temp - target < 0) {
                left++;
            } else {
                return result;
            }
        }
    }

    return result;
}

P20 有效的括号

0、题目

在这里插入图片描述

1、思路

使用map来存储两个括号之间的对应关系,然后使用数组来存储,有对应的就消除前括号,没有就继续存储

在这里插入图片描述

2、代码
public static boolean isValid(String s) {
    HashMap<Character, Character> map = new HashMap<>();
    map.put('(', ')');
    map.put('[', ']');
    map.put('{', '}');

    if (s.length() % 2 == 1) {
        return false;
    }
    char[] chars = new char[s.length()];
    int temp = 0;
    for (int i = 0; i < s.length(); i++) {
        if (map.containsKey(s.charAt(i))) {
            chars[temp] = s.charAt(i);
            temp++;
            continue;
        }
        if (temp == 0){
            return false;
        }
        if (map.get(chars[temp - 1]) == s.charAt(i)) {
            chars[temp-1] = '0';
            temp--;
        } else {
            return false;
        }
    }

    return (chars[1] == '0');
}

P22 括号生成

0、题目

在这里插入图片描述

1、思路

使用递归和剪枝进行

image.png

当左括号 < 右括号时,return

当左括号数目 > n时,return

左括号必须比右括号先加入

当左括号 == 右括号 == n时,把当前结合出来的字符串加到list里,return

2、代码
public List<String> list = new ArrayList<>();
int n;

public List<String> generateParenthesis(int n) {
    this.n = n;
    String str = "";
    curr(str, 0, 0);
    return list;
}

public void curr(String str, int left, int right) {
    if (right == n && left == n) {
        list.add(str);
        return;
    }

    if (left < right || left > n) {
        return;
    }

    curr(str + "(", left + 1, right);
    curr(str + ")", left, right + 1);
    return;
}

当时的疑问:这样左括号不会一直先加入吗,直到左括号加完?

解答:这里的两个curr是两个不相关的递归,这里的str不是相同的,先加入的“(”执行到最后也会return,只是把符合要求的加到list里

P28 找出字符串中第一个匹配项的下标

0、题目

在这里插入图片描述

1、KMP算法(重点)

在这里插入图片描述

视频:

https://www.bilibili.com/video/BV1AY4y157yL/?spm_id_from=333.880.my_history.page.click&vd_source=d222e7454d13bc863f6b03624d77b447

图解

https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/solution/duo-tu-yu-jing-xiang-jie-kmp-suan-fa-by-w3c9c/

使用next数组来辅助计算,两个指针,i指针会一直往右走,而j指针会向左和向右

当i发现两个字符串对应不上时,用当前j指针向左走,走当前指针所对应的next数组值

next数组计算:用下边字符串的前后缀进行计算,如果找到一样的时候,next数组的值加1,

​ 如果找不到时,会一直减1直至0,然后从0开始重新计算

2、代码
next数组代码
public static int[] getNext(String needle) {
    int[] next = new int[needle.length()];
    for (int right = 1, left = 0; right < needle.length(); right++) {
	// 定义好两个指针right与left
	// 在for循环中初始化指针right为1,left=0,开始计算next数组,right始终在left指针的后面
        while (left > 0 && needle.charAt(left) != needle.charAt(right)) {
		// 如果不相等就让left指针回退,到0时就停止回退
            left = next[left - 1];//进行回退操作;
        }
        if (needle.charAt(left) == needle.charAt(right)) {
            left++;
        }
        next[right] = left;
	// 这是从 1 开始的
    }
    return next;
// 循环结束的时候,next数组就已经计算完毕了
}
对比代码
public static int strStr(String haystack, String needle) {
    int[] next = getNext(needle);
    for (int i = 0, j = 0; i < haystack.length(); i++) {

        while (j > 0 && haystack.charAt(i) != needle.charAt(j)) {
            j = next[j - 1];
        }
        if (haystack.charAt(i) == needle.charAt(j)) {
            j++;
        }
        if (j == needle.length()) return i - needle.length() + 1;
    }
    return -1;
}

P29 两数相除

0、题目

在这里插入图片描述

1、算法思路

如果是一个一个加上去的话,算法会很慢,因为量太多了

我们利用2的n次方来计算,比如divisor = 3:3—》6—》12—》24—》48这样来跳着计算

而且n是从最大值32开始逐步向下减少,一旦发现dividend>>i 大于 divisor就开始改变dividend

2、代码
public static int divide(int dividend, int divisor) {
    if (dividend == 0) return 0;
    if (dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE;

    int flag = dividend ^ divisor; // 如果两个符号不相同,flag是负数,相同则为正数或是0(两者相同的情况下)
    System.out.println(flag);
    long new_dividend = Math.abs((long) dividend);
    long new_divisor = Math.abs((long) divisor);
    int result = 0;

    // >>i相当于除以2的i次方
    for (int i = 32; i >= 0; i--) {
        if (new_dividend >> i >= new_divisor) {
            new_dividend -= new_divisor << i;
            result += 1 << i;
        }
    }

    return flag < 0 ? -result : result;

}
3、视频
https://www.bilibili.com/video/BV1Su411Z71w/?spm_id_from=333.337.search-card.all.click&vd_source=d222e7454d13bc863f6b03624d77b447

P34 在排序数组中查找元素的第一个和最后一个位置

0、题目

在这里插入图片描述

1、思路

这里看到时间复杂度为logN级就应该想到要使用二分查找法,但我们普通的二分查找法需要有序且不重复的序列,这里要对所使用的二分查找法进行修改

先是左边界,再找出右边界

因为普通的/法,比如3/2=1,那么可以应用到查找左边界,同理(3+1)/2=2就可以应用到右边界

左边界需要先定义r,右边界则是l

2、代码
public static int[] searchRange(int[] nums, int target) {
    if (nums.length == 0) {
        return new int[]{-1, -1};
    }
    int l = 0, r = nums.length - 1;
    int mid = 0;
    // 求出左边界
    while (l < r) {
        mid = (l + r) >> 1;
        if (target <= nums[r]) r = mid;
        else l = mid + 1;
    }
    int L = l;
    if (nums[r] != target) return new int[]{-1, -1};
    l = 0;
    r = nums.length - 1;
    // 求出右边界
    while (l < r) {
        mid = (l + r + 1) >> 1;
        if (nums[l] <= target) l = mid;
        else r = mid - 1;
    }
    return new int[]{L, r};
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值