4.10·Java进阶内容

 增强for循环

forEach遍历方法

lambda表达式

import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
import java.util.function.Consumer;

public class test{
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();
        Random r = new Random();
        for(int i = 0 ; i < 10 ; i++){
            // 将int转String
            coll.add(Integer.toString(r.nextInt(10)));
        }

        // 增强for循环
        for (String coll2 : coll) {
            System.out.print(coll2 + " ");
        }
        System.out.println();

        coll.forEach(new Consumer<String>() {

            @Override
            public void accept(String t) {
                System.out.print(t+ " ");
            }
            
        });

        System.out.println();
        
        coll.forEach(t -> System.out.print(t+ " "));

        System.out.println();
    }
}

补充

总结Collection 

ArrayList、HashMap、LinkedList、HashSet

import java.util.*;

public class CollectionExample {
    public static void main(String[] args) {
        // 创建ArrayList,可以存储String类型的数据
        List<String> arrayList = new ArrayList<>();

        // 向ArrayList中添加元素
        arrayList.add("Hello");
        arrayList.add("World");
        arrayList.add("!");

        // 遍历ArrayList中的元素
        for (String element : arrayList) {
            System.out.println(element);
        }

        // 创建HashMap,存储String类型的键和值
        Map<String, String> hashMap = new HashMap<>();

        // 向HashMap中添加键值对
        hashMap.put("name", "John");
        hashMap.put("age", "30");
        hashMap.put("address", "New York");

        // 获取HashMap中的值
        String name = hashMap.get("name");
        String age = hashMap.get("age");
        String address = hashMap.get("address");

        // 打印HashMap中的值
        System.out.println("Name: " + name);
        System.out.println("Age: " + age);
        System.out.println("Address: " + address);
    }
}


import java.util.LinkedList;

public class LinkedListExample {
    public static void main(String[] args) {
        // 创建LinkedList,可以存储任意类型的数据
        LinkedList<String> linkedList = new LinkedList<>();

        // 插入元素到链表尾部
        linkedList.add("A");
        linkedList.add("B");
        linkedList.add("C");

        // 插入元素到链表头部
        linkedList.addFirst("D");

        // 插入元素到指定位置
        linkedList.add(2, "E");

        // 遍历LinkedList中的元素
        for (String element : linkedList) {
            System.out.println(element);
        }
    }
}



import java.util.HashSet;
import java.util.Set;

public class HashSetExample {
    public static void main(String[] args) {
        // 创建HashSet,可以存储任意类型的数据
        Set<String> hashSet = new HashSet<>();

        // 插入元素到哈希表中
        hashSet.add("A");
        hashSet.add("B");
        hashSet.add("C");
        hashSet.add("D");

        // 检查元素是否在哈希表中
        System.out.println(hashSet.contains("A"));
        System.out.println(hashSet.contains("E"));

        // 遍历HashSet中的元素
        for (String element : hashSet) {
            System.out.println(element);
        }
    }
}


import java.util.TreeSet;

public class TreeSetExample {
    public static void main(String[] args) {
        // 创建TreeSet,可以存储任意类型的数据,但需要实现Comparable接口
        TreeSet<String> treeSet = new TreeSet<>();

        // 插入元素到树集中
        treeSet.add("A");
        treeSet.add("B");
        treeSet.add("C");
        treeSet.add("D");

        // 获取最小和最大元素
        System.out.println(treeSet.first());
        System.out.println(treeSet.last());

        // 获取小于或等于指定元素的最大元素
        System.out.println(treeSet.floor("C"));

        // 遍历TreeSet中的元素
        for (String element : treeSet) {
            System.out.println(element);
        }
    }
}

import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapExample {
    public static void main(String[] args) {
        // 创建LinkedHashMap,存储String类型的键和值
        Map<String, String> linkedHashMap = new LinkedHashMap<>();

        // 插入键值对到哈希表中
        linkedHashMap.put("A", "1");
        linkedHashMap.put("B", "2");
        linkedHashMap.put("C", "3");

        // 修改键值对的值
        linkedHashMap.put("B", "4");

        // 删除键值对
        linkedHashMap.remove("C");

        // 遍历LinkedHashMap中的键值对
        for (Map.Entry<String, String> entry : linkedHashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}
  1. ArrayList(动态数组)
  2. LinkedList(双向链表)
  3. HashSet(不重复元素的无序集合)
  4. HashMap(键值对的无序集合)
  • 优点:LinkedList是一种链式结构的集合,它可以高效地进行插入和删除操作,因为不需要移动大量元素,只需要改变相邻元素之间的链接关系。此外,LinkedList还可以用作双向列表,因为它有一个prev指针和next指针来维护前后元素的链接关系。
  • 使用场景:当需要频繁进行插入和删除操作,且不需要随机访问元素时,可以使用LinkedList。

  • 优点:HashSet是一种哈希表结构的集合,它可以高效地进行插入、删除和查找操作,因为它使用对象的散列码作为索引,可以快速定位到对应的元素。此外,HashSet还可以去重,因为它不允许存在相同的元素。
  • 使用场景:当需要高效地进行插入、删除和查找操作,并且不需要维护元素间的顺序或排序时,可以使用HashSet。

  • 优点:TreeSet是一种红黑树结构的集合,它可以自动维护元素的顺序,并且支持按照自然顺序或指定Comparator进行排序。此外,TreeSet还可以高效地进行查找操作,因为它使用二分搜索算法来定位元素。
  • 使用场景:当需要对元素进行排序,或者按照某种顺序访问元素时,可以使用TreeSet。

算法题

用Java写出算法题:查找字符串中最长连续不重复的子串。要带上详细的注释,多生成几个方案

方案一:
```java
import java.util.HashSet;

public class LongestNonRepeatSubstring {
    public static void main(String[] args) {
        String input = "abcabcbb";
        String longestSubstr = findLongestSubstr(input);
        System.out.println(longestSubstr);
    }

    private static String findLongestSubstr(String input) {
        int maxLength = 0; // 最长子串的长度
        int start = 0; // 最长子串的起始位置
        int end = 0; // 最长子串的结束位置
        int currStart = 0; // 当前子串的起始位置
        HashSet<Character> set = new HashSet<>(); // 存储当前子串中出现的字符

        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (!set.contains(c)) { // 当前字符不在set中
                set.add(c); // 将当前字符加入set
                if (i - currStart + 1 > maxLength) { // 如果当前子串比最长子串还长
                    maxLength = i - currStart + 1;
                    start = currStart;
                    end = i;
                }
            } else { // 当前字符在set中
                while (input.charAt(currStart) != c) { // 将当前字符之前的所有字符从set中移除
                    set.remove(input.charAt(currStart));
                    currStart++;
                }
                currStart++; // 跳过当前字符
            }
        }

        return input.substring(start, end + 1); // 返回最长子串
    }
}
```

方案二:
```java
import java.util.HashMap;

public class LongestNonRepeatSubstring {
    public static void main(String[] args) {
        String input = "abcabcbb";
        String longestSubstr = findLongestSubstr(input);
        System.out.println(longestSubstr);
    }

    private static String findLongestSubstr(String input) {
        int maxLength = 0; // 最长子串的长度
        int start = 0; // 最长子串的起始位置
        int end = 0; // 最长子串的结束位置
        int currStart = 0; // 当前子串的起始位置
        HashMap<Character, Integer> map = new HashMap<>(); // 存储当前子串中出现的字符及其下标

        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (!map.containsKey(c)) { // 当前字符不在map中
                map.put(c, i); // 将当前字符和下标加入map
                if (i - currStart + 1 > maxLength) { // 如果当前子串比最长子串还长
                    maxLength = i - currStart + 1;
                    start = currStart;
                    end = i;
                }
            } else { // 当前字符在map中
                int lastIndex = map.get(c); // 获取当前字符最近一次出现的下标
                while (currStart <= lastIndex) { // 将当前字符之前的所有字符从map中移除
                    map.remove(input.charAt(currStart));
                    currStart++;
                }
                map.put(c, i); // 将当前字符和下标加入map
                currStart = lastIndex + 1; // 跳过当前字符
            }
        }

        return input.substring(start, end + 1); // 返回最长子串
    }
}
```

方案三:
```java
public class LongestNonRepeatSubstring {
    public static void main(String[] args) {
        String input = "abcabcbb";
        String longestSubstr = findLongestSubstr(input);
        System.out.println(longestSubstr);
    }

    private static String findLongestSubstr(String input) {
        int maxLength = 0; // 最长子串的长度
        int start = 0; // 最长子串的起始位置
        int end = 0; // 最长子串的结束位置
        int currStart = 0; // 当前子串的起始位置

        for (int i = 0, j = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            while (j < i && input.charAt(j) == c) { // 当前字符与当前子串中已有的字符重复
                j++; // 跳过当前子串中已有的字符
            }
            if (i - j + 1 > maxLength) { // 如果当前子串比最长子串还长
                maxLength = i - j + 1;
                start = j;
                end = i;
            }
        }

        return input.substring(start, end + 1); // 返回最长子串
    }
}
```
好的,下面我将分别对方案一、方案二和方案三的具体思路进行讲解。

#### 方案一

方案一采用暴力枚举的方法来解决这个问题。具体流程如下:

1. 创建一个变量`maxLength`用于记录最长子串的长度,创建两个变量`startIndex`和`endIndex`分别用于记录最长子串的起始位置和结束位置。
2. 使用双重循环遍历原字符串,外层循环从头开始遍历,内层循环从外层循环指向的位置开始遍历。
3. 在每次遍历时,使用Set集合来存储当前的子串中包含的字符。如果当前字符不在Set集合中,则将其加入Set集合,并继续下一轮遍历;如果当前字符已经在Set集合中出现过,说明以当前字符为结尾的子串中有重复字符,则停止当前轮遍历。
4. 记录当前子串的长度,如果大于最长子串的长度,则更新`maxLength`、`startIndex`和`endIndex`为当前子串的长度、起始位置和结束位置。
5. 当所有子串都枚举完成后,返回最长子串。

可以看到,这个算法的时间复杂度是$O(n^3)$(因为需要三重循环),所以当字符串比较长的时候会非常慢。

#### 方案二

方案二采用滑动窗口的方法来解决这个问题。一般情况下,使用滑动窗口可以使得算法的时间复杂度从$O(n^2)$降低到$O(n)$。具体流程如下:

1. 创建三个变量,`startIndex`、`endIndex`和`maxLength`,分别用于记录最长子串的起始位置、结束位置和长度。创建一个HashMap,用于存储字符与它们的出现位置之间的映射。
2. 使用两个指针`start`和`end`分别指向滑动窗口的左右边界,开始向右移动。
3. 如果当前字符不在HashMap中,将其加入,并更新`end`指针的位置;如果当前字符已经在HashMap中出现过,则需要将滑动窗口的左边界移动到上次出现该字符的位置之后,并更新该字符的位置信息。
4. 在每次遍历时,记录当前滑动窗口的长度,如果大于最长子串的长度,则更新`maxLength`、`startIndex`和`endIndex`为当前子串的长度、起始位置和结束位置。
5. 当所有字符都遍历完成后,返回最长子串。

可以看到,这个算法的时间复杂度是$O(n)$,因为只需要遍历一遍字符串,而滑动窗口的大小也只会增加不会减少,所以最坏情况下的时间复杂度也是$O(n)$。相比方案一来说,这个算法速度要快得多。

#### 方案三

方案三也采用滑动窗口的方法来解决这个问题,跟方案二有些类似。不同之处在于,在方案三中使用一个数组来存储每个字符上次出现的位置,而不是使用HashMap。具体流程如下:

1. 创建三个变量,`startIndex`、`endIndex`和`maxLength`,分别用于记录最长子串的起始位置、结束位置和长度。创建一个大小为128的整型数组`lastIndex`,初始化为-1,用于存储每个字符上次出现的位置。
2. 使用两个指针`i`和`j`分别指向滑动窗口的左右边界,开始向右移动。同时,遍历字符串中当前滑动窗口中包含的所有字符,并更新它们最后一次出现的位置。
3. 如果当前字符上次出现的位置在滑动窗口内,则将左指针移动到上次出现位置的下一个字符所在的位置;如果当前字符上次出现的位置在滑动窗口外(即小于左指针的位置),则不需要改变左指针的位置。
4. 在每次遍历时,记录当前滑动窗口的长度,如果大于最长子串的长度,则更新`maxLength`、`startIndex`和`endIndex`为当前子串的长度、起始位置和结束位置。
5. 当所有字符都遍历完成后,返回最长子串。



这里是第三种方案的另一种写法:
```java
public class LongestNonRepeatSubstring {
    public static void main(String[] args) {
        String input = "abcabcbb";
        String longestSubstr = findLongestSubstr(input);
        System.out.println(longestSubstr);
    }

    private static String findLongestSubstr(String input) {
        int maxLength = 0; // 最长子串的长度
        int start = 0; // 最长子串的起始位置
        int end = 0; // 最长子串的结束位置
        int[] lastIndex = new int[128]; // 存储上一个出现该字符的下标

        for (int i = 0, j = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            int index = lastIndex[c]; // 获取上一个出现该字符的下标
            if (j <= index && index <= i) { // 当前字符在当前子串中已经出现过
                j = index + 1; // 将窗口左边界移到当前字符之后
            }
            if (i - j + 1 > maxLength) { // 如果当前子串比最长子串还长
                maxLength = i - j + 1;
                start = j;
                end = i;
            }
            lastIndex[c] = i; // 更新上一个出现该字符的下标
        }

        return input.substring(start, end + 1); // 返回最长子串
    }
}
```
这个方案与方案三大致相同,只是使用了一个数组来存储每个字符上一次出现的位置。每次遇到一个新字符时,先获取这个字符上一次出现的位置,然后判断这个位置是否在窗口内,如果是则将窗口左边界移到这个位置之后。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值